Namazu-devel-ja(旧)


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

improvements of scoring method



馬場@京大宇宙物理 です。

スコアリングまわりの改造をやっています。二週間ほど前にいじってしば
らくほっておいたのですが、ある程度動くようになっているので、とりあ
えず devel-ja に投げてみることにしました。マージするかも含め、ご意
見をお聞かせ頂ければありがたいです。


できるようになったことは、とりあえず4つです。

  (1) スコアリングに文書の「サイズ」を考慮するようにした。
  (2) スコアリングに文書の「新しさ」を考慮するようにした。
  (3) スコアリングにURIの特徴(URIの長さや index.html で終っている
      など)を考慮するようにした。
  (4) PageRank を読むようにした。

(1)-(3) は単なるヒューリスティクスですが、これらの機能だけでも、用
途に応じたカスタマイズの余地が出てくるんじゃないかなとはおもってい
ます。(4) の PageRank の話は長くなるので、また別に。



(1) は、Namazu で見過ごされてきたtf・idf 法のポイントです。いまは、
NMZ.i の中の TF に IDF を掛けるだけでスコアとしているわけですが、
文書サイズが大きくなったら TF がそれに応じてでっかくなるのはある意
味であたりまえです。ですから TF をスコアリングのファクタとして用い
るためには、文書のサイズ "DL: Document Length" を用いて適当な正規
化を図ってやる方がいいはずです。

で、本当は、文書に含まれる「単語数」で割って正規化してやるのが一番
好ましいのですが、単語数を簡単に得る方法がわかんなかったので、とり
あえず文書サイズをファクタとして用いてます(だから、現状ではバイナ
リフォーマットに不利です)。単純に dl で割ってしまうとちょっと具合
いが悪かったので、適当な関数形(とりあえず1/sqrt(x)) を置いてスケー
リングするようにしています。つまり、ファイルサイズ10倍なら、スコア
1/3程度にdumpさせるということです。1/sqrt() に正当性はまるでないん
ですが、そこそこreasonable な範囲のスケーリングとはおもっています。
むろん簡単に単語数が分かるようなら、それに置き換える方が良いです。


(2) は、「新鮮度」つまり「URI は新しいほど求めるもののはずだ」とい
う heuristics に基づいてみたものです。そこで、適当なスケールタイム 
t0 を考え、f(t) = lambda*2**(-lambda*t/t0) の dumpping function を
考えています。つまり、同じtfなら、時間 t0/lambda だけ古い文書のス
コアを半分にします。ですから、タイムスタンプが信頼できない文書群に
使っても無意味(あるいは有害かも)です。

ちなみに、デフォルトのスケールタイムt0は 36ヶ月、lambda=2 というこ
とにしてみましたので、18ヶ月で1/2になります(Moore's Law に従ったも
のです)。t0 は SCORE_FRESHNESS ディレクティブで変えられますが、ス
ケールタイムを1日とか1週間とかにしてしまったら、それぞれ4日や1ヶ月
以上前の文書は簡単にスコア: 1 に dump してしまうので注意が必要です。


(3) は、「URI は短いほど求めるもののはずだ」という heuristics に基
づいてみたものです。文書にキーワードさえ含まれていれば、あとは文書
の詳しい中身に関係なく、http://foo.jp/A/B/C/D/E.html よりも 
http://foo.jp/ のスコアを上げてやろうと言うことです。

ただし、これは極めて美しくない、泥臭い方法です。具体的には、ディレ
クトリセパレータ '/' の数で、'/' が少ないほどスコアが大きくなるよ
うに適当にスケーリングしてます。スケーリングの数値に根拠は何もあり
ません X-)。また、index.html や / などで終っている URI のスコアが
大きくなるようにしています(とりあえず2倍にしている)。要するに、こ
の方法は根拠皆無なのでやめといた方がいいかもしれませんが、まあとり
あえずこんな方法も考えられるかも、ということで作ってみたものです。


(4) は、Google 式のPageRank をあらかじめ計算しておいてそれをスコア
リングに利用するものです。具体的には、インデックス時に別のプログラ
ムで NMZ.field.pagerank をあらかじめ作っておいて、namazu.cgi では
それを検索時に読んでいるだけです。で、肝心の NMZ.field.pagerank 作
成プログラムの紹介は、長くなるので別のメールにします。_o_




これらの機能の導入をそれぞれ簡単に切り替えるために、namazurc での
Scoring ディレクティブを拡張してみました。従来の simple と tfidf 
に加えて、doclength, freshness, urilength, pagerank を加えました。
また、それぞれを | でつなげられるようにしました。例:

  Scoring  doclength
  Scoring  tfidf|doclength
  Scoring  tfidf|freshness|urilength|pagerank
  Scoring  simple|freshness|urilength

さらに特別なタグとして、default と all も追加しています。default 
は保守的に、これまでと同じ tfidf 単独になるようになっています。
all は全部の効果を入れたものです。設定しないと default になります。

namazurc で Scoring ディレクティブを適当に変えながら --debug を付
けて検索してみれば、スコアがどのように再計算されて変わっているかを
簡単に見て取ることができます。


再計算されるスコアは、QDS (Query-Derived Score) と DIS
(DocID-Intrinsic Score) との二種類に分けて考えています。なお、
QDS,DISは僕が勝手に考えた造語です。別のところで使っても通じません。

QDS は検索式に由来するスコアで、tf・idf・dl を当てています。DIS は
検索式に関係ない文書ID そのものの持つ重み(スコア)と定義していて、
新鮮度とURI長ファクタとPageRankを掛けたものがこれに相当します。い
まのところ、表示されるスコアには、QDS と DIS とのファクタの大きさ
がだいたい同じになるように(具体的には Σqds(i) = Σdis(i) になるよ
うに)スケーリングした上で、足し算しています。

ちなみに足し算がいいか掛け算がいいかは、実のところどうしたらいいの
か良く分からないのです。そもそも評価方法の違うものを足し算するのは
おかしいので、ベクトル空間で内積を取るとかするほうがいいのかもしれ
ませんが、とりあえず安直に足し算にしているだけです。また、足し算に
するにしても QDS と DIS の比を同じにする必要もありません。このへん
は結果も変わってくるので、Google その他でどうやっているかを知りた
いところなのですが、どなたかご存じありませんでしょうか。




以下に cvs-current からの差分を付けておきます。差分をあてて 
autogen.sh からやり直せばできるとおもいます。変更点のポイントは、

・nmz/score.[ch] を新設。
・nmz/search.c::nmz_search() の中で、ソートする前に、
  nmz/score.c::nmz_recalc_score() を呼ぶようにしたこと。
・src/rcfile.c を新規パラメータ用に改造。
・その他少々

くらいです。一見してわかる単純な変更だとおもうので、いくらでもカス
タマイズできるとおもいます。

他には、nmz/hlist.c の中の nmz_get_hlist() で NMZ.i から TF を読み
込んでいてIDF も掛けているのでこの部分と、将来のベクトル空間モデル
化をにらんで nmz_{and|or|not}merge() を score.c に移して、もう少し
抽象化できないかな、とおもったくらいですが、おもっただけで実際には
やっていません。


まあ、説明はこんなくらいで。あとはコードを見てくださいませ。
--
馬場  肇 ( Hajime BABA )            E-mail: baba@xxxxxxxxxxxxxxxxxxxxxx
京都大学理学部宇宙物理学教室 博士後期課程
--



begin 644 scoring.tar.gz
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
M2\4JZV!$Z\&!@<97KT@xxxxxxxxxxxx*/IYZL[FZ9\(\"C#6GDC8]1\LI/LI
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
8HJ*BHJ*BHJ*BHJ*B\@?R"T&&S)$`>```
`
end