排序(ranker )模型⾥⾯常⽤的评价指标梳理
1. 写在前⾯
最近的任务需要⽤到排序性的模型了,也就是LearningToRank的排序算法,关于这种排序算法, 预测的⽬标并不是点击或者不点击这样的⼆分类问题, ⽽是⼀个列表的整体排列顺序,是⼀种list-wise的⽅法,有时候更加符合推荐的场景。之前也整理过, 具体的可以参考。说到排序结果, 必然需要有指标⽤于评价排序结果的好坏,所以这篇⽂章的话主要是整理搜索排序⾥⾯⽤到的常⽤评价指标,⽐如我们常听到的MRR(Mean Reciprocal Rank), MAP(Mean Average Precison), NDCG(Normalized Discounted Cumulative Gain),
RC(Rank Correction)等, 之前学习的时候, 如果不⽤会发现很快就忘记怎么算,尤其是NDCG这个, 感觉好难懂的样⼦,所以这次想把这些都整理到⼀块, 进⾏对⽐和强化理解, 也⽅便以后的回看。
看完这些, 再也不要说不懂排序的评价指标了哈哈,下⾯开始:
2. 精确率&召回率&F1值&AUC
这些内容, 这⾥就不整理了,这⼏个主要是评价⼆分类问题中模型的性能好坏, 我之前已经有⾮常详细的⼀篇⽂章进⾏梳理, 这⾥⾯最重要的就是AUC, 这个计算⼀定要会, 计算代码也得会⼿撸, 具体参
考:, 这篇⽂章有些长,可以根据⽬录直接定位到性能度量那⼀节
, 这篇⽂章是作为补充, 并把回归的⼀些评估和损失加⼊了进去,转载的⼀篇不错的⽂字
通过这两篇⽂章, 应该可以把ML⾥⾯常⽤的评估指标和损失函数给拎起来, 这⾥⾯分类的重点就是AUC, 这个⾯试⼀般必考。下⾯开始介绍另外⼀些指标, 下⾯这些,主要是衡量的推荐列表topK的评价了。
3. Hit Ratio(HR)
在top-k推荐⾥⾯, HR是⼀种常⽤的衡量召回率的指标,也⽐较好理解,所以就先整理下, 计算公式:
分母是所有测试集合, 分⼦表⽰每个⽤户top-K列表中属于测试集合的个数总和。
举个简单的例⼦,三个⽤户在测试集中的商品个数分别是10,12,8,模型得到的top-10推荐列表中,分别有6个,5个,4个在测试集中,那么此时HR的值是
这个理解起来,就是对于每个⽤户来说, 我假设给他推荐了M个商品, 在tok-K⾥⾯,看看有⼏个⽤户真的看了(Hit了多少), 假设N的话, 那么对于该⽤户我的击中率就是, 最后的HR就是所有⽤户的击中率之和。
代码实现:HR @K =GT
NumberOfHits @K
=(10+12+8)(6+5+4)
0.5
M N
# 这个也是我⽬前根据公式试着写的, 不⼀定对哈, 看⽹上都是那种简化版,并没看到分组的意思
# 我这⾥的test_df ,有3列, user_id, label(表⽰⽤户是否点击), pred_score(模型预测分数), 列表要对应起来作为参数传⼊
# 思路就是根据⽤户id 分组,然后分组统计HitRate ,最后加和
def  HitRate (test_df , topk =20, user_col_name ='item_id', label_col_name ='labels', pred_col_name ='scores'):
# 按照item_id 分组
item_groups = test_df .groupby (user_col_name )
hit = 0
user_item_cou = 0
# 遍历每个组
for  item_id in  test_df [user_col_name ].unique ():
group_df = item_groups .get_group (item_id )
if  group_df .shape [0] >= topk :
group = group_df .sort_values (pred_col_name , ascending =False )[:topk ]
hit += sum (group [label_col_name ])          # 前tokp ⾥⾯击中的个数
user_item_cou += group_df .shape [0]    # 每个⽤户测试集⾥⾯的被推荐item 个数
return  hit / user_item_cou
4. Mean Average Precision(MAP)
⾸先,先来看下AP(Average Precision), 平均正确率
假使当我们使⽤google搜索某个关键词,返回了10个结果。当然最好的情况是这10个结果都是我们想要的相关信息。但是假如只有部分是相关的,⽐如5个,那么这5个结果如果被显⽰的⽐较靠前也是⼀个相对不错的结果。但是如果这个5个相关信息从第6个返回结果才开始出现,那么这种情况便是⽐较差的。这便是AP所反映的指标,与recall的概念有些类似,不过是“顺序敏感的recall
主要是看计算, 对于⼀个⽤户, 给他推荐物品, 的平均正确率:
其中表⽰groud-truth的结果, 表⽰物品在推荐列表中的位置,表⽰物品在推荐列表中排在物品之前。 这个这样说,可
能不容易理解, 来个例⼦⽴马了然了:
这个的感觉, 就是按照预测得分从⼩到⼤排序, 从右边开始遍历, 遇到1的时候, 就看左边⼜多少个1,加上当前的1除以位置,就是当前位置的precision,然后把top-K的precision就是AP。
上⾯这是对于⼀个⽤户来讲, 如果所有⽤户的AP的平均值, 就是MAP了。
也是⽐较好理解, 代码看下:
u u AP =u Ωu 1i ∈Ωu
∑p ui h p <p +1
∑j ∈Ωu (uj ui )Ωu p ui i p <uj p ui j i MAP =
∣U ∣AP ∑u ∈U u
# 我这⾥的test_df ,DataFrame 结构,有3列, user_id, label(表⽰⽤户是否点击), pred_score(模型预测分数)
from  sklearn .metrics import  average_precision_score
def  MAP (test_df , topk =20, user_col_name ='item_id', label_col_name ='labels', pred_col_name ='scores'):
# 按照item_id 分组
user_groups = test_df .groupby (user_col_name )
pred_map = 0
user_cou = 0
# 遍历每个组
for  user_id in  test_df [user_col_name ].unique ():
cur_user = user_groups .get_group (user_id )
if  cur_user .shape [0] >= topk :
cur_ap = average_precision_score (cur_user [label_col_name ].values .ravel (), cur_user [pred_col_name ].values .ravel ())
if  not  np .isnan (cur_ap ):
pred_map += cur_ap
user_cou += 1
return  pred_map / user_cou
5. Mean Reciprocal Rank(MRR)
这个也⽐较好理解, 出该query相关性最强的⽂档所在位置,并对其取倒数,即这个query的MRR值, 计算公式:
其中, 是⽤户的个数, 表⽰对于第个⽤户,推荐列表中第⼀个ground-truth在结果item中的排列位置。
如果对于⼀个⽤户,也是返回⼀个列表的话,那么公式应该是这样:
最后各个user的score取平均就是MRR。这个是在天池新闻推荐⾥⾯遇见过的,具体解释下啥意思, 对于⼀个⽤户,看看怎么计算得分:
假设给某个⽤户推荐了5篇⽂章: article1, article2, article3, article4, article5.
假如article1就是真实的⽤户点击⽂章,也就是article1命中, 则s(user1,1)=1, s(user1,2-4)都是0, 如果article2是⽤户点击的⽂章, 则s(user,2)=1/2,s(user,1,3,4,5)都是0。也就是score(user)=命中第⼏条的倒数。如果都没中, 则score(user1)=0。 这个是合理的, 因为我们希望的就是命中的结果尽量靠前, ⽽此时分数正好⽐较⾼。
如果有1和3命中, 那么该score=1/1 + 1/3这种。
最后求个平均分就是MRR
该指标的缺陷在于仅仅考虑了相关性最强的⽂档所在的位置带来的损失。具体代码计算:
MRR =Q 1i =1∑∣Q ∣
rank i 1
∣Q ∣rank i i  score (user )=k =1∑topK
k s (user ,k )
from  sklearn .metrics import  label_ranking_average_precision_score
def  MRR (test_df , topk =20, user_col_name ='item_id', label_col_name ='labels', pred_col_name ='scores'):
# 按照item_id 分组
user_groups = test_df .groupby (user_col_name )
y_trues , y_preds = [], []
rank函数怎么用
# 遍历每个组
for  item_id in  test_df [user_col_name ].unique ():
cur_user = user_groups .get_group (item_id )
if  cur_user .shape [0] >= topk :
y_trues .append (cur_user [label_col_name ].values )
y_preds .append (cur_user [pred_col_name ].values )
return  label_ranking_average_precision_score (np .array (y_trues ), np .array (y_preds ))
6. Normalized Discounted Cummulative Gain(NDCG)
归⼀化折损累计增益,这个东西⽬前也是⽤的较多的⼀个, 看这个名字可能不太好理解, 但背后的思想⽐较简单。 个推荐系统返回⼀些item并形成⼀个列表,我们想要计算这个列表有多好,每⼀项都有⼀个相关的评分值,通常这些评分值是⼀个⾮负数,这就是gain(增益).此外对于这些没有⽤户反馈的
项我们通常设置起增益为0.
下⾯先从累计增益(CG)说起。
6.1 累计增益CG
对于⼀个⽤户, 模型会给推荐的每个item打分表⽰与当前⽤户的相关性。假设当前推荐item的个数为个,我们把这个item的相关分数进⾏累加,就是当前⽤户的累积增益:
这⾥的就是位置的推荐item与当前⽤户相关性, 这⾥的推荐列表是根据预测分数排好序之后的了。
上⾯就是CG,⾮常简单,就是相关分数整之和, 但是有个问题就是并没有考虑item的位置, ⽐如理想的排序结果是B3, B2, B1, 那么我最终推荐列表是B1, B3, B2, 对于CG来说, 是⼀样的。
6.2 折损累计增益(DCG)
CG的⼀个缺点是没有考虑每个推荐结果处于不同位置对整个推荐效果的影响,例如我们总是希望相关性⾼的结果应该排在前⾯. 显然,如果相关性低的结果排在靠前的位置会严重影响⽤户的体验,所以在CG的基础上引⼊位置影响因素,即DCG(Discounted Cumulative Gain),这⾥指的是对于排名靠后推荐结果的推荐效果进⾏“打折处理”。排序越往后, 价值就越低。
这个地⽅理解起来,就是对于所处位置进⾏了个加权, 权重⼤⼩是, 排名越靠后,权重越⼩, 那么带有加权之后的CG就是:这就是DCG, 在CG的基础上位置加权, 两个结论:推荐结果的相关性越⼤,DCG越⼤
相关性好的排在推荐列表前⾯的话,推荐效果越好,DCG越⼤.
p p CG =p rel i =1∑p i
rel i i log (i +1)21
DCG =p =i =1∑p log (i +1)2rel i rel +1i =2∑p
log (i +1)2rel i
⽽NDCG的话,其实就是想让不同的⽤户推荐列表之间进⾏⼀个横向的评估。因为上⾯的DCG和CG, ⽬前都是⽤⼀个⽤户来说的, 看起来计算也⾮常简单, 但是如果多个⽤户的结果进⾏⽐较呢? 或者,如何得到最终的平均值呢? 这个就不能直接DCG平均了, 因为DCG⾥⾯的计算过程⾥⾯⽤到了整个的排序列表, ⽽这个排序列表,显然, 每个⽤户是不同的, 所以不能平等的简单相加除以⽤户总数, 所以NDCG的意思,就是先每个⽤户DCG归⼀化, 然后再所有⽤户求平均, 再简单点理解,就是每个⽤户DCG求平均的时候,需要加个权。加权之后的DCG就是NDCG。
那么是怎么加权的呢?
6.3 归⼀化折损累计增益(NDCG)
DCG仍然有不⾜之处,即不同的推荐的推荐列表之间,很难进⾏横向的评估,⽽我们评估⼀个推荐系统不可能仅使⽤⼀个⽤户的推荐列表及相应结果进⾏评估,⽽是对整个测试机中的⽤户及其推荐列表结果进⾏评估.那么不同的⽤户的推荐列表的评估分数就需要进⾏归⼀化
介绍NDCG之前还需要知道另⼀个概念,IDCG(Ideal DCG),指推荐系统为某⼀⽤户返回的最好推荐结果列表,即假设返回结果按照相关性排序,最相关的结果放在前⾯,此序列的DCG为IDCG.因此DCG的值介于(0,IDCG],故NDCG的值介于(0,1]。NDCG计算公式:
IDCG为理想情况下最⼤的DCG值:
其中表⽰,结果按照相关性从⼤到⼩的顺序排序, 取前p个结果组成的集合,即按照最优的⽅式排序。
这个其实就是每个DCG的权重了。这⾥还是拿个例⼦来说(下⾯第2个连接):
假设⼀个⽤户,推荐了6篇⽂章, 与该⽤户的相关性分数是3, 2, 3, 0, 1, 2,这个指的真实的相关性。
那么该⽤户的CG:
可以看到只是对相关的分数进⾏了⼀个关联的打分,并没有召回的所在位置对排序结果评分对影响。 也就是⽆论模型怎么按照预测分数排
序, CG都是这个。 这⽆法衡量排序好坏呀, 所以就有了DCG计算, 假设,我们按照模型的预测分数,从⼤到⼩排序之后
此时, DCG就是:
nDCG =p IDCG p
DCG p
IDCG =p i =1∑∣REL ∣
log (i +1)
22−1rel i ∣REL ∣CG =3+2+3+0+1+2
DCG =3+1.26+1.5+0+0.38+0.71=6.86