概要
フレームベースのタスク指向型対話システムに向けて、SVM(scikit-learn)の役割と学習データの作成手順を整理する。
フレームベースでは、発話から対話行為タイプや必要なスロット情報を推定し、対話管理に渡す。状態遷移ベースより柔軟な入力を扱いやすくなる一方で、学習データの作り方が重要になる。
ここでは、SVMの準備、発話行為タイプとコンセプトの定義、学習データ生成の流れを確認する。
この記事の構成
- 対象環境と利用上の注意
本文記載時の環境と現在そのまま利用できない箇所を確認。 - 作業時の注意点
設定変更やコマンド実行前に確認しておきたい注意点を整理。 - タスク指向型対話システム(フレームベース)のシステム構成について
タスク指向型対話システム(フレームベース)のシステム構成についてと各要素の役割を整理。 - フレームと対話行為を推定するSVM(sklearn)の概要とインストール
フレームと対話行為を推定するSVM(sklearn)の概要とインストールの手順と確認ポイントを整理。 - 学習データの作成
学習データの作成の手順と確認ポイントを整理。
対象環境と利用上の注意
- 本文記載時の環境
CentOS \(7\)、Python \(3.6\)系、当時のscikit-learn・MeCab・dillを前提とした学習用実装。 - 確認時期
2026年8月にscikit-learn公式資料と照合し、インストール方法、モデル評価、保存時の注意を見直した。 - 現在そのまま利用できない箇所
データ生成・学習コード全体は現行環境で再実行していない。
旧OSや未固定の旧ライブラリをそのまま新規利用せず、現行環境では依存版を固定し、学習用・評価用データを分離して結果を検証する。
作業時の注意点
- フレームと状態の違い
状態遷移は流れを管理し、フレームは発話から埋める情報構造として考える。 - 学習データの偏り
例文が少ない、表現が偏ると、推定結果も偏りやすい。 - 用語の違い
インテント、エンティティ、スロットなどはサービスによって呼び方が異なる。
実装内容
タスク指向型対話システム(フレームベース)のシステム構成について
- 構成要素と処理の流れ
システム構成は、状態遷移ベースと同様にPythonで作成する対話システム、Telegramサーバー、Telegramをインストールしたクライアント端末(Android、iPhone、Windows、Linux、Macなど)の単純な3構成でデータベースも使わない。
※ 以降、Telegramインストールした端末をTelegramクライアントと表現する。大まかな処理の流れとしては、Pythonで作成する対話システムを起動すると、これがBotとしてTelegramクライアントに表示され、ユーザー要求の入力待ち状態となる。
その後、ユーザーが要求(入力)するとTelegramサーバーを介し、対話システム(Bot)の応答制御処理を経て、結果をユーザーに返し対話していく。
ここからは、SVM(sklearn)の概要、インストール、学習データ作成を確認する。
また、次の記事以降も継続して解説していく、タスク指向型システムのフレームベースについての動作環境は、状態遷移ベースと同じでTelegramクライアントがWindows、Pythonの対話システムがLinux(CentOS7)となるので、コマンドは特に自身の環境に合わせて読み替えること。
フレームと対話行為を推定するSVM(sklearn)の概要とインストール
- SVMによる発話行為推定
まず、フレームとは、一度に複数の情報を受付けられるようにスロットと呼ばれる属性と値からなる入力情報を複数持つデータ構造のことで、遂行手法としてフレームベースとも呼ばれる。
フレームベースの基本処理構成は、発話処理部、対話管理部(状態更新、行動決定)、発話生成部を順に接続するパイプラインとして表せる。ソフトウェア開発工程を表すVモデルとは別の概念となる。
※ ここでは、フレームベースに関する解説は概要レベルに留め、次の記事以降でSVMやCRFを絡めた実装を例に掘り下げていく。始めのユーザーの発話直後に処理される発話処理部では、発話内容から発話の意図となる対話行為を推定する。
対話行為は、対話の大分類(意図)を表す対話行為タイプとその詳細情報であるコンセプトで構成される。
例えば、ユーザーの入力値が「福岡の明日の天気は?」の場合 発話行為タイプを「天気情報の要求」に分類し、コンセプトの場所(都道府県)を「福岡」、日付を「明日」、情報種別を「天気」という具合に分類(推定)する。
対話行為タイプとコンセプトは、開発環境(*1)、(*2)によって呼び方が違うので注意。
- (*1)「Google Home」は、対話行為タイプをインテント、コンセプトをエンティティと呼ぶ。
- (*2)「Amazon Alexa」は、対話行為タイプをインテント、コンセプトをスロットと呼ぶ。
(上記フレームのスロット(属性/値)と(*2)のスロットは表現は同じ「スロット」という表現だが意味が違うので混同注意。)
この対話行為タイプを推定するためにパターン識別用の機械学習方法の一つであるSVM(support vector machine)を使用する。
※ SVMは2値分類を基本とするが、scikit-learnのSVCは複数の2値分類器を組み合わせて多クラス分類にも対応する。どの分類器が最も良いかは、データ量、特徴量、評価指標などによって異なる。SVMによる学習は、Pythonの機械学習ライブラリであるsklearn(scikit-learn)を用いて実装する。
-
scikit-learn、dillのインストール
$ pip3 install scikit-learnまた、学習結果を保存するため シリアライズモジュールであるdillもインストール。
$ pip3 install dill
以上がSVMの環境準備。
また、対話行為の分類基準を確立するため、学習データ(事例)が必要になってくるため、次項で作成方法を解説する。
学習データの作成
- 教師データの生成
この案内対話で使用する発話行為タイプとコンセプトを以下のように定義する。
-
発話行為タイプ
発話行為名 発話行為キー 備考 天気情報の要求 request-weather ユーザーが天気情報を要求している。 伝達情報の初期化 initialize ユーザーが案内の初期化を要求している。 伝達情報の訂正 correct-info ユーザーが発話行為の推定を訂正要求している。 -
コンセプト
属性名 属性キー 値 場所(都道府県) place 都道府県のいずれか 日付 date 今日、明日等の日付 情報種別 type 天気 or 気温
例えば、上記発話行為タイプの学習データを作成する場合
前方を「発話行為キー」、後方を「ユーザーの入力値」とし、そのまま愚直に書くとrequest-weather 福岡の天気は? request-weather 明日の福岡の気温教えて correct-info 大阪じゃなくて福岡です correct-info 天気じゃなく気温 initialize もう一度はじめから initialize リセットして! …という具合に発話行為タイプ別の都道府県別でさらにニュアンスを変えた学習データを一つ一つ手動で書かなければならず、現実的でないため、タグ付けした学習データを作り、これを量産するプログラムを準備する。
まず、学習データにコンセプトの属性キーでタグを付けた代表データ(examples.txt)なるものを準備する。
※ da=XXXX でどの発話行為タイプのデータか定義している。- examples.txt
da=request-weather 大阪 大阪です 明日 明日です 天気 天気です 大阪の明日 大阪の明日です 大阪の天気 大阪の天気です 大阪の天気を教えてください 明日の天気 明日の天気です 明日の天気を教えてください 明日の大阪の天気 明日の大阪の天気です 明日の大阪の天気を教えてください 大阪の明日の天気 大阪の明日の天気です 大阪の明日の天気を教えてください da=initialize もう一度はじめから はじめから はじめからお願いします 最初から 最初からお願いします 初期化してください キャンセル すべてキャンセル da=correct-info 大阪じゃない 大阪じゃなくて 大阪じゃないです 大阪ではありません 明日じゃない 明日じゃなくて 明日じゃないです 明日ではありません 天気じゃない 天気じゃなくて 天気じゃないです 天気ではありません
次に上記代表データを読込んで対象のタグに応じた辞書(都道府県名、日付、情報種別)からランダムで一つ抽出し、学習データを作成するプログラム(generate_da_samples.py)を準備する。
※ 下記のプログラムでは、代表データ一行あたり\(1000\)個の学習データを作成している。- generate_da_samples.py
import re import random import json import xml.etree.ElementTree # 都道府県名のリスト prefs = ['三重', '京都', '佐賀', '兵庫', '北海道', '千葉', '和歌山', '埼玉', '大分', '大阪', '奈良', '宮城', '宮崎', '富山', '山口', '山形', '山梨', '岐阜', '岡山', '岩手', '島根', '広島', '徳島', '愛媛', '愛知', '新潟', '東京', '栃木', '沖縄', '滋賀', '熊本', '石川', '神奈川', '福井', '福岡', '福島', '秋田', '群馬', '茨城', '長崎', '長野', '青森', '静岡', '香川', '高知', '鳥取', '鹿児島'] # 日付のリスト dates = ["今日","明日"] # 情報種別のリスト types = ["天気","気温"] # サンプル文に含まれる単語を置き換えることで学習用事例を作成 def random_generate(root): buf = "" # タグがない文章の場合は置き換えしないでそのまま返す if len(root) == 0: return root.text # タグで囲まれた箇所を同じ種類の単語で置き換える for elem in root: if elem.tag == "place": pref = random.choice(prefs) buf += pref elif elem.tag == "date": date = random.choice(dates) buf += date elif elem.tag == "type": _type = random.choice(types) buf += _type if elem.tail is not None: buf += elem.tail return buf # 学習用ファイルの書き出し先 fp = open("da_samples.dat","w") da = '' # examples.txt ファイルの読み込み for line in open("examples.txt","r"): line = line.rstrip() # da= から始まる行から対話行為タイプを取得 if re.search(r'^da=',line): da = line.replace('da=','') # 空行は無視 elif line == "": pass else: # タグの部分を取得するため,周囲にダミーのタグをつけて解析 root = xml.etree.ElementTree.fromstring("<dummy>"+line+"</dummy>") # 各サンプル文を1000倍に増やす for i in range(1000): sample = random_generate(root) # 対話行為タイプ,発話文,タグとその文字位置を学習用ファイルに書き出す fp.write(da + "\t" + sample + "\n") fp.close()
以下、上記
generate_da_samples.py処理の実行順解説。-
都道府県名、日付、情報種別の辞書を定義
… (省略) … # 都道府県名のリスト prefs = ['三重', '京都', '佐賀', '兵庫', '北海道', '千葉', '和歌山', '埼玉', '大分', '大阪', '奈良', '宮城', '宮崎', '富山', '山口', '山形', '山梨', '岐阜', '岡山', '岩手', '島根', '広島', '徳島', '愛媛', '愛知', '新潟', '東京', '栃木', '沖縄', '滋賀', '熊本', '石川', '神奈川', '福井', '福岡', '福島', '秋田', '群馬', '茨城', '長崎', '長野', '青森', '静岡', '香川', '高知', '鳥取', '鹿児島'] # 日付のリスト dates = ["今日","明日"] # 情報種別のリスト types = ["天気","気温"] … (省略) … -
da_samples.datを書き込みモードで定義
作成する学習用データファイルda_samples.datを新規作成し、書き込みモードで開いたfpを定義。
※ openは、指定したファイルが存在しなければ新規作成した状態で開き、存在していれば上書き状態で開く。
但し、指定したファイルがおかれているディレクトリが存在しない場合は、エラーとなるので注意。… (省略) … # 学習用ファイルの書き出し先 fp = open("da_samples.dat","w") … (省略) … -
examples.txtファイルの読み込み
上記で準備した代表データ(examples.txt)を読み込みモードで開き、読み込んだ行数分ループし(*3)or(*4)or(*5)を実行。- (*3)正規表現モジュール
reでda=から始まるかどうかチェックし、始まるのであれば、右辺の発話行為タイプをdaに保持。 - (*4)空白行とみなしスキップし、何もしない。
-
(*5)XMLとして解析するため(最上位のタグは1つでなければならない)、対象行を
dummyタグで囲い、fromstringで文字列からxml.etree.ElementTree.Elementにパースしたものをrootに保持。 このrootを引数に\(1000\)回random_generateメソッド(処理詳細は下記(*6)を参照)を呼出し、対話行為タイプとrandom_generateの戻り値sampleをfpに書き込む。da = '' for line in open("examples.txt","r"): line = line.rstrip() if re.search(r'^da=',line): da = line.replace('da=','') elif line == "": pass else: root = xml.etree.ElementTree.fromstring("<dummy>"+line+"</dummy>") for i in range(1000): sample = random_generate(root) fp.write(da + "\t" + sample + "\n") - (*6)random_generate メソッドの処理
引数の
root内にタグが存在しない場合(len(root) == 0)、root直下のテキストをそのまま返す。
以降は、root内のタグに応じて、都道府県名 or 日付 or 情報種別の辞書からそれぞれからランダム抽出した結果と対象タグ末尾の発話文(tail)をbufに加算して返す。def random_generate(root): buf = "" if len(root) == 0: return root.text for elem in root: if elem.tag == "place": pref = random.choice(prefs) buf += pref elif elem.tag == "date": date = random.choice(dates) buf += date elif elem.tag == "type": _type = random.choice(types) buf += _type if elem.tail is not None: buf += elem.tail return buf
- (*3)正規表現モジュール
-
最後に
fpを閉じて処理終了… (省略) … fp.close() … (省略) …
最後に実行確認
-
generate_da_samples.pyの実行確認
上記の代表データ(examples.txt)がgenerate_da_samples.pyと同じフォルダ内にある状態で実行。
(もちろんgenerate_da_samples.pyまでのファイルパスは各自環境に合わせて読み替える。)$ python ~/gitlocalrep/dsbook/generate_da_samples.py -
da_samples.datの確認
代表データ(examples.txt)が40個あり、それぞれから\(1000\)個を生成するため、重複を含めた出力行数は \(40 \times 1000 = 40000\) 行となる。※ 同じテンプレートから置換して作った文は互いに強く似る。モデル評価では、生成後の行を無作為に分けるだけでなく、元テンプレート単位で学習用と評価用を分離し、類似文の混入によるデータ漏洩を避ける。
$ cat ~/gitlocalrep/dsbook/da_samples.dat request-weather 高知 request-weather 大分 request-weather 京都 … (省略) … equest-weather 青森です request-weather 神奈川です request-weather 神奈川です … (省略) … request-weather 今日 request-weather 明日 request-weather 今日 … (省略) … initialize もう一度はじめから initialize はじめから initialize はじめからお願いします … (省略) … correct-info 福岡じゃない correct-info 神奈川じゃない correct-info 広島じゃない … (省略) …
以上で学習データの準備が完了。
-
まとめ
- フレームベースでは、発話から対話行為タイプやスロット情報を推定。
- SVMは発話行為タイプの分類に使う。
- 学習データの設計と品質が、後続の推定精度に大きく影響する。
参考文献
- 東中 竜一郎、稲葉 通将、水上 雅博(\(2020\))『Pythonでつくる対話システム』株式会社オーム社
- GitHub - 『Pythonでつくる対話システム』サポートリポジトリ(日本語・書籍の学習データと実装例)
- scikit-learn公式ドキュメント - Installing scikit-learn(英語・インストール手順)
- scikit-learn公式ドキュメント - SVC(英語・SVM分類器のAPI仕様)
- dill公式ドキュメント(英語・シリアライズ機能の公式解説)