Azure Applied AI Services Form Recognizer を Docker コンテナーで使ってみる (2) Custom Model

Microsoft Azure の AI サービスである Cognitive Services | Applied AI Services は Web API として利用できるだけでなく、Docker コンテナーとして稼働させることが可能です。エッジデバイス にインストールして利用するといった用途が考えられ、ダイレクトに (Web を介さずに) 分析できるので速い、クラウドへ分析データを送信しないというメリットがあります。

今回はローカル PC で稼働する Docker 環境で利用できる Cognitive Services | Applied AI Services コンテナーのうち、 Form Recognizer (フォームデータ読み取りサービス) を稼働させ、使ってみる方法を紹介します。[前回 (ID Document)](https://qiita.com/annie/items/3a11d4f4d2e77117b8f7) に引き続き、カスタムモデルを作成して分析できる Custom コンテナーを使ってみます。

>Form Recognizer は機能別にコンテナーが分かれており、別の機能を利用したり、Cognitie Services Computer Vision サービス (Read OCR) を利用したりする場合があります。

> クラウドに分析データを送信することはありませんが、サービスの利用量を計上するためのインターネット接続が必要になる場合があります。(完全にオフライン環境で利用できるサービスは Private Preview で提供中)

Form Recognizer コンテナー (v2.1) は、2022月3月現在 Gated Preview (申込→承認されて利用可能に) のサービスで、**予め申し込みが必要** なサービスです。

# 環境

以下の手順は、こちら↓の PC 環境で動作確認をしています;

Windows 11 Enterprise 21H2
x64 | Intel Core i7 | RAM 16 GB

Docker Desktop for Windows 4.6.1
Visual Studio Code 1.66
Windows PowerShell 7.2.2

Form Recognizer Sample Labeling tool for Windows (exe版, v2.1 GA)

# 準備

[Form Recognizer を Docker コンテナーで使ってみる (1) ID Document](https://qiita.com/annie/items/3a11d4f4d2e77117b8f7) と同じ手順で以下を準備しておきます。

> Form Recognizer の Custom Model は Computer Vision サービスは不要です。

- Azure サブスクリプション申し込み & Form Recgonizer サービス作成
    - Key, Endpoint の取得
- Form Recognizer Studio へのアクセス
- Form Recognizer Container: Gated Preview の申し込み
- Docker 環境のインストール
    - Docker Desktop (& Docker) のインストール 
    - Docker Compose のインストール(確認)

## Form Recognizer Sample Labeling tool for Windows

Custom コンテナーを使って、カスタムモデル (オリジナルの分析モデル) を作成するには、ラベル付けツールが必要です。ローカルで稼働する [Form Recognizer Sample Labeling tool](https://github.com/microsoft/OCR-Form-Tools/) をローカル環境で稼働させて使用します。学習データをローカル環境に保持できるのは [Windows 版 exe 版](https://github.com/microsoft/OCR-Form-Tools/releases/tag/v2.1-ga) だけのようなので、こちらをダウンロードしてインストールしておきます。

# 手順

Form Recognizer は 機能別にコンテナーが分かれています。また、Form Recognizer の別のコンテナーのサービスを利用したりしているため、複数コンテナーを同時に起動して呼び出しを行う環境が必要になります。

こちらでは Docker Compose を使って、Form Recognizer Custom API コンテナーが利用できる環境を構築します。

## Docker Compose 設定ファイルの作成

作業フォルダーを準備し、以下のようなフォルダー構成とファイルを作成していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/11a40406-3154-9078-5315-475cae8f1d86.png)

まずは、作業フォルダー (ここでは form-recognizer-custom) に **output** と **shared** という名前でフォルダーを作成しておきます。

### .env の作成

作業フォルダーを Visual Studio Code で開きます。**.env** という名前で新規ファイルを作成し、以下をコピー＆ペーストします。

```
SHARED_MOUNT_PATH="C:\\\YOUR_FOLDER\\form-recognizer-custom\\shared"
OUTPUT_MOUNT_PATH="C:\\\YOUR_FOLDER\\form-recognizer-custom\\output"
FORM_RECOGNIZER_ENDPOINT_URI="https://YOUR_FORMRECOGNIZER_NAME.api.cognitive.microsoft.com/"
FORM_RECOGNIZER_API_KEY="YOUR_FORMRECOGNIZER_KEY"
RABBITMQ_HOSTNAME="rabbitmq"
RABBITMQ_PORT=5672
NGINX_CONF_FILE="C:\\\YOUR_FOLDER\\form-recognizer-custom\\nginx.conf"
```

フォルダー名(YOUR_FOLDER)、Form Recognizer のエンドポイント(https://YOUR_FORMRECOGNIZER_NAME.api.cognitive.microsoft.com/) および キー(YOUR_FORMRECOGNIZER_KEY) はご自身のものに書き換えます。
.env を保存しておきます。

### docker-compose.yml の作成

Visual Studio Code から **docker-compose.yml** という名前で新規ファイルを作成します。
以下をコピー＆ペーストし、保存しておきます。

```yml
version: '3.3'
services:
 nginx:
  image: nginx:alpine
  container_name: reverseproxy
  volumes:
    - ${NGINX_CONF_FILE}:/etc/nginx/nginx.conf
  ports:
    - "5000:5000"
 rabbitmq:
  container_name: ${RABBITMQ_HOSTNAME}
  image: rabbitmq:3
  expose:
    - "5672"
 layout:
  container_name: azure-cognitive-service-layout
  image: mcr.microsoft.com/azure-cognitive-services/form-recognizer/layout
  depends_on:
    - rabbitmq
  environment:
    eula: accept
    apikey: ${FORM_RECOGNIZER_API_KEY}
    billing: ${FORM_RECOGNIZER_ENDPOINT_URI}
    Queue:RabbitMQ:HostName: ${RABBITMQ_HOSTNAME}
    Queue:RabbitMQ:Port: ${RABBITMQ_PORT}
    Logging:Console:LogLevel:Default: Information
    SharedRootFolder: /shared
    Mounts:Shared: /shared
    Mounts:Output: /output
  volumes:
    - type: bind
      source: ${SHARED_MOUNT_PATH}
      target: /shared
    - type: bind
      source: ${OUTPUT_MOUNT_PATH}
      target: /output
  expose:
    - "5000"

 custom-api:
  container_name: azure-cognitive-service-custom-api
  image: mcr.microsoft.com/azure-cognitive-services/form-recognizer/custom-api
  restart: always
  depends_on:
    - rabbitmq
  environment:
    eula: accept
    apikey: ${FORM_RECOGNIZER_API_KEY}
    billing: ${FORM_RECOGNIZER_ENDPOINT_URI}
    Logging:Console:LogLevel:Default: Information
    Queue:RabbitMQ:HostName: ${RABBITMQ_HOSTNAME}
    Queue:RabbitMQ:Port: ${RABBITMQ_PORT}
    SharedRootFolder: /shared
    Mounts:Shared: /shared
    Mounts:Output: /output
  volumes:
    - type: bind
      source: ${SHARED_MOUNT_PATH}
      target: /shared
    - type: bind
      source: ${OUTPUT_MOUNT_PATH}
      target: /output
  expose:
    - "5000"

 custom-supervised:
  container_name: azure-cognitive-service-custom-supervised
  image: mcr.microsoft.com/azure-cognitive-services/form-recognizer/custom-supervised
  restart: always
  depends_on:
    - rabbitmq
  environment:
    eula: accept
    apikey: ${FORM_RECOGNIZER_API_KEY}
    billing: ${FORM_RECOGNIZER_ENDPOINT_URI}
    CustomFormRecognizer:ContainerPhase: All
    CustomFormRecognizer:LayoutAnalyzeUri: http://azure-cognitive-service-layout:5000/formrecognizer/v2.1/layout/analyze
    Logging:Console:LogLevel:Default: Information
    Queue:RabbitMQ:HostName: ${RABBITMQ_HOSTNAME}
    Queue:RabbitMQ:Port: ${RABBITMQ_PORT}
    SharedRootFolder: /shared
    Mounts:Shared: /shared
    Mounts:Output: /output
  volumes:
    - type: bind
      source: ${SHARED_MOUNT_PATH}
      target: /shared
    - type: bind
      source: ${OUTPUT_MOUNT_PATH}
      target: /output
```

### nginx.conf の作成

Visual Studio Code から **nginx.conf** という名前で新規ファイルを作成します。以下をコピー＆ペーストし、保存しておきます。

```conf
worker_processes 1;

events { worker_connections 1024; }

http {

    sendfile on;
    client_max_body_size 90M;
    upstream docker-api {
        server azure-cognitive-service-custom-api:5000;
    }

    upstream docker-layout {
        server  azure-cognitive-service-layout:5000;
    }

    server {
        listen 5000;

        location = / {
            proxy_set_header Host $host:$server_port;
            proxy_set_header Referer $scheme://$host:$server_port;
            proxy_pass http://docker-api/;

        }

        location /status {
            proxy_pass http://docker-api/status;

        }

        location /ready {
            proxy_pass http://docker-api/ready;

        }

        location /swagger {
            proxy_pass http://docker-api/swagger;

        }

        location /formrecognizer/v2.1/custom/ {
            proxy_set_header Host $host:$server_port;
            proxy_set_header Referer $scheme://$host:$server_port;
            proxy_pass http://docker-api/formrecognizer/v2.1/custom/;

        }

        location /formrecognizer/v2.1/layout/ {
            proxy_set_header Host $host:$server_port;
            proxy_set_header Referer $scheme://$host:$server_port;
            proxy_pass http://docker-layout/formrecognizer/v2.1/layout/;

        }
    }
}
```

## Docker 環境の起動

Visual Studio Code のターミナルから、docker-compose コマンドで環境を起動します。
5つコンテナーが起動するのでしばらく時間が掛かります。

```shell-session
> docker-compose up
```

無事起動すると、Docker Desktop から **form-recognizer-custom** およびその配下に5つのコンテナー (custom-api, custom-supervised, layout, reverseproxy, rabbitmq) が作成されているのが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/d06ac74f-09f2-e8a5-a7c6-0c26a0b83bff.png)

UI として動いている **reverseproxy** にアクセスします。Docker Desktop から **OPEN IN BROWSER** を選択、またはブラウザーから http://localhost:5000 を開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/8462a23b-1cbd-604d-21b0-ff36f21fc434.png)

Cognitive Services Container のページが表示されたら OK です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/340d271b-07c9-ac0e-3d46-63a34d772600.png)


## ローカルコンテナーでの Custom Model 作成

### 学習データの準備

カスタムモデルを作成するための学習データ (画像 (jpg, png, pdf), 1 モデルあたり 5 枚以上) を準備します。Doker 環境作成時に作成した **作業フォルダー\shared** の配下にサブフォルダーを作成し、学習データを保存しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/601f470e-554b-aac5-1e53-80c9e266f50c.png)


### Form Recognizer Sample Labeling tool を使ったラベル付けとカスタムモデル作成

Form Recognizer Sample Labeling tool を起動します。

まずは、学習データを取得するフォルダーの設定 (Connection) を行います。
左端バーの Connection アイコンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/800d2c90-59c1-ee94-7d9b-aafbcd538be4.png)

*Connection Settings* のページで、以下の情報を入力します。

- **Display Name** : 接続名 (ご自身が識別しやすい名前を) (ここでは **localfolder**)
- **Local File system** : **[Browse]** をクリックして、学習用データを入れるローカルフォルダーを選択します。Docker 環境作成時に作成しておいた **作業フォルダー\shared** を指定します。(ここでは **C:\・・・・・\form-recognizer\shared**)

**[Save Connection]** をクリックして保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/32c7743f-8beb-6130-016f-fed7e63ddd73.png)

> 学習用データのフォルダーは、Docker 環境時に作成した **作業フォルダー\shared** を必ず指定してください。Docker 環境で稼働している Form Recognizer Custom API (カスタムモデル学習&作成) は、**これ以外のローカルフォルダーにアクセスすることができません**。

作成、保存した Connection が一覧に表示されたら、左端バーの Home アイコンからトップページに戻ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/17d2d724-201f-3edc-dff6-24ba2e2a2724.png)

トップページから **Use Custom ...** をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/ca007900-2fe7-0078-979d-bf4f7066012b.png)

**New Project** をクリックして、新規プロジェクト (Form Recgonizer カスタムモデル) を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/ebd41a8a-1202-ae23-e7f5-f9ec7d3df176.png)

*Project Settings* 画面で以下の情報を入力します。

- **Display Name** : プロジェクト名 (ご自身で識別しやすいものを。**学習データのサブフォルダー名**　にしておくと分かりやすいかと) ここでは **receipt202203**
- **Source connection** : 前の手順で作成した接続名 (ここでは *localfolder*)
- **Folder Path** : 学習データのサブフォルダー名。ここでは **receipt202203**
- **Form Recogizer service URI** : ローカルで稼働中の Form Recognizer エンドポイント (これまでの手順通りなら **http://localhost:5000**)
- **API Key** : ローカルに保存しておいた Form Recognizer キー

**[Save Project]** をクリックしてプロジェクトを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/ee8d940c-bddf-bf2d-41ae-d90e1a7769c7.png)


Tag Editor 画面に遷移し、画像が読み込まれます。(OCR, Layout が読み込まれるため、Docker コンテナーが稼働しているローカル環境によっては時間が掛かります。)
OCR と Layout によって、文字認識されている箇所は黄色の枠、フォーム(表)は 表のマークが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/ed827f66-ac40-5edd-a0f4-b7e215da8c16.png)

右上の **+ (Add new tag)** をクリックして、ラベルタグを追加します。
タグ名を入力してEnter、でタグが作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/7e2521b4-a09e-fac0-bfcc-a0a167718b99.png)

画像で文字認識されている枠をクリックして選択し (選択した枠は緑色で表示されます)、その状態でタグをクリックすると、認識されている文字がタグに紐づけられます。黄色の枠がタグの色に替わり、タグの方に認識された文字が表示されたらOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/3abdf9e3-fdc8-a570-4186-acd9c6f82470.png)

同様に、タグを追加 → 文字をタグ付けを繰り返して、抽出したいエリアの文字列をタグ付けします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/b6017c6b-fe02-3f52-cab1-90c1467fc90b.png)

他の画像も同じ手順でタグ付けを行います。
全部終わったら、画面左バーの上から3つ目のアイコン (Train) をクリックして、モデルの学習に進みます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/72e1a0bf-ccd4-b0b4-2276-3ba86699299c.png)

**Train a new model** の画面で、**Model name**(ご自分で識別しやすいものをつけてください) を入力して、**[Train]** をクリックすると、カスタムモデルの学習が始まります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/8adc4094-6d31-84cd-45eb-b116ce10a811.png)

学習が終了すると、**Train Result** が表示され、精度などの情報を見ることができます。
作成したモデルをテストするには、画面左バーの上から5つ目のアイコン(**Analyze**) をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/efc84728-765b-989c-fdf6-09a4fe54268d.png)

**Choose an image to analyze with** の欄でテスト画像を選択し、**[Run Analysis]** をクリックします。

> ローカルフォルダーの画像を使いたい場合は、Docker 環境時に作成した **作業フォルダー\shared** 配下に保存しておき、選択してください。

しばらくして分析結果が表示されることを確認してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/d0a7fab4-b792-6f71-4194-0b9ded3882b1.png)

## Docker 環境の停止

Visual Studio Code のコンソールで Ctrl+C をクリックして、環境を停止します。

> 次回からは Docker Desktop の **Start** ボタンからも起動することができます。(停止も可能です)
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117583/5e718bb0-c545-94c9-e922-d0798d6c8ee2.png)