最近面试被问到比较多的就是评价指标,感觉这部分还是需要梳理一下,所以进行了一些相关资料的整理收集和记录,如有错误,烦请指正,不甚感激。
工业界推荐系统由于其商业性质,评测指标会更加多元化。通常对于一个推荐系统,我们会获得两种指标,离线指标和在线指标。
1 离线指标
离线评测的指标众多,各有各的作用,实际上这些指标都是 “探索-利用” 问题的侧面反映,它们解答了两个问题:
- 一是系统有多好,即推荐系统当前的性能如何,对数据利用得彻不彻底,这类指标我们称之为利用类指标;
- 二是系统还能好多久,即推荐系统长期的健康状况,是否能够探索出用户新的兴趣,这类指标我们称之为探索类指标。
下面我们将详细介绍各类指标。
1.1 利用类指标
利用类指标用来评价系统有多好,主要分为两大类:一类是深度类,深度类就是在评分预测和行为预测上面做得是否到位;另一类是广度类,更加偏向于全局评测。
首先先来讲深度类指标,包括评分准确性,分类准确性,排序指标和商业指标。
1.1.1 评分准确度
评分准确度考虑推荐算法的预测打分与用户实际打分的相似程度,在评分类的显式用户反馈中,预测准确度非常重要。
评测背景和符号说明:系统在已知用户与物品集合 的真实评分 情况下在测试集 上生成预测评分 。
1.1.1.1 平均绝对误差(MAE)
推荐算法的整体准确度是所有用户准确度(对所有物品的评估)的平均。研究表明,如果评分系统是基于整数建立的(即用户给的评分都是整数),那么对预测结果取整数会降低 的误差。
优点:
-
计算方法简单,易于理解;
-
每个系统的平均绝对误差唯一,从而能够区分两个系统平均绝对误差的差异。
局限性:
-
对 MAE 指标贡献比较大的往往是那种很难预测准确的低分商品。 所以即便推荐系统 A 的 MAE 值低于系统 B,很可能只是由于系统 A 更擅长预测这部分低分商品的评分,即系统 A 比系统 B 能更好的区分用户非常讨厌和一般讨厌的商品,显然这样区分的意义不大。
-
在用户喜好偏差的程度比较小时也并不适用,因为用户只关心把好产品错归为坏产品,或者把坏产品错归为好产品的比例。例如,以 3、5 个星为界区分好坏,那么把 4 预测成了 5,或者把 3 预测成了 2 都对用户没有影响。
1.1.1.2 均方根误差(RMSE)
优点:
- 相比于 MAE,RMSE 加大了对预测不准的用户物品评分的惩罚(平方项的惩罚),因而对系统的评测更加苛刻。
局限性:
-
容易收到高频用户或高频物品的影响。 RMSE 需要取一个平均值。这是对所有用户在所有物品上评分误差的估计的平均。那么,如果一个用户在数据集里面对很多物品进行了评分,这个用户误差的贡献就会在最后的平均值里占很大一部分。也就是说,最后的差值大部分都用于描述这些评分比较多的用户了。这个弊端会造成如果我们得到了一个比较好的 RMSE 数值,往往很可能是牺牲了大部分用户的评分结果,而对于少部分的高频用户的评分结果有提高。说得更加直白一些,那就是 RMSE 小的模型,并不代表整个推荐系统的质量得到了提高。
-
RMSE 指标并没有反应真实的应用场景。 真实的应用场景,我们往往是从一大堆物品中,选择出一个物品,然后进行交互。在这样的流程下,物品单独的评分其实并不是最重要的。更进一步说,就算一个推荐系统能够比较准确地预测评分,也不能证明这个推荐系统能够在真实的场景中表现优异。
MAE 和 RMSE 在不均衡的测试集下的调整: 如果测试集的物品分布不均衡,在此测试集上计算出的 MAE 或 RMSE 可能会受到高频物品产生的误差的严重影响。如果我们需要一个方法来代表任意物品的预测误差,那么最好分别计算每个物品的 MAE 或 RMSE,然后再取所有物品的平均值。类似地,如果测试集的用户分布不均衡,有希望推断出一个随机抽样的用户所要面临的预测误差,那么我们可以计算每个用户的平均 MAE 和 RMSE。
MAE 和 RMSE 在特殊评分语义下的调整: MAE 和 RMSE 仅取决于误差的幅度大小。而在某些应用中,评分的语义可能使得预测误差不仅取决于其幅度大小。在这样的域中可能需要一个合适的失真测量 d(^r,r),而不是平方差或绝对差。例如,一个应用有一个 3 星系统,1 表示 “不喜欢”,2 表示 “中立”,3 表示 “喜欢”,而且推荐给用户不喜欢的物品比不给用户推荐物品更糟糕,这时一个设置为 d(3,1)=5,d(2,1)=3,d(3,2)=3,d(1,2)=1,d(2,3)=1和 d(1,3)=2的失真测量可能是合理的。
1.1.3 MAE 和 RMSE 的变种
归一化均方误差(NMRSE)和归一化平均绝对误差(NMAE):在一定评分范围内归一化了的 RMSE 和 MAE。由于它们只是 RMSE 和 MAE 的缩减版本, 算法排序结果与未归一化时相同。
1.1.2 分类准确度
分类准确度定义为推荐算法对一个物品用户是否喜欢判定正确的比例,通常用于行为预测。因此,当用户只有二元选择时,用分类准确度进行评价较为合适。
评测背景和符号说明: 在行为预测准确度评估中,一般是对 推荐的准确度评估,这里的 N 和实际推荐系统场景有关,就是实际每次推荐系统需要输出几个结果。在 推荐中,设 为根据训练建立的模型在测试集上的推荐,为测试集上用户的选择。
1.1.2.1 准确率(Precision)
准确率定义为系统的推荐列表中用户喜欢的物品和所有被推荐物品的比率,也称为查准率,即在推荐(预测)的物品中,有多少是用户真正(准确)感兴趣的。
1.1.2.2 召回率(Recall)
召回率表示一个用户喜欢的物品被推荐的概率,也称为查全率,即用户喜欢的物品中,有多少是被推荐了的。
优点:
- 准确率和召回率对比评分准确度指标更能反应推荐系统在真实场景中的表现。
局限性:
-
未发生交互行为的物品很难确定用户是否真正喜欢。准确率和召回率的定义依赖于用户喜欢和不喜欢的物品分类 。如何定义用户是否喜欢一个物品,尤其是用户是否喜欢一个没有打分的物品还是十分困难的。在推荐系统中,无法知道用户是否喜欢某些未知的物品,所以召回率在纯粹意义上讲并不适合度量推荐系统。因为召回率需要知道每个用户对未选择物品的喜好,然而这与推荐系统的初衷是相悖的。
-
准确率和召回率必须要一起使用才能全面评价算法的好坏 。准确率和召回率是两个很相似的指标,这两个指标存在负相关的关系,他们分别从不同的角度来评价推荐系统,单独的指标不足以说明算法的好坏,必须一起使用才是更加全面的评价。
准确率和召回率可以有两种计算方法:以用户为中心和以系统为中心。以用户为中心的方法中分别计算每个用户的准确率和召回率,再对所有的用户进行平均。这种方法的重点在于考虑用户的感受,保证每个用户对系统表现的贡献强度是一致的。以系统为中心的方法以考察系统的总体表现为目的,不需要对所有用户做平均。
准确率和召回率存在负相关关联,取决于推荐列表的长度,互相牵制。
-
当准确率很高的时候,表明希望推荐的物品绝大多数是用户感兴趣的,因此推荐较为保守,只推少量最有把握的物品,这样的话,有些用户可能感兴趣但排名没有那么靠前的物品则会被忽略。因此召回率就比较低。
-
如果召回率很高,表明目标是尽可能把用户感兴趣的物品全部召回,则门槛就会降低,为了捕获更多用户感兴趣的物品,召回的总量就多了,而准确率就低了。
在不同的应用场景中需要自己判断希望准确率比较高或是召回率比较高。比如系统的任务是发现所有用户喜欢的产品,召回率就变得很重要,因此需要在一定的召回率水平下考虑准确率。如果是做实验研究,也可以绘制 Precision-Recall 曲线来帮助分析。在需求不是很明确的时候,准确率和召回率容易出现的矛盾的情况,为了同时考察准确率和召回率,Pazzani M 等人把二者综合考虑提出了 F 指标(F-measure)。
1.1.2.3 F 指标(F-measure)
F 指标又称综合评价指标,F-Measure 又称为 F-Score,来源于是 IR(信息检索)领域,常用于评价分类模型的好坏。F-Measure 是 Precision 和 Recall 加权调和平均:
其中, 为准确率, 为召回率,当参数 时,就是最常见的 ,也即:
指标把准确率和召回率统一到一个指标,因此得到了广泛的应用。
分类准确度指标图示
分类准确度在稀疏打分情况下的影响及调整
应用于实际的离线数据时,分类准确度可能会受到打分稀疏性的影响。当评价一个推荐列表的质量时,列表中的某些物品很可能还没有被该用户打分,因此会给最终的评价结果带来偏差。需注意的是,这里的打分可以有不同的几种反馈类型,但一定是二元相关度的方式,比如可以是数值评分的二元化(如 MovieLens 数据为 5 分制 ,通常 3~5 分被认为是用户喜欢的,1~2 分被认为是用户不喜欢的),可以是二元打分(喜欢和不喜欢),也可以是用户交互行为(有行为反馈和没有行为反馈)。
一个评价稀疏数据集的方法就是忽略还没有打分的物品,那么推荐算法的任务就变成了“预测已经打分的物品中排名靠前的物品”。
另外一个解决数据稀疏性的方法就是假设存在默认打分,常常对还没有打分的物品打负分 。这个方法的缺点就是默认打分常常与实际的打分相去甚远。
还有一种方法是计算用户打分高的物品在推荐列表中出现的次数,即度量系统在多大程度上可以识别出用户十分喜欢的物品。这种方法的缺点是容易把推荐系统引向偏的方向:一些方法或者系统对某数据集中已知的数据表现非常好,但是对未知的数据表现十分差。
分类准确度评价并不直接评价算法的评分是否准确,如果分类的信息准确无误,与实际打分存在偏差也是允许的,对于分类准确率,我们更加在乎系统是否正确地预测用户是否会对某个物品产生行为。
1.1.3 排序准确度
排序是推荐系统非常重要的一个环节,因为把用户偏爱的物品放在前面是推荐系统的天职,因此检测推荐系统排序能力非常重要。
关于排序能力的评测指标,我们自然会想到搜索引擎中的排序指标,它们在某种程度是可以应用于推荐系统发的评测,但是会有些问题。由于推荐系统输出结果是非常个人化的,除了用户本人,其他人都很难替他回答哪个好哪个不好,而搜索引擎评价搜索结果和查询相关性,具有很强的客观属性,可以他人代替评价。所以通常评价推荐系统排序效果很少采用搜索引擎排序指标,比如 MAP、MRR、NDCG。推荐系统评价排序通常采用 AUC。
1.1.3.1 AUC
1)AUC原理
在许多分类学习器中,产生的是一个概率预测值,然后将这个概率预测值与一个提前设定好的分类阈值进行比较,大于该阈值则认为是正例,小于该阈值则认为是负例。如果对所有的排序结果按照概率值进行降序排序,那么阈值可以将结果截断为两部分,前面的认为是正例,后面的认为是负例。我们可以根据实际任务的需要选取不同的阈值。如果重视精确率,我们可以设定一个很高的阈值,如果更重视召回率,可以设定一个很低的阈值。
到这里,有两个问题需要考虑:
- 设定阈值然后再来计算精确率,召回率和 F1-Score 太麻烦了,这个阈值到底该设定为多少呢?有没有可以不设定阈值来直接评价模型性能的方法呢?
- 排序结果也很重要,不管预测值是多少,只要正例的预测概率都大于负例的就好了。
ROC 和 AUC 便可以解决上面的两个问题。 ROC 全称是 “受试者工作特征”,(receiver operating characteristic)。我们根据学习器的预测结果进行排序,然后按此顺序逐个把样本作为正例进行预测,每次计算出两个重要的值,分别以这两个值作为横纵坐标作图,就得到了 ROC 曲线。ROC 曲线的横轴为 “假正例率” (False Positive Rate,FPR),又称为 “假阳率”;纵轴为 “真正例率”(True Positive Rate,TPR),又称为 “真阳率”,而 AUC(area under the curve)就是 ROC 曲线下方的面积,见下图。
从上图可以看出,阈值最大时,对应坐标点为 (0,0), 阈值最小时,对应坐标点 (1,1),最理想的目标:TPR=1,FPR=0,即图中 (0,1) 点,故 ROC 曲线越靠拢 (0,1) 点,越偏离 45 度对角线越好,分类效果越好。
虽然用 ROC 曲线来表示分类器的性能很直观很好用。但是人们更希望能有一个数值来表示分类器的好坏。于是 (Area Under ROC Curve, AUC) 就出现了。AUC 值是一个概率值,当随机挑选一个正样本以及负样本,当前的分类算法根据计算得到的 Score 值将这个正样本排在负样本前面的概率就是 AUC 值,AUC 值越大,当前分类算法越有可能将正样本排在负样本前面,从而能够更好地分类。
AUC 量化了 ROC 曲线表达的分类能力。这种分类能力是与概率、阈值紧密相关的,AUC 值越大,则说明分类能力越好,那么预测输出的概率越合理,因此排序的结果越合理。如此 AUC 这个值在数学上等价于:模型把关心的那一类样本排在其他样本前面的概率。最大是 1,完美结果,而 0.5 就是随机排列,0 就是完美地全部排错。 在 CTR 预估中,我们不仅希望分类器给出是否点击的分类信息,更需要分类器给出准确的概率值,作为排序的依据。所以,这里的 AUC 就直观地反映了 CTR 的准确性(也就是 CTR 的排序能力)。
2)拓展:AUC的计算
因为这个地方面试还被问过AUC的计算和实现(当时的我只会调用sklearn包来实现),这里就多写一点,接下来看看AUC的计算。
第一种方法
最直观的,根据 AUC 这个名称,我们知道,计算出 ROC 曲线下面的面积,就是 AUC 的值。事实上,这也是在早期 Machine Learning 文献中常见的 AUC 计算方法。由于我们的测试样本是有限的。我们得到的 AUC 曲线必然是一个阶梯状的(如下图)。
因此,计算的 AUC 也就是这些阶梯 下面的面积之和。这样,我们先把 score 排序 (假设 score 越大,此样本属于正类的概率越大),然后一边扫描就可以得到我们想要的 AUC。但是,这么做有个缺点,就是当多个测试样本的 score 相等的时候,我们调整一下阈值,得到的不是曲线一个阶梯往上或者往右的延展,而是斜着向上形成一个梯形。此时,我们就需要计算这个梯形的面积。由此,我们可以看到,用这种方法计算 AUC 实际上是比较麻烦的。
第二种方法
一个关于 AUC 的很有趣的性质是,它和 Wilcoxon-Mann-Witney Test(威尔科克森-曼-维特尼试验)是等价的。Wilcoxon-Mann-Witney Test 就是测试任意给一个正类样本和一个负类样本,正类样本的 score 有多大的概率大于负类样本的 score。有了这个定义,我们就得到了另外一中计算 AUC 的办法:得到这个概率。我们知道,在有限样本中我们常用的得到概率的办法就是通过频率来估计之。这种估计随着样本规模的扩大而逐渐逼近真实值。这和上面的方法中,样本数越多,计算的 AUC 越准确类似,也和计算积分的时候,小区间划分的越细,计算的越准确是同样的道理。具体来说就是统计一下所有的 (为正类样本的数目,为负类样本的数目) 个正负样本对中,有多少个组中的正样本的 score 大于负样本的 score。当二元组中正负样本的 score 相等的时候,按照 0.5 计算。然后除以 MN。实现这个方法的复杂度为 。 为样本数(即 )
第三种方法
第三种方法实际上和上述第二种方法是一样的,是基于上面算法的简化,使得复杂度减小了。它也是首先对 score 从大到小排序,然后令最大 score 对应的 sample 的 rank 为 n,第二大 score 对应 sample 的 rank 为 ,以此类推,其中要注意,如果几个样本分数一样,需要将其排序值调整为他们的平均值。然后把所有的正类样本的 rank 相加,再减去 种两个正样本组合的情况。得到的就是所有的样本中有多少对正类样本的 score 大于负类样本的 score。然后再除以 (即正负样本相对排序总共的组合可能性)。公式如下:
公式解释:
-
为了求的组合中正样本的 score 值大于负样本,如果所有的正样本 score 值都是大于负样本的,那么第一位与任意的进行组合 score 值都要大,我们取它的 rank 值为 n,但是 n-1 中有 M-1 是正样例和正样例的组合这种是不在统计范围内的(为计算方便我们取 n 组,相应的不符合的有 M 个),所以要减掉,那么同理排在第二位的 n-1,会有 M-1 个是不满足的,依次类推,故得到后面的公式 ,我们可以验证在正样本 score 都大于负样本的假设下,AUC 的值为 1;
-
根据上面的解释,不难得出,rank 的值代表的是能够产生 score 前大后小的这样的组合数,但是这里包含了(正,正)的情况,所以要减去这样的组(即排在它后面正例的个数),即可得到上面的公式。
关于 AUC,越接近 1 越好是肯定的,但是并不是越接近 0 就越差,最差的是接近 0.5,如果 AUC 很接近 0 的话,只需要把模型预测的结果加个负号就能让 AUC 接近 1。
当时面试官问到一个问题,给你两组list,你能不能直接算出AUC,都写到这里了,当然得实现一下:
import numpy as np
from sklearn.metrics import roc_curve, auc
def auc_v1(probs, labels):
'''
使用第一种方法计算AUC
:param probs: 模型预测值
:param labels: 真实标签
:return: auc
'''
argsort_scores = np.argsort(probs)[::-1]
labels_sorted = labels[argsort_scores]
TPR = np.cumsum(labels_sorted) / np.sum(labels_sorted)
FPR = np.cumsum(1 - labels_sorted) / np.sum(1 - labels_sorted)
AUC = np.trapz(TPR, FPR)
return AUC
def auc_v2(probs, labels, pos_label=1):
'''
使用第二、三种方法计算AUC
:param probs: 模型预测值
:param labels:真实标签
:param pos_label: 正类样本标签
:return: auc
'''
lst = list(zip(probs, labels))
num_positive = np.sum(labels == pos_label)
num_negative = len(labels) - num_positive
ranked_probs, ranked_labels = zip(*sorted(lst, key=lambda x: x[0]))
ranks = np.arange(1, 1+len(ranked_probs))
valid_groups = 0
for i in range(len(ranked_labels)):
if ranked_labels[i] == pos_label:
mask = ranked_probs[i] == ranked_probs
valid_groups += np.sum(mask * ranks) / np.sum(mask)
auc = (valid_groups - (num_positive * (num_positive + 1) / 2)) / (num_negative * num_positive)
return auc
# 样例
labels = np.array([1, 1, 0, 1, 1, 0, 0])
prob = np.array([0.8, 0.7, 0.3, 0.4, 0.4, 0.5, 0.5])
# 计算结果
print("auc v1", auc_v1(prob, labels))
print("auc v2", auc_v1(prob, labels))
fpr, tpr, thresholds = roc_curve(labels, prob, pos_label=1)
print("sklearn:", auc(fpr, tpr))
可以看到,最终结果均一致:
auc v1 0.6666666666666667
auc v2 0.6666666666666667
sklearn: 0.6666666666666667
3)再次拓展:工业界AUC的应用
拓展一:工业界AUC指标分析
AUC 是推荐系统中非常重要的一个指标,在工业界模型的不同阶段会有三个不同的 AUC 指标,如下图:
这三个 AUC 理论上差距应该比较小,但实际中依然可能出现较大的差异,对比分析这三个 AUC 值,其实可以看出很多问题。
-
train AUC >> test AUC:通常情况是模型出现了过拟合。
-
test AUC >> Online AUC :通常是因为特征不一致产生的,那特征不一致是如何产生的呢?
- test 可能用了未来的数据,这种现象被称为 “偷窥数据”、“数据穿越”、“时间穿越” 问题,虽然看起来不可思议,但其实在工业界这种错误非常常见,所以一定要注意数据在时间上的正交性;
- 某些特征需行为产生后才能拿到,比如位置信息,test 数据有位置信息,而 serving 没有获取到,又如训练模型的时候把停留时长作为特征,但 serving 也获取不到;
- 因模型太大,在线特征可能会做些裁剪,如特征淘汰、模型淘汰、参数淘汰。
注意,上面提到的 AUC 不一致并不是一定要让 online AUC 和 test AUC 一致才行,重点是要分析不一致的原因。有的不一致是合理的,比如 online 端的位置信息,因获取不到就给一个默认值而导致的不一致,还有很多是由于 bug 导致的不一致,比如时间穿越问题,又如训练特征和预测特征不一致产生的,尤其是线上实时存的特征和线下拼接的特征不一致。具体的分析,可能就要涉及下述指标GAUC了。
拓展二:AUC改进:GAUC
除了直接计算全局 AUC,通常还可能会分用户组计算 AUC,然后得到不同用户组的加权后的 AUC,这在推荐里面也被叫做GAUC。
为什么会提出GAUC呢,这就不得不说一说AUC的缺点了,AUC在推荐的主要缺点是:线下AUC提升不一定能带来线上效果的提升。主要有以下两点:
-
AUC反映整体样本间的排序能力,表示正样本得分比负样本得分高的概率,对样本不区分用户地计算整体样本的AUC。
-
AUC计算的时候,不仅会涉及同一个用户的不同item,也会涉及不同用户的不同item ,而线上排序系统每次排序只针对同一个用户的不同item进行打分。
换句话说AUC更关注的是一个整体的得分,他并不太会将注意力集中在某一个用户身上,但是线上推荐的时候,往往是针对单个用户去进行推荐的,因此就会带来一定的问题。
另外补充一点:GAUC实现了用户级别的AUC计算,GAUC可以用于评估多类别分类模型的性能,而AUC只能用于评估二分类模型的性能。
拓展三:用户AUC
除此之外,还有一种计算AUC的方法,用户平均AUC。简单来说,GAUC由用户分组加权计算得出,而这里计算的 AUC 是所有用户 AUC 的平均,表示单用户 AUC,计算公式如下:
为什么需要分用户算 AUC?
-
不同用户间比较 AUC 是没有意义的,因为不同的用户间习惯和行为都不同,比如有的用户不喜欢点赞,可能学习到的某个指标值永远是 0,虽然学习准确但是最终目的还是要给用户推荐其喜欢的文章、视频或者物品等。
-
推荐系统希望满足所有人的需求,比如有的用户一天浏览了一万个商品,而另外一个用户只看了十个商品,如果计算全局 AUC,模型会把那些看过特别多的那些用户的偏好学的特别好,行为比较少的用户可能完全被忽略了。但对于平台而言,每个用户是同样重要的,所以可以先算每个用户的 AUC,然后再算一个平均,算是单用户 AUC 与全局 AUC 各有侧重,两方面考虑吧。
工业界多高的 AUC 算是好的 AUC?
AUC 指标与训练数据有很大关系,无法确切地说达到多少的 AUC 就是好的,AUC 一定是相对来看的,并且是在训练数据、数据分布和待解决问题在同一度量空间下进行比较,AUC 的值才有意义。
1.1.3.2 MAP
MAP(Mean Average Precision) :平均精度均值,单个主题的平均准确率是每篇相关文档检索出后的准确率的平均值。
首先定义排名为第 k 位的准确率 P@k(Precision at position k) :
其中,k 表示排第 k 位的位置, 为整个排序列表,l为相应位置的相关性二元标签,值为 1(相关)或 0(不相关); 是指示器,如果对应位置文档是相关的则是 1,不相关为 0;表示排序列表 中排名为第 的位置。
接下来定义平均准确率 AP(Average Precision) :
其中,m 是与查询关键字 q 关联的文档总数,是标签为 1 的文档数量。
测试集中所有查询关键字的 AP 求平均后就是 MAP(Mean Average Precision)。
举个例子,如下图表示,某个查询关键字的检索出的网页排序列表,蓝色表示相关,白色表示不相关。
则该查询关键字
MAP 的衡量标准比较单一,q(query,搜索词) 与 d(doc,检索到的 doc) 的关系非 0 即 1,核心是利用 q 对应的相关的 d 出现的位置来进行排序算法准确性的评估。
贴一下代码:
def average_precision(y_true):
"""
计算单个查询的平均准确率(Average Precision)
"""
# 初始化变量
total_relevant = 0
precision_sum = 0.0
for i, value in enumerate(y_true):
if value == 1:
total_relevant += 1
precision_sum += total_relevant / (i + 1)
if total_relevant == 0:
return 0
return precision_sum / total_relevant
def mean_average_precision(y_true_list):
"""
计算多个查询的平均准确率的平均值(Mean Average Precision)
"""
return sum(average_precision(y_true) for y_true in y_true_list) / len(y_true_list)
# 示例数据,每个查询的真实值(0代表不相关,1代表相关)
query_results = [
[0, 1, 0, 1, 1], # 第一个查询的真实值
[1, 0, 0, 1, 0], # 第二个查询的真实值
# ... 更多查询的真实值
]
# 计算MAP
map_score = mean_average_precision(query_results)
print("Mean Average Precision (MAP):", map_score)
1.1.3.3 MRR
MRR(Mean Reciprocal Rank):倒数排序法,把标准答案在被评价系统给出结果中的排序取倒数作为它的准确度,再对所有的问题取平均。
其中|Q|是查询个数,ranki是第 i个查询,第一个相关的结果所在的排列位置。
我感觉这是指标中最简单的一个,因为他的评估假设是基于唯一的一个相关结果,比如 q1 的最相关是排在第 3 位,q2 的最相关是在第 4 位,那么 MRR=(1/3+1/4)/2,MRR 方法主要用于寻址类检索(Navigational Search)或问答类检索(Question Answering)。
1.1.3.4 NDCG
NDCG(Normalized Discounted Cumulative Gain): 直译为归一化折损累计增益,计算相对复杂。对于排在结束位置 n 处的 NDCG 的计算公式如下图所示:
其中:
-
表示第 j 个位置的相关度,分母的对数通常取以 2 为底,是 IDCG 的倒数,IDCG 是理想情况下最大 DCG 值。
-
在 MAP 中,四个文档和 query 要么相关,要么不相关,也就是相关度非 0 即 1。NDCG 中改进了下,相关度分成从 0 到 r 的 r+1 的等级 (r 可设定)。当取 r=5 时,等级设定如下图所示:
| Relevance Rating | Value(Gain) |
|---|---|
| Perfect(最相关,r(j)=5) | |
| Excellent(相关,r(j)=4) | |
| Good(中性,r(j)=3) | |
| Fire(不相关,r(j)=2) | |
| Bad(最不相关,r(j)=1) |
这样还是不好理解,我们先从NDCG最起源开始讲,按照G - CG - DCG - NDCG这个顺序一步一步得到NDCG,这样好理解一些。
- Gain:表示一个列表中所有item的相关性分数。rel(i)表示item(i)相关性得分。
- Cumulative Gain:表示对K个item的Gain进行累加。
CG只是单纯累加相关性,不考虑位置信息。 如果返回一个list_1= [A,B,C,D,E],那list_1的CG为0.5+0.9+0.3+0.6+0.1=2.4 如果返回一个list_2=[D,A,E,C,B],那list_2的CG为0.6+0.5+0.1+0.3+0.9=2.4 所以,顺序不影响CG得分。如果我们想评估不同顺序的影响,就需要使用另一个指标DCG来评估。
- Discounted Cumulative Gain: 考虑排序顺序的因素,使得排名靠前的item增益更高,对排名靠后的item进行折损。
CG与顺序无关,而DCG评估了顺序的影响。DCG的思想是:list中item的顺序很重要,不同位置的贡献不同,一般来说,排在前面的item影响更大,排在后面的item影响较小。(例如一个返回的网页,肯定是排在前面的item会有更多人点击)。所以,相对CG来说,DCG使排在前面的item增加其影响,排在后面的item减弱其影响。 怎么实现这个思想呢?DCG在CG的基础上,给每个item的相关性比上log2(i+1),i越大,log2(i+1)的值越大,相当于给每个item的相关性打个折扣,item越靠后,折扣越大。
- IDGC(ideal DCG)–理想的DCG:IDCG的依据是:是根据rel(i)降序排列,即排列到最好状态。算出最好排列的DCG,就是IDCG。
- NDCG:因为不同query的搜索结果有多有少,所以不同query的DCG值就没有办法来做对比。所以提出NDCG。这样的话,NDCG就是一个相对值,那么不同query之间就可以通过NDCG值进行比较评估。
具体实现代码:
import numpy as np
def dcg_at_k(r, k):
"""
计算DCG@K(Discounted Cumulative Gain at K)
r: relevance scores
k: 排名前K个文档
"""
r = np.asfarray(r)[:k] # 将r转换为浮点数类型,仅保留前k个元素
if r.size:
return np.sum(r / np.log2(np.arange(2, r.size + 2)))
return 0.0
def ndcg_at_k(r, k):
"""
计算NDCG@K(Normalized Discounted Cumulative Gain at K)
r: relevance scores
k: 排名前K个文档
"""
dcg_max = dcg_at_k(sorted(r, reverse=True), k)
if not dcg_max:
return 0.0
return dcg_at_k(r, k) / dcg_max
# 示例数据,每个查询的相关性得分
query_results = [
[3, 2, 3, 0, 1], # 第一个查询的相关性得分
[2, 1, 2, 3, 2], # 第二个查询的相关性得分
# ... 更多查询的相关性得分
]
# 计算NDCG@K
k = 3 # 假设计算前3个文档的NDCG
ndcg_scores = [ndcg_at_k(r, k) for r in query_results]
mean_ndcg = np.mean(ndcg_scores)
print(f"Mean NDCG@{k}: {mean_ndcg}")
1.1.3.5 RC
RC(Rank Correlation)指标是衡量模型预测的一个排序list 与groudtruth标准排序list 之间的相关性。用带权重的Kendall等级相关系数作为评估准则,评估两个排序list中两两不一样的pairwise排序一致性情况,计算公式定义如下:
其中,sgn是符号函数,定义如下:
从上述公式可以看出,模型预测的排序list中两两pair对相对排序顺序与真实groudtruth中两个pair相对排序顺序一致的pair对越多,则RC计算的分值越大。
1.2 探索类指标
在推荐系统中,需要数据不断更新,这样系统才是一个活系统,用户兴趣客观上会变迁,数据源客观上也是会用光的一天,所以推荐系统如果不能应对这两个变化,就好不了太久。探索类指标则是用来衡量推荐系统是否健康的标准。
1.2.1 个性化
虽然说到推荐系统时,言必称个性化,但实际上能做到真正个性化很难,那要求用户每个人都独立思考、爱好明确、不受群体影响。但是个性化程度的确能够反映推荐系统的健康程度。
按照 “是否需要推荐系统” 的公式来看:
如果没有个性化,那么分子上增加的连接数,其实是不受分母上增加的物品数影响的,因为所有人都只消费那少数几个物品,那么你其实不需要推荐系统。
个性化如何检测呢?有一个直观的方法,取一天的日志,计算用户推荐列表的平均相似度,如果用户量较大,对用户抽样。
1.2.2 基尼系数
基尼系数(Gini index、Gini Coefficient) 的来自于经济学的概念,用于衡量某个经济或社会指标的不平等程度的统计量。常被用来衡量一个国家或地区居民收入差距,财富分配的不均衡程度。推荐系统借鉴过来用以衡量推荐系统的马太效应,反向衡量推荐的个性化程度,即推荐是否集中在少数热门物品上,所以基尼系数也是一个偏全局的统计量,可以衡量推荐系统的健康度。
计算过程如下:
-
将物品按照热度(物品各自累计推荐次数)从低到高排列,假设有 n 个物品,则排列顺序为 1∼n,1 表示最冷门(推荐次数最少),n 表示最热门(推荐次数最高)。
-
设 i 为热度由低到高序列中排第 i 个位置,pi 为排列位置 i 处的物品推荐次数占总推荐次数的比值,则按以下公式计算基尼系数:
这个公式是根据基尼系数的原理迁移到推荐系统中的一个变体计算公式,根据推荐的特点,作了一些简化和调整:
-
整个公式可以看作是通过对每个推荐物品的推荐次数占比和排序位置进行加权求和,部分是排序位置影响下的权重, 部分是一个归一化因子,用于将求和结果除以 n−1,使得基尼系数的取值范围在 0 到 1 之间。
-
是这个公式的关键,它可以理解为一个缩放因子,其值范围是 ,它放大了序列前后两个头部的影响程度,越靠近头部(最冷门和最热门)的位置权重越大,影响程度为 (靠近序列前面冷门的头部是负影响,靠近序列后面热门头部是正影响),越靠近序列中间位置的位置,权重越小,影响程度越接近 0。
-
最后整个基尼系数的值范围在 [0,1],推荐越均衡越接近于 0,推荐越不均衡,马太效应越严重,则基尼系数越接近于 1。
马太效应,是指强者越强,弱者越弱的效应。推荐系统的初衷是希望消除马太效应,使得各物品都能被展示给对它们感兴趣的人群。但是,很多研究表明,现在的主流推荐算法(协同过滤)是具有马太效应的
基尼系数从经济学迁移转化到推荐系统中的计算原理可参考下图:
基尼系数除了可以衡量推荐系统个性化推荐的健康程度外,也可以用作优化升级不同算法的相互比较的一个指标,即如果两个算法,其中基尼系数值越小的算法推荐物品分布越均衡。
1.2.3 洛伦兹曲线
与基尼系数深度深度相关的一个概念是洛伦兹曲线(Lorenz curve,也译为 “劳伦兹曲线”),洛伦兹曲线和基尼系数都是用于衡量分布不平等程度的统计工具,不同的是,基尼系数是一个单一的数值指标,而洛伦兹曲线则是一种可视化工具,通过绘制曲线来展示分布的不平等程度,可以看到更多细节。在经济学中,洛伦兹曲线常用于评估收入或财富等分布的不平等程度的,因此也常被称收入分配曲线,具体图示如下:
- 在经济学领域,区域 A 和区域 B 之间的曲线就是洛伦兹曲线,洛伦兹曲线越向 B 处凹陷(A 区域越大),则代表收入分配越不均衡;
- A 区域上方的 y=x 的直线称为绝对平等线,洛伦兹曲线越接近绝对平等性(A 区域越小),收入分配越均衡。
- 区域 A 的面积叫做 “不平等面积”,区域 A+B 的面积叫做 “完全不平等面积”。
- 注意 X 轴和 Y 轴都是按顺序的累计,推荐系统也是如此。大部分情况,推荐系统也具有马太效应,洛伦兹曲线明显向 B 区域凹陷。
我们也可以根据洛伦兹曲线的概念,来绘制推荐系统的 “洛伦兹曲线”,洛伦兹曲线也可以用于多种算法做比较,画多条曲线观察关键位置累计的推荐情况,选择更符合自己商业目的那个算法。推荐系统的 “洛伦兹曲线” 可按以下步骤绘制:
- 收集物品的推荐次数或点击次数等热度指标。
- 将物品按照热度指标从低到高进行排序。
- 计算累积推荐次数占总推荐次数的比例以及累积物品数量占总物品数量的比例。
- 绘制洛伦兹曲线,横坐标表示累积物品数量占比,纵坐标表示累积推荐次数占比。
- 根据洛伦兹曲线的形状和斜率,评估推荐系统的热度分配不均衡程度。
通过观察洛伦兹曲线的形状,可以了解推荐系统中热门物品和冷门物品的推荐次数分布情况。如果曲线偏离对角线越远,表示推荐系统的热度分配越不均衡,更加偏向于推荐热门物品。而曲线越接近对角线,表示热度分配越均衡,推荐结果更加平衡。通过洛伦兹曲线的应用,推荐系统可以更好地了解热度分布特征,进而进行相应的优化和调整,以提供更平衡和多样化的推荐结果。
1.2.4 多样性
多样性不但要在推荐服务吐出结果时需要做一定的保证,也要通过日志做监测。多样性可能会损失一些效果指标,但是从长远上来看,对推荐系统所在平台是有利的。多样的推荐结果也会让产品显得生机勃勃,提升品牌形象。多样性衡量方式通常要依赖维度体系选择,例如常见的是在类别维度上衡量推荐结果的多样性。方法是下面这样的。
多样性衡量实际上在衡量各个类别在推荐时的熵,一共有 n 个类别,分母是各个类别最均匀,都得到一样的推荐次数情况下对应的熵。
分子则是实际各个类别得到的推荐次数,pi 是类别 i 被推荐次数占总推荐次数的比例,计算它的熵。两者求比值是为了在类别数增加和减少时,可以互相比较。这种计算多样性是一个整体的评价,还可以具体评价每次推荐的多样性,每个用户的多样性,也就是 PV 多样性和 UV 多样性。
2 在线指标
线上评测的核心就是在线可控实验,有时候又称作是在线实验,主要方式就是在线 A/B 实验,也称 AB 测试、A/B test。
AB 测试有点类似于生物学实验,一般会设置 “控制组”(Control Bucket)和 “对照组”(Treatment Bucket)两个实验组,实验时两个实验组会保留唯一一个不同的变量,这个变量称为 “独立变量”(Independent Variable),是推荐系统中某个改进的部分,而其余保持不变。这样,我们就希望通过在线实验以及假设检验的工具,来认定这个 “独立变量” 是否会带来系统性能上的提高或是降低。这里面还有一个需要提前确定的,就是需要评测的指标,特别是用户指标,比如网站的点击率、搜索的数量等。这些指标我们称之为 “依赖变量”(Dependent Variable)。说白了,我们就是希望能够在 “独立变量” 和 “依赖变量” 之间通过假设检验建立联系。
2.1 场景转化率
该类指标关注的是将用户从一个场景转化到我们希望的场景去的效果。这个过程需要用户付出主动的行动,如常见的点击行为。这类指标关注推荐系统的漏斗效果,主要有:
-
pv 点击率(点击量/pv) :经典,但易受攻击; pv 点击率是比较经典的指标,计算用户每次进入页面的点击情况。pv 点击率能粗略衡量转化效果,但是它的问题是容易受攻击:少数用户贡献大量点击会掩盖在这个指标后。
-
uv 点击率(点击量/uv) :不受用户路径影响(用户重复浏览页面),完整单元点击效果;分母是整个产品的 uv,而不是有点击行为的 uv。uv 点击率相对 pv 点击率的优势在于,它不受用户行为路径影响(不受重复浏览某个产品的影响),能记录用户在一个完整 session 的点击效果。据了解,阿里不少团队的指标也由 pv 点击率改为 uv 点击率了。
-
曝光点击率(点击量/曝光次数) :上下翻 feed 流,每一屏转化效果(目前 feed 流,属主流)。曝光点击率适合支持上拉/下拉翻页的产品,比如 Feed 流。相比 pv 点击率,曝光点击率的分母随用户刷屏次数增加而变大,能更真实的记录每一屏的转化情况。
-
uv 转化率(点击 uv/进入 uv) :吸引用户的比例,消费宽度。入口型产品,如 APP 首页。与 uv 转化率相对的是页面的流失率。uv 转化率衡量对用户的转化情况,通过我们的产品设计能把多大比例的用户从一个场景转化到另一个我们希望的场景去。uv 转化率相对于前两个指标,更健壮,不易受到攻击。
uv 转化率尤其适合衡量入口型产品,比如视频 app 的首页,如果用 uv 点击率去衡量会不太科学,因为一般用户在首页点击某个视频后,会进入详情页深度消费,很少会返回首页继续消费。这个用户已经被转化,但不再贡献点击,所以 uv 点击率不合理,用 uv 转化率就更加合理。如果某页面的 uv 转化率较低,则表示大部分人对该页面都不感兴趣,遵循 “不行就分” 的简单原则,这样的页面去掉可能对产品更好。
-
人均点击次数(点击量/点击 uv) :每个用户消费的次数,消费深度。 人均点击次数与 uv 转化率相辅相成。uv 转化率表示页面的消费宽度(吸引的用户比例),而人均点击次数表示页面的消费深度(每个用户消费的次数)。
-
除了比例类的商业指标,还要关注绝对量的商业指标,常见的有:社交关系数量,用户停留时长,GMV(Gross Merchandise Volume,成交金额)。 关注绝对数量,除了因为它才是真正商业目标,还有一个原因,是要看推荐系统是否和别的系统之间存在零和博弈情况。假如推荐系统导流效果提升,搜索引擎导流下降,从整个平台来看,因为整个平台的商业目标并没有那么成绩喜人,也需要警惕。
3 总结
-
不要太依赖算法指标(偏技术的指标):上面我们介绍的准确度指标(评分准确度、分类准确度、排序准确度)都是侧重于对算法或模型的评价,它们无法直接体现产品的好坏,对于产品,还要多关心商业指标,以及一些系统的整体评价指标,判断是否能驱动业务发展、策略调整。
-
各项指标都有局限性:之所以出现这么多指标,就是因为没有一个完美的指标能够反应所有的情况,每个指标都有自身的局限性,这些局限性或侧重的角度不同,或为了凸显某方面指标带来一些不好的影响,总之,要认清指标都有局限性。
-
指标不宜过多:过多的指标不利于最终优化决策,每套推荐系统应需根据自身特点和业务需求选择核心的几个。如果真的要关注多个指标,可以试着把这些直观指标再次综合成一个更高阶的指标,从而减少关注的目标,集中优化精力,帮助决策。