botに賢く会話をさせる
人生どうでも飯田里穂です、こんにちは
かねのbotの作成から1年弱です。プログラミングの技術も多少は向上してきて、以前書いたコードがスパゲティにしか見えなくなってきたので、そろそろコードの書き直しついでに全面的にバージョンアップさせようと思っています。
そこで、会話機能を少し改良するところから始めることにしました。
まず最初に、かねのbotならではの制約、というかポリシーといったものを。
「かねの本人のツイートデータベースがあるので、つぶやくツイートは全てそこから選択する。なるべく内容も改変せずそのままの文章を使用する。」
つまり、返信文として「どのツイートを選択するか」というのが今回の問題になります。
さて、ぼくは情報系の人間ではないので、高度な自然言語処理とかはできないです。
そのため、自分が実装できる範囲の簡単なアルゴリズムを考える必要がありました。
リプライが飛んできたとき、その文章の返信として一番ふさわしいツイートをデータベースから選択する。
例えば
「今日、松屋に行きますか?」
と話しかけられたときどういう返答が自然か。
ちなみに、「はい」や「うん」などの相づちは確かに会話として成立しますし不自然でもありませんが、botとしての性質を考えてこのような返答は不適切としておきます。
「はい、行きます」
「松屋が私の生きがいです」
「今日は眼科に行かないといけないので…」
などが適切な返答でしょうか。
つまり、「今日」、「松屋」、「行く」など文章中に出現した単語に言及していればそこまで不自然な会話にはならなさそうです。
というわけで、このアイディアに沿って返信文選択を実装してみることにします。
リプライとして送られた文章と、共通する単語を多く含むツイートを返信文として選択する
これをプログラム上で機械的に行う必要があります。
そこで、まず最初に目をつけたのは形態素解析。
日本語を機械的に処理するためにはコレがないと始まらないのですが、幸いにも使いやすいライブラリがあり、以前触ったこともあったので活用することにしました。
かの有名な形態素解析エンジンMeCabの.NET移植版である、NMeCabです。
使い方はこちらを参考にしました。
NMeCabで形態素解析 | too young to die , too drink to live
このライブラリを使って、ツイートデータベースの各文を形態素に分けます。
ツイートデータベースは約60000件あり、さすがに起動時に毎回解析し直すのは時間がかかるので、解析結果のうち必要な情報だけを抽出しツイートデータベースに付け加える形であらかじめ保存しておきました。
各ノードは品詞と原形の情報だけを抽出しています。
ちんちんは副詞なのか…
さて、この操作によって文中の単語を抜き出し、原形に統一できたので、先ほどのアイディアにある「共通する単語」を見つけるのは簡単そうになりました。
定量的に評価する手法を探します。
「共通する単語が多い」というのはつまり「文同士が似ている」わけで、文同士の類似度を測る方法を利用できないか考えます。文同士の類似度計算となれば需要はあるでしょうし、情報は多そうです。
調べてみると、文同士の類似度を測る一般的な方法に、どうやらコサイン類似度という量を計算する方法があるようです。
これは単語単位での共通項の数を積算する方法で、今回のアイディアにピッタリです。
おまけにコードも簡単です。
参考URLでは単語の種類によって適度な重み付けをすることも提案されているので、せっかくなので適当に重み付けをすることにします。
文意への寄与をパっと考えると、名詞を重く、助詞・助動詞を軽くした方がよさそうなので、他の品詞の重みを1として名詞を重み2、助詞・助動詞を重み0という感じで少し大げさに設定してみました。
これでアルゴリズム(?)は完成です。後はこれらの処理を行うクラスやメソッドを実装するだけです。
今回は
形態素解析用の専用のクラスを作り、
そのメンバーとして文と形態素の配列を与え、
インスタンス同士のコサイン類似度を計算するメソッドを組み込みました。
ツイートデータベース全体についてforループで一つ一つ文をインスタンス化し、それぞれリプライ文と類似度を計算→一番高かったツイートを採用という流れになります。
コンソールで適当に会話を試した結果がこちら
こんなもんでしょうか。
NMeCabの辞書ファイルの編集、品詞の重み付けの変更、類似度が極端に低い場合の別処理など改良しがいはあると思います。