自分をコピーするbotを作る

自分のTwitterアカウントをコピーするbotを簡単に作れるmirror botというものを作りました。@naoty_botはこれを使って作りました。

コピーbotを作る手順

1. bot用のアカウントを作ります。

2. 人間とbotそれぞれのアカウントでTwitterアプリケーションを作ります。ここから作れます。そして、人間とbotの両方のアカウント用の「Consumer Key」「Consumer Secret」「Access Token」「Access Token Secret」を取得します(下のスクショのモザイクかかってるところです)。

f:id:naoty_k:20141110230321p:plain

f:id:naoty_k:20141110230752p:plain

f:id:naoty_k:20141110230803p:plain

追記: bot用のアプリケーションを作成する際、権限をRead and Writeにする必要があります。一度Read Onlyでアクセストークンを発行している場合は権限を変更した後もう一度発行しなおして、Herokuアプリケーションの環境変数を新しいアクセストークンに替えてください。

3. こちらのHerokuボタンを押します(別サイトに飛びます)。もしHerokuのアカウントがなければ作ってください。

Deploy

4. 適当なHerokuアプリ名を入れて、「Env」の各項目に2.で取得した「Consumer Key」「Consumer Secret」「Access Token」「Access Token Secret」を入力します。人間のアカウントのものは「HUMAN*」に、botのアカウントのものは「BOT*」に入れていきます。

f:id:naoty_k:20141110232038p:plain

5.「Deploy for Free」ボタンを押してしばらく待ちます。Herokuにアプリケーションがデプロイされます。その後、アプリのダッシュボード画面で以下のようにdynosを1xにすると、アプリケーションが起動します。

f:id:naoty_k:20141110232352g:plain

6.終わり。

コピーbotの機能

  • 過去のtweetからランダムに選んで投稿します。ランダムに選ぶとき、現在の時間帯を考慮します。だから、朝には朝っぽいことをtweetするはずです。
  • 一日のtweet数やどの時間帯にtweetされる傾向があるかを計算し、そのパターンに従います。例えば、一日にたくさんtweetする人のbotはたくさんtweetしますし、あなたが通勤時間と帰宅時間にtweetする傾向があると、botもその時間帯にtweetする確率が高いです。
  • 話しかけるとreplyを返します。replyは過去にあなたがその人に返したreplyからランダムに選ばれます。
  • あなたがfavりやすいtweetを学習し、favります。

技術的な話

tweetするタイミングの決定

人間のtweetはHeroku Postgresqlのfree planの上限である10000レコードまで保存されます(上限を超えると古い順に消します)。そのとき、一日の中で何分目に投稿されたかを同時に記録します。たとえば、01:00の投稿は60分目だし、02:00の投稿は120分目、23:59の投稿は1439分目となります。すると、投稿数が多い分と少ない分がわかります。なので、n分に投稿される確率=n分の投稿数/総投稿数を計算することができます。ここから0分から1359分までの確率分布を作ることができます。この確率分布を累積分布にすると実装が簡単になります。そして、0から1までの乱数を生成して累積分布上の重なる分数をbottweetする分数として決定します。これを一日にtweetする回数分行い、その日tweetする分を事前に決定しておきます。

以上のようなことを行っているのが./lib/mirror_bot/scheduler.rbというファイルです。

favりやすいtweetの学習と分類

人間は大量に流れてくるtweetの中から特定のtweetだけを選んでfavっています。この行動は大量のメールの中から迷惑メールだけをゴミ箱送りにする行動と似ています。つまり、大量のデータから特定のカテゴリーに含まれるものを識別する、という問題に一般化できると考えました。そこで、スパムフィルタリングと同じアルゴリズムで、大量のtweetから特定のtweetだけをfavoriteというカテゴリーに分類する実装をしました。

スパムフィルタリングの実装はごく普通のベイジアンフィルタです。簡単に言ってしまうと、favられたtweetに含まれやすい単語とか含まれにくい単語を調べていくということをしていきます。形態素解析にはokuraを使いました。とても便利でした。各単語の各カテゴリーに含まれた回数はRedis(redistogo)に保存しています。

以上のようなことを行っているのが./lib/mirror_bot/classifier.rbというファイルです。

事前学習とHerokuボタン

以上の2つのモジュール、schedulerとclassifierを機能させるには事前に多くのデータを学習させることが必要です。schedulerについては過去3,200件のtweet、classifierについては800件ずつのfavったtweetとfavってないtweetを学習させています。これを行っているのがtrainerです。trainerは./lib/mirror_bot/trainer.rbで定義されており、./bin/mirror_botスクリプトから実行します。

Herokuボタンからbotをデプロイする場合、デプロイして起動するまでにtrainerを実行する必要があります。Herokuボタンによるデプロイを設定するapp.jsonにはscriptsという項目があり、こうしたセットアップのための設定が可能です。以下のように指定するだけです。

{
    "scripts": {
        "postdeploy": "bundle exec sequel -m migrations $DATABASE_URL && bin/mirror_bot train scheduler && bin/mirror_bot train classifier"
    }
}

これでデプロイしてから起動する直前に事前学習を実行することができました。

なぜ作ったか

最近、機械学習とか自然言語処理に興味が出てきて勉強をしはじめたのですが、何か具体的な目標がほしいと思って「ちょっと賢いbot」を作ることにしました。いろいろ試行錯誤した結果、自分の行動パターンを学習してまねするbotを作ることにしました。

得られた知見

  • botっぽいアイコンはhttp://robohash.org/で生成できて便利。
  • botによる発言を自動生成させようと試行錯誤したけど、結局コピーにすることにしました。まず、マルコフ連鎖を使った文章生成はbotっぽいけど人間らしさはないので、今回は却下。次に、word2vecを使って文章に含まれる単語を類義語と入れ替えることで似たような文章の生成を試みました。word2vecをRubyから使う術がないので、別プロセスでPythonのgensimを使った類義語サーバーを立ててプロセス間通信でRubyに返すみたいな実装をしてみました。ですが、これだとそもそもHerokuの無料枠では不可能でした。さらに、word2vecで使われるモデルファイルが大きすぎてディスクにのっかりません。VPSで挑戦してみましたが、今度は1GBのメモリにのっかりきらずに動きませんでした。思ったほど意味の通じる文章を生成できるわけでもなかったので、この方法は諦めました。
  • herokuのオペレーションをスムーズに進めるときにhttps://github.com/ddollar/heroku-confighttps://github.com/ddollar/heroku-redis-cliが便利でした。

参考

「集合知プログラミング」を読んでる

集合知プログラミング」という本を先週から読み始めた。この本は機械学習をテーマとしていて、現実にありそうな問題(例えば、映画の評点から似ているユーザーを推薦するとか、数ある旅行プランの組み合わせから最適なプランを選択するとか)を題材にさまざまなアルゴリズムチュートリアル形式で学んでいける。登場するサンプルコードはすべてPythonで書かれているため、まずこの本を読む前に軽くPythonについて勉強した。機械学習の本というと、むずかしい数式がたくさんでてきて近寄りがたいイメージがあるのだけど、この本についてはほとんど数式は出てこないので、カジュアルに読み進められる。

まだ半分も進んでないけど、その中で一番おもしろかったのが最適化アルゴリズムの話だった。ある最適な値を求めたいとき、「となりあう値と比較して良い方を選択する」というのを繰り返していくとどこか最適な値で落ち着くはずというアルゴリズムヒルクライム)があるのだけど、これだと局所最適に陥ってしまうということを最近勉強した。つまり、全体を見渡すともっと最適な値があるのだけど、近くの値とだけ比較しているとそれを見逃してしまうということだ。また、別のアルゴリズム(模擬アニーリング)は、試行回数が少ないうちは悪い結果を受け入れ、回数を経るにつれてその悪い結果を受け入れ難くしていくことで局所最適を回避する。

これはいろんなところで当てはまりそうな考え方だなと思った。見える範囲、理解できる範囲だけで最適な選択をとろうとするとより適切な解を見落としてしまう。若いうちは結果が悪かろうともそれを受け止めることで局所最適を回避し全体最適に近づくことができるのかもしれない。

この本を読もうと思った理由としては、いろんな領域と機械学習を組み合わせるとなんか面白いものが作れそうな気がしたから。これまで自分が作ってきたソフトウェアの中で自分自身気に入っているものの多くは別の領域のアイデアを持ち込むところから生まれている。だから、組み合わせの可能性が大きい領域を何か新しく学びたいと思ったときに機械学習というものが浮かんでてきてこの本から取り組んでみることにした。今は「iOS x 機械学習」みたいな掛け合わせで何か面白いものが作れないか考えている。

集合知プログラミング

集合知プログラミング