Hyper Estraierを使って類似記事の表示機能を追加する方法

今回は類似記事の検索機能を作ってみました。
私の運営するサイトは山の記録をみんなで投稿するサイトなのですが、
記事間の関連がいまいち希薄だったので、類似記事の検索機能を付けてみました。


機能としては記録に対して関連する記事を表示するだけでいいので、
リアルタイムの類似記事検索は不要です。
つまり、各記事に対して、関連する記事を事前に調べてしまって、
利用者が表示するときには、できるだけ軽い処理で関連する記事を
表示できるようにする必要がありました。


これに使えるプログラムとして、今回は
■HyperEstraier
http://hyperestraier.sourceforge.net/
を利用させていただきました。


perl用のAPIも用意されているらしいのですが、今回は
コマンドをsystem関数で読んで無理やり実現しました。


処理の大枠は以下2ステップ。perlスクリプトを1日1回、cronで処理することにしました。

  1. perlスクリプト
    1. MySQL中の記事を「文書ドラフト」形式にしてファイルに書き出す。
    2. 書き出したデータをHyperEstraierのDBに食わせる。
    3. 文書ごとに(文書のID、類似文書のID、URL)をMySQLに格納する。
  2. 記事のページを表示するときに、上記のデータから類似文書を表示する。

HyperEstraierのインストール(事前準備)

# yum install hyperestraier
で、インストールできました。

MySQL中の記事を「文書ドラフト」形式にしてファイルに書き出す。

HyperEstraierには「文書ドラフト」という独自形式があります。
http://hyperestraier.sourceforge.net/uguide-ja.html#formats


これを使うと、ファイル中にURLを記載してHyperEstraierのDBに文書を
食わせることができます。
"@url="の欄に記事のページのURLを書き出して、
コンテンツをUTF-8形式で出力すればOK。


自分の場合は、MySQL中の記事はEUCだったので、JcodeなりEncodeなりで
UTF-8に変換して、/tmp/esti/rec/配下に記事単位でファイルを生成しました。
ファイル名は記事番号.estにしました。(例:2038.est)
記事は1万件あったので1万ファイルできます。


ちなみに文書ドラフトの参考はこんな感じです。

@uri=http://www.yamareco.com/modules/yamareco/detail-100.html
@title=西表島縦走
@author=matoyan
@cdate=2007-01-22T23:45:11+09:00
@mdate=2007-01-23T15:12:03+09:00
category=record

西表島縦走(九州・沖縄)[無雪期ピークハント/縦走]
...以下略

書き出したデータをHyperEstraierのDBに食わせる。

まずchdirで、ファイルを置いた場所の一個上の場所に移動します。
今回の例だと/tmp/esti/に移動します。


次に、記録を書き出します。
# /usr/bin/estcmd gather -sd -il ja rec rec


「文書ドラフト」中にタイムスタンプをきちんと格納している場合は、
sdのオプションは外した方がいいようです。


類似検索は遅いらしいので、マニュアルに従って以下のコマンドを実行します。
# /usr/bin/estcmd extkeys rec


あとは、$iを1〜記事数まで回しながら、バッククォートで処理を実行します。
@result= `/usr/bin/estcmd search -vu -max 11 -sim $i rec`;


@resultには、HyperEstraierの記事IDとURLがリストで格納されます。
上記のコマンドをコマンドラインで実行するとこんな感じ。

# /usr/bin/estcmd search -vu -max 11 -sim 100 rec
--------[02D18ACF6BB53EC2]--------
VERSION 1.0
NODE    local
HIT     1524+
TIME    0.802445
DOCNUM  10040
WORDNUM 149187
VIEW    URI

--------[02D18ACF6BB53EC2]--------
78      http://www.yamareco.com/modules/yamareco/detail-1076.html
100     http://www.yamareco.com/modules/yamareco/detail-1097.html
800     http://www.yamareco.com/modules/yamareco/detail-1752.html
158     http://www.yamareco.com/modules/yamareco/detail-1150.html
218     http://www.yamareco.com/modules/yamareco/detail-1204.html
3080    http://www.yamareco.com/modules/yamareco/detail-22213.html
8091    http://www.yamareco.com/modules/yamareco/detail-27213.html
9683    http://www.yamareco.com/modules/yamareco/detail-671.html
1566    http://www.yamareco.com/modules/yamareco/detail-20587.html
8149    http://www.yamareco.com/modules/yamareco/detail-27271.html
8940    http://www.yamareco.com/modules/yamareco/detail-2947.html
--------[02D18ACF6BB53EC2]--------:END

で、このHyperEstraierの記事番号100以外のものが類似記事になります。
適当に正規表現を書いて取り出しました。


注意点として、もともとの記事の番号(URL中の番号)と、
HyperEstraier中の記事番号が違うので、
その対応を取りつつ記事番号を抽出する必要があります。
上記の例の場合、HyperEstraierの記事番号が100で、もともとの記事IDは1097です。


もう一点、処理を強制終了したりするとHyperEstraierのDB中で
記事が重複してしまう場合があります。

そのときは
# /usr/bin/estcmd repair rec
を実行すると、重複が整理されるようです。
スクリプト中に1回実行するようにしておきました。

文書ごとに(文書のID、類似文書のID、URL)をMySQLに格納する。

記事のIDや類似文書のIDを記録するだけだと、コンテンツを生成するときに
結局動的に生成しないといけないので、今回は
MySQL中にコンテンツそのものを格納してしまいました。

要は「content」というフィールドを追加して、その中に
タイトルやリンクを含むHTMLをそのまま格納しました。

その他

あとは省略します。

コンテンツの種類による補正とか、キーワードの重要度とか
細かいことを言い出すと結局理論の勉強が必要です。
今回は面倒だから手を出さずじまい。


アルゴリズムを本気で勉強したい人は
「tf-idf」とか「ベクトル空間」とかでググると勉強できそうです。

参考(になりそうなリンク)

Hyper Estraier(公式)
http://hyperestraier.sourceforge.net/
IPA未踏ソフトウェア創業事業」で開発されたプログラムらしいです。
  作者の平林幹雄さんは、スーパークリエーター認定されて、
  今はmixiでご活躍中のようです。もう開発はしてないのでしょうか。


Hyper Estraier 導入: greenplastic.net
http://www.greenplastic.net/2007/02/04_0630.php
⇒ 一番参考になりました。ありがとうございます。


Hyper Estraier インストール(4) Perlバインディングのインストール : Enjoy Hyper Estraier
http://blog.livedoor.jp/hyperes/archives/50585887.html
⇒ 今回は使いませんでしたが。


■HyperEstraier の Perl Binding を使ってみた。: 鷹の島
http://espion.just-size.jp/archives/05/277184034.html
⇒ これも、今回は使いませんでしたが。


■tf-idf: Wikipedia
http://ja.wikipedia.org/wiki/Tf-idf
⇒ 基本的な理論はこの辺なんでしょうか。


■シムエントリ - 記事同士でブログをむすぶブログパーツ
http://se.koemu.com/
⇒ この方はどうやって実現してるのでしょうか。
  こういうみんなに使われるサービスを提供できるのは素晴らしいです。


■Introduction to Information Retrieval #7 の復習資料CommentsAdd Star: naoyaのはてなダイアリー
http://d.hatena.ne.jp/naoya/20080622/1211032221
⇒ 今回のモチベーションの発端。