第8回 意図理解・チャットボット

一覧に戻る

第8回 意図理解・チャットボット

今回のテーマは意図理解・チャットボットです。チャットボットとは「チャット」と「ボット」を組み合わせた造語で、AIを活用した自動会話プログラムのことです。最近ではChatGPTなどの大規模言語モデルを用いたチャットボットの登場により、ますます注目が集まっている分野です。
チャットボットは、日本語や英語などの自然言語をコンピュータで処理する技術である自然言語処理や、音声に含まれる情報を認識する音声認識といった技術によって実現されています。

今回の実装のソースコードはこちらからダウンロードできます。

目次

解説動画

概要

  • 実装内容
    • Microsoft Azureの会話言語理解 (CLU)を使用して、メール操作に対する意図理解を行うモデルを作成し、予測を行います。意図理解についての説明は解説動画をご覧ください。
  • 実行環境
    • Google Colaboratory ※Google Colaboratoryに関する説明はこちら
  • サンプルデータ
    • こちらからダウンロードしてください。JSONがプレーンテキストで表示されるので、右クリック→「名前を付けて保存」で保存してください。

会話言語理解 (CLU)

今回使用するクラウドAPIはMicrosoft Azureの会話言語理解(以下CLUと表記)です。CLUはAzure Cognitive Service for Languageの機能の1つで、トレーニングを行うことで自然言語を理解して、文章の意図理解やエンティティの抽出を行うことができます。

利用にはAzureのリソースを作成する必要があります。リソースはこちらから作成してください。(事前にAzureの無料アカウントを作成する必要があります。)

リファレンス

サンプルデータについて

AzureのGitHubにあるサンプルのデータセットを使用します。概要にダウンロード方法について記載したので、そちらからダウンロードしてください。

データセットの言語は英語なので、英語のみ予測可能です。日本語での予測を行いたい場合は、日本語のデータを各自用意し、学習時のトレーニングモード(trainingMode)を高度なトレーニング(advanced)に設定して学習してください。※advancedは最大1時間まで無料です。

サンプルデータは以下の形式となっています。これはCLUがインポートするプロジェクトの形式に準拠しています。サンプルのプロジェクトは、メール操作に対する命令(メールの読み取り、メールの削除、メールへのドキュメントの添付など) を予測することを目的としています。学習に使うデータは「send an email to my brother」などの命令文と「SendEmail」のような意図、それから文章内に現れるエンティティの位置情報やカテゴリがセットになったものとなっています。

{
  "projectFileVersion": "{API-VERSION}",  // APIのバージョン
  "stringIndexType": "Utf16CodeUnit",
  "metadata": {
    "projectKind": "Conversation",
    "settings": {
      "confidenceThreshold": 0.7  // 意図の信頼度スコアの閾値
    },
    "projectName": "{PROJECT-NAME}",  // プロジェクト名
    "multilingual": true,
    "description": "Trying out CLU",
    "language": "{LANGUAGE-CODE}"  // 言語コード
  },
  "assets": {
    "projectKind": "Conversation",
    "intents": [  // 使用する意図の定義
      {
        "category": "intent1"
      },
      {
        "category": "intent2"
      }
    ],
    "entities": [  // 使用するエンティティの定義
      {
        "category": "entity1"
      }
    ],
    "utterances": [  // 学習用の文章とラベルのリスト
      {
        "text": "text1",  // 学習用の文章
        "dataset": "{DATASET}",  // 学習用とテスト用を分けるラベル
        "intent": "intent1",  // 文章の意図
        "entities": [  // 文章内に含まれるエンティティのリスト
          {
            "category": "entity1",  // エンティティのカテゴリ
            "offset": 5,  // エンティティの位置(文章の先頭の文字を0としたときのエンティティの始まりの位置)
            "length": 5  // エンティティの文字の長さ
          }
        ]
      },
      {
        "text": "text2",
        "language": "{LANGUAGE-CODE}",
        "dataset": "{DATASET}",
        "intent": "intent2",
        "entities": []
      }
    ]
  }
}

JSONの各項目の詳細についてはこちらのリファレンスをご覧ください。

意図とエンティティのカテゴリ一覧

以下はそれぞれサンプルデータに定義されている意図とエンティティのカテゴリ一覧です。

意図カテゴリ日本語訳
AddFlagフラグ追加
AddMore追加する
Cancelキャンセル
CheckMessagesメッセージ確認
Confirm確認する
Delete削除する
Forward転送する
QueryLastText最後のテキストを問い合わせる
ReadAloud読み上げる
Reply返信する
SearchMessagesメッセージ検索
SendEmailメール送信
ShowNext次を表示
ShowPrevious前を表示
Noneなし

表1. 意図のカテゴリ一覧

エンティティカテゴリ日本語訳
Attachment添付ファイル
Categoryカテゴリ
ContactName連絡先名
Date日付
EmailSubjectメール件名
FromRelationshipName関係名(送信元)
Line
Messageメッセージ
OrderReference順序参照
PositionReference位置参照
RelationshipName関係名
SearchTextsテキスト検索
SenderName送信者名
Time時間

表2. エンティティのカテゴリ一覧

実装

事前準備

ファイルのアップロード

学習用のファイルをGoogle Colaboratoryにアップロードします。ファイルのアップロード方法についてはこちらをご覧ください。

パッケージのインストール

%pip install azure-ai-language-conversations
Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/
Collecting azure-ai-language-conversations
Downloading azure_ai_language_conversations-1.0.0-py3-none-any.whl (123 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 123.4/123.4 kB 6.4 MB/s eta 0:00:00
Collecting azure-core<2.0.0,>=1.24.0 (from azure-ai-language-conversations)
Downloading azure_core-1.26.4-py3-none-any.whl (173 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 173.9/173.9 kB 16.9 MB/s eta 0:00:00
Collecting isodate<1.0.0,>=0.6.1 (from azure-ai-language-conversations)
Downloading isodate-0.6.1-py2.py3-none-any.whl (41 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 41.7/41.7 kB 3.5 MB/s eta 0:00:00
Requirement already satisfied: requests>=2.18.4 in /usr/local/lib/python3.10/dist-packages (from azure-core<2.0.0,>=1.24.0->azure-ai-language-conversations) (2.27.1)
Requirement already satisfied: six>=1.11.0 in /usr/local/lib/python3.10/dist-packages (from azure-core<2.0.0,>=1.24.0->azure-ai-language-conversations) (1.16.0)
Requirement already satisfied: typing-extensions>=4.3.0 in /usr/local/lib/python3.10/dist-packages (from azure-core<2.0.0,>=1.24.0->azure-ai-language-conversations) (4.5.0)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.18.4->azure-core<2.0.0,>=1.24.0->azure-ai-language-conversations) (1.26.15)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests>=2.18.4->azure-core<2.0.0,>=1.24.0->azure-ai-language-conversations) (2022.12.7)
Requirement already satisfied: charset-normalizer~=2.0.0 in /usr/local/lib/python3.10/dist-packages (from requests>=2.18.4->azure-core<2.0.0,>=1.24.0->azure-ai-language-conversations) (2.0.12)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests>=2.18.4->azure-core<2.0.0,>=1.24.0->azure-ai-language-conversations) (3.4)
Installing collected packages: isodate, azure-core, azure-ai-language-conversations
Successfully installed azure-ai-language-conversations-1.0.0 azure-core-1.26.4 isodate-0.6.1

モジュールのインポート

import json

from azure.core.credentials import AzureKeyCredential
from azure.ai.language.conversations.authoring import ConversationAuthoringClient
from azure.ai.language.conversations import ConversationAnalysisClient

認証情報とパラメータの設定

Azureに接続するための認証情報を入力します。エンドポイントとキーは各自が作成したものを使用してください。
プロジェクト名とデプロイメント名は何でも良いです。今回はメール操作のサポートをするチャットボットを作成するためプロジェクト名をEmailAppDemoとしました。

# 認証情報の設定
ENDPOINT = 'https://ai-seminar-chatbot.cognitiveservices.azure.com/'
credential = AzureKeyCredential('エンドポイントキー')  # Azureから取得したエンドポイントのキーを入力する

# パラメータの設定
PROJECT_NAME = 'EmailAppDemo'  # プロジェクト名
API_VERSION = '2022-05-01'  # APIのバージョン
DEPLOYMENT_NAME = 'staging'  # デプロイメント名

プロジェクトのインポート

学習に使用するプロジェクトをインポートします。プロジェクトとは学習用のデータセットを含む、モデルを構築するための作業領域です。
プロジェクトのインポート、モデルの学習・デプロイにはConversationAuthoringClientクラスを使用します。詳細な使い方はAPIリファレンスをご覧ください。

# 学習用クライアント
authoring_client = ConversationAuthoringClient(ENDPOINT, credential)
# 学習用データの読み込み
with open('EmailAppDemo.json') as f:
    import_json = json.load(f)
# プロジェクトのインポート
poller = authoring_client.begin_import_project(PROJECT_NAME, import_json)

プロジェクトのインポートには時間がかかることがあるため、begin_import_projectメソッドの戻り値はポーリングを行うLROPollerクラスとなっています。実行結果を確認するには以下のようにresultメソッドを実行します。

poller.result()
{'jobId': '21c3a220-d0c8-44ff-898e-301f252af063_638203104000000000',
 'createdDateTime': '2023-05-22T01:11:04Z',
 'lastUpdatedDateTime': '2023-05-22T01:11:05Z',
 'expirationDateTime': '2023-05-29T01:11:04Z',
 'status': 'succeeded'}

statussucceededとなっており、インポートが完了しました。

学習

学習の実行には以下の形式のJSONをbegin_trainメソッドに渡します。各パラメータの意味は以下の通りです。

キー
modelLabelモデルの名前。Model1
trainingModeトレーニングに使用するトレーニングモード。サポートされているモードは、[標準トレーニング]と[高度なトレーニング]があります。
標準トレーニングは英語のみ使用可能で、高度なトレーニングは他の言語や多言語プロジェクトでもサポートされていますが、トレーニング時間が長くなります。
トレーニングモードの詳細については、ドキュメントを参照してください。
standard
kind分割方法。設定可能な値は、percentageまたはmanualです。モデルのトレーニング方法のセクションを参照してください。percentage
trainingSplitPercentageトレーニングセットに含まれるタグ付きデータの割合。推奨値は80です。80
testingSplitPercentageテスト用セットに含めるタグ付けされたデータの割合。推奨値は20です。20

表3. 学習のJSONパラメータ

# 学習
train_json = {
    'modelLabel': 'Model1',
    'trainingMode': 'standard',
    'evaluationOptions': {
        'kind': 'percentage',
        'testingSplitPercentage': 20,
        'trainingSplitPercentage': 80
    }
}
poller = authoring_client.begin_train(PROJECT_NAME, train_json)

プロジェクトのインポートと同様にresultメソッドで実行結果を確認できます。

poller.result()
{'result': {'modelLabel': 'Model1',
  'trainingConfigVersion': '2022-09-01',
  'trainingMode': 'standard',
  'trainingStatus': {'percentComplete': 100,
   'startDateTime': '2023-05-22T01:12:22.3565319Z',
   'endDateTime': '2023-05-22T01:12:35.6003226Z',
   'status': 'succeeded'},
  'evaluationStatus': {'percentComplete': 100,
   'startDateTime': '2023-05-22T01:12:40.1051218Z',
   'endDateTime': '2023-05-22T01:13:25.995957Z',
   'status': 'succeeded'}},
 'jobId': '12255d6d-eec6-4f09-9032-cbd4006309c4_638203104000000000',
 'createdDateTime': '2023-05-22T01:12:20Z',
 'lastUpdatedDateTime': '2023-05-22T01:13:40Z',
 'expirationDateTime': '2023-05-29T01:12:20Z',
 'status': 'succeeded',
 'warnings': [{'code': 'Warning',
   'message': 'Entity `FromRelationshipName` is tagged in `9` training dataset examples, it is recommended to have `10` tags for better model quality.'},
  {'code': 'Warning',
   'message': 'Entity `PositionReference` is tagged in `8` training dataset examples, it is recommended to have `10` tags for better model quality.'}]}

デプロイ

学習が完了したら、モデルをデプロイします。デプロイすることで、モデルを予測に使用することができるようになります。

キー
trainedModelLabelデプロイに割り当てられるモデル名。
正常にトレーニングされたモデルのみ割り当てることができます。
この値は、大文字と小文字が区別されます。
myModel

表4. デプロイのJSONパラメータ

# モデルのデプロイ
deploy_json = {
    'trainedModelLabel': 'Model1'
}
poller = authoring_client.begin_deploy_project(PROJECT_NAME, DEPLOYMENT_NAME, deploy_json)
poller.result()
{'deploymentName': 'staging',
 'modelId': 'Model1-20230522T011340-6780dbbfffc24df7a178d6590a1442f5',
 'lastTrainedDateTime': '2023-05-22T01:13:40.7819051Z',
 'lastDeployedDateTime': '2023-05-22T01:15:43Z',
 'deploymentExpirationDate': '2024-08-31',
 'modelTrainingConfigVersion': '2022-09-01'}

予測

デプロイが完了したら、モデルを用いて予測を行います。予測には予測用のクライアントクラスConversationAnalysisClientを使います。

# 予測用クライアント
analysis_client = ConversationAnalysisClient(ENDPOINT, credential)

ここで、2つの関数を定義します。1つ目のformat_responseは予測結果のレスポンスを見やすいように整形するための関数です。
2つ目のpredictは予測に必要な処理を1つにまとめた関数です。これでpredictに意図理解を行いたいテキストを渡すだけで予測が実行できます。

# レスポンス整形処理
def format_response(response):
    entities = response['result']['prediction']['entities']
    top_intent = response['result']['prediction']['topIntent']
    entities_dict = {}
    for entity in entities:
        if entities_dict.get(entity['category']) is None:
            entities_dict[entity['category']] = [entity['text']]
        else:
            entities_dict[entity['category']].append(entity['text'])
    ret_dict = {
        '意図': top_intent,
        'エンティティ': entities_dict
    }
    return ret_dict
# 予測処理
def predict(query):
    predict_json = {
        'kind': 'Conversation',
        'analysisInput': {
            'conversationItem': {
                'id': '1',
                'participantId': '1',
                'text': query,
            }
        },
        'parameters': {
            'projectName': PROJECT_NAME,
            'deploymentName': DEPLOYMENT_NAME,
            'stringIndexType': 'TextElement_V8'
        }
    }

    response = analysis_client.analyze_conversation(predict_json)
    return format_response(response), response

予測の準備が整ったので実際に予測します。「トムとジャックに来週のランチについてのメールを送って」という内容の文章を英語で送信してみます。

# 予測の実行
# 「トムとジャックに来週のランチについてのメールを送って」という内容の文章を送信
formatted_response, response = predict("send an email to Tom and Jack about next week's lunch")
print(formatted_response)
{'意図': 'SendEmail', 'エンティティ': {'ContactName': ['Tom', 'Jack'], 'EmailSubject': ['lunch']}}

意図がSendEmail(メール送信)、エンティティのContactName(連絡先)がTom(トム)とJack(ジャック)、EmailSubject(メール件名)がlunch(ランチ)という結果が得られており、学習および予測が正常に行われたことが確認できました。

結果の見やすさのため、整形したレスポンスを表示しましたが、predictのもう一つの戻り値のresponseを表示することで、詳細なレスポンスを確認できます。詳細なレスポンスには意図とエンティティの信頼度スコアなどが含まれています。

信頼度スコアとはモデルの予測がどの程度信頼できるものかを表す数値で、0~1の範囲の値で、数値が大きければ大きいほど信頼度は高くなります。上の結果の意図は最も信頼度スコアが大きかったもののみを表示しています。


今回はこれで以上になります。次回からは機械学習のアルゴリズムなど、より基礎的な内容を取り扱います。次回のテーマは住宅価格の予測です。

最後までお読みいただきありがとうございました。それでは引き続きよろしくお願いいたします。


一覧に戻る