Xapian实现Simple BM25F

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

1 thought on “Xapian实现Simple BM25F

Leave a Reply

Your email address will not be published.