tf–idf 與 BM25 是兩個在資訊檢索 (IR) 裡面的經典演算法,也常被用在搜尋引擎技術上。
前陣子在練 Go,剛好找個主題來練,tf-idf 已經很熟了,但 BM25 沒有實際寫過,而自己的 blog 也累積了七千多篇,這個數量還算好用,不用自己另外 dump 維基百科的文章跑... (而且量太大)
第一步是拆成 token,我這邊就拿 bigram 拆了,但英文的部分把一整個詞當作一個單位,而非一個字母一個字母拆。
btw,這邊 tf-idf 與 BM25 的公式就請大家自己去維基百科上翻了...
tf-idf 概念上很簡單,而也沒有什麼 magic number 在公式裡面。
如果把 tf-idf 當一個 function 來看的話會是 score = tfidf(w, d, D),表示一個字 w 在一份文件 d 裡面的分數 (而 D 是所有文件)。
而 tf 只跟文件本身有關,可以預先算好放著,df 在後續文件新增刪除時都可以 incremental update,不需要重頭算,是個對於平行化運算很友善的演算法。
接著是看 BM25,如果把 BM25 當作一個 function 來看的話,會是 array = bm25(Q, D),針對 query words Q 與所有文章 D 傳回一個排序結果 array,裡面會是排序過的 document id,通常會包括分數。
而從公式可以看到 BM25 其實就是把 Q 裡面的每個字丟進 tf-idf 後加起來的變形,只是多考慮到文件大小對分數的影響,另外裡面引入了一些花招,像是 k1 與 b 這兩個常數項。
所以我就寫了兩個版本,一個是單純用 tf-idf 相加 (這樣長文章分數應該會比較高),另外一個是用 BM25 的公式跑... 算是趣味趣味的寫法。
算是清掉了之前一直放著的項目...