GETAを関連エントリ抽出に使ってみる

Geta 3

汎用連想計算エンジン GETA というのを使って、ブログの”このエントリに似た内容のエントリ”を出せるかどうかのテストをしてみる。
しくみとしては、ドキュメントの単語に分解して、行にドキュメント、列に単語を並べたほとんどの要素がゼロになるような巨大な行列をつくって、その行列から出現頻度が似ているものを取り出すことで似ているかどうかを調べることができるようです。
GETAは後半の巨大な行列の計算をしてくれますが、前半部分のtokenizeと行列の生成は自分でやる必要があります。

build

まずはGETA のごく簡単なチュートリアルに従ってgetaをつくります。
今回使用する環境は solaris10 for x86 です。

% uname -a
SunOS dokodemo 5.10 Generic_118844-26 i86pc i386 i86pc Solaris

tarを展開して

export GETAROOT=$HOME/geta
./configure --with-gmake --prefix=$GETAROOT
make

するとこういうエラー

In file included from tst.c:26:
/usr/local/lib/gcc-lib/i386-pc-solaris2.10/3.3.6/include/varargs.h:4:2: #error "GCC no longer implements ."
/usr/local/lib/gcc-lib/i386-pc-solaris2.10/3.3.6/include/varargs.h:5:2: #error "Revise your code to use ."

が出るので、言われたとおりに書き換える。以下diff.

--- tst.old.c 2001-09-06 15:23:30.000000000 +0900
+++ tst.c 2006-11-06 16:14:29.363363000 +0900
@@ -23,7 +23,7 @@
#include <string.h>
#include <syslog.h>
#include <unistd.h>
-#include <varargs.h>
+#include <stdarg.h>

#include <geta/wam.h>
#define NEED_WEIGHT_TYPES

そうすると問題なくmakeが終了します。

setting up

インストールしたGETAを動かすための設定ファイルを記述していきます。

 まず $GETAROOT/etc/ci.conf を作成します。中身はこう。

handle: getatest.dokodemo.local
short-name: getatest

次にGETAのデータディレクトリを作成します。

mkdir -p $GETAROOT/data/$handle
vi $GETAROOT/data/$handle/freqfile

@Ra
3 Ci
2 Ciii
1 Civ
@Rb
1 Cii
5 Ciii
@Rc
4 Ciii
3 Civ

とりあえずは、動作を確認したいのでチュートリアルに書いてある行列を freqfile に入れておきます。その次の GETA による電子メール検索システムの製作を読むとわかりますが、このfreqfileを作ることが類似検索を行う上での主な作業になるかんじです 。
とりあえずは上記のfreqfileで

$GETAROOT/sbin/mkw $handle $GETAROOT/data/$handle/freqfile

を実行してみて $GETAROOT/data/$handle に cw.c, cw.r, xr.c, xr.r が生成されるのを確認できればオーケー。

creating freqfile

このプログのエントリの類似検索を行うためには、まず全てのエントリの本文を取り出してfreqfileを作成する必要があります。本文を得るのはRSSとして出力させるのが最も簡単そうなのでRSSの出力部分を少し細工して全てのエントリについて出力されるようにします。

wordpressのRSSに何件エントリが出力されるかは、DBのwp_optionsテーブルにあるposts_per_rss で設定されているので、これを一時的に500に設定して全エントリを取り出しました。(と思ったら実行時間が30秒を越していて333エントリしか出ていなかったけれど、十分な量が取得できたのでvimでXMLを整形しなおしてそのまま利用することにします)

このRSSファイルすらfreqfileを作るために rss2wam.pl というのを作りました。中でmecabを呼ぶのでmecabをインストールしておいてください。コードはやや長いので別ファイルにしました。

このスクリプトで生成した zeromem.freq をGETAにかけてみます。

$GETAROOT/sbin/mkw $handle $GETAROOT/data/$handle/zeromem.freq

を実行すると

ocf: info: asc:[/export/home/kuma/geta/data/getatest.dokodemo.local/zeromem.freq]
mkw: counting number of keys in freqfiles: cnrr: info: f=0, *s=0, p=0
*8192/10 lt-level: 0
ht-level: 0, usage=0.000000
ht-level: 1, usage=0.014160
ht-level: 2, usage=0.036092
ht-level: 3, usage=0.128845
rrvq: info: turn over (pos=160846)

mkw: time=0.015
mkw: rnum=333
mkw: total_elem_num=13756
...
mkw: time=0.089812, ht_usage=0.156342, num_lword=0, rehash=1 nhts=8192

ちゃんと動きました。
チュートリアルに従って確認します。

$GETAROOT/bin/dumpwam $handle cw_row
nrsyms = 333

http://ido.nu/kuma/2003/03/27/cdma2000-1x/


http://ido.nu/kuma/2003/02/07/%e3%81%8a%e3%81%97%e3%82%89%e3%81%9b/

...

http://ido.nu/kuma/2003/02/07/kumadidonu-revives/


http://ido.nu/kuma/2004/06/19/%e6%b1%a0/

ちゃんと行方向の要素であるURLが出てきて、その数も333になっています。

列方向を出すと

$GETAROOT/bin/dumpwam $handle cw_col
nrsyms = 5123
の
こと
よう
...
PSP
SCE
^(
_(
。

というように単語が出てきます。この表示順は使っている数が多い順になっていて、これだけ見てもとりあえず楽しめますよと、ドキュメントに書いてありました。自分の場合はじめに出てくるまともな単語は”自分”でした… 単語としてへんなのが混じってるのは当面無視して先に進みます。

perl interface を使う

GETAのチュートリアルに従うと次は C 言語プログラミングインタフェースの基礎 になっています。いちおうGETAを展開したときにできるディレクトリ geta2/tutorial/wam にある saru.c をてきとうに書き換えて動作するのを確認しましたが、もうC/C++でコードを書くのとかはつらいので Perl インタフェース活用術 ― 連想検索GUIの実現方法 に進みます。
perlモジュールのインストールは

cd geta2/ext/wam
perl Makefile.PL
make
make test
sudo make install

で問題なくできました。

チュートリアルをみつつ

#!/usr/bin/perl

use wam ('WAM_ROW', 'WAM_COL');
use strict;
use Data::Dumper;

my $GETAROOT = "/home/kuma/geta";
my $haondle = 'getatest.dokodemo.local';
my $phrase = shift;
my $limit = 3;

&wam::init($GETAROOT) == 0 or die "ERROR: Cannot initialize WAM\n";

my $w = &wam::open($handle);
$w != 0 or die "ERROR: Cannot open $handle\n";

my $rq = &wam::wstem($phrase, $w, &WAM_COL, "./mecab.sh");
my $rd = &wam::wsh($rq, $w, &WAM_COL, &wam::WT_SMART, $limit, 0, $w);
print Dumper $rd;

というコードを書きました。
ここでひっかかったのが mecab.sh の中身です。ドキュメントには解説がないのですが wstem() の第一引数に与えたフレーズから、1行1単語になるように変換するスクリプトを渡してやれば大丈夫でした。この引数は省略は不可能なので、あらかじめ$phraseに改行で区切った単語を渡すとしても何かしら書いてあげる必要があります。
今回は mecab.sh というスクリプトを用意しました。

#!/bin/sh
mecab -O simple | perl -nle 's/†t.*// and print'

これでためしに”javascriptのデバッグ”でフレーズ検索してみると

$VAR1 = [
  {
    'weight' => '0.246256387811898',
    'name' => 'http://ido.nu/kuma/2006/02/03/safari-developer-faq/',
  },
  {
    'weight' => '0.211735083019879',
    'name' => 'http://ido.nu/kuma/2006/01/26/javascript-debugger-on-ie/',
  },
  {
    'weight' => '0.171730865533088',
    'name' => 'http://ido.nu/kuma/2006/02/14/google-groups-google-maps-api/',
  }
];

こういう結果になりました。フレーズを”gmailの携帯対応”にかえてやってみると

$VAR1 = [
  {
    'weight' => '0.281598804146414',
    'name' => 'http://ido.nu/kuma/2006/11/06/gmail-now-available-on-your-mobilephone-except-docomo/',
  },
  {
    'weight' => '0.189384032645797',
    'name' => 'http://ido.nu/kuma/2004/04/26/old-images/',
  },
  {
    'weight' => '0.16645682413921',
    'name' => 'http://ido.nu/kuma/2006/02/10/gmail-code-hints-at-coming-domain-feature/',
  }
];

こんなかんじです。きわめてweightが低くなっています(というのはこの時点ではわからなくて、あとでわかるのだけど)。検索しているフレーズが短いから似てるかどうかの判断をしにくいのかもしれないと思って、長くしてみました。

今度はエントリの全文をフレーズにして検索してみます。ディスクの製品名を取得する の全文をphraseにいれて検索すると

$VAR1 = [
  {
    'weight' => '3.59599605285567',
    'name' => 'http://ido.nu/kuma/2006/02/11/how-to-get-hdd-model-name/',
  },
  {
    'weight' => '0.970084836828966',
    'name' => 'http://ido.nu/kuma/2006/03/09/smartmontools-hdd-disk-description-vendor-name-product-name-hdd/',
  },
  {
    'weight' => '0.42963463665656',
    'name' => 'http://ido.nu/kuma/2006/01/24/wordpress%e3%81%ab%e7%a7%bb%e8%a1%8c/',
  }
];

自分自身はきわめて高いweightになりました。ヒットしてほしかった smartmontools はweightが0.97になっています。三つ目の WordPressに移行 はぜんぜん関係ないんだけどweightは0.42になっています。似ているか似ていないかを判断する閾値をどこに設定するかが一番の問題なのかもしれません。

もうひとつ、今度は javascript debugger on IE の本文をフレーズにして検索してみました。

$VAR1 = [
  {
    'weight' => '5.4857902437993',
    'name' => 'http://ido.nu/kuma/2006/01/26/javascript-debugger-on-ie/',
  },
  {
    'weight' => '0.907485571866733',
    'name' => 'http://ido.nu/kuma/2006/02/03/safari-developer-faq/',
  },
  {
    'weight' => '0.873584423589035',
    'name' => 'http://ido.nu/kuma/2006/07/05/firefox-extensions-for-web-development/',
  }
];

比較的高いweightで似ているとして帰ってきた ≫ Safari Developer FAQ, ≫ WEB開発用 firefox extension メモ ともにまあ妥当な結果です。

というわけでGETAを使って、似ているかんじのエントリを探してみるテストをしてみました。あとでmovabletypeのプラグイン Related Entries Plugin と組み合わせてみたりするかもしれません。 Related Entries はキーワードのところに要れた単語をstrictにマッチングして出すだけみたいなので類似しているものを出す目的では使えません。

とりあえずおなかがすいたので今日はここまで。


About this entry