Azure AI コンテナーを触ってみた

NTTデータ先端技術株式会社の原田です。
とあるイベントの登壇ネタに Azure AI コンテナーによる個人情報のマスク処理を触ってみましたので、自分自身の備忘も兼ねて記事にいたします。

## 条件・制約
* <font color="red"><strong>!!!!! コードの実行は自己責任でお願いします !!!!!</strong></font>
* Windows 10 22H2 にて開発・確認を行っています
* リンク先等の掲載内容は本稿執筆時点のものとなります

## Azure AI コンテナーとは
[Azure AI コンテナーの概要](https://learn.microsoft.com/en-us/azure/ai-services/cognitive-services-container-support)

ひと言で言うと Azure AI サービスを Docker コンテナーで動作させられるものです。
AKS・ACI は当然として Docker が動けばオンプレミス環境も OK で更にインターネットから切断された環境でも動作可能と言う、厳しいセキュリティやガバナンス要件にも対応可能な仕組みとして提供されています。
が当然いくつか制約もあり、

* SLA は提供されません[^1]
* 切断された環境での使用にはリクエストフォームによる申請が必要(条件厳しそう) [^2]
* 切断された環境では従量課金制が使えずコミットメントプランのみ

と特に切断された環境での使用にはちょっと壁がある感じです。
ただ送信されるのは課金情報のみで顧客データは送信されない旨が明記されていますので、適用できるケースは結構多いのではないでしょうか。[^3]

それでは次のセクションから開発環境の構築手順を追っていきます。

## Docker 環境の構築
最初の手順では WSL (Windows Subsystem for Linux) を使用して Docker 環境を構築していきます。 
環境依存の手順もありますので合致しない場合は参考情報としてご覧ください。

1. WSL のインストール
    https://learn.microsoft.com/ja-jp/windows/wsl/install<br><br>
    
2. WSL の DNS 設定
    WSL からの名前解決がうまくできない事象に当たりましたので、その対処のためのコマンドを記載しておきます。
    ```
    echo [network] >> /etc/wsl.conf
    echo generateResolvConf = false >> /etc/wsl.conf
    sudo rm /etc/resolv.conf
    echo nameserver 8.8.8.8 > /etc/resolv.conf
    sudo chattr +i /etc/resolv.conf
    ```
    Windows 11 22H2 以降の環境では DNS トンネリングも使えるようです。[^4]<br><br>

3. Zscaler ルート証明書のインストール
    Zscaler Internet Access を使用している場合は WSL にルート証明書をインストールする必要があります。 
    Windows PC にて Zscaler のルート証明書をエクスポートして WSL に移送した上で、
    ```
    sudo cp -p Zscaler-Root-CA.crt /usr/local/share/ca-certificates/
    sudo update-ca-certificates –fresh
    ```
    とコマンドを実行してください。<br><br>
    
4. Docker のインストール
    ちょっと回り道しましたがようやく Docker のインストールです、Docker Desktop はライセンスの件がありますので ↓ のリポジトリを使用した手順にてインストールしています。
    https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository<br>
    インストール後に ↓ も実施してください。
    https://docs.docker.com/engine/install/linux-postinstall/

## Azure AI サービスの作成
[クイック スタート: Azure AI サービス リソースを作成する](https://learn.microsoft.com/en-us/azure/ai-services/multi-service-resource?pivots=azportal) の手順に沿ってリソースを作成したのち、

* Language のエンドポイント URI
* API キー 

を取得して手元に控えてください。

## コンテナーイメージの取得
[個人を特定できる情報 (PII) 検出コンテナーをインストールして実行する](https://learn.microsoft.com/en-us/azure/ai-services/language-service/personally-identifiable-information/how-to/use-containers) の手順に沿ってコンテナーイメージの取得および確認を行います。

## Python 実行環境の構成
サンプルコードを Python にて提供していますのでその実行環境を構成します。

1. [Python Releases for Windows](https://www.python.org/downloads/windows/) よりインストーラーをダウンロードして実行
2. [Azure Text Analytics クライアント ライブラリ](https://learn.microsoft.com/ja-jp/python/api/overview/azure/ai-textanalytics-readme?view=azure-python#install-the-package) のインストール

## サンプルコードの実行
お待ちかねのコード実行、の前にテストデータが必要ですね。
個人情報っぽいテストデータの生成には [個人情報テストデータジェネレーター](https://testdata.userlocal.jp/) を使わせていただきました。

ではテストデータを配置してコンテナーを起動して、
```
import configparser
config_ini = configparser.ConfigParser()
config_ini.read("config.ini.local", encoding="utf-8")

# This example requires environment variables named "LANGUAGE_KEY" and "LANGUAGE_ENDPOINT"
language_key = "**********"
language_endpoint = "https://**********.cognitiveservices.azure.com/"
source_path = "testdata.csv"
source_encoding = "UTF-8-sig"
destination_path = "redacted.txt"
destination_encoding = "UTF-8"

from azure.ai.textanalytics import TextAnalyticsClient
from azure.core.credentials import AzureKeyCredential

# Authenticate the client using your key and endpoint 
def authenticate_client():
    ta_credential = AzureKeyCredential(language_key)
    text_analytics_client = TextAnalyticsClient(
            endpoint=language_endpoint, 
            credential=ta_credential)
    return text_analytics_client

client = authenticate_client()

# Example method for detecting sensitive information (PII) from text 
def pii_recognition_example(client):
    s = open(source_path, "r", encoding=source_encoding)
    data = [s.read()]

    response = client.recognize_pii_entities(data, language="en")
    result = [doc for doc in response if not doc.is_error]
    for doc in result:
        print("Redacted Text: {}".format(doc.redacted_text))
        for entity in doc.entities:
            print("Entity: {}".format(entity.text))
            print("\tCategory: {}".format(entity.category))
            print("\tConfidence Score: {}".format(entity.confidence_score))
            print("\tOffset: {}".format(entity.offset))
            print("\tLength: {}".format(entity.length))

    d = open(destination_path, "w", encoding=destination_encoding)
    d.write(doc.redacted_text)

pii_recognition_example(client)
```
を実行しましょう、パラメーターは環境に合わせて適宜修正をお願いします。
実行が完了しますと、  

![8c3997812784c2419e4a_01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/999138b1-2fe9-e94a-9e66-dba0933bc3a8.png)  

こんな感じのデータが、  

![8c3997812784c2419e4a_02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/6708e040-eb65-39c0-5ff4-9040bb0b5030.png)  

と個人情報がマスクされて出力されます、結構精度ありそうですね。

## 補足
* [Azure AI Language のサービス制限](https://learn.microsoft.com/en-us/azure/ai-services/language-service/concepts/data-limits)
    リクエストサイズに制限がありますので、大きなデータを取り扱う場合チャンク処理を考慮する必要があります。
* [Azure AI Language の価格](https://azure.microsoft.com/ja-jp/pricing/details/cognitive-services/language-service/)
    切断されたコンテナーは年間コミットメントプランのみ、おいそれと触ってみることのできる値付けではない…。

## おわりに
Azure AI コンテナーいかがでしたでしょうか？
実は事前調査で PII コンテナーが日本語に対応しているとの情報が掴めず精度出ないんじゃないかと思ってましたが、結果それは杞憂で日本語でも精度良く検知が行われる結果となりました。
もうちょっとテストのバリエーションは必要ですがこれはいけるんじゃないかとの感触です。
あとサンプルコードがチャンク処理してないので、もう少しブラッシュアップしたら GitHub にて公開しようかと思います。

本稿を読んでいただきありがとうございました！


## 注釈
[^1]: https://learn.microsoft.com/en-us/azure/ai-services/containers/container-faq#what-is-the-service-level-agreement--sla--for-azure-ai-containers 
[^2]: https://learn.microsoft.com/en-us/azure/ai-services/containers/disconnected-containers#request-access-to-use-containers-in-disconnected-environments
[^3]: https://learn.microsoft.com/en-us/azure/ai-services/containers/container-faq#how-does-billing-work
[^4]: https://learn.microsoft.com/en-us/windows/wsl/networking#dns-tunneling