nDCG笔记及在spark中的实现(更新中)
admin
2024-03-01 06:48:13
0

目录

  • 0. 前言
  • 1. 原理
  • 2. 步骤
    • 2.1 计算CG
    • 2.2 计算DCG
    • 2.3 计算nDCG
  • 3. 本地代码实现
    • 3.1 自己编写代码
    • 3.2 使用sklearn.metrics.ndcg_score
    • 3.3 两种代码的速度比较
    • 3.4 两种代码之间的误差
  • 4. spark代码实现
    • target为回归
    • target为0-1分类

0. 前言

之前在排序项目中用到了nDCG,其离线指标对模型训练及上线具有一定的参考意义,在此做一个总结。因为计算是在集群的hive表进行的,所以加上了spark上的计算代码。

1. 原理

  • 高度相关的文档在搜索引擎结果列表的前面显示时更有用。
  • 高度相关的文档比不相关的文档更有用,勉强相关的文件比不相关的文件更有用

2. 步骤

有以下例子:

位置(iii)预测值排序真实的相关性分(relirel_ireli​)折损值(log2(i+1)log_2(i+1)log2​(i+1))折损后的相关性(reli/log2(i+1)rel_i/log_2(i+1)reli​/log2​(i+1))
1D1313
2D221.5851.262
3D3321.5
4D402.3220
5D512.5850.387
6D622.8070.712

2.1 计算CG

CGCumulative Gain)是结果列表中所有 items 的相关性得分的总和。CGk\text{CG}_kCGk​是前 kkk 个 items 的相关性得分的总和。

CGk=∑i=1kreli\text{CG}_k=\sum_{i=1}^{k}rel_i CGk​=i=1∑k​reli​

比如上面表格中的 CG1=3\text{CG}_1=3CG1​=3, CG2=5\text{CG}_2=5CG2​=5, CG3=8\text{CG}_3=8CG3​=8…

缺点:CG 忽略了位次的重要性,比如CG3=8\text{CG}_3=8CG3​=8的序列有{(3,3,2), (3,2,3), (2,3,3)},但最优的序列是将最大值都排在前面的序列,如(3,3,2)。

2.2 计算DCG

DCG(Discounted CG),折损累计收益。因为CG的缺点是不能区分位次,所以将位次作为折损,位次越靠后,折损越大,所以DCG的计算为:

DCGk=∑i=1krelilog2(i+1)\text{DCG}_k=\sum_{i=1}^{k}{\frac {rel_i} {log_2(i+1)}} DCGk​=i=1∑k​log2​(i+1)reli​​

2.3 计算nDCG

通常在排序中,nDCG(normalized DCG)是使用最多的。即对DCG进行归一化,归一化就是理想的排序结果,即相关性最大的排在前面,其DCG成为IDCG(Ideal DCG)。

nDCGk=DCGIDCG\text{nDCG}_k={\frac {DCG} {IDCG}} nDCGk​=IDCGDCG​

3. 本地代码实现

3.1 自己编写代码

def get_dcg(y_true, y_scores, k):'''@pred_arr: 预测顺序的相关度,ndarray,shape=(n,1)@gt_arr: 实际顺序相关度,ndarray,shape=(n,1)@k: 要计算的最大位置,int'''arr = [x[1] for x in sorted(zip(y_scores, y_true), reverse=True)[:k]]weights = np.power(np.log2(range(2,len(arr)+2)), -1)dcg = np.sum(arr*weights)return dcgdef get_ndcg(y_true, y_scores, k):dcg = get_dcg(y_true, y_scores, k)idcg = get_dcg(sorted(y_true), np.arange(len(y_true)), k)return dcg/idcg

3.2 使用sklearn.metrics.ndcg_score

使用方式可参考我的博文sklearn.metrics模块重要API的原理与应用总结 的“DGC和nDCG”一节。

3.3 两种代码的速度比较

随机生成不同数量的样本,查看两个函数的运行时长。

m = 100  # 样本量
y_true = np.random.randint(0,1000000,m).reshape(1,-1)
y_scores = np.random.randint(0,1000000,m).reshape(1,-1)k = 10  # 前k个最高排名的ndcg
ndcg1 = get_ndcg(y_true[0], y_scores[0], k=k)
ndcg2 = ndcg_score(y_true, y_scores, k=k)# 在jupyter中执行
"""
%%timeit
get_ndcg(y_true[0], y_scores[0], k=k)%%timeit
ndcg_score(y_true, y_scores, k=k)
"""
样本量(m)自己的代码sklearn
1000022.7 ms5.61 ms
500010.5 ms3.06 ms
30005.99 ms1.93 ms
20003.79 ms1.44 ms
10001.81 ms0.95 ms
500896 µs711 µs
400717 µs639 µs
350631 µs618 µs
300547 µs588 µs
250462 µs558 µs
100212 µs458 µs

可以看到,当样本数量较大时,使用sklearn提供的函数速度较。值得注意的是,自己写的代码还没有优化,与复杂度与数据量大致成线性增长,可以使用前k大个数算法进行优化,进一步缩短时长。

3.4 两种代码之间的误差

两种代码之间的误差主要来自于对相同y_scores的值不同的排序位次导致的,比如:

y_scores = [0.9, 0.5, 0.6, 0.9, 0.9]
y_true = [7, 4, 1, 0, 0]

根据y_scores,y_true的排序方式及相应的ndcg为:

相关性排序Value
[7,0,0,1,4]0.896
[0,7,0,1,4]0.638
[0,0,7,1,4]0.547

如果y_score保留小数点后6位的话,两个方法的误差将在10−610^{-6}10−6数量级。

4. spark代码实现

target为回归

target为0-1分类

相关内容

热门资讯

河南夫妇冒雨移开折断大树,“不... 评论员 陈柯旭 折断的大树能挡住马路,但挡不住普通人身上的微光。 近日,在河南驻马店突降暴雨,一棵大...
现货黄金跌破4300美元关口 8日,现货黄金盘中跳水,跌破4300美元/盎司,跌超0.6%。 消息面上,据新华社,当地时间7日晚,...
英法德联合出手! 文丨陆弃 当英国首相斯塔默、法国总统马克龙和德国总理默茨准备与乌克兰总统泽连斯基在伦敦举行会晤时,外...
两个月来伊以首次互袭,特朗普想... 当地时间6月7日,伊朗向以色列发射了四轮导弹袭击,以回应数小时前以色列对黎巴嫩首都贝鲁特进行的致命空...
德韩竞标谁能赢? 崔轶亮:难分... 加拿大正在推进“巡逻潜艇项目”,计划采购最多12艘柴电潜艇,以替换预计于2030年代中期退役的4艘“...
向以色列发射三波导弹后,伊朗威... 据参考消息6月8日报道,6月7日晚,伊朗向以色列发射三波导弹,以回应以色列不断升级对黎巴嫩的军事行动...
原创 恐... 6月8日,国际足坛再次出现球员在比赛中昏迷的情况。在丹麦同乌克兰的热身赛中,34岁的埃里克森忽然晕倒...
原创 歼... 这两天,咱们国产隐身机歼-35的外贸版(也就是歼-35AE)算是彻底曝光了,连带着那架编号0001的...
以色列遭多波导弹袭击!特朗普:... 据美国有线电视新闻网(CNN)报道,当地时间7日,伊朗伊斯兰革命卫队(IRGC)发布声明称,当日用弹...
原创 W... WB集团的Warmate“滞留弹药,配备气动弹射器,起飞前使用。(WB集团) 波兰军备局与WB集团签...