1. 原理
对于搜索引擎,本质是用户搜一个query, 引擎返回一个结果列表,那么如何衡量这个结果列表的好坏?
-
我们希望把最相关的结果放到排名最靠前的位置,因为大部分用户都是从上往下阅读的, 那么最相关的前面可以最大程度减少用户的阅读时间。
-
我们希望整个列表的结果尽可能的和query相关。
第一个条件的满足是首要的,而第二个条件的加入是保证整体结果质量,而这两个条件都体现在了NDCG里面,首先,计算NDCG,需要计算Gain,这个gain即是每条结果的质量的定义,NDCG把所有结果相加最终相加保证,整体质量越高的列表NDCG值越大。同时,Discounted的设计使得越靠前的结果权重越大,这保证了第一条,更相关的排在靠前的结果会有更大的NDCG值。从这两点看,以NDCG为优化目标,保证了搜索引擎在返回结果总体质量好的情况下,把更高质量结果排在更前面。
计算公式如下:
CG的实现, 即将所有的打分进行累加, 如下:
其是个不断累加的值,gain可以使自己定义的一种获得,用来衡量网页的相关性程度:
DCG(Discounted Cumulative Gain),Discounted的factor为:
那么,NDCG就是被IDEA DCG规格化的值,即DCG/IDCG。
2. 实现
改造自github的代码, 输入即是 label, qid, score----样本,查询id,分数。实现的功能是计算多个query的平均ncdg。
import sys, math
topK = int(sys.argv[1])
def DCG(label_list):
dcgsum = 0
for i in range(len(label_list)):
dcg = (2**label_list[i] - 1)/math.log(i+2, 2)
dcgsum += dcg
return dcgsum
def NDCG(label_list):
global topK
dcg = DCG(label_list[0:topK])
ideal_list = sorted(label_list, reverse=True)
ideal_dcg = DCG(ideal_list[0:topK])
if ideal_dcg == 0:
return 0
return dcg/ideal_dcg
def queryNDCG(label_qid_score):
tmp = sorted(label_qid_score, key = lambda x:-x[2])
label_list = []
for label,q,s in tmp:
label_list.append(label)
return NDCG(label_list)
last_qid = ""
l_q_s = []
ndcg = 0
cnt = 0
for line in sys.stdin:
label, qid, score = line.rstrip().split(" ")
if last_qid != "" and qid != last_qid:
ndcg += queryNDCG(l_q_s)
cnt += 1
l_q_s= []
last_qid = qid
l_q_s.append([int(label),qid, float(score)])
if last_qid != "":
ndcg += queryNDCG(l_q_s)
cnt += 1
#print cnt
print ndcg/cnt
3. 应用
主要是应用评价搜索引擎的排序是否合理。应用主要有三种不同的方案:
- 最精准的方案是,用人工标注的方案,如果doc的数据量较少的时候,可以采用这种方案, 如果数据量较大, 会花费很多人力物力。
- 拉取竞品的排序数据,可以为一个排序打分参考, 如果竞品做得比较好的,还是值得参考的。
- 拉取用户点击行为进行评估。
有了排序的分值,即可实现NDCG的评估和计算。