【開発Tips】UiPath Document Understanding の分類編

# はじめに

- 本記事では、DUのオリジナル分類器の実装サンプルを紹介します。
- 記事の内容は、個人の見解または確認結果であり、UiPath の公式見解ではありません。
- 製品仕様や参考画像は 23.10 バージョンのもので構成しています。

# オリジナル分類器の実装サンプル
## 💡なぜ、オリジナル分類器をつくろうとおもったのか？

UiPath Document Understanding では、次の分類器が用意されています↓↓

◆1. キーワード分類器（無料）

https://qiita.com/tomohiroArai/items/07a39515068c7b6e492b

◆2. インテリジェントキーワード分類器（コスト：極小）

https://qiita.com/Kajiita/items/0a6f48d851b9f76c7e3b

◆3. マシンラーニング分類器（コスト：小）

https://docs.uipath.com/ja/activities/other/latest/document-understanding/machine-learning-classifier

いずれも、キーワードを手動で設定するか、学習用のドキュメントを読み込ませるだけで利用可能（※）なので、設定作業のハードルは低い。
※3番のマシンラーニング分類器はトレーニングとデプロイが別途必要。
ただ、1番のキーワード分類をのぞき、***判定ロジックがブラックボックスで不明***なため、期待する精度が得られなかった際の***チューニングが難しい***ケースが出てきます。

たとえば、2番と3番はドキュメントを読み込み、単語ベクトルなるものを学習させることで分類精度を上げる訳ですが、決算帳票のサンプルでいえば***出現位置の早い***「決算短信」などのキーワードのスコアがものすごく高くなり、その他の帳票固有のキーワードが活きないケースが出てきます。
次の決算書でいえば『連結財務諸表』というキーワードで判定できるとよい訳ですが、『財務諸表』を別のドキュメントで管理している場合、財務諸表に分類される確率が高くなります。

（例）某自動車メーカー様の決算短信
![オリジナルキーワード分類0.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/fa4aa1de-ba58-348c-6e60-980c402dab14.jpeg)

この場合、本来は ***『連結財務諸表』というキーワードがきた際は、必ず連結財務諸表に分類する***、または ***『連結』というキーワードを含む場合は単体の財務諸表では”ない”*** と判定できるような***重みづけ***ができればよいのです。

## キーワード分類では実現できないのか？

***1つ2つのキーワードで判定できる場合は有効です。***
ただ、上の例の様に***同じ様なキーワードを多く含むドキュメント***を分類するのは難しいでしょう。

::: note
キーワード分類の ***比較仕様が、”完全一致”*** のため、PDF内のテキストデータの成り立ちやOCRの返却文字列の影響も強く受けてしまいます。
（例）
キーワード分類器に「テスト」を設定
読み取った文字列：テスト　→　分類される
PDFから読み取った文字列：***テスト実行　→　分類されない***
OCRから読み取った文字列：***テ ス ト →　分類されない***
:::

## 実装サンプル（作り方）

タクソノミーマネジャーを起動し、ドキュメント種類IDを調べます。
値は「ドキュメントの種類名」の隣のボタンからコピーできます。

![オリジナルキーワード分類6.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/79d2d492-7709-1320-81d1-2cb44bc23cbf.jpeg)

設定ファイルを用意します。
CSV形式で、ヘッダーは「DocumentId」「Value」「Score」とします。
先ほどコピーしたドキュメント種類IDを「DocumentId」列に、キーワードの値と重みを「Value」「Score」に指定します。

（例）
![オリジナルキーワード分類7.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/0c9ef6c4-35d3-973d-0f07-fba71cc31f72.jpeg)

::: note
💡ポイント
Score列はDouble型として、マイナスを許容するようにします。
これにより、NGワードの判定を実現します。
:::

作成したCSVを読み込む

![オリジナルキーワード分類8.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/38e09b24-02c2-3266-a364-c1fcd1169096.jpeg)

入力ドキュメントに含まれていたキーワードを保管するための判定結果テーブルを構築

![オリジナルキーワード分類9.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/8e6fe852-62a1-5a52-5398-9c60776a9388.jpeg)

キーワードが一致したら判定結果テーブルに行追加
（条件式：ドキュメントテキスト.Contains(CurrentRow.item(1).ToString)）

![オリジナルキーワード分類10-1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/b9ec5fa0-02ba-ae54-181e-26c176c7ae99.jpeg)
![オリジナルキーワード分類10-2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/6a028e93-7adf-1cc9-2572-42e0abdb00e0.jpeg)

判定結果（ドキュメント種類IDとスコア）をログ
```
判定結果を格納するテーブル.AsEnumerable().GroupBy(Function(row) row.Field(Of String)("DocumentId")).Select(Function(Group) New With {.DocumentId = Group.Key, .TotalScore = Group.Sum(Function(row) row.Field(Of Double)("Score"))}).OrderByDescending(Function(x) x.TotalScore).First().ToString
```

![オリジナルキーワード分類11.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/ef81a9b6-0205-5284-b4ee-e0580fd5c92d.jpeg)

（出力例）
![オリジナルキーワード分類13.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/e9b52d06-5085-dabd-90cb-f37a0e619afc.jpeg)

判定結果（ドキュメント種類ID）の返却
```
判定結果を格納するテーブル.AsEnumerable().GroupBy(Function(row) row.Field(Of String)("DocumentId")).Select(Function(Group) New With {.Key = Group.Key, .TotalScore = Group.Sum(Function(row) row.Field(Of Double)("Score"))}).OrderByDescending(Function(x) x.TotalScore).First().Key
```
![オリジナルキーワード分類12.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/7e89292e-6ee0-7663-d840-f9d14cb4e7a7.jpeg)

# さいごに

いかがでしたでしょうか。
インテリジェントキーワード分類器で期待結果が得られなかった際は、「学習を管理」から単語ベクトルのデータを取得し、ご紹介したオリジナル分類器を作成・試してみるのをお勧めします。
（インテリジェントキーワード分類器で単語ベクトルを手動編集することはできません）

![オリジナルキーワード分類14.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/0a1c868b-63d5-6746-59d6-2e6f8d3948b5.jpeg)

最後までお読みいただきありがとうございます(・ω・)ノ