概要
フレームベースのタスク指向型対話システムで使うSVMモデルを学習し、発話行為タイプを推定する流れを整理する。
前の記事で作成した学習データをMeCabで単語分割し、TF-IDFで数値ベクトルに変換し、SVMで発話行為タイプを分類するモデルを作成する。
ここでは、モデル学習、モデル保存、推定用プログラム、実行結果の確認までを順番に見る。
この記事で扱うこと
- MeCabで発話を単語列に変換する流れ。
- TfidfVectorizerで発話を素性ベクトル化する方法。
- LabelEncoderでラベルを数値化する理由。
- SVMモデルを保存し、別プログラムで読み込んで推定する流れ。
作業前に確認すること
| 確認項目 | 内容 |
|---|---|
| 学習データ | pid42で作成したda_samples.datを用意する。 |
| ライブラリ | MeCab、scikit-learn、dillを使える状態にする。 |
| 確認観点 | 学習、保存、読み込み、推定を分けて確認する。 |
注意したい点
| 注意したい点 | 確認する観点 |
|---|---|
| 素性ベクトルとラベルの混同 | Xは発話内容の数値表現、Yは発話行為タイプの数値表現。 |
| モデル保存の中身 | vectorizer、label_encoder、svcをセットで保存しないと推定時に同じ変換ができない。 |
| 学習データの品質 | 例文が偏ると、正しくない発話行為タイプを推定しやすくなる。 |
解説と実装サンプル
モデル学習の実装サンプル
先行記事で作成した学習データda_samples.dat(*1)をMeCab(*2)で最小単位の単語(形態素)に分割し、SVM(*3)で対話行為タイプを推定(モデル学習)する実装サンプル。
- (*1)Python - タスク指向型対話 : フレームベースの環境準備 > SVM(sklearn)と学習データの作成 > 学習データの作成
- (*2)Python - タスク指向型対話 : 状態遷移ベースの環境準備 > MeCab, SCXML > 対話の文章を解析する「MeCab」の概要とインストール
- (*3)Python - タスク指向型対話 : フレームベースの環境準備 > SVM(sklearn)と学習データの作成 > フレームと対話行為を推定するSVM(sklearn)の概要とインストール
- train_da_model.py
import MeCab
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import SVC
from sklearn.preprocessing import LabelEncoder
import dill
# MeCabの初期化
mecab = MeCab.Tagger()
mecab.parse('')
sents = []
labels = []
# generate-samples.txt の出力である samples.dat の読み込み
for line in open("da_samples.dat","r"):
line = line.rstrip()
# samples.dat は発話行為タイプ,発話文,タグとその文字位置が含まれている
da, utt = line.split('\t')
words = []
for line in mecab.parse(utt).splitlines():
if line == "EOS":
break
else:
# MeCabの出力から単語を抽出
word, feature_str = line.split("\t")
words.append(word)
# 空白区切りの単語列をsentsに追加
sents.append(" ".join(words))
# 発話行為タイプをlabelsに追加
labels.append(da)
# TfidfVectorizerを用いて,各文をベクトルに変換
vectorizer = TfidfVectorizer(tokenizer=lambda x:x.split(), ngram_range=(1,3))
X = vectorizer.fit_transform(sents)
# LabelEncoderを用いて,ラベルを数値に変換
label_encoder = LabelEncoder()
Y = label_encoder.fit_transform(labels)
# SVMでベクトルからラベルを取得するモデルを学習
svc = SVC(gamma="scale")
svc.fit(X,Y)
# 学習されたモデル等一式を svc.modelに保存
with open("svc.model","wb") as f:
dill.dump(vectorizer, f)
dill.dump(label_encoder, f)
dill.dump(svc, f)
以下、処理解説。
※ 機械学習では、推定対象となるものをラベルやクラスと呼ぶ。
今回のデータでは、発話行為タイプのrequest-weatherがラベルとなるため、コメントや変数名の表現もラベルとなっている。
- sentsとlabelsの作成
学習データda_samples.datを読み込み、それぞれの行に対して下記要領で発話文字列を単語分割したsentsと発話行為タイプlabelsをそれぞれ作成する。
- (*4)行の末尾の空白を削除し(rstrip)、タブで分割(split)する。
- (*5)1つ目分割結果(発話行為タイプ)を
daに、2つ目分割結果(発話文字列)をuttに格納する。 - (*6)mecabで最小単位の単語(形態素)に分割し(splitlines)、最後(EOS)であればループを終了、最後(EOS)でなければタブで分割(split)し、先頭単語(word)のみを単語配列(words)に追加する。
- (*7)単語配列(words)を空白区切りに置き換え
sentsに追加する。 - (*8)発話行為タイプ(da)を
labelsに追加する。
for line in open("da_samples.dat","r"):
line = line.rstrip()
# samples.dat は発話行為タイプ,発話文,タグとその文字位置が含まれている
da, utt = line.split('\t')
words = []
for line in mecab.parse(utt).splitlines():
if line == "EOS":
break
else:
# MeCabの出力から単語を抽出
word, feature_str = line.split("\t")
words.append(word)
# 空白区切りの単語列をsentsに追加
sents.append(" ".join(words))
# 発話行為タイプをlabelsに追加
labels.append(da)
- 発話情報の変換
上記で取得した発話内容sentsと発話行為タイプlabelsの関連付けを学習させるために発話情報を数値列に変換する必要がある。
その作成にはTfidfVectorizerを用いて変換する。
これを素性ベクトルと呼び、後続処理でラベルとの関連付け学習を行うために作成している。
fit_transformで発話内容sentsを素性ベクトルに変換したものがXで、発話情報にある単語の登場頻度と学習データから推測できる単語の重要度をもとに素性ベクトルを自動生成している。
# TfidfVectorizerを用いて,各文をベクトルに変換
vectorizer = TfidfVectorizer(tokenizer=lambda x:x.split(), ngram_range=(1,3))
X = vectorizer.fit_transform(sents)
- 変換後の数値列を
Yに保持
上記と同様にfit_transformで発話行為タイプlabels(ラベル)を数値列に変換したものをYに保持する。
# LabelEncoderを用いて,ラベルを数値に変換
label_encoder = LabelEncoder()
Y = label_encoder.fit_transform(labels)
- ラベルを取得するモデルを学習させる
コメントの通り、発話内容sentsを素性ベクトル化したXから、発話行為タイプlabels(ラベル)を数値列化したYを取得するモデルをSVMを用いて学習させている。
※ モデルとは素性ベクトルの各要素とラベルとの関連を定義した大量の数値データのこと指す。
# SVMでベクトルからラベルを取得するモデルを学習
svc = SVC(gamma="scale")
svc.fit(X,Y)
- svc.modelに保存
最後に素性ベクトル作成時に用いたvectorizerとラベルを数値列化する際に用いたlabel_encoder、そしてモデル学習時に用いた svc達をdillを用いて、ファイルに出力する。
# 学習されたモデル等一式を svc.modelに保存
with open("svc.model","wb") as f:
dill.dump(vectorizer, f)
dill.dump(label_encoder, f)
dill.dump(svc, f)
- 実行確認
上記実装サンプル(train_da_model.py)の実行確認
※ 実行後svc.modelが生成されていれば成功。
$ python ~/gitlocalrep/dsbook/train_da_model.py
学習結果の確認
前項で作成したsvc.modelが正しく推定できるか以下のテストプログラムを実行して確認する。
da_extractor.py(テストプログラム)
import MeCab
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import SVC
from sklearn.preprocessing import LabelEncoder
import dill
mecab = MeCab.Tagger()
mecab.parse('')
# SVMモデルの読み込み
with open("svc.model","rb") as f:
vectorizer = dill.load(f)
label_encoder = dill.load(f)
svc = dill.load(f)
# 発話から発話行為タイプを推定
def extract_da(utt):
words = []
for line in mecab.parse(utt).splitlines():
if line == "EOS":
break
else:
word, feature_str = line.split("\t")
words.append(word)
tokens_str = " ".join(words)
X = vectorizer.transform([tokens_str])
Y = svc.predict(X)
# 数値を対応するラベルに戻す
da = label_encoder.inverse_transform(Y)[0]
return da
for utt in ["大阪の明日の天気","もう一度はじめから","東京じゃなくて"]:
da = extract_da(utt)
print(utt,da)
以下、テストプログラム(da_extractor.py)の処理解説。
- SVMモデルの読み込み
まずsvc.modelを開き、上記で出力したvectorizer、label_encoder、svcをdillを用いて読み込む。
# SVMモデルの読み込み
with open("svc.model","rb") as f:
vectorizer = dill.load(f)
label_encoder = dill.load(f)
svc = dill.load(f)
- 発話から発話行為タイプを推定
発話行為タイプを推定するextract_daメソッド。
下記の要領で推定結果daを返す。
- (*9)mecabで最小単位の単語(形態素)に分割し(splitlines)、最後(EOS)であればループを終了、最後(EOS)でなければタブで分割(split)し、先頭単語(word)のみを単語配列(words)に追加する。
- (*10)単語配列(words)を空白区切りに置き換え
tokens_strに格納する。 - (*11)上記で読み込んだ
vectorizerをTfidfVectorizerを用いて素性ベクトルに変換する。 - (*12)(*11)の
Xと上記で読み込んだsvcのpredictを用いて推定結果を取得する。 - (*13)上記で読み込んだ
label_encoderをもとに数値列からラベル名に戻して返す。
# 発話から発話行為タイプを推定
def extract_da(utt):
words = []
for line in mecab.parse(utt).splitlines():
if line == "EOS":
break
else:
word, feature_str = line.split("\t")
words.append(word)
tokens_str = " ".join(words)
X = vectorizer.transform([tokens_str])
Y = svc.predict(X)
# 数値を対応するラベルに戻す
da = label_encoder.inverse_transform(Y)[0]
return da
- 推定結果の取得
テスト用の3つの発話内容で上記extract_daメソッドを呼び、発話行為タイプの推定結果を取得する。
for utt in ["大阪の明日の天気","もう一度はじめから","東京じゃなくて"]:
da = extract_da(utt)
print(utt,da)
- 実行結果
上記テストプログラム(da_extractor.py)の実行結果
それぞれの発話内容(左)に対して、正しい発話行為タイプの推定(右)ができている。
$ python ~/gitlocalrep/dsbook/da_extractor.py
大阪の明日の天気 request-weather
もう一度はじめから initialize
東京じゃなくて correct-info
実務とのつながり
- 意図分類の基本
問い合わせ分類、チャットBot、FAQルーティングなどでは、発話から意図を推定する処理が重要になる。 - 推論環境への展開
学習時に使った前処理器とモデルを一緒に保存しておくと、別プログラムで再利用しやすい。
まとめ
- MeCabで発話を単語分割し、TF-IDFで数値ベクトルに変換する。
- SVMは発話ベクトルから発話行為タイプを分類する。
- 推定時には、学習時と同じvectorizer、label_encoder、svcを読み込む必要がある。
参考文献
- 東中 竜一郎、稲葉 通将、水上 雅博(\\(2020\\))『Pythonでつくる対話システム』株式会社オーム社