NDCG 计算
NDCG 计算题解
题意
给定 JSON 数据,包含:
y_true:每个样本的真实相关性标签;y_score:模型给出的预测分数;K:只计算前K个结果的指标。
需要计算并输出:
dcg:按预测分数降序排序后的折损累计增益;idcg:按真实相关性降序排序后的理想折损累计增益;ndcg:归一化折损累计增益,即
最终以紧凑 JSON 格式输出。
计算公式
对于排序后的前 个结果,第 个位置的真实相关性记为 ,则:
理想排序是将所有样本按真实相关性从大到小排序,此时得到:
因此:
若 ,则令 。
思路
读取 JSON 字符串,解析出
y_true、y_score、K。将每个样本组织为三元组:
按预测分数从大到小排序。若分数相同,则按下标从小到大排序,保证结果稳定。
取排序后的前 个样本,按照 DCG 公式累加,得到
dcg。再按真实相关性从大到小排序。若相关性相同,则按下标从小到大排序,得到理想排序。
取前 个样本,按照同样公式累加,得到
idcg。计算
ndcg = dcg / idcg,若idcg == 0,则ndcg = 0。将
idcg和ndcg保留 6 位小数,最后用紧凑 JSON 输出。
正确性说明
DCG 的定义是对排序结果中每个位置的相关性进行指数加权,并根据位置进行对数折损。按预测分数排序得到实际排序,因此计算出的就是模型排序下的 DCG。
IDCG 是同一批样本在最优排序下的 DCG。由于指数函数 单调递增,将真实相关性从大到小排列即可使 DCG 最大,因此该排序就是理想排序。
NDCG 用 IDCG 对 DCG 归一化,使得不同查询或不同样本集合之间的指标可以比较。
复杂度分析
设样本数量为 。
- 排序两次,时间复杂度为:
- 计算 DCG 和 IDCG 各遍历前 个元素,时间复杂度为:
因此总时间复杂度为:
空间复杂度为:
参考代码
import json
import numpy
s = input().strip()
data = json.loads(s)
ytrue = data['y_true']
yscore = data['y_score']
k = data['K']
n = len(ytrue)
arr = []
for i in range(n):
arr.append((i, ytrue[i], yscore[i]))
# 按预测分数降序排序,同分按下标升序
arr.sort(key=lambda x: (-x[2], x[0]))
dcg = 0
for r in range(1, k + 1):
dcg += (2.0 ** arr[r - 1][1] - 1) / numpy.log2(r + 1.0)
# 按真实相关性降序排序,同分按下标升序
arr.sort(key=lambda x: (-x[1], x[0]))
idcg = 0
for r in range(1, k + 1):
idcg += (2.0 ** arr[r - 1][1] - 1) / numpy.log2(r + 1.0)
ndcg = 0
if idcg != 0:
ndcg = dcg / idcg
dcg = round(dcg, 6)
idcg = round(idcg, 6)
ndcg = round(ndcg, 6)
data = {
"dcg": dcg,
"idcg": idcg,
"ndcg": ndcg
}
print(json.dumps(data, separators=(',', ':')))输入示例
{"y_true":[3,2,0,1],"y_score":[0.2,0.8,0.8,0.1],"K":3}输出示例
{"dcg":6.5,"idcg":9.392789,"ndcg":0.69202}注意事项
排序时需要保证同分情况下结果稳定,因此代码中使用下标作为第二排序关键字。
题目通常保证 ,否则取前 个元素时可能越界。
idcg为 时不能直接做除法,需要单独处理。输出时使用:
json.dumps(data, separators=(',', ':'))可以避免 JSON 中出现多余空格。
NDCG 计算
https://mingsm17518.github.io/2026/11/03/刷题笔记/机考/1031京东/01_NDCG 计算/