MeCab(Natto) + マルコフ連鎖 ( + Ruby )でレシピ文章を自動生成①



暇なのでMeCabを用いてマルコフ連鎖を試してみようと思い、Rubyで実装してみました。素人です。

できたものはこれです。
簡単レシピ創作:http://tools.beightlyouch.com/cook
制作時間:1日




《新しいレシピを創作する》を押すと、膨大なレシピ文章を元に文章を自動生成し、
新しいレシピ文章が表示されます。

新しい料理がいつかできるかもしれません。やってみてください。


以下ではマルコフ連鎖について、そして実際に書いた具体的なコードについて、いくつかの記事に分けて説明します。


MeCabとは


MeCabとは、オープンソースの日本語用形態素解析エンジンです。
形態素解析については、実際にMeCabを動かした結果を見ると分かりやすいでしょう。
今回はRubyからMeCabを使うために、NattoというGemを用いています。

gem 'natto'

bundle installします。
以下が、使い方の一例です。

require 'natto'

test_str = '我は海の子'

natto = Natto::MeCab.new
natto.parse(test_str) do |n|
  puts "#{n.surface}: #{n.feature}"
end

これを実行すると

我: 名詞,一般,*,*,*,*,我,ワガ,ワガ
は: 助詞,係助詞,*,*,*,*,は,ハ,ワ
海: 名詞,一般,*,*,*,*,海,ウミ,ウミ
の: 助詞,連体化,*,*,*,*,の,ノ,ノ
子: 名詞,一般,*,*,*,*,子,コ,コ
: BOS/EOS,*,*,*,*,*,*,*,*


のように、品詞ごとに分解、そしてそれぞれを解析してくれます。これが形態素分析らしいです。ちなみに変数nには,
#<Natto::MeCabNode:0x00007fa5c7071038 @pointer=#<FFI::Pointer address=0x000000010dc790e0>, stat=0, @surface=”我”, @feature=”名詞,一般,*,*,*,*,我,ワガ,ワガ”>
例えばこんなものが入っているらしく、区切った単語そのものを”surface”, 解析したものを”feature”と呼んでいるようです。

マルコフ連鎖とは


マルコフ連鎖の概要はWikipediaに任せます。マルコフ過程とは、

未来の挙動が現在の値だけで決定され、過去の挙動と無関係であるという性質を持つ確率過程である。

https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%AB%E3%82%B3%E3%83%95%E9%81%8E%E7%A8%8B


黄色い服、青い服、赤い服を毎日選んで着る人がいるとします。
日ごとに、以下のチョイスで服を選んでいる①、②の2人がいるとしましょう。
(青⇆黄色への遷移確率は①、②ともに一緒とします)

① 黄色 => 黄色 => 黄色 => 黄色 => 黄色 => 青
②  青 =>  青 =>  青 =>  青 =>  青 => 青

さて、7日めに2人は何色の服を着る可能性が高いでしょうか。
①は1〜5日ずっと黄色の服を着ています。綺麗好きからすると、黄色の服は洗濯して、青を着るべきでしょう。同じ理屈から、②は一度も着ていない黄色を着るべきです。

ただし、マルコフ過程においては、現在(6日目)の情報のみで未来が決定されるため、1 ~ 5日に何を着ていたかは、一切関係ありません。
よって、①と②は、同じ確率で服の色をどれにするか選ぶことになります。


マルコフ連鎖で文章を生成する


以上のような理屈で、文章を生成していきます。つまり、自然言語がマルコフ過程によって組み立てられるとして、単語をつなぎ合わせていきます。

以下の2文について考えます。
我は海の子です
我は山の神です

まず、MeCabを用いた形態素分析によって、単語ごとに分解して書きます(分かち書き)。


文頭を”我”から始めてみます。2文とも”我”の次は “は” なので、”は”に移ります。”は”から遷移できる単語は、”海”と”山”の2つがあります…このようにやっていくと、以下のようになり、


我は山の神です
我は海の子です

---------------------------------------------------------------------------------------
我は山の子です


我は海の神です

このように、新しい2文が生成されました。具体的なこの部分のプログラムは後ほど紹介します。


N階マルコフ連鎖

先ほどの2文に1つ付け足し、3文で同じことをしてみます。

我の名はゼウスです
我は海の子です
我は山の神です


結果はこのようになります。


新しく生成された文

我は山の神です
我は海の子です
我の名はゼウスです

---------------------------------------------------------------------------------------
我は海の神です

我は山の子です

我の神です

我の子です

我の子は海です

我の子は山です

我の名は海です

我の名は山です

我はゼウスの名です

我は山の名です

我は海の名です


1文増やしただけで生成された文がずいぶん増えました。しかし、”我はゼウスの名です” “我の名は山です” など、少し意味の通りにくい文が生成されてしまいます(この文はまだ意味が分かる方かもしれませんが)。ユニークで詩的な文としては素敵ですが、改善の余地があります。

原因は、マルコフ連鎖の副作用です。《我は海の子》の《の》と、《我の名はゼウス》の《の》が同列に扱われるため、《の》の前に《海》がこようと《我》がこようと《神、子、名》のどれかが続くことになります。

これを解決する方法に、N階マルコフ連鎖があります。仕組みは今まで見てきたマルコフ連鎖とほとんど同じものです。

N=2、 2階マルコフ連鎖の場合

N=2の場合を考えてみます。今までの場合、現在の状態だけで未来の情報を決めていましたが、今度は1つ前の状態も勘案して決めることになります (つまり、今までのマルコフ連鎖はN=1でした)。

実際に先述の3文でやってみましょう。

分かち書きをした単語を、先頭から2つずつグループ分けします。


以下の図から分かるように、《我, の》から続く物は《名》しかありません。そのため、以前のように《我の神》《我の子》のような文章はできません。
また、《山, の》から続く単語は《神》以外にありません。そのため、《海の神》《山の子》といったフレーズもできません。


そのため、今回の条件下では、新しく生成される文章はありません。条件が厳しくなっているため、フレーズ生成の自由度が下がっているからです。Nの数、元の文章の数を調整することで、意味不明さとフレーズ生成の自由度を天秤にかけて、より優れたオリジナル文章を生成します。

Nを大きくすればするほど、日本語として意味の通りやすい文章になります。
逆に、Nが大きくなると、自由度を失い、独自のオリジナル文章ができにくくなります。



以上が、マルコフ連鎖のまとめになります。
次回は実際のプログラムではどのように実装したのか見ていきます。

コメント

タイトルとURLをコピーしました