すなぶろ

Pythonistaを目指しているつもりが、Hyのおかげで無事脱線。Clojurian目指すことになりました。

Python初心者に送る「人工知能の作り方」 Part6 前編

このシリーズも佳境となり、ついにAIが初めての自分の言葉を発しました。人間の赤ちゃんだって親の真似をするのですから、前回は猿真似をする猿から一気に人間の赤ちゃんまで進化したと言えます。

sandmark.hateblo.jp

今回はさらに踏み込んで、「ゼロから言葉を作る」という試みをやってみましょう。しかし課題が課題なので、今回は実装方法の説明に留め、次回から本格的にコーディングしていきます。

f:id:sandmark:20171012013103p:plain
ゼロから始めるマルコフ連鎖


既存のアルゴリズムを利用する

今回はUnmoResponderには触れません。下準備として、マルコフ連鎖というアルゴリズムを使用します。Wikipediaには難しいことが書いてありますが、ここでプログラムと数学の不思議な関係をひとつ言わせてください。

アルゴリズムを考え出すのは数学屋の仕事。アルゴリズムを実装するのは技術屋の仕事。

「ここの処理どうしよう」といった実装面での議題はプログラマが解決するものですが、「素数を求める速いアプローチを考える」というアルゴリズムに片足を突っ込んだ議題は数学者と協力すべきです。エラトステネスの篩が代表的な例で、このアルゴリズムをCPUやメモリと相談しながら実装するのがプログラマです。

今回はマルコフモデルを用いますが、これは数学者の提唱した定理なので、「これをプログラムに落とし込むにはどうするか」を考えるのがテーマです。

2つの文の共通点を見つける

たくさんの猿に無限の時間を与えてタイプライターを叩かせていれば、いつかはシェイクスピアに到達する。

という話があります。裏を返せば、文字を組み合わせる方法が完全にランダムである場合、意味を持つ文章になる確率はゼロに限りなく近いものになるということでもあります(それが理由で第二次AIブームが終わったのですが、ディープラーニングの発見により現在は第三次AIブームが到来しています)。

では、文章を構成する「ルール」のようなものはないでしょうか。日本語の文法を覚えさせるのは手間になりそうなので、もうちょっとラフな形で探してみます。以下の文章を見てください。

あたし -> は -> おしゃべり -> が -> 好き -> な -> プログラム -> の -> 女の子 -> です

「あたし」の次には「は」が来ます。「は」の次は「おしゃべり」で、その次には「が」……というように続いています。もうひとつ別の文章を見てください。

あたし -> が -> 好き -> な -> の -> は -> おしゃべり -> と -> 月餅 -> です

これら2つの文章の単語のつながりをまとめてみると、こんな感じになります。

単語 続く可能性のある単語
あたし は, が
おしゃべり
おしゃべり が, と
好き
好き
プログラム,の
プログラム
女の子, は
女の子 です
です END
月餅
月餅 です

この表を見ていると、「あたし」の次に続く単語は「は」あるいは「が」で、どちらも意味の通じる正しい日本語です。ここから新しい文章を作ることができないでしょうか。

「あたし」の次に「は」を選択します。「は」に続くのは「おしゃべり」だけなのでこれを選択し、次の「が」あるいは「と」では「と」を選択します。「と」のあとは「月餅->です->END」と一直線なので、これを続けてみると

あたしはおしゃべりと月餅です

という文章ができあがりました。意味はよくわかりませんが、日本語として間違ってはいません。それどころか、テンプレート辞書を用いていないのにまったく独自の文章を作り出しています。また、適度にランダム性があります。

マルコフモデル -- 機械学習との違い

文章に登場する単語の順序には法則性があります。広い意味では「文法」と呼ばれるべきものですが、手本とする文章(ユーザーの発言など)から単語といっしょに「つながり」に関する情報を抽出することで、この法則性を学習することができます。

文章を生成するときは単語と「つながり情報」をもとに、複数の選択肢があればランダムに選択しながら単語をつないでいきます。この結果、単語のつながりは保たれたまま、複数の文章が混ざったような文章が得られます。

文章中に単語Aが登場したとき、次にどんな単語が登場するかは単語Aによってある程度絞られます。ある状態が起こる確率が直前の状態から決まることを『マルコフ連鎖』といい、マルコフ連鎖によって状態が偏移することを表した確率モデルを『マルコフモデル』と呼びます。

チャットボットを作る上で、このマルコフ連鎖はよく使用されるアプローチです。しかし同時に、このモデルを用いて学習したAIはマルコフ連鎖の限界を超えられません。「猫」を含む文章を作り出すことはできますが、「猫」が具体的に何を意味する言葉なのかを判別できないからです。最近になってAIが注目され始めた背景には、「AIの学習方法」が劇的に変化したという理由があります。AIが自らの学習方法を自ら定義するというディープラーニングの実用化により、単一のコンピュータではなくネットワークを介した数万ものCPUを演算させ、ネットワークに存在する膨大なデータを学習させることで、今やコンピュータは「猫」を認識できるようになりました。チャットボットにおいては、真の意味での『感情』が理解される日もさほど遠くないでしょう。

マルコフ連鎖を実装するにあたって

今回はこのマルコフ連鎖を採用し、「単語のつながり」を学習するマルコフ辞書を作成してみましょう。A -> Bという単語のつながりにおいては、AprefixBsuffixと呼ぶことにします。マルコフ辞書の学習とは、形態素解析されたprefixsuffixのペアとして記録することです。

prefixがひとつだけだと、再現される一番短いつながりは2単語。これでは構成要素が細かすぎて意味の通らない文章になる確率が高くなってしまいます。かといって単語数を多くすると、もとの文章がそのまま出力されやすくなる傾向があります。prefixを2つくらいにしておくと、バランスの取れた文章が生成されるようです。

先ほどの2つの文章を、2単語(prefixが2つ)のマルコフ辞書で表してみます。

Prefix1 Prefix2 Suffix
あたし ->おしゃべり
あたし ->好き
おしゃべり ->が,と
おしゃべり ->好き
おしゃべり ->月餅
好き ->な
好き ->プログラム,の
プログラム ->の
->は
プログラム ->女の子
女の子 ->です
->おしゃべり
女の子 です ->END
月餅 ->です
月餅 です ->END

2 prefixesモデルのマルコフ辞書では、「あたし は」と「あたし が」が別のprefixとして区別されています。

文章を生成するときは、prefixの2単語に続くsuffixをランダムに選択し、prefix2といま選択したsuffixを新たなprefixとして、それに続くsuffixをまた選択し……という処理を、ENDマークに到達するか、決められた回数になるまで繰り返します。やってみましょう。

まずは適当に「あたし が」を選択します。続くのは「好き」のみなので「あたし が 好き」になります。「が 好き」に続く単語のうち該当するのは「な」だけなので、「あたし が 好き な」となります。「好き な」に続くのは「プログラム」か「の」ですが、「プログラム」を選択してみます。あとは一本道で「の 女の子 です」と続き、結果は

あたしが好きなプログラムの女の子です

となります。大丈夫かな、これ……。

次の課題

今回は設計の説明に紙面を費やしたので、これを実装するのは次回に回しましょう。新たにmarkov.pyというファイルを作り、Responderとして追加する前にマルコフ連鎖をテストしてみます。

Python的には

  • リスト操作
  • ジェネレータ

なんかが焦点になってきます。