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
好东西,顶啊!我去好好研究下~