Rubyでマルコフ連鎖botを作る話[4/3:実装編]
前回のラブライブ!
マルコフ連鎖を理解しbotの実装を考えた mwc922-hsm.hatenablog.com
Rubyでマルコフ連鎖botを実装する
ここまで来たらあとはやるだけ。やりましょう。
まずはTwitterAPIを使うためのbotクラスを定義していきます。
class Bot attr_accessor :client def initialize @client = Twitter::REST::Client.new do |config| config.consumer_key = CONSUMER_KEY config.consumer_secret = CONSUMER_SECRET config.access_token= OAUTH_TOKEN config.access_token_secret= OAUTH_SECRET end end def post(text = "") @client.update(text) end # user_nameのツイートを過去tweet_count個取得する def get_tweet(user_name, tweet_count) tweets = [] @client.user_timeline(user_name, {count: tweet_count, exclude: retweets}).each do |timeline| tweet = @client.status(timeline.id) if not tweet.text.include?("RT") tweets.push(tweet2textdata(tweet.text)) end end return tweets end end
とりあえずツイートをするためのpostメソッドとツイートを取得するためのget_tweetメソッドを実装しておきました。
次に、形態素解析をするためのNattoParserクラスを作ります。こんな名前でいいのかはわかりません。
class NattoParser attr_accessor :nm def initialize() @nm = Natto::MeCab.new end def parseTextArray(texts) words = [] index = 0 for text in texts do words.push(Array[]) @nm.parse(text) do |n| if n.surface != "" words[index].push(n.surface) end end index += 1 end return words end end def genMarcovBlock(words) array = [] # 最初と最後はnilにする words.unshift(nil) words.push(nil) # 3単語ずつ配列に格納 for i in 0..words.length-3 array.push([words[i], words[i+1], words[i+2]]) end return array end
命名ガバガバです。parseTextArrayは与えられた文字列配列を分割された単語の配列の集合に変換します。
genMarcovBlockは単語の配列から3単語ずつのブロックを生成します。
最後にマルコフ連鎖をする関数を作ります。
def findBlocks(array, target) blocks = [] for block in array if block[0] == target blocks.push(block) end end return blocks end def connectBlocks(array, dist) i = 0 for word in array[rand(array.length)] if i != 0 dist.push(word) end i += 1 end return dist end def marcov(array) result = [] block = [] # nilから始まるブロックを選ぶ block = findBlocks(array, nil) result = connectBlocks(block, result) # resultの最後の単語がnilになるまで繰り返す while result[result.length-1] != nil do block = findBlocks(array, result[result.length-1]) result = connectBlocks(block, result) end return result end
marcovがマルコフ連鎖ループ本体です。
resultに最終的に生成される単語の集合、blockは考えられるブロックを一時的に格納しておく配列です。
マルコフ連鎖ループの中で何度も呼び出される
- 考えられるブロックを抽出する
- ブロックを連結する
の操作は個別に関数にしてあります。
完成
ということで完成したものがこちらです。
なんか一気に飛ばした気もしますがコード全部載せても仕方ないのであとの細かいところはGitHubを参照してください。
マルコフ連鎖botは簡単なのでみんな作りましょう。自分の文体でbotが支離滅裂な文章を生成してツイートしてるの見るのは結構面白いです。