いんたーねっと日記

141文字以上のものを書くところ

@ymrl_dnhrは@dnhrに改名しました+人工無脳dnhrの話いろいろ

4限の空きコマの思いつきで、@ymrl_dnhrを@dnhrに改名してみました。前の名前だとNatsuLion for iPhoneで@ymrl_dnhr宛てのリプライが僕宛てのリプライみたいに表示されてしまったり、エゴサーチにヒットし過ぎてウザかったりというのが主な理由です。Twitter検索で「ymrl or やまある」したフィードを購読してたら半分以上がbotのこと指してたんだぜ……!!
dnhrの名前の意味は、「ダイナミック ネガティブ ハイパー ローター(Dynamic Negative Hyper Rotor)」らしいです(略語復元ジェネレーター)。

@dnhrの仕様について

  • Followingの発言から単語を覚えて、5分くらいごとに文章を生成してPost
  • 毎時0分には時報っぽいことをする
  • Followすると1時間以内にFollow返しされる
    • ただし、最後のPostが「日本語っぽいPost」じゃないとFollowしない
    • 「日本語っぽくない」と判断されても、1時間おきに「日本語っぽいPost」をしているか見るので安心してください
  • Removeすると1時間以内にRemoveされる
  • 「@dnhr」で始まるPostは5倍よく覚える
    • 「@dnhr」の部分は無視して、次の単語から始まる文として認識します
  • ときどき文字化けする

もうちょっと詳しく

dnhrの中身は実質初めて作ったRubyのプログラムなので、同じようにプログラムの勉強とか練習とかをしてみたい人のために、dnhrの中でどういう処理がされているのかを書いておきます。
dnhrを実装するときには人工無脳は考えるというサイトをかなり参考にしたのでこちらもご覧ください。

学習のしくみ

人工無脳の作りかたについて調べると、たいてマルコフ連鎖形態素解析が出てくるので、たぶん非常に重要なんだと思います。マルコフ連鎖について書かれたWikipediaのページを理解するほどの脳がないんですが、どうやら人工無脳を作る上で重要なのは、「単語どうしの前後の繋がりが保持されていればそこそこ文っぽいものになるよ」という話みたいです。本当にそうなのかは知りません。
とにかく、dnhrがやっていることは、タイムラインにある日本語の文章を単語ごとに区切って、それを2個ずつの単語の繋がりにして保存してそれを再構築しているよということです。英語の場合は単語の区切りがスペースで明示されているのですが、日本語はそうはいかないので、形態素解析プログラムというのを使う必要があります。dnhrで現在使われているのは、MeCabというやつですが、作った当初はYahoo!Japanの形態素解析APIを使っていました。圧縮新聞Yahoo!のAPIで動いているらしいです。ちなみにこのphaさんの圧縮新聞の解説のほうが100倍勉強になると思います。
たとえば、「今日は日曜日でいい天気だったので一日中ひきこもってインターネットしていた」という文をMeCab形態素解析するとこうなります。

今日	名詞,副詞可能,*,*,*,*,今日,キョウ,キョー
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
日曜日	名詞,副詞可能,*,*,*,*,日曜日,ニチヨウビ,ニチヨービ
で	助詞,格助詞,一般,*,*,*,で,デ,デ
いい	形容詞,自立,*,*,形容詞・イイ,基本形,いい,イイ,イイ
天気	名詞,一般,*,*,*,*,天気,テンキ,テンキ
だっ	助動詞,*,*,*,特殊・ダ,連用タ接続,だ,ダッ,ダッ
た	助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
ので	助詞,接続助詞,*,*,*,*,ので,ノデ,ノデ
一	名詞,数,*,*,*,*,一,イチ,イチ
日	名詞,接尾,助数詞,*,*,*,日,ニチ,ニチ
中	名詞,接尾,副詞可能,*,*,*,中,チュウ,チュー
ひきこもっ	動詞,自立,*,*,五段・ラ行,連用タ接続,ひきこもる,ヒキコモッ,ヒキコモッ
て	助詞,接続助詞,*,*,*,*,て,テ,テ
インターネット	名詞,一般,*,*,*,*,インターネット,インターネット,インターネット
し	動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
て	助詞,接続助詞,*,*,*,*,て,テ,テ
い	動詞,非自立,*,*,一段,連用形,いる,イ,イ
た	助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。	記号,句点,*,*,*,*,。,。,。

こんな感じで、文章がどんな単語でできていてその単語がどんな品詞なのかを知ることができるのが形態素解析のいいところです。ですが、品詞ごとに処理をするなんて非常に面倒なので、dnhrでは単語で区切れればいいやという使い方しかしていません。つまり、「今日は日曜日でいい天気だったので一日中ひきこもってインターネットしていた。を「今日 は 日曜日 で いい 天気 だっ た ので 一 日 中 ひきこもっ て インターネット し て い た 。」というふうに区切れればいいのです。
形態素解析によって単語に区切られた書き込みは、2つごとの組にして出現頻度をカウントされます。上の例だと、[今日-は][は-日曜日][日曜日-で][で-いい][いい-天気][天気-だっ][だっ-た][た-ので][ので-一][一-日][日-中][中-ひきこもっ][ひきこもっ-て][て-インターネット][インターネット-し][し-て][て-い][い-た][た-。][。-(文章おわり)]という感じにして、それぞれの組についてこれまでに何度出たかという数に1を足していきます。
また、先頭にある単語(上の例だと「今日」)についても、同じように最初の単語としての出現頻度をカウントしておきます。これは文を組み立てるときのキーになっています。
「dnhr宛てのリプライは5倍早く覚える」というのも、実は大したことはやっていなくて、単純にこれまで出た回数に1を足すのではなく5を足しているだけです。

文の組み立て

dnhrは文を作るときに、まず最初の単語を決めなければなりません。学習のときに最初の単語の出現頻度も数えていたのはこれのためです。最初の単語を特定の何かに決める理由などは思いつかないので、これまでに最初の単語として出てきたものの中からランダムにひとつを選びだします。このとき、先頭語として出現頻度の高い単語はそのぶん高確率で選ばれるようにしてあります。ここでは、「昨日」という先頭語が見つかったとします。
先頭語が決ったら、次にくる単語を探すわけですが、ここで2つごとに区切って記憶した意味が出てきます。2つの単語のうち、先の単語が先頭語と同じものを探せばいいのです。複数ある場合はランダムで、やはり頻度の高いものが高確率で優先して選ばれるようになっています。[昨日-は][昨日-、][昨日-の]などから1つ選ぶと2つ目までの単語が決まるわけです。
3つ目以降は、2つめを選ぶときと同じように2つ目を先頭にもつ繋がりを探して3つめを決めて、3つ目を先頭にもつ繋りを探して4つめを決めて……というふうに繰り返していきます。文章終わりの記号が来たら(実際のプログラム中ではnilだったら)終わりということになります。140字制限を超えないようにする工夫をしたりはしてませんが、とてつもなく長くなる確立はすごい低いだろうと思って放ってあります。