機械学習を使って作る対話システム

<h1>はじめに</h1>
最近はチャットボットが何かと流行っています。このような機械と自然言語で対話するシステムというのは昔から多くの人に期待されていました。
今回はそんな期待に応えるべく？機械学習を使って対話システムを作ってみました。

<h1>作成物の概要</h1>
今回作成したのは対話を通じてレストラン検索を行うシステムです。イメージとしては以下のような対話を行います。"U"とついているのがユーザ発話、"S"がシステム発話です。

<table>
<thead>
<tr>
<th>発話</th>
</tr>
</thead>
<tbody>
<tr>
<td>U：スペイン料理店を探して。</td>
</tr>
<tr>
<td>S：スペイン料理店ですね。場所はどの辺りですか？</td>
</tr>
<tr>
<td>U：新宿周辺</td>
</tr>
<tr>
<td>S：新宿のスペイン料理店ですね。予算の上限はどのくらいですか？</td>
</tr>
<tr>
<td>U：4000円くらい</td>
</tr>
<tr>
<td>S：4000円ですね。それではこのお店はどうでしょうか？</td>
</tr>
<tr>
<td>S：(店を出す)</td>
</tr>
</tbody>
</table>
このような対話を通じて、レストランの検索に必要な情報をユーザから取得し、レストラン検索を行います。
今回、レストラン検索には<a href="http://webservice.recruit.co.jp/hotpepper/reference.html">HotPepperグルメサーチAPI</a>を利用させていただきました。ありがとうございます。

<h1>システムアーキテクチャ</h1>
対話システムは複数のモジュールから構成されています。今回は、各モジュールは独立に動作させず、前段階のモジュールの処理が終わった段階で駆動されるようにしています。
最終的なシステムアーキテクチャは以下の図のようになりました。

<img width="736" alt="スクリーンショット 2017-04-19 9.51.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/8618f4dd-f371-4151-fc01-9efb7c8f1eb0.png">


今回のアーキテクチャに沿って処理の流れを説明すると以下のようになります。

<ol>
	<li>ユーザがテキストを入力すると、入力したテキストは言語理解部に入力されます。</li>
	<li>言語理解部では入力されたテキストを解析して、対話行為と呼ばれる抽象的な意味表現に変換します。</li>
	<li>言語理解部から出力された対話行為は、対話管理部に入力されます。対話管理部では入力された対話行為を基に、対話システムの内部状態を更新します。また、更新した内部状態を基にシステムの行動を決定し、対話行為を出力します。</li>
	<li>対話管理部から出力された対話行為は言語生成部に入力されます。言語生成部では入力された対話行為を基に、システム発話を決定します。</li>
</ol>
以上が今回作成する対話システムの処理の流れです。主に言語理解部に機械学習を使っております。

それでは、処理の流れもわかったところで、各モジュールで行っている詳細な処理について見ていきましょう。


<h1>言語理解部</h1>
言語理解部ではユーザの発話から対話行為を推定します。この対話行為の推定は対話行為タイプの推定と属性抽出から構成されています。ここで、対話行為タイプは発話の種類を表しており、属性は属性名と属性値からなります。

対話行為タイプをact_type、属性名をa、属性値をvと書くことにすると、対話行為はact_type(a<sub>1</sub>=v<sub>1</sub>, a<sub>2</sub>=v<sub>2</sub>)のように表せます。

この表記を用いてユーザ発話の対話行為を推定すると以下のようになります。

<table>
<thead>
<tr>
<th>発話</th>
<th>対話行為</th>
</tr>
</thead>
<tbody>
<tr>
<td>U：スペイン料理店を探して。</td>
<td>INFORM_GENRE(genre=スペイン料理)</td>
</tr>
<tr>
<td>S：スペイン料理店ですね。場所はどの辺りですか？</td>
<td></td>
</tr>
<tr>
<td>U：新宿周辺</td>
<td>INFORM_LOC(location=新宿)</td>
</tr>
<tr>
<td>S：新宿のスペイン料理店ですね。予算の上限はどのくらいですか？</td>
<td></td>
</tr>
<tr>
<td>U：4000円くらい</td>
<td>INFORM_MONEY(money=4000円)</td>
</tr>
<tr>
<td>S：4000円ですね。それではこのお店はどうでしょうか？</td>
<td></td>
</tr>
<tr>
<td>S：(店を出す)</td>
<td></td>
</tr>
</tbody>
</table>
ここで行っている対話行為タイプの推定と属性抽出について機械学習を用いて行います。それでは、各処理について順に説明していきます。

<h2>対話行為タイプの推定</h2>
言語理解部がユーザが入力したテキストを受け取ったら、対話行為タイプの推定を行います。
今回推定する対話行為タイプは以下の4つです。

<ul>
	<li>ジャンル指定（INFORM_GENRE）</li>
	<li>場所指定（INFORM_LOC）</li>
	<li>上限金額指定（INFORM_MONEY）</li>
	<li>その他（OTHER）</li>
</ul>
この対話行為タイプの推定は文書の分類問題と考えることができます。
したがって、データから学習済みの分類器があれば対話行為タイプの推定はできるはずです。以下のようなイメージです。

<img width="814" alt="スクリーンショット 2017-04-19 9.52.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/689695de-4f74-2dfa-2000-29df460815de.png">

今回は受け取ったテキストを形態素解析した後に、特徴抽出を行い、モデルの学習をさせました。
WatsonのNLC使えばもっと楽にできそうです。

<h2>属性抽出</h2>
言語理解部がユーザが入力したテキストを受け取ったら、属性抽出を行います。
今回抽出する属性は以下の3つです。レストランを検索する際にはこの3つの情報を使います。

<ul>
	<li>料理ジャンル</li>
	<li>場所</li>
	<li>予算上限</li>
</ul>
属性抽出には系列ラベリングを用います。系列ラベリングとは、入力が記号列であるときに、その各記号に対してラベルを振り、ラベル系列を出力する処理です。以下のようなイメージです。

<img width="840" alt="スクリーンショット 2017-04-19 9.53.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/acf746c6-ef7a-8dd4-a1c1-b185cae3527b.png">

B<sub>genre</sub>、I<sub>genre</sub> というラベルがこれらの文字列が属性値であることを表現しています。B は属性値の始まり、I は属性値が続いていることを意味しています。属性値でない部分には O というラベルが付与されます。このようにして属性抽出を行います。

今回は受け取ったテキストを形態素解析した後に、特徴抽出を行い、学習をさせました。以前私が書いた<a href="http://qiita.com/Hironsan/items/326b66711eb4196aa9d4">【チュートリアル】機械学習を使って30分で固有表現抽出器を作る</a>を見るとできると思います。

ここまでで得られた対話行為タイプと属性を対話行為として次の対話管理部に渡します。

<h1>対話管理部</h1>
対話管理部では以下の２つのことを行います。

<ul>
	<li>内部状態更新</li>
	<li>行動選択</li>
</ul>
それぞれについて何をしているのか見ていきます。

<h2>内部状態更新</h2>
内部状態更新では、言語理解の結果を基に対話システムの内部状態を更新していきます。内部状態の更新は規則を用いて行います。また内部状態には様々な情報を持たせることができますが、今回は簡単のためにユーザの意図だけを持たせます。ユーザの意図とは過去に得られた属性と属性値のことです。それでは内部状態更新の例を見てみましょう。

仮に現在の内部状態が以下のようになっているとします。

<img width="421" alt="スクリーンショット 2017-04-19 9.53.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/57626348-ba96-c062-365a-73196f2a0e13.png">

このとき、言語理解部から以下のような対話行為が得られたとします。

<img width="421" alt="スクリーンショット 2017-04-19 9.54.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/70047906-a124-9242-5c3a-65466395a514.png">

また、以下のような内部状態更新の規則があったとします。
<pre>if 対話行為タイプ == ジャンル指定:
    ユーザ意図[ジャンル] = スペイン料理
</pre>
この場合、内部状態は以下のように更新されます。

<img width="456" alt="スクリーンショット 2017-04-19 9.54.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/3805da41-2580-bff3-be43-c0446a1e7824.png">

ジャンル属性の属性値が埋まっていることがわかります。

<h2>行動選択</h2>
行動選択部では内部状態と規則をもとに次の行動を決定します。
具体的には、対話行為タイプを出力して次の言語生成に引き渡します。
この対話行為タイプをもとに、言語生成部でテキストを生成します。必要なら外部連携部を呼び出します。

たとえば、内部状態が以下のようになっていたとします。

<img width="456" alt="スクリーンショット 2017-04-19 9.54.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/3805da41-2580-bff3-be43-c0446a1e7824.png">

また、以下のような行動選択の規則があったとします。
<pre>if ユーザ意図[予算上限] == 未指定:
    output {対話行為タイプ: 上限金額要求}
</pre>
このとき、内部状態と行動選択規則から、対話行為タイプとして以下を出力します。

<img width="332" alt="スクリーンショット 2017-04-19 9.55.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/3ce9b686-731e-8585-71c7-8cecd573e6fe.png">

<h1>外部連携</h1>
外部連携部では、データベース呼び出しや外部API呼び出しを行います。
今回はレストラン検索をするために、外部APIとして<a href="http://webservice.recruit.co.jp/hotpepper/reference.html">HotPepperグルメサーチAPI</a>を使用しています。
対話を通じて、お店の場所、料理ジャンル、上限金額が得られたらAPIを使ってレストラン検索を行います。

<h1>言語生成</h1>
言語生成部では、規則と対話管理部から受け取った対話行為をもとに言語生成を行います。
たとえば、対話管理部から以下のような対話行為を受け取ったとします。

<img width="332" alt="スクリーンショット 2017-04-19 9.55.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/3ce9b686-731e-8585-71c7-8cecd573e6fe.png">

また、以下のような言語生成の規則があったとします。
<pre>if 対話行為タイプ == 上限金額要求:
    output 予算の上限はどのくらいですか？
</pre>
このとき、規則と受け取った対話行為タイプから「予算の上限はどのくらいですか？」という文を生成してユーザに提示します。

以上が言語生成部で行っていることです。

<h1>完成したシステム 「HotPepperGourmetDialogue」</h1>
完成したシステムは「<a href="https://github.com/Hironsan/HotPepperGourmetDialogue">HotPepperGourmetDialogue</a>」と名付けました。
以下のGIF画像のように動作します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/77079/be0b57a4-6c18-1255-4bb4-10013037e903.gif" width="350" height="170" />

どうでしょう？簡単なシステムですが動くと嬉しいものです。

<h1>ソースコード</h1>
以下のリポジトリからソースコードをダウンロードして動かすことができます。
<font color="red">スターつけていただけるとやる気がでますm(＿＿)m</font>

<ul>
	<li><a href="https://github.com/Hironsan/HotPepperGourmetDialogue">HotPepperGourmetDialogue</a></li>
</ul>

<h1>おわりに</h1>
今回は対話を通じてレストラン検索を行う簡単な対話システムを作成してみました。作成したシステムはコマンドラインで動作するテキストベースのアプリケーションでしたが、媒体としては、ChatBotやVirtual Agent、Pepperのようなロボットと組み合わせてもいいですし、技術としては画像処理、動画処理やタブレットからの入力と組み合わせてマルチモーダル化したりすることもできると思います。

今後の課題としては以下の点が挙げられます。

<ul>
	<li>入力エラーへの対応</li>
	<li>学習データ収集方法</li>
	<li>評価方法</li>
</ul>
入力エラーへの対応は、特に音声対話システムでは必須だと思われます。このような状況に対応するために現在はPOMDPを用いた対話管理について勉強中です。また、今回は対話管理部でルールを用いて内部状態更新と行動選択を行っていましたが、POMDPのような強化学習の枠組みを使うことでルールを明示的に書かなくても対話管理ができるようになると考えています。
また、十分な量の学習データの収集や評価も難しいです。論文を読むとユーザシミュレータを作成して行うことが多いようなので取り組んでいきたいです。

今回の解説が何かのお役に立てれば幸いです。

<h1>参考資料</h1>

<ul>
	<li><a href="http://www.amazon.co.jp/%E5%AF%BE%E8%A9%B1%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0-%E8%87%AA%E7%84%B6%E8%A8%80%E8%AA%9E%E5%87%A6%E7%90%86%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E4%B8%AD%E9%87%8E-%E5%B9%B9%E7%94%9F/dp/433902757X/ref=sr_1_1?ie=UTF8&amp;qid=1460355114&amp;sr=8-1&amp;keywords=%E5%AF%BE%E8%A9%B1%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0">対話システム</a></li>
	<li><a href="http://qiita.com/Hironsan/items/326b66711eb4196aa9d4">【チュートリアル】機械学習を使って30分で固有表現抽出器を作る</a></li>
</ul>