MAP @ k计算

时间:2019-03-03 06:51:04

标签: python matlab information-retrieval precision-recall average-precision

根据wikiml metrics at kaggle和此答案:Confusion about (Mean) Average Precision k (对于答案中的前k个元素)计算的平均平均精度。应该以k处的平均精度的平均值计算,其中k处的平均精度的计算公式为:

enter image description here

其中:P(i)是列表中截止i时的精度; rel(i)是一个指标函数,如果等级i的项是相关文档,则等于1,否则为零。

分隔符min(k, number of relevant documents)的含义是答案中相关条目的最大可能数目。

这种理解正确吗?

对于所有排名列表,MAP @ k总是小于计算得出的MAP吗?

我担心的是,这不是许多作品中计算MAP @ k的方式。

通常,分隔符不是min(k, number of relevant documents),而是 top-k 中的相对文档数。这种方法将提供更高的MAP @ k价值。


HashNet: Deep Learning to Hash by Continuation" (ICCV 2017)

代码: https://github.com/thuml/HashNet/blob/master/pytorch/src/test.py#L42-L51

    for i in range(query_num):
        label = validation_labels[i, :]
        label[label == 0] = -1
        idx = ids[:, i]
        imatch = np.sum(database_labels[idx[0:R], :] == label, axis=1) > 0
        relevant_num = np.sum(imatch)
        Lx = np.cumsum(imatch)
        Px = Lx.astype(float) / np.arange(1, R+1, 1)
        if relevant_num != 0:
            APx.append(np.sum(Px * imatch) / relevant_num)

relevant_num不是min(k, number of relevant documents),而是结果中相关文档的数量,与相对文档总数或 k 不同。

我读错了代码吗?


Deep Visual-Semantic Quantization of Efficient Image Retrieval CVPR 2017

代码:https://github.com/caoyue10/cvpr17-dvsq/blob/master/util.py#L155-L178

def get_mAPs_by_feature(self, database, query):
    ips = np.dot(query.output, database.output.T)
    #norms = np.sqrt(np.dot(np.reshape(np.sum(query.output ** 2, 1), [query.n_samples, 1]), np.reshape(np.sum(database.output ** 2, 1), [1, database.n_samples])))
    #self.all_rel = ips / norms
    self.all_rel = ips
    ids = np.argsort(-self.all_rel, 1)
    APx = []
    query_labels = query.label
    database_labels = database.label
    print "#calc mAPs# calculating mAPs"
    bar = ProgressBar(total=self.all_rel.shape[0])
    for i in xrange(self.all_rel.shape[0]):
        label = query_labels[i, :]
        label[label == 0] = -1
        idx = ids[i, :]
        imatch = np.sum(database_labels[idx[0: self.R], :] == label, 1) > 0
        rel = np.sum(imatch)
        Lx = np.cumsum(imatch)
        Px = Lx.astype(float) / np.arange(1, self.R+1, 1)
        if rel != 0:
            APx.append(np.sum(Px * imatch) / rel)
        bar.move()
    print "mAPs: ", np.mean(np.array(APx))
    return np.mean(np.array(APx))

其中的分隔符为rel,其计算公式为np.sum(imatch),其中imatch是一个二进制向量,指示该条目是否相关。问题在于它仅需要第一个Rimatch = np.sum(database_labels[idx[0: self.R], :] == label, 1) > 0。因此,np.sum(imatch)将在返回的列表中以大小R给出相关条目的数目,而不是min(R, number of relevant entries)。并且请注意,本文中使用的R的值小于DB中的条目数。


Deep Learning of Binary Hash Codes for Fast Image Retrieval (CVPR 2015)

代码:https://github.com/kevinlin311tw/caffe-cvprw15/blob/master/analysis/precision.m#L30-L55

    buffer_yes = zeros(K,1);
    buffer_total = zeros(K,1);
    total_relevant = 0;

    for j = 1:K
        retrieval_label = trn_label(y2(j));

        if (query_label==retrieval_label)
            buffer_yes(j,1) = 1;
            total_relevant = total_relevant + 1;
        end
        buffer_total(j,1) = 1;
    end

    % compute precision
    P = cumsum(buffer_yes) ./ Ns';

    if (sum(buffer_yes) == 0)
        AP(i) = 0;
    else
        AP(i) = sum(P.*buffer_yes) / sum(buffer_yes);
    end

此处的分隔符为sum(buffer_yes),它是返回列表中大小为 k 而不是min(k, number of relevant documents)的相关文档数。


"Supervised Learning of Semantics-Preserving Deep Hashing" (TPAMI 2017)

代码:https://github.com/kevinlin311tw/Caffe-DeepBinaryCode/blob/master/analysis/precision.m

代码与先前的论文相同。


Learning Compact Binary Descriptors with Unsupervised Deep Neural Networks (CVPR 2016)

相同的代码:https://github.com/kevinlin311tw/cvpr16-deepbit/blob/master/analysis/precision.m#L32-L55



我想念什么吗?上面论文中的代码是否正确?为什么它与https://github.com/benhamner/Metrics/blob/master/Python/ml_metrics/average_precision.py#L25-L39不符?


更新

我发现了这个已关闭的问题,并提到了相同的问题:https://github.com/thuml/HashNet/issues/2

以下索赔是否正确?

  

AP是排名指标。如果排名列表中的前2个检索是相关的(并且仅前2个),则AP为100%。您正在谈论的是召回率,在这种情况下确实为0.2%。

据我了解,如果我们将AP视为PR曲线下的面积,则上述说法是不正确的。


P.S。我对此是否应该交叉验证或StackOverflow感到怀疑。如果您认为最好将它放置到“交叉验证”,我不在乎。我的理由是,这不是一个理论问题,而是一个参考实际代码的实现。

1 个答案:

答案 0 :(得分:1)

找到这个,您是完全正确的,做得很好。考虑到代码的相似性,我猜测是有一个源错误,然后接连不断地在不仔细检查的情况下复制了错误的实现。

“麻烦”问题的提出者也是完全正确的,我将给出相同的例子。我不确定“ kunhe”是否理解该论点,当然在计算平均精度时召回当然很重要。

是的,该错误应该使数字膨胀。我只是希望排名列表足够长并且方法足够合理,以使它们在排名列表中达到100%的召回率,在这种情况下,该错误不会影响结果。

不幸的是,对于审稿人而言,很难做到这一点,因为通常情况下,他们不会审阅论文代码。值得与作者联系以尝试使他们更新代码,使用正确的编号更新论文,或者至少不继续进行在他们未来的作品中犯错误。如果您打算写一篇比较不同方法的论文,则可以指出问题并报告正确的数字(以及可能带有错误的数字,目的只是为了比较苹果而制作苹果)。

要回答您的附带问题:

  

对于所有排名列表,MAP @ k总是小于计算得出的MAP吗?

不一定,MAP @ k实质上是在计算MAP的同时对可能的情况进行规范化,在这种情况下,仅进行k次检索就无法做得更好。例如。考虑具有相关性的返回排名列表: 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 并假设总共有6个相关文件。此处的MAP应该略高于50%,而MAP @ 3 = 100%,因为您不能做比检索1 1 1更好的事情。 但这与您发现的错误无关,因为MAP @ k的错误至少与真实的MAP @ k一样大。