LoginSignup
6
3

Azere Cognitive SearchとAzure Form Recognizerの連携を試してみた ~②Azure Functions編~

Last updated at Posted at 2022-08-29

本記事は、前回の記事の続きとなります。もし良ければ、前回の記事もご覧ください。

今回は「Azere Cognitive SearchとAzure Form Recognizerの連携を試してみた ~②Azure Functions編~」ということで、Azure FunctionsからForm RecognizerのAPIを呼び出す部分について解説します。
Cognitive_arch.png
フローでいうと③->④、⑥->⑦の部分となります。具体的にはAzure Functionsのコードを記述・デプロイして、外部からAPIを叩いて正しいレスポンスが得られることをゴールとします。

開発環境の用意

筆者のAzure Functionsの開発環境は下記の通りです。

  • Ubuntu 20.04 on WSL 2
  • VSCode
  • Python 3.8

WSLを使うかどうかは好みですが、VSCodeはFunctionsのコードを開発する上でとても便利なので、入れておくことを推奨します。VSCodeの拡張機能としてAzure AccountAzure Functionsも入れておきましょう。
画像2.png

コード開発

それではFuntionsのコードを書いていきます。といっても、Cgnitive SearchからForm Recognizerを呼び出すためのテンプレートがGitHubに公開されているので、git cloneして、必要な部分だけ切り出します。

$ git clone https://github.com/Azure-Samples/azure-search-power-skills.git
$ cp -r azure-search-power-skills/Vision/FormRecognizer ./
$ code FormRecognizer

コードのレポジトリはこちらです。

FormRecognizerのディレクトリをVSCodeで開くと、Functionsの拡張機能が勝手にフォルダを認識し、Yesをクリックすると開発に必要な設定(.venvや.vscode)を入れてくれます。RuntimeはPythonを選択しましょう。
画像3.png

こちらのコードですが、GitHubにもかいてある通りFormRecognizerのエンドポイントキーを環境変数として設定する必要があります。

This function requires a FORMS_RECOGNIZER_ENDPOINT and a FORMS_RECOGNIZER_KEY settings set to a valid Azure Forms Recognizer API key and to your custom Form Recognizer 2.1-preview endpoint. If running locally, this can be set in your project's local environment variables. This ensures your key won't be accidentally checked in with your code. If running in an Azure function, this can be set in the application settings.

ルートディレクトリ(FormRecognizer/)にlocal.settings.jsonを作成することで、Functionsのローカル実行時環境変数等の設定を読み取ってくれます。下記の通りFORM_RECOGNIZER_ENDPOINTFORM_RECOGNIZER_KEYを記述します。

local.settings.json
{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "FORM_RECOGNIZER_ENDPOINT": "https://formrecognizer-test-1234.cognitiveservices.azure.com/",
    "FORM_RECOGNIZER_KEY": "XXXXXXXXXXXXXXXXXXXXXXXXXXX"
  }
}

FORM_RECOGNIZER_ENDPOINTFORM_RECOGNIZER_KEYはAzure Portalから取得します。
画像4.png

Azure Functions Core Tools のインストール

次にFunctionsのCLIをインストールします。OS毎にインストールの方法が異なるので、こちらの公式ドキュメントをご参照ください。

Ubuntuの場合は下記のコマンドを実行します。

$ curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
$ sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
$ sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" > /etc/apt/sources.list.d/dotnetdev.list'
$ sudo apt-get update
$ sudo apt-get install azure-functions-core-tools-4

ローカル実行

一通り実行の準備が整ったので、ローカルでコードを実行してみます。
Pythonの仮想環境に入って、azure-ai-formrecognizerのバージョンを上書きします。

$ source .venv/bin/activate
$ func start
Found Python version 3.8.10 (python3).

Azure Functions Core Tools
Core Tools Version:       4.0.4653 Commit hash: N/A  (64-bit)
Function Runtime Version: 4.6.1.18388


Functions:

        AnalyzeDocument: [GET,POST] http://localhost:7071/api/AnalyzeDocument

For detailed output, run func with --verbose flag.
[2022-08-25T06:38:55.703Z] Worker process started and initialized.
[2022-08-25T06:39:00.230Z] Host lock lease acquired by instance ID '0000000000000000000000007EA5C856'.

上記のような出力が表示されれば、正しく動作しています。

コードテスト

次にコードをテストしましょう。GitHubのドキュメントによると、APIの入力フォーマットは下記の通りになります。

{
    "values": [
        {
            "recordId": "record1",
            "data": { 
                "model": "prebuilt-invoice",
                "formUrl": "https://github.com/Azure-Samples/azure-search-power-skills/raw/master/SampleData/Invoice_4.pdf",
                "formSasToken":  "?st=sasTokenThatWillBeGeneratedByCognitiveSearch"
            }
        }
    ]
}

今回モデルはカスタムモデルを使うので、modelの部分は前回作成したtrain-receipt-model-v2を使います。
画像5.png
formUrlformSasTokenはAzure Data Lake StorageにアップロードしたpdfファイルからSAS生成で取得しましょう。
画像6.png

入力データとしてinput.jsonをルート以下に作りました。

input.json
{
    "values": [
        {
            "recordId": "record1",
            "data": { 
                "model": "train-receipt-model-v2",
                "formUrl": "https://sampledata234.blob.core.windows.net/data/pdf/train-receipt/%E6%96%B0%E5%B9%B9%E7%B7%9A_220728.pdf",
                "formSasToken": "?sp=r&st=2022-08-25T06:53:43Z&se=2023-07-13T14:53:43Z&spr=https&sv=2021-06-08&sr=b&sig=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
            }
        }
    ]
}

formSasTokenの先頭には必ず"?"を付けましょう。PortalからBLOB SAS トークンをそのままコピーすると先頭に"?"がついていませんのでご注意を。

このJSONファイルを入力に、APIを叩いてみましょう。

$ curl -X POST -H "Content-Type: application/json" -d '@input.json' http://localhost:7071/api/AnalyzeDocument

レスポンスがこちらです。

APIレスポンス
{
    "values": [
        {
            "recordId": "record1",
            "data": {
                "documents": [
                    {
                        "name": "山田太郎",
                        "card_company": "JCB",
                        "from": "品川",
                        "to": "新大阪"
                    }
                ],
                "tables": [
      ...

kvpのフィールドを見ると、前回取得できていた日時とか合計金額が消えていることが分かります。Form Recognizerが返すレスポンスを見てみると日時や合計金額は正しく取得できていました。実際にはFunctionsの出力結果に変換する際、String型のフィールドのみkvpに追加する、というコードになっていました。

__init__.py
def get_fields(result):
    fields = []
    for idx, doc in enumerate(result.documents):
        kvp = {}
        for kv_pair in doc.fields.items():
            if kv_pair[1].value_type == "string":  #この部分
                kvp[kv_pair[0]] = kv_pair[1].content
            elif kv_pair[1].value_type == "list":
                line_items = []
                items = {}
                for rows in kv_pair[1].value:
                    for row in rows.value.items():
                        items[row[0]] = row[1].content
                        
                    line_items.append(items)

                kvp[kv_pair[0]] = line_items

        fields.append(kvp)
    return fields

なので、下記の通り、思い切って全フィールドをstring型に変換してkvpに追加する、というコードに書き換えました。

__init__.py
def get_fields(result):
    fields = []
    for idx, doc in enumerate(result.documents):
        kvp = {}
        for kv_pair in doc.fields.items():
            kvp[kv_pair[0]] = str(kv_pair[1].content) # この部分

        fields.append(kvp)
    return fields

もう一度APIを叩くとすべてのフィールドが取得できるようになっていました。

APIレスポンス
{
    "values": [
        {
            "recordId": "record1",
            "data": {
                "documents": [
                    {
                        "name": "山田太郎",
                        "reservation_number": "2010",
                        "total_amount": "¥14,720",
                        "date_of_purchase": "2022年7月27日",
                        "date_of_departure": "7月28日",
                        "card_company": "JCB",
                        "from": "品川",
                        "to": "新大阪"
                    }
                ],
               ...

今回は簡単なコード修正にとどめておきますが、Functionsでのレスポンスがほとんどそのままインデックスになるので、このフェーズで変数型のチェックや値のチェック、必要に応じてデータ加工を行うべきです。今回で言うと、"total_amount"の"¥"や","を削除する、"date_of_departure"を"yyyymmdd"に変換する、等です。

コードデプロイ

Azure Accountsの拡張機能からAzureにSign Inすると下記のようにサブスクリプションのリソースが一覧で確認できるようになります。Function Appを右クリックしFuntion Appのリソースを作成しましょう。リソース名を適当に設定し、ランタイムはPythonを選択します。リソースがが作成できたことを確認しましょう。
画像7.png

次にWORKSPACEを見ると、先ほどローカルで実行したFunctionsのプロジェクトが確認できます。右上のアイコンからローカルプロジェクトをAzureにデプロイします。リソースは先ほど作成したものを選択します。
画像8.png
Azure Portal側からもデプロイできていることが確認できます。
画像9.png
次に環境変数を設定します。[構成]->[アプリケーション設定]->[新しいアプリケーション設定]をクリックします。
画像10.png
先ほどローカルで設定した環境変数FORM_RECOGNIZER_ENDPOINTFORM_RECOGNIZER_KEYを同様に設定しましょう。
画像11.png

Functionsのテスト

最後に[コードとテスト]->[テストと実行]をクリックし、ボディに先ほど作成したinput.jsonの内容をコピペし、実行をクリックします。
画像12.png
レスポンスが返ってくることを確認できました。
画像13.png

まとめ

今回はCognitive SearchとForm Recognizerの連携部分でAzure Functionsについて解説しました。少しコードを読み解いて改修する部分はありますが、テンプレートが用意されているのでそれほど手間はかからないと思います。次回はCognitive Searchの設定部分について解説予定です。

6
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3