《sklearn机器学习——多标签排序指标》
在多标签的机器学习中,每个样本可以有很多与之相关联的真实标签。目标是提供高的评分和较好的真实值排名。
覆盖误差
coverage_error函数计算必须包含在最中预测的标签的平均数量,以预测所有真实的标签。如果想知道高分数标签的个数,需要可以预测不缺少任何真实标签的平均值。因此,该指标的最佳值是真实标签的平均值。
注意:这里实现的得分比在Tsoumakas et al., 2010文献中提供的计算方式大1.这个延伸是为了解决一些例子中不包含真实标签的退化现象。
在形式上,提供真实标签y∈{0,1}nsamples×nlabelsy \in \{0, 1\}^{n_{samples} \times n_{labels}}y∈{0,1}nsamples×nlabels的二分类指标矩阵,以及与每一个标签相联合的得分f^∈Rnsamples×nlabels\hat{f} \in R^{n_{samples} \times n_{labels}}f^∈Rnsamples×nlabels,覆盖误差定义如下:
coverage(y,f^)=1nsamples∑i=0nsamples−1maxj:yij=1rankij\begin{aligned} coverage(y, \hat{f}) = \frac{1}{n_{samples}} \sum_{i=0}^{n_{samples}-1} max_{j:y_{ij}=1} rank_{ij} \end{aligned}coverage(y,f^)=nsamples1i=0∑nsamples−1maxj:yij=1rankij
其中,rankij=∣k:f^ik≥f^ij∣rank_{ij} = |k: \hat{f}_{ik} \geq \hat{f}_{ij}|rankij=∣k:f^ik≥f^ij∣。给定等级定义,通过给出将被分配给所有绑定值的最大等级,yscoresy_scoresyscores中的关系被破坏。
这是使用该函数的小例子:
>>> import numpy as np
>>> from sklearn.metrics import coverage_error
>>> y_true = np.array([[1, 0, 0], [0, 0, 1]])
>>> y_score = np.array([[0.75, 0.5, 1], [1, 0.2, 0.1]])
>>> coverage_error(y_true, y_score)
2.5
标签排名平均精确度
label_ranking_average_precision_score
函数执行标签排名平均精确度(label ranking average precision LRAP
)。这个指标与average_precision_score
函数相联系,但是基于标签排名的概念而不是精确度和召回率。
主要参数
- y_true:真实标签的二进制矩阵,形状为(n_samples, n_labels),其中1表示相关标签,0表示不相关 1 。
- y_score:预测得分矩阵,形状与 y_true 相同,数值越高表示标签排名越靠前 。
- sample_weight (可选):样本权重数组,用于加权计算最终得分 。
- return(返回值)
返回标签排名平均精度(LRAP),范围在(0,1]之间,1表示完美排序。该指标通过计算每个样本的真实标签在预测排名中的平均精度,综合反映模型对多标签的排序能力。
标签排名平均精确度(LRAP)平均所有样本,并是回答如下问题的答案:对于每一个真实标签,多大的较高排名标签的比例是真实的标签?如果可以与每一个样本相关的排名,这个指标测量值会较高。得到的评分均会大于0,最佳值是1。如果每个样本有一个确定的相关标签,标签排名平均精确度等价于mean reciprocal rank
。
形式上地,给定一个真实标签的二分类指标矩阵y∈{0,1}nsamples×nlabelsy \in \{0, 1\}^{n_{samples} \times n_{labels}}y∈{0,1}nsamples×nlabels,且与每个标签相联系的评分f^∈Rnsamples×nlabels\hat{f} \in R^{n_{samples} \times n_{labels}}f^∈Rnsamples×nlabels,平均精确度的定义如下:
LRAP(y,f^)=1nsamples∑i=0nsamples−11∣∣yi∣∣0∑j:yij=1∣Lij∣rankij\begin{aligned} LRAP(y, \hat{f}) = \frac{1}{n_{samples}} \sum_{i=0}^{n_{samples}-1} \frac{1}{||y_i||_0} \sum_{j:y_{ij}=1} \frac{|L_{ij}|}{rank_{ij}} \end{aligned}LRAP(y,f^)=nsamples1i=0∑nsamples−1∣∣yi∣∣01j:yij=1∑rankij∣Lij∣
其中Lij={k:yik=1,f^ik≥f^ij}L_{ij} = \{k: y_{ik} = 1, \hat{f}_{ik} \geq \hat{f}_{ij}\}Lij={k:yik=1,f^ik≥f^ij},rankij=∣{k:f^ik≥f^ij}∣rank_{ij} = |\{k: \hat{f}_{ik} \geq \hat{f}_{ij}\}|rankij=∣{k:f^ik≥f^ij}∣,∣⋅∣|\cdot|∣⋅∣计算集合的基数(例如,集合中元素的数量),∣∣⋅∣∣0||\cdot||_0∣∣⋅∣∣0是l0l_0l0“范式”(它计算向量中非零元素个数)。
如下是使用该函数的小例子:
>>> import numpy as np
>>> from sklearn.metrics import label_ranking_average_precision_score
>>> y_true = np.array([[1, 0, 0], [0, 0, 1]])
>>> y_score = np.array([[0.75, 0.5, 1], [1, 0.2, 0.1]])
>>> label_ranking_average_precision_score(y_true, y_score)
0.416...
排序损失
label_ranking_loss函数用于计算排序损失,它计算样本中标签匹配排序不正确的个数均值。例如,真实标签的得分比错误标签的得分低,赋予排序的错误和正确标签匹配个数倒数作为权重。最低的排序损失值为0。
label_ranking_loss函数
参数
y_true
: 一个二进制矩阵,尺寸为(n_samples, n_labels)
,代表真实的标签情况。每个元素是0或1,表示样本中是否存在对应的标签。y_score
: 预测得分矩阵,尺寸同样为(n_samples, n_labels)
。这些分数反映了模型对样本属于对应标签的信心程度。
返回值
loss
: 一个浮点数,表示平均的标签排序损失。这个值越低,说明模型在区分正负标签方面表现越好。
函数内部计算数学形式:
给定一个真实标签y∈{0,1}nsamples×nlabelsy \in \{0, 1\}^{n_{samples} \times n_{labels}}y∈{0,1}nsamples×nlabels的二分类指标矩阵,以及与每一个标签相联系的评分f^∈Rnsamples×nlabels\hat{f} \in R^{n_{samples} \times n_{labels}}f^∈Rnsamples×nlabels,排序损失函数的定义如下:
ranking_loss(y,f^)=1nsamples∑i=0nsamples−11∣∣yi∣∣0(nlabels−∣∣yi∣∣0)∣{(k,l):f^ik≤f^il,yik=1,yil=0}∣\begin{aligned} ranking\_loss(y, \hat{f}) = \frac{1}{n_{samples}} \sum_{i=0}^{n_{samples}-1} \frac{1}{||y_i||_0 (n_{labels} - ||y_i||_0)} |\{(k, l): \hat{f}_{ik} \leq \hat{f}_{il}, y_{ik} = 1, y_{il} = 0\}| \end{aligned}ranking_loss(y,f^)=nsamples1i=0∑nsamples−1∣∣yi∣∣0(nlabels−∣∣yi∣∣0)1∣{(k,l):f^ik≤f^il,yik=1,yil=0}∣
其中,∣⋅∣|\cdot|∣⋅∣计算集合的基数(例如,集合中各元素的个数)以及∣∣⋅∣∣0||\cdot||_0∣∣⋅∣∣0是l0l_0l0"范式"(它计算向量中非零元素的个数)。
如下是使用该函数的小例子:
>>> import numpy as np
>>> from sklearn.metrics import label_ranking_loss
>>> y_true = np.array([[1, 0, 0], [0, 0, 1]])
>>> y_score = np.array([[0.75, 0.5, 1], [1, 0.2, 0.1]])
>>> label_ranking_loss(y_true, y_score)
0.75...
>>> # With the following prediction, we have perfect and minimal loss
>>> y_score = np.array([[1.0, 0.1, 0.2], [0.1, 0.2, 0.9]])
>>> label_ranking_loss(y_true, y_score)
0.0
Normalized Discounted Cumulative Gain(NDGG)
Discounted Cumulative Gain(DCG)和Normalized Discounted Cumulative Gain(NDCG)均是排序指数;它们对预测序列和真实得分进行比较,例如一个查询答案的相关性。
概述
nDCG是一种评价排序结果好坏的度量标准,尤其适用于搜索引擎或推荐系统等场景。它通过计算Discounted Cumulative Gain(DCG)并将其规范化到[0, 1]区间来实现这一点,其中1表示最佳可能的排序。
参数
y_true
: 一个数组或列表,包含每个项目的实际相关性得分。这些值直接反映了项目的真实重要性或用户对其的兴趣程度。y_score
: 一个数组或列表,长度与y_true
相同,包含模型预测的得分。这些分数反映模型认为各个项目对用户的潜在价值。k
(可选): 整数,表示仅考虑前k个结果进行评估。如果未指定,则默认使用所有提供的结果。
计算
- DCG: 通过累加从第一位开始的实际相关性得分,并根据其位置给予不同的权重来计算。位置越靠后,得分折扣越多。
- IDCG (Ideal DCG): 在相同的前k项下,假设结果是按最佳方式排序时所能达到的最大DCG值。
- nDCG: 将DCG除以IDCG得到的值。这将原始的DCG标准化到[0, 1]范围内,1表示最好的排序效果。
返回值
nDCG@k
: 一个浮点数,代表在考虑前k个结果的情况下的规范化折损累积增益。该值越接近1,表明排序效果越好。
“Discounted cumulative gain(DCG)是一个对排序质量的测量。在信息检索中,该指标经常被用来搜索引擎算法或者相关应用的效率。在搜索引擎结果集中,使用文档的分级相关性比例,DCG基于它在结果列表中的位置,测量文档有用性,或者增益。增益在结果表中是由上到下的累积,根据每一个结果的增益,不考虑较低的排名。”
DCG在预测序列中,给正确标签排序(例如查询答案的相关性),然后乘以对数衰减,最后汇总求和。在最先的K个结果后,求和会被截断,因此称它为DCG@K。KDCG,或者NDCG@K是DCG除以最佳预测对应的DCG,从而该值是介于0和1之间的。通常,NDCG优先于DCG。
与排序损失作比较,NDCG能够考虑相关性评分,而不是真实值排序。所以,所过真实值仅由一个序列构成,使用排序损失会更好;如果真实值是由实际有用性评分构成(例如,0表示不相关,1表示相关,2表示非常相关),可以选用NDCG。
NDGG内部计算的数学形式:
对于某样本,给定每一个标签y∈RMy \in R^My∈RM的连续真实值向量,其中MMM是返回值的个数,预测值是y^\hat{y}y^,它是排序函数的索引fff,DCG评分是:
∑r=1min(K,M)yf(x)log(1+r)\begin{aligned} \sum_{r=1}^{min(K, M)} \frac{y f(x)}{\log(1 + r)} \end{aligned}r=1∑min(K,M)log(1+r)yf(x)
并且,NDCG评分是DCG评分除以从yyy中得到的DCG评分。
以下是他的一个简单示例:
import math
import numpy as npdef dcg_at_k(relevance_scores, k):"""计算前 k 个结果的折损累积增益 (DCG@k)Args:relevance_scores: 相关性得分列表,按排序位置排列 (例如: [3, 2, 3, 0, 1])k: 计算到第 k 个位置Returns:DCG@k 值"""# 确保只计算前 k 个scores = relevance_scores[:k]# DCG 公式: sum( rel_i / log2(i+1) ),其中 i 从 1 开始# 注意: Python 索引从 0 开始,所以位置 i 对应索引 i-1dcg = 0.0for i, rel in enumerate(scores):# i+1 是实际位置 (1-indexed)# log2(i+1) 是折损因子,位置 1 的折损是 log2(2)=1,位置 2 是 log2(3)≈1.58,依此类推dcg += rel / math.log2(i + 2) # i+2 因为 i 从 0 开始,位置是 i+1return dcgdef ndcg_at_k(relevance_scores, k):"""计算前 k 个结果的归一化折损累积增益 (NDCG@k)Args:relevance_scores: 相关性得分列表,按排序位置排列k: 计算到第 k 个位置Returns:NDCG@k 值 (范围在 0 到 1 之间)"""# 计算当前排序的 DCGactual_dcg = dcg_at_k(relevance_scores, k)# 计算理想排序 (IDCG): 将相关性得分从高到低排序ideal_scores = sorted(relevance_scores, reverse=True)ideal_dcg = dcg_at_k(ideal_scores, k)# 避免除以零if ideal_dcg == 0:return 0.0# NDCG = DCG / IDCGreturn actual_dcg / ideal_dcg# --- 示例 ---
if __name__ == "__main__":# 假设我们有一个搜索查询,返回了 5 个文档# 我们对每个文档的相关性进行打分 (例如: 3=非常相关, 2=相关, 1=轻微相关, 0=不相关)# 情况 1: 排序效果很好 (高相关性结果排在前面)good_order = [3, 2, 3, 0, 1] # 实际排序的相关性得分# 情况 2: 排序效果很差 (高相关性结果排在后面)bad_order = [0, 1, 3, 2, 3] # 实际排序的相关性得分k = 5 # 我们关心前 5 个结果的整体排序质量# 计算好排序的 NDCGndcg_good = ndcg_at_k(good_order, k)print(f"好排序 {good_order} 的 NDCG@{k}: {ndcg_good:.4f}")# 计算差排序的 NDCGndcg_bad = ndcg_at_k(bad_order, k)print(f"差排序 {bad_order} 的 NDCG@{k}: {ndcg_bad:.4f}")# 让我们再看一个只看前 3 个结果的例子 (NDCG@3)k3 = 3ndcg_good_3 = ndcg_at_k(good_order, k3)ndcg_bad_3 = ndcg_at_k(bad_order, k3)print(f"\n只看前 {k3} 个结果:")print(f"好排序 {good_order[:k3]} 的 NDCG@{k3}: {ndcg_good_3:.4f}")print(f"差排序 {bad_order[:k3]} 的 NDCG@{k3}: {ndcg_bad_3:.4f}")# 验证: 理想排序的 NDCG 应该是 1.0ideal_order = sorted(good_order, reverse=True) # [3, 3, 2, 1, 0]ndcg_ideal = ndcg_at_k(ideal_order, k)print(f"\n理想排序 {ideal_order} 的 NDCG@{k}: {ndcg_ideal:.4f} (应该接近 1.0)")
输出为:
好排序 [3, 2, 3, 0, 1] 的 NDCG@5: 0.9073
差排序 [0, 1, 3, 2, 3] 的 NDCG@5: 0.6827只看前 3 个结果:
好排序 [3, 2, 3] 的 NDCG@3: 1.0000
差排序 [0, 1, 3] 的 NDCG@3: 0.5714理想排序 [3, 3, 2, 1, 0] 的 NDCG@5: 1.0000 (应该接近 1.0)