SimpleBM25F是BM25F的基础拓展版本,主要用于多个域的拓展,感兴趣的可以看《Simple BM25 Extension to Multiple Weighted Fields》。
主要观点:按照权重将不同域重复相应次数,拼成无结构的混合文本桶,然后只计算一次BM25得分。
而之前很多人采用的各个域先计算不同的BM25,再线性组合的做法,则破坏了词项独立性而效果很差。
传统:bm25.cpp
#include <xapian.h> #include <iostream> using namespace std; #define DOC1_TITLE "这 是 一条 新闻 " #define DOC2_TITLE "这 是 一条 男篮 新闻 " #define DOC1_CONTENT "70比 69, 这是 男篮 亚锦赛 历史上 的 最小 分 差 比赛 , 笑 到 最后 的是 东道主 中国队 。 可以说 , 这是 一次 最 惊险 的 胜利 ; 也可 以 说 , 这是 中国男篮 最 幸运 的 结局 。终 > 场 哨 响 , 中国队 主教练 邓 华德 和 篮管中心 副主任 胡 加时 紧紧拥抱 在一 起 , 两人 都 激动 得 热泪盈眶 —— 中 队 赢了 , 赢得 很 庆幸 。 男篮 " #define DOC2_CONTENT "70比 69, 这是 男篮 亚锦赛 历史上 的 最小 分 差 比赛 , 笑 到 最后 的是 东道主 中国队 。 可以说 , 这是 一次 最 惊险 的 胜利 ; 也可 以 说 , 这是 中国男篮 最 幸运 的 结局 。终 > 场 哨 响 , 中国队 主教练 邓 华德 和 篮管中心 副主任 胡 加时 紧紧拥抱 在一 起 , 两人 都 激动 得 热泪盈眶 —— 中 队 赢了 , 赢得 很 庆幸 。 " #define INDEX_PATH "./index_data" #define F_DOCID 1 int main() { try { //Text to be indexed string doc1_text(DOC1_TITLE); doc1_text += DOC1_CONTENT; string doc2_text(DOC2_TITLE); doc2_text += DOC2_CONTENT; //Open an Database for write Xapian::WritableDatabase db(string(INDEX_PATH), Xapian::DB_CREATE_OR_OPEN); //Prepare TermGenerator, just split word by space, not chinese analysis Xapian::TermGenerator indexer; //Make && Index Doc1 Xapian::Document doc1; doc1.add_value(F_DOCID, string("doc1")); indexer.set_document(doc1); indexer.index_text_without_positions(doc1_text); db.add_document(doc1); //Make && Index Doc2 Xapian::Document doc2; doc2.add_value(F_DOCID, string("doc2")); indexer.set_document(doc2); indexer.index_text_without_positions(doc2_text); db.add_document(doc2); //Flush to disk db.commit(); } catch(const Xapian::Error &e) { cout << e.get_description() << endl; } return 0; }
结果,由于doc1的content多一个“男篮”,所以比doc2得分高,doc1排第一。
Query is Xapian::Query(男篮:(pos=1)) 2 results found 0: doc1 1: doc2
再看Simple BM25F,注意权重使用函数的第2个参数wdf就行了:
void Xapian::TermGenerator::index_text_without_positions ( const Xapian::Utf8Iterator & itor, Xapian::termcount wdf_inc = 1, const std::string & prefix = std::string() )
#include <xapian.h> #include <iostream> using namespace std; #define DOC1_TITLE "这 是 一条 新闻 " #define DOC2_TITLE "这 是 一条 男篮 新闻 " #define DOC1_CONTENT "70比 69, 这是 男篮 亚锦赛 历史上 的 最小 分 差 比赛 , 笑 到 最后 的是 东道主 中国队 。 可以说 , 这是 一次 最 惊险 的 胜利 ; 也可 以 说 , 这是 中国男篮 最 幸运 的 结局 。终 > 场 哨 响 , 中国队 主教练 邓 华德 和 篮管中心 副主任 胡 加时 紧紧拥抱 在一 起 , 两人 都 激动 得 热泪盈眶 —— 中 队 赢了 , 赢得 很 庆幸 。 男篮 " #define DOC2_CONTENT "70比 69, 这是 男篮 亚锦赛 历史上 的 最小 分 差 比赛 , 笑 到 最后 的是 东道主 中国队 。 可以说 , 这是 一次 最 惊险 的 胜利 ; 也可 以 说 , 这是 中国男篮 最 幸运 的 结局 。终 > 场 哨 响 , 中国队 主教练 邓 华德 和 篮管中心 副主任 胡 加时 紧紧拥抱 在一 起 , 两人 都 激动 得 热泪盈眶 —— 中 队 赢了 , 赢得 很 庆幸 。 " #define WEIGHT_TITLE 2 #define WEIGHT_CONTENT 1 #define INDEX_PATH "./index_data" #define F_DOCID 1 int main() { try { //Open an Database for write Xapian::WritableDatabase db(string(INDEX_PATH), Xapian::DB_CREATE_OR_OPEN); //Prepare TermGenerator, just split word by space, not chinese analysis Xapian::TermGenerator indexer; //Make && Index Doc1 Xapian::Document doc1; doc1.add_value(F_DOCID, string("doc1")); indexer.set_document(doc1); indexer.index_text_without_positions(string(DOC1_TITLE), WEIGHT_TITLE); // WEIGHT_XX is integer for tf indexer.index_text_without_positions(string(DOC1_CONTENT), WEIGHT_CONTENT); // WEIGHT_XX is integer for tf db.add_document(doc1); //Make && Index Doc2 Xapian::Document doc2; doc2.add_value(F_DOCID, string("doc2")); indexer.set_document(doc2); indexer.index_text_without_positions(string(DOC2_TITLE), WEIGHT_TITLE); // WEIGHT_XX is integer for tf indexer.index_text_without_positions(string(DOC2_CONTENT), WEIGHT_CONTENT); // WEIGHT_XX is integer for tf db.add_document(doc2); //Flush to disk db.commit(); } catch(const Xapian::Error &e) { cout << e.get_description() << endl; } return 0; }
再看结果,由于title重复了两次,所以doc2多含了一个tf的“男篮”,因此doc2排1:
Query is Xapian::Query(男篮:(pos=1)) 2 results found 0: doc2 1: doc1
好东西,顶啊!我去好好研究下~