マルコフ連鎖

【PHP】api.aiの応答をマルコフ連鎖で文章生成してみた

※api.aiは、2017年10月にDialogflowへと名称変更されました。

前回、api.aiをAPIとして活用する方法について触れました。今回はその逆?あと?で、api.aiからの応答にも、何らかの学習結果を反映させてみよう、という内容です。

昨今では、「RNNによる学習結果から応答内容を生成する」みたいな事もよく見かけますが、とりあえずまずは「マルコフ連鎖」という”人口無能”なしくみからチャレンジしてみたいと思います。

マルコフ連鎖とは?

先日の佐賀&福岡でのチャットボットハンズオンの中でも取り上げたのですが

「ある事象の1つ先の未来を、これまでの経験から推測するモデル」の事で、文章に当てはめる場合は、形態素解析した単語群をDBに突っ込み、次の単語を予測し続けることで文章をつくる、といった感じになります。

一見難しい話のようですが、形態素解析された単語3つを1要素として配列に入れ、要素を繋ぎ直して文章を作るというもの。

https://takuti.me/note/twitter-bot/

この仕組みを使ってみます。

api.aiのResponse Text

応答文生成に必要な学習用データは、どのようにでも調達可能なのですが、今回はapi.aiをそのまま使ってみます。

api.aiは、Response Textに複数回答を要ししておけば、そのいずれかがランダムに帰ってくる仕様になっていますが、結局は固定の文章になってしまいます。これをある程度は文章生成させてみようかと。

対話例として、「あんたバカァ?」と聞いたら、アスカ風なマルコフ連鎖用文章データを返します。

文頭で「私」と言っているのは、それをキーといて文章を作るからですね。このResronse Textを、前回のようにAPIでJSONから抽出した後、マルコフ連鎖にかけます。

マルコフ連鎖のPHPコード

こちらを参考にしました。

http://omimo.net/code_snippets/php/1114/%E6%96%87%E7%AB%A0%E3%82%92%E8%87%AA%E5%8B%95%E7%9A%84%E3%81%AB%E8%A6%81%E7%B4%84%E3%81%99%E3%82%8B%E3%80%81%E7%9F%AD%E3%81%8F%E3%81%99%E3%82%8B%EF%BC%88%E5%BD%A2%E6%85%8B%E7%B4%A0%E8%A7%A3%E6%9E%90/

精度を高めるため文字列ではなく配列を使い、精度が高そうだったパターン1を採用。EOFまで文用生成を繰り返すととてつもなく長くなることがあるため、「。」「!」「?」といった文章の切れ目までの生成としたのが、次のsummarize関数。

function summarize($nodesArray) { if (is_array($nodesArray)) $words = $nodesArray; else return false; $table = array(); for ( $i = 0; $i < count($words) - 2; $i++ ) { $table[] = array( 'head' => $words[$i],'middle'  => $words[$i + 1],'end'  => $words[$i + 2]  ); } $t1 = $table[0]['head']; $t2 = $table[0]['middle']; $summary = $t1 . $t2; while (true) {  $a = array();  foreach ($table as $h) {if ($h['head'] === $t1 && $h['middle'] == $t2) $a[] = $h;  }  if (count($a) === 0) break;  $num = array_rand($a);  $summary .= $a[$num]['end'];  if ($a[$num]['end'] === "。" | $a[$num]['end'] === "?" | $a[$num]['end'] === "!") break ;  $t1 = $a[$num]['middle'];  $t2 = $a[$num]['end']; } return preg_replace('/EOS$/', '', $summary);}

これにより得られた応答が、以下。

文章はそれなりにそのままだったりかぶったりもしますが、それなりに生成もされていたりしてちょっと楽しいし、話していて飽きない。

api.aiの場合、自分で登録できるResponse textにも限りがあるので、こうやってパターンを無数に増やせるのは重要。やはり何かしら応答側の学習も必要ですね。

マルコフ連鎖は人工無能、と言えど、やる価値はあると思いました。