AI-102 HandsOn Azure AI Document Intelligenceを勉強する

AI-102の試験のため、以下のGitHubのHandsOnの勉強を実施します。

https://github.com/MicrosoftLearning/mslearn-ai-services
https://github.com/MicrosoftLearning/mslearn-ai-vision
https://github.com/MicrosoftLearning/mslearn-ai-language
[https://github.com/MicrosoftLearning/mslearn-ai-document-intelligence　←　本記事はこれ](https://github.com/MicrosoftLearning/mslearn-ai-document-intelligence)
https://github.com/MicrosoftLearning/mslearn-knowledge-mining
https://github.com/MicrosoftLearning/mslearn-openai

MSLearnのドキュメントはこちら。
　
[Azure AI Document Intelligence ソリューションを計画する](https://learn.microsoft.com/ja-jp/training/modules/plan-form-recognizer-solution/)

# AI Visionとの違い
Azure AI は画像分析も提供している、文字を抜き出すだけの単純なものならOCRの方がよい。
> テキストを抽出するだけで、コンテキスト情報が必要ない場合は、Azure AI Vision OCR サービスを検討するのが適切です。 
> Azure AI Document Intelligence には、より洗練されたドキュメント分析が含まれています。 たとえば、キーと値のペア、テーブル、コンテキスト固有のフィールドを識別できます。

# モデルの種類
モデルには種類がある。

- ドキュメントの分析を抜き出したい　→　Document Analysis
- よくある形式のデータから情報をキーバリュー形式で抜き出したい　→　事前構築済みモデル
- 固有のフォーマットのファイルから情報を抜き出したい　→　カスタムモデル

## Document analysis
OCR機能を利用。

### Read
一番単純、ドキュメントから単語と行を抜き出す。 

### Layout ... 構造化されたデータ
フォームからテキスト、テーブル、表等の情報を抽出する。 

### General Documents
一般的なドキュメントからキーバリューを抜き出す。

## 事前構築済みモデル
- 請求書
- 領収書
- 名刺
等に対して以下を実施。
　

- テキストの抽出
- キーと値のペア抽出
- エンティティ、種類としては、人、場所、日付など
- 選択マーク、 ラジオ ボタンやチェック ボックスなど
- テーブル、フォームのテーブルを抽出
- フィールド

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/e83d7d5e-c4e4-bb4a-849a-538b998620a7.png)

## カスタムモデル
固有のフォームに対しての分析、抽出。
フォームの書式が若干揺れがある場合、複数のパターンから自動的に判別させるようなことも可能。
> 可能性のあるすべての入力の例がトレーニング フォームに含まれるようにします。たとえば、手書き入力と印刷入力の両方を想定している場合は、両方をトレーニングに含めます。
    
### カスタム テンプレート モデル。 
比較的単純な一貫性のあるテンプレートに沿ったドキュメントの分析。

### カスタム ニューラル モデル。
構造が定義されていないドキュメントの場合ニューラルモデルを利用する。

## 構成済みモデル
複数のモデルのを1つのEndpointにまとめるような機能。
> 複数のカスタム モデルで構成されるものです。... ユーザーが構成済みモデルにフォームを送信すると、Document Intelligence はそれを自動的に分類し、その分析においてどのカスタム モデルを使用するべきかを決定します。

# HandsOn
# 事前構築済みモデルを使う
## Read
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/c6ad115e-9272-75b1-b09a-5b51a80d707e.png)

Document Intelligenct Dashboardを開くと以下の様にドキュメントをドラッグドロップして分析できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/b95a8a5d-aa03-122d-fef6-a7e9b15dd51b.png)

Jsonはこんな感じで返ってくる。
analyzeResult.contentに抽出された文章が入る。パラグラフごとの抽出とかも可能ぽい。

すごいのはpages > wordsの中に一つ一つの単語に対して画像の位置をpolygonとしてもっていること。これのせいでデータ量が半端なく多くなっている。
```
{
	"status": "succeeded",
	"createdDateTime": "2024-04-17T04:40:10Z",
	"lastUpdatedDateTime": "2024-04-17T04:40:12Z",
	"analyzeResult": {
		"apiVersion": "2023-07-31",
		"modelId": "prebuilt-read",
		"stringIndexType": "utf16CodeUnit",
    　  "content": "ここにデータ本文が入る",
		"pages": [
			{
				"pageNumber": 1,
				"angle": 0,
				"width": 458,
				"height": 595,
				"unit": "pixel",
				"words": [
					{
						"content": "While",
						"polygon": [
							129,
							27,
							153,
							27,
							153,
							39,
							129,
							39
						],
						"confidence": 0.98,
						"span": {
							"offset": 0,
							"length": 5
						}
					},
                    // これが延々と続く
		],
				"spans": [
					{
						"offset": 0,
						"length": 4682
					}
				]
			}
		],
		"styles": [
			{
				"confidence": 0.1,
				"spans": [
					{
						"offset": 4588,
						"length": 28
					}
				],
				"isHandwritten": true
			}
		]
	}
}
```

# カスタムモデル
いわゆる教師あり機械学習。
トレーニング用サンプルは最低限5,6個必要。Storage Accountにアップし、読み込ませる。

## トレーニング
スタジオからCustom Extraction Modelを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/bb201f4f-ddef-37cd-e14b-fefd82801986.png)

以降はStorage Accountにjpgファイルのトレーニングデータを配置している前提。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/8f4e3bed-0603-4b98-65a8-c53ef7d7ba26.png)

Storage Accountをひもづける。（この辺キーとか何もないけど裏の認証どうなっているんだろう？）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/7578ca58-faec-d641-757f-4121ea07e4d1.png)

フィールドを追加して、手動でマッピングしていく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/624c298a-c6a2-8baf-2eae-828d13064394.png)

表やサイン、チェックボックスをラベリングする専用の機能もある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/1bdcbe3a-0395-47f2-2b3b-f74470bc0d05.png)

表形式は固定、動的を選べる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/020c3c94-fdc6-310f-1640-522efd91ece6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/4b7da0b1-8634-95c5-6f25-e8e5139b51d0.png)

ある程度一般的な形式であれば自動的にラベリングする機能もある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/56d6c86e-80c5-ebe0-d40b-ae9f21219751.png)

トレーニングはTemplateとNeuralを選べる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/847f5a24-b50f-98e7-70b5-6b8c94798ccc.png)

トレーニングが完了するとStorage Accountにトレーニングするために必要な情報がファイルとして出力されている。（ちなみにこれらのファイルを別環境に持っていき、再度トレーニングをするだけで、環境を再現できるのでラベリング作業を繰り返す手間が省ける。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/e8a927a9-02bb-4b2e-eac3-c1fc48f812c2.png)

この画像のLayoutとかFieldとかがStorage Accountに保存される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/3e62d013-a3f2-76aa-bbbc-023c22773479.png)


- fields.json →　ラベル情報（これは全体で1つ）
```
{
    "fields": [
        {
            "fieldKey": "Merchant",
            "fieldType": "string",
            "fieldFormat": "not-specified"
        },
        {
            "fieldKey": "PhoneNumber",
            "fieldType": "string",
            "fieldFormat": "not-specified"
        },
```

- {ファイル名}.jpg.labels.json　→　手動でラベリングした結果（座標とか）が入っている
```
{
    "document": "Form_1.jpg",
    "labels": [
        {
            "label": "Merchant",
            "key": null,
            "value": [
                {
                    "page": 1,
                    "text": "Hero",
                    "boundingBoxes": [
                        [
                            0.3658823529411765,
                            0.09409090909090909,
                            0.46352941176470586,
                            0.09272727272727273,
                            0.46294117647058824,
                            0.12090909090909091,
                            0.3652941176470588,
                            0.12090909090909091
                        ]
                    ]
                },
```

- Form_1.jpg.ocr.json　→　実際にトレーニングで読み取れた値かな？

## モデルの確認
トレーニングが完了するとモデルができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/e9c8af97-ae47-f4e0-d2cb-15b4324f63de.png)

## テスト
Responseは通常のDocument Analysisと似ているが、analyzeResultにModelIDやConfidenceが入るようになる。
```
{
	"status": "succeeded",
	"createdDateTime": "2023-10-18T23:39:50Z",
	"lastUpdatedDateTime": "2023-10-18T23:39:54Z",
	"analyzeResult": {
		"apiVersion": "2022-08-31",
		"modelId": "DocIntelModel",
		"stringIndexType": "utf16CodeUnit",
		"content": "本文",
		"pages": [
			{
				"pageNumber": 1,
				"angle": 0,
				"width": 1159,
				"height": 1486,
				"unit": "pixel",
				"words": [
					{
						"content": "Purchase",
						"polygon": [
							89,
							90,
							174,
							91,
							174,
							112,
							88,
							112
						],
						"confidence": 0.996,
						"span": {
							"offset": 0,
							"length": 8
						}
					},
                    ...
```

# 複合モデル
例えばある調査でフォームの形式が複数から選べて、読み取りにカスタムモデルを複数作った場合、どのモデルが最適かを自動的に選択させることができる。

通常はどのモデルを利用するかを以下の様にModelIDで明示的に定義する。
```
string modelId = "<modelId>";
Uri fileUri = new Uri("<fileUri>");
AnalyzeDocumentOperation operation = 
    await client.StartAnalyzeDocumentFromUriAsync(modelId, fileUri);
```

複合モデルを作ってみる。
GUIからやる場合はもう一つ同じようなProjectを作成する。
モデルを2つ選択してComposeをクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/4277e046-0dae-8fa6-4e44-837adc4aa46b.png)

たったこれで普通に複合モデルができた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/7ce05ddf-754d-05ca-7943-419dc9b4b85d.png)

ちゃんと調べられていないが、このやりかただと、Taxや保険証等のPredefined Modelは複合モデルに入れることはできないみたい？

構成済みモデルはコードで開発することもできる。
```
// モデルのリストを渡す
List<string> modelIds = new List<string>()
{
    firstCustomModel.ModelId,
    secondCustomModel.ModelId,
    thirdCustomModel.ModelId,
};

BuildModelOperation operation = await client.StartCreateComposedModelAsync(modelIds, modelDescription: "Composed model example");
Response<DocumentModel> operationResponse = await operation.WaitForCompletionAsync();
```

構成済みモデル専用のモデルIDができるので、普通のモデルを呼ぶのと同じ方法で呼び出せる。
```
// 複合モデルの場合、doctypeにどのモデルを使ったががコロン区切りで出ている。
		"documents": [
			{
				"docType": "composedmodel:testmodel",
				"boundingRegions": [
					{
						"pageNumber": 1,
						"polygon": [
							0,
							0,
							8.5,
							0,
							8.5,
							11,
							0,
							11
						]
					},
```

裏はどういうロジックになっているのかはもう少し後で調べる。

# Azure AI SearchとDocument Intelligenctの統合
Skillsetを使う。

AI SearchのSkillsetについては以前以下で書いたので詳しくはそちらで。

https://qiita.com/smr1/items/2d13a3753d5da0bc9d78

基本的にはAzure Functionを経由して、Storage AccountのファイルデータをDocuement IntelligenceのModelに対して投げて、Azure AI SearchのIndexをEnrichする感じになる。



# 要注意？Storage Accountのセキュリティ設定について
Storage Accountに事前にCORSを設定しろという情報も見かけたが、実際には自動的に追加されていた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/f96bb16d-8afd-e536-ca89-9e7616f94856.png)

また、ContainerもPrivateのままでもいけた。この辺Azure OpenAIのOn Your DataとかだとPrivate Containerだとエラーになる。Document IntelligenceはStorage Accountにどうやってアクセスしているのだろう？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/6d6077c5-efdd-5612-d2cb-8ae8e73b9cae.png)

Document IntelligenceではManaged IDはOffのままで、Storage Account側でRBAC設定されている形跡もない。Document Intelligence Studio側でもStorage Account設定時にキーを入力する箇所はなく、設定を見てもキーやSASTokenを保持しているような箇所はない。。。

、、、と思ったら何気なしにStorage AccountのPrimary KeyをRotateしてみたらDocument Intelligence Studio側で認証エラーになった。どうやらStudioの裏でキーを実は保持しているらしい。再トレーニングもできなくなった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654538/2a4008af-0094-aa52-324f-04a46ca6e577.png)

これは面倒だ。StudioからはStorage Accountのキーを設定しなおすこともできないぽい。
Storage Accountのキーを変更したら、新しくDocument Intelligence Studio側でProjectを作り直すしかないんだろうか。モデル名やEndpointが変わるとすると中々影響が大きい気がする。

Azure AIのサービスは他にもLanguage StudioとかいろんなStudioがあるけど、やることは大体同じで、テストデータアップロード、トレーニング、モデル作成、テスト、Endpointの管理等大体似た作りになっている。
ただStorage Accountへの接続の管理はどうも一貫していないようだ？