当前位置: 首页 > 工具软件 > Xapian > 使用案例 >

Xapian索引-文档检索过程分析之匹配百分比

危钱明
2023-12-01

    本文属于文档检索过程分析的一部分,重点分析文档匹配百分比(percent)的计算过程。

1 percent是什么?

    我们之前分析的检索demo:

Xapian::Query term_one = Xapian::Query("T世界");
Xapian::Query term_two = Xapian::Query("T比赛");
Xapian::Query query = Xapian::Query(Xapian::Query::OP_OR, term_one, term_two); // query组装

std::cout << "query=" << query.get_description() << std::endl;

Xapian::Enquire enquire(db);
enquire.set_query(query);
Xapian::MSet result = enquire.get_mset(0, 10); // 执行检索,获取结果
std::cout << "find results count=" << result.get_matches_estimated() << std::endl;

for (auto it = result.begin(); it != result.end(); ++it) {
    Xapian::Document doc = it.get_document();
    std::string data = doc.get_data();
    double doc_score_weight = it.get_weight();
    /// 匹配百分比
    int doc_score_percent = it.get_percent();
    std::cout << "doc=" << data << ",weight=" << doc_score_weight << ",percent=" << doc_score_percent << std::endl;
}
percent就是 int doc_score_percent = it.get_percent(); 这里获取的文档匹配百分比。

2 为什么需要percent?

    先说一下背景,做过搜索的人应该都知道BM25算法,这也是xapian内部默认的相关性打分算法,它是一个针对term做打分的公式。这个公式可以分为三个部分,第一部分跟term的idf有关,第二部分跟term在doc的权重有关,第三部分跟term在query中的权重有关(可以参考这里理论分析部分的介绍)。公式第一部分的idf是以索引库中的文档来统计的,第二部分中用到的文档平均长度,也是以索引库中的文档来计算的,当我们的数据含有多个业务,并且每个业务有独立的索引库时,相同term的idf在不同索引库中差别可能很大,而idf又是BM25公式中一个很重要的因子,这就可能导致同一个query在A库中搜索出来的doc BM25打分永远比在B库中搜索出来的要高,并且,xapian的打分支持term-boost的(打分系数),乘上系数之后,BM25打分值差距可能就更大了。所以,我们不能也不应该直接拿BM25打分来比较哪个doc更相关,我们需要一个归一化后的数值,这就是percent。

3 percent是怎么算出来的?

    percent是某个doc跟query的相关性归一化分值,是为做相关性打分排序而存在的,它的基本原则是:归一化。

    分成两个层面,首先是term匹配个数:匹配个数为分子,query term个数为分母,term匹配的越多,percent越高;然后是term匹配个数一样多时,某个doc的BM25打分为分子,最大的doc BM25打分为分母,BM25打分越高,percent越高。

    相关代码:

void MultiMatch::get_mset(Xapian::doccount first, Xapian::doccount maxitems,
             Xapian::doccount check_at_least,
             Xapian::MSet & mset,
             Xapian::Weight::Internal & stats,
             const Xapian::MatchDecider *mdecider,
             const Xapian::KeyMaker *sorter) {
......
    {
        /// greatest_wt_subqs_matched 表示最大的匹配query数
        /// total_subqs 表示总query数
/// percent_scale是计算percent的一个因子,后面会看到怎么用
percent_scale = greatest_wt_subqs_matched / double(total_subqs); percent_scale /= greatest_wt; } ...... }
int MSet::Internal::convert_to_percent_internal(double wt) const {
    LOGCALL(MATCH, int, "Xapian::MSet::Internal::convert_to_percent_internal", wt);
    if (percent_factor == 0) {
        RETURN(100);
    }
    /// wt是文档的打分,percent_factor是之前计算的 percent_scale * 100
    // Excess precision on x86 can result in a difference here.
    double v = wt * percent_factor + 100.0 * DBL_EPSILON;
    int pcent = static_cast<int>(v);
    LOGLINE(MATCH, "wt = " << wt << ", max_possible = " << max_possible << " =>  pcent = " << pcent);
    if (pcent > 100) pcent = 100;
    if (pcent < 0) pcent = 0;
    if (pcent == 0 && wt > 0) pcent = 1;

    RETURN(pcent);
}

     以上就是xapian源码中percent的计算过程。

4 percent并不适用所有场景

    percent是归一化后的相关性分值,从上面的分析,我们知道它的计算方式非常依赖匹配词个数,如果只用percent的方式来做匹配doc的排序,会有一个明显的缺陷: 当某term(词)极其重要时,percent值可能难以有所体现,譬如:有检索语句:A or B or C,其中,A term的重要性极高,只要它匹配了就认为相关性高达90%,而B、C term不重要,加起来只有10%的权重。如果是采用BM25的分值,我们可以通过对A term加权到极大,保证A term匹配到的doc的BM25打分比B、C term匹配到的高一个数量级。但是,如果采用了percent,则命中B term和C term的percent值在2/3左右,而只命中A term的percent值在1/3左右,这样子无法体现A term的重要性。解决办法有很多,譬如:既使用percent(归一化后的BM25打分)又使用score(BM25打分),综合考虑;或者,将A term设置为必出词(And语法),这样子保证了召回的doc(文档)中A词必定出现,doc之间的排序就变成了只会在含有A词的doc之间排。

    

转载于:https://www.cnblogs.com/cswuyg/p/10552564.html

 类似资料: