ページ

2012-06-11

[web] ”ろぐほー”の内側 (形態素 マルコフ連鎖 ツイート)

自作Webサービス”ろぐほー”に使われている技術の一部を紹介したいと思います。

”ろぐほー”では、はてブのエントリーを記録するついでに、ホットエントリーを要約したつぶやきを作成し、Twitterでボットにつぶやかせています。

使用している技術は特別なものは特に無く、ネット上に転がっている色々な技術を組み合わせただけです。
使用した技術を並べると以下のものになります。

  1. ホットエントリー取得プログラム(自作)
  2. 形態素解析 (Yahooの日本語形態素解析API)
  3. マルコフ連鎖 (よくわかんないけどぐぐると出てくる)
  4. twitter4j (Javaでtwitterのプログラムを作るためのライブラリ)
  5. Linuxのコマンド数個
後で、一つずつ説明していこうと思います。

全体としては、ホットエントリー取得プログラムの中に形態素解析とマルコフ連鎖の機能が入っています。

プログラミング言語としてJavaが一番得意なので、Javaを使って記述しています。と言っても、まともにアルゴリズムを実装した部分はマルコフ連鎖の部分だけで、あとはHTTPでURLにアクセスしたり、その結果を読み込んだりしただけです。


1.ホットエントリー取得プログラム

JavaでHTTP通信をするためのプログラムを前に書いたことがあるので、それを使って以下のURLにリクエスト(アクセス)しました。
ホットエントリー:http://feeds.feedburner.com/hatena/b/hotentry
ちなみに新着エントリー:http://b.hatena.ne.jp/entrylist?mode=rss
レスポンス(結果)はXMLで返ってくるので、Javaに元々あるDocumentクラスとか、getDocumentElement()を使って適当にXML解析してタイトルやURL、ブックマーク数を抜き取りました。

取得したタイトルやURLはプログラムのコマンドライン引数に従って、標準出力にprintするようになっています。
ちなみに、コマンドライン引数によって形態素解析した結果をマルコフ連鎖したりして出力できます。

コマンドライン引数によって色々なフォーマットで出力できるので、適当なフォーマットを考えてコマンドラインで実行し、リダイレクトして結果をファイルに書き込んでいます。
その結果をデータベースに保存するスクリプトやTweetするボットに読ませたりしています。

2.形態素解析

形態素解析っていうのは文章を”形態素”っていうのに分解する事を言います。
”形態素”っていうのは、ある言語が意味を持つ為の最小の単位を言うそうです。

「お待ちしております。」を形態素解析すると、「お待ち」、「し」、「て」、「おり」、「ます」、「。」だそうです。

僕は国語の成績が芳しく無かったので、wikipediaのほうが詳しいと思います。

詳しいことは分からなくても、以下のページを参考にすれば誰でも簡単に形態素解析ができるそうです。
http://developer.yahoo.co.jp/webapi/jlp/ma/v1/parse.html
こいつも、ホットエントリーを取得した時と同様に、URLに対してリクエストするとレスポンスがXMLで返ってくるので適当に解析して、適当に結果を保持します。

この結果を、次のマルコフ連鎖って技術を使うとなんかそれっぽい文章が出来ます。


3.マルコフ連鎖

マルコフ連鎖っていうのは、僕は学校でマルコフチェーンっていうかっこいい言葉で習った記憶があるけど、よく覚えてないです。

なんか有限がどうのこうのっぽくて、フィルタ関数の有限応答に似てるなーって思ったこともあります。

詳しいことは、ググるか勉強するとして、こいつをうまく使うとさっきの”形態素解析”でバラバラにした形態素ってのを良い感じの文章に戻すことが出来ます。
私 は 身長 が 大きい です 。
僕 は 身長 が 小さい です 。
私 は 自転車 を 買う 。
って、文章から作った形態素(スペース区切り)から最初に2つの形態素(私 は)を選びます。

この2つの形態素
[私 は]に続くことが可能な形態素を選びます。
この場合は、[身長]か[自転車]になります。

ここで、身長が選ばれた場合、
[私 は] + [身長]となります。

次に
[は 身長]に続くことが可能な形態素を選びます。
ここでは、[が]のみになるので[が]を選びます。
よって
[私 は 身長] + [が]になります。

次に
[身長 が]に続くことが可能な形態素を選びます。
ここでは[大きい]か[小さい]になります。
ここで、[小さい]を選ぶと
[私 は 身長 が] + [小さい]になります。

次に
[が 小さい]に続くことが可能な形態素を選びます。
ここでは[です]のみになります。
さらに、[小さい です]に続くことが可能な形態素は[。]のみなので、結果的に以下の文が作成されます。

「私は身長が小さいです。」

こういう感じで、2個前の形態素を下にして現在の形態素を決定しながら形態素を繋げていくことを、”2次のマルコフ連鎖”というそうです(形態素に限った話ではありません)。

ここではn番目の形態素を決定するために[n-2]と[n-1]番目の形態素を使用した為に2次というっぽいです。

一般的にいうとn番目の形態素を決定する為に[n-k]…[n-2][n-1]を使用するとk次のマルコフ連鎖となるのでしょうか? 

話は戻って。

こうした操作を取得したホットエントリーのタイトルと説明(description)に対して行うことで、要約を作成しています。

同様の技術を使用したものとして圧縮新聞しゅうまい君などがあります。というか、参考にさせて頂きました。有難うございます。

4.Twitter4j

もうこれは界隈では有名なのですが、日本人が作った世界最高のTwitterのJavaライブラリです。
こいつを使ってコマンドライン引数で受け取った文字列をTweetするだけのプログラムを作っています。

エラー処理を除けば2行程度のプログラムで非常に簡単です。

Twitter4jのリンクを以下に張ります。
http://twitter4j.org/ja/index.html
素晴らしいものを提供して頂き、有難うございます。


5.Linuxのコマンド数個

ここまで紹介したプログラム自体コマンドとして使用できる形になっていますが、Linuxに標準で搭載されているコマンドを組み合わせることで余計な手間をかけずに目的を達成できました。


まず、”ろぐほー”のホットエントリーを記録する処理は以下のような流れで動いています。

”ホットエントリー取得→結果をテキストファイルに保存→エントリーをデータベースに保存→ボットがTweet”

この処理を一つのスクリプトに記述し、そのスクリプトを1時間に1回実行しています。

1時間に1回実行する為に、crontabというコマンドを使用しています。

crontabは指定したコマンドを指定した時刻、または周期で実行してくれます。こいつがなかったら、自分でデーモンとして動くプログラムを作らなくてはいけませんでした。

”ホットエントリーを取得”のプログラムは先にも述べた通り、自作のプログラムで、Tweetする為の文字列とエントリーのタイトルやURLなどを出力するプログラムです。

この出力結果をリダイレクトして”結果をテキストファイルに保存”しています。
$command > textfile
的な感じです。
リダイレクトの機能がなかったら、自分でファイルに書き込むプログラムを書かなければいけませんでした。

”エントリーをデータベースに保存”はPHPというスクリプト言語で自分で作ったものです。先のテキストファイルを読み込んでMySQLというデータベースに記録しているだけです。
これの詳細は別の機会に説明しようと思います。

最後に”ボットがTweet”ですが、これを実現するために複数のコマンドを組み合わせています。
単純にTweetするだけであれば、4(Twitter4j)で説明したボットのプログラムを実行するだけで良いのですが、3(マルコフ連鎖)で作成した要約文に”ろぐほーのURL”を追加してからつぶやかせたかったのです。

そのため、先に作成したテキストファイルから要約文を抜き出し、さらにURLを末尾に追記してからツイートしなくてはいけません。

上記の処理をする為のプログラムを書いても良かったのですが、Unixの精神に反するような気がしたので、既に用意されているコマンドを組み合わせて出来ないかと考えました。

まず、要約文の抜き出しですが、テキストファイルの2行目に要約文を保存するというルールを設けてありました(勝手に作りました)。

その為、その部分を抜き出すにはテキストファイルの先頭から任意の行を抜き出すコマンド(head)と末尾から任意の行を抜き出すコマンド(tail)を使って、以下のようにしました。
head -n 2 textfile | tail -n 1  #そぎ落とすイメージ
こうすることで、textfileの2行目だけを抜き出せます。”|”というのはパイプと言われるもので、ここではheadの出力結果をtailに渡しています。
このコマンドを細かく説明すると、まずheadでファイルの先頭2行目までを抜き出し、その結果をtailが末尾1行目を取り出しています。

さて、2行目を抜き出せたのはいいのですが、URLの結合をどうしようかと考えました。

2つのファイルを水平に連結するコマンドpasteがあったのでこいつを使うことにしました。

ここからは、ちょっと無理矢理な方法ですが、恥さらしとして書いておきます。
まず、2行目にURLを記載したテキストファイルurlfileを用意しておいて、textfileとurlfileをpasteします(-dオプションでスペース区切りにします)。そうすることで、2行目に要約文とurlが記載された新たなテキストが作成されます。
その結果をhead tailすることで、要約文とURLが結合された文が作成されます。
実際、”ろぐほー”はこのような処理で動いているのですが、”2行目にURL”というのが無理矢理っぽくてイマイチです。

head -n 2 textfile | tail -n 1した結果をpasteできたら良かったのですが、うまく出来ませんでした。
うまい方法があったら教えていただきたいです。

そんなこんなで、ボットがつぶやくための文章が出来ました。
この文章もパイプで渡しているのですが、コマンドライン引数を受け取るJavaのプログラムにうまくパイプで文章が渡せませんでした。

そこで、パイプで受け取った文章をJavaのプログラムの引数として渡す為に以下のようなスクリプトを作成しました。

read l
java -jar twitText.jar "$l"

”read l”でパイプで受け取った内容を読み取り、
"$l"でJavaのプログラムに渡しています。

そんなこんなで、Linuxに元々あったコマンドを良い感じで組み合わせることで、最小限のプログラムだけを作成して目的が達成できました。


”ろぐほー”作る前はパイプとか意味不明だったけど、実際使ってみると理解が深まりました。必要は発明の母とまでは行きませんでしたが、必要に迫られると理解が早まりますね!

0 件のコメント:

コメントを投稿