【多标签, 极限的多标签算法】评价指标梳理

news2024/11/24 10:03:17

具体研究多标签极限多标签 (XML) 的时候, 合理使用评价指标是关键.
最近在研究极限多标签算法的时候发现了它和传统多标签算法的评价指标是有异的, 而且我曾经积累的传统多标签评价指标也没有一个系统的体系 (很混乱). 于是写下本文用于自我总结.

查询目录<想看什么直接通过这来查>

  • 1.常规多标签的评价指标
  • 2.极限多标签的评价指标
    • 2.1 Precision@k
    • 2.2 NDCG@k
    • 2.3 PSP@k
    • 2.4 PSnDCG@k
  • 3 私货环节 (随时更新)
    • 3.1 Python
    • 3.2 Matlab
  • 4 后记

1.常规多标签的评价指标

常规的多标签学习是训练一个 N × L N \times L N×L的标签预测矩阵 Y ^ \hat{\mathbf{Y}} Y^, 然后基于 Y ^ \hat{\mathbf{Y}} Y^全体数据 (不忽略任何值)来讨论与同形的目标标签矩阵 Y \mathbf{Y} Y的关系.
N N N是标签向量的个数, L L L是单个标签向量的维度, 有任意 ∣ y i ∣ = L |\mathbf{y}_i|=L yi=L.
注意, 这里 y i j ∈ { 0 , 1 } L y_{ij}\in\{0,1\}^L yij{0,1}L, y ^ i j ∈ R L \hat{y}_{ij}\in\mathbb{R}^{L} y^ijRL. 前者是二元的, 后者是实型的, 其范围由不同算法的预测方式不同而不同, 往往是介于区间 [ 0 , 1 ) [0, 1) [0,1)的小数.
细节上, 评价指标主要分为 基于样本的评价指标基于标签的评价指标

  • 基于样本的评价指标 : 对测试集中所有样本预测结果与真实情况之间进行一定的评价对比.
  • 基于标签的评价指标 : 通过对每个单独标签的预测结果进行度量, 最后再对所有标签结果取平均.

往往前者的方法我们喜欢称之为微平均, 使用前者的喜欢加上前缀Micro-; 后者的方法称之为宏平均, 使用前者的喜欢加上前缀Macro-. 基本上下述所有的算法都可以从这两个角度去考虑.

1.1 逐项匹配的占比

逐项匹配的占比的评价指标喜欢先对 Y ^ \hat{\mathbf{Y}} Y^实行一次二值化预测后再来对比. 细节上, Y ^ \hat{\mathbf{Y}} Y^将大于阈值 θ \theta θ的实数预测为+1, 其余预测为-1. 如此后, y ^ i j \hat{y}_{ij} y^ij 也成为了二值化数值.
然后就有了下面1.1.1与1.1.2中的两个评价指标.
一般来说, 这种通过逐项匹配来计算占比的算法并没有明显的基于样本标签的特点, 或者说, 通过这两种方案算出来的结果没有不同.

1.1.1 Accuracy

对于准确度Accuracy来说, 判断 Y \mathbf{Y} Y与二值化后 Y ^ \hat{\mathbf{Y}} Y^的关系, 若有 y ^ i j ∈ Y ^ \hat{y}_{i j}\in \hat{\mathbf{Y}} y^ijY^, 同样的 i i i j j j, 也有 y i j ∈ Y y_{i j}\in\mathbf{Y} yijY, 这时若有 y i j = y ^ i j y_{i j} = \hat{y}_{ij} yij=y^ij, 则视为预测正确, 否则视为预测失败. 故Accuracy可以认为是正确预测的占比, 它自然越大是越好的:

Acc = ∑ i , j y ^ i j ⊙ y i j ∣ Y ∣ \textrm{Acc}=\sum_{i,j}\frac{\hat{y}_{ij}\odot y_{ij}}{|\mathbf{Y}|} Acc=i,jYy^ijyij这里 ⊙ \odot 表示"同或", y ^ i j \hat{y}_{ij} y^ij y i j y_{ij} yij不同即0, 否则返回1.

Accuracy有个老生常谈的问题, 当标签稍微稀疏点, 零元素一多, 预测失误会被过多的零元素掩盖.

1.1.2 Hamming Loss

Hamming Loss与Accuracy是类似的, Hamming Loss通过计算多标签分类器预测的标签结果 y ^ i j \hat{y}_{ij} y^ij (二值化后)与实际标签 y i j y_{ij} yij的距离差来进行度量. 本质上也是一个逐个权衡然后再除全体个数. 它的公式如下:

Hamming-Loss = ∑ i , j y ^ i j ⊕ y i j ∣ Y ∣ \textrm{Hamming-Loss}=\sum_{i,j}\frac{\hat{y}_{ij}\oplus y_{ij}}{|\mathbf{Y}|} Hamming-Loss=i,jYy^ijyij

这里 ⊕ \oplus 是异或, y ^ i j \hat{y}_{ij} y^ij y i j y_{ij} yij保持不同就是1, 其余都是0, 因此Hamming Loss越小越好. 我的理解是Hamming Loss是Accuracy的对偶问题, 自然它们也共享相同的缺点

1.2 混沌矩阵有关参数引导的评价指标 (非排序版)

其余还有些零零散散的基于混沌矩阵的参数实现的全局的评价指标

1.2.1 Precision与Recall

其中主要有查准率和查全率的方案. 查准率 (Precision)用来判断在所有我的预测为正的样本中, 我预测对了的占比:

P = ∑ i , j ∈ Y ^ + y ^ i j × y i j ∣ Y ^ + ∣ \textrm{P} = \sum_{i,j \in \hat{\mathbf{Y}}^{+}}\frac{\hat{y}_{ij}\times y_{ij}}{|\hat{\mathbf{Y}}^{+}|} P=i,jY^+Y^+y^ij×yij

这里的 Y ^ + \hat{\mathbf{Y}}^{+} Y^+就简单表示那些预测为正的标签组成的集合, ∣ Y ^ + ∣ |\hat{\mathbf{Y}}^{+}| Y^+则是它们的数量.
查全率 (Recall)用来判断在所有正样本中, 我们预测对了的占比:

R = ∑ i , j ∈ Y + y ^ i j × y i j ∣ Y + ∣ \textrm{R} = \sum_{i,j \in \mathbf{Y}^{+}}\frac{\hat{y}_{ij}\times y_{ij}}{|\mathbf{Y}^{+}|} R=i,jY+Y+y^ij×yij

1.2.2 非排序版 F 1 -Score F_1\text{-Score} F1-Score

基于这两个准则而建立起来的全局的的 F 1 F_1 F1- Measure \text{Measure} Measure (又称之为 F 1 F_1 F1- Score \text{Score} Score):

F 1 -Score = 2 P R P + R F_1\text{-Score} = \frac{2PR}{P+R} F1-Score=P+R2PR

最好的情况下 P = R = 1.0 P=R=1.0 P=R=1.0, 这个时候 F 1 F_1 F1- Score \text{Score} Score的值为1.
注意, 这种先计算全局的Precision和Recall后再直接算 F 1 F_1 F1- Score \text{Score} Score的叫做 Micro- F 1 \text{Micro-}F_1 Micro-F1, 即微平均 F 1 F_1 F1
而另外存在一个宏平均的 F 1 F_1 F1, 即 Macro- F 1 \text{Macro-}F_1 Macro-F1, 这种方法会先计算每个标签的Precision和Recall, 从而计算出每个标签 F 1 -Score F_1\text{-Score} F1-Score, 最后求每个标签的 F 1 -Score F_1\text{-Score} F1-Score的平均.
当然, 无论是 Macro- F 1 \text{Macro-}F_1 Macro-F1还是 Micro- F 1 \text{Micro-}F_1 Micro-F1, 他们都是一种静态的, 基于一个不加以改动的 Y ^ \hat{\mathbf{Y}} Y^ Y \mathbf{Y} Y的比较来获得.
可见:
微平均 F 1 F_1 F1: Micro- F 1 \text{Micro-}F_1 Micro-F1基于样本的评价指标
宏平均 F 1 F_1 F1: Macro- F 1 \text{Macro-}F_1 Macro-F1基于标签的评价指标


稍微扩展下, F β -Score F_\beta\text{-Score} Fβ-Score其实是 F 1 -Score F_1\text{-Score} F1-Score的一般形式, F 1 -Score F_1\text{-Score} F1-Score F β -Score F_\beta\text{-Score} Fβ-Score β = 1 \beta=1 β=1的特殊情况. 这个公式如下:

F β -Score = ( 1 + β 2 ) P R ( 1 + β 2 ) ( P + R ) F_\beta\text{-Score} = \frac{(1+\beta^2)PR}{(1+\beta^2)(P+R)} Fβ-Score=(1+β2)(P+R)(1+β2)PR

其中, 当我们认为Recall更重要时, 往往会将 β = 2 \beta=2 β=2, 另外一些情况, 我们认为Precision更重要, 我们会取 β ∈ ( 0 , 1 ) \beta\in(0,1) β(0,1), 一般情况我们会折中取1, 这就是 F 1 -Score F_1\text{-Score} F1-Score被我们官方使用的原因. 当然, 如果你的数据有显然的偏向性, 也可以另辟蹊径试试新的 F F F值.


1.2.3 非排序版AUC

此外还有基于ROC曲线诞生的另一个评价指标AUC (AUC是ROC的曲线面积).
AUC的计算方案没有一个完全统一的标准, 但是就其本质方法来说, 是基于最佳的排序绘制出ROC曲线, 然后计算这个曲线的面积, 但是这个是排序评价指标, 稍后会提到, 故这里不过多赘述.
此外AUC还有其他的一些计算准则, 例如目标空间中有确定的 M M M个正样本和 N N N个负样本, 这样我们就能找到 M × N M \times N M×N个正>负的标签对 ( y i , y j ) ( i ∈ Y + , j ∈ Y − ) (y_i, y_j)(i\in \mathcal{Y}^+, j\in \mathcal{Y}^- ) (yi,yj)(iY+,jY). 然后将目标空间中这些标签映射到预测空间, 如果发现预测空间也满足 y ^ i > y ^ j \hat{y}_i > \hat{y}_j y^i>y^j, 那么定义 I ( y i , y j ) = 1 I(y_i, y_j) = 1 I(yi,yj)=1, 其余定义见下式:

AUC = ∑ i , j I ( y i , y j ) M × N , I ( y i , y j ) = { 1 , y ^ i > y ^ j 0.5 , y ^ i = y ^ j 0 , y ^ i < y ^ j \textrm{AUC} = \frac{\sum_{i,j} I(y_i, y_j)}{M\times N}, I(y_{i}, y_{j})=\left\{\begin{array}{l} 1, \hat{y}_{i}>\hat{y}_{j} \\ 0.5, \hat{y}_{i}=\hat{y}_{j} \\ 0, \hat{y}_{i}<\hat{y}_{j} \end{array}\right. AUC=M×Ni,jI(yi,yj),I(yi,yj)=1,y^i>y^j0.5,y^i=y^j0,y^i<y^j

倘若我们的目标空间与预测空间都是全体的 Y \mathbf{Y} Y Y ^ \hat{\mathbf{Y}} Y^, 那么这就是:
微平均 AUC \text{AUC} AUC: Micro-AUC \text{Micro-AUC} Micro-AUC, 是基于样本的评价指标
倘若我们的目标空间与预测空间都是每个标签 y k \mathbf{y}_k yk y k ^ \hat{\mathbf{y}_k} yk^ ( k k k表示某个任意标签), 那么我们会计算出每个标签的 AUC k \text{AUC}_k AUCk, 最终进行求和算平均, 这就是:
宏平均 AUC \text{AUC} AUC: Macro-AUC \text{Macro-AUC} Macro-AUC, 是基于标签的评价指标
这个方法可以认为是我们后续讲述的Ranking Loss评价指标的一种对偶.

1.3 基于排序的一些评价指标

我将这里的基于排序的方法与混沌矩阵有关参数的方法区分来看.

1.3.1 One-Error

One-Error 衡量了测试集的样例中排名第一的标签不是相关标签的比例, 这个值我们希望越小越好.

One-Error = 1 m ∑ i = 1 m ( ( arg max ⁡ 0 ≤ j < L y ^ i ) ∉ Y i + ) \textrm{One-Error} = \frac{1}{m}\sum^{m}_{i=1}\left((\argmax_{0\le j <L}\mathbf{\hat{y}}_i)\notin \mathbf{Y}^+_i\right) One-Error=m1i=1m((0j<Largmaxy^i)/Yi+)

这里的 y ^ i \mathbf{\hat{y}}_i y^i表示第 i i i个标签, arg min ⁡ 0 ≤ j < L y ^ i \argmin_{0\le j <L}\mathbf{\hat{y}}_i 0j<Largminy^i取出了 i i i-th标签向量中预测实数值最高的那个值的下标 j j j (L是下标可取的上界), Y i + \mathcal{Y}^+_i Yi+ Y \mathbf{Y} Y中的第 i i i个标签中正标签的下标集合. 因此, 如果我们对于第 i i i个标签中最高预测值预测错了, 那么求和式就会+1, One-Error就会增加.
在这里插入图片描述

1.3.2 Coverage

Coverage 衡量了在预测的排序标签列表上需要从头走多少步, 才能覆盖真实标签中所有的正例:

One-Error = 1 m ∑ i = 1 m ( max ⁡ j ∈ Y i + rank ( y ^ i ) − 1 ) \textrm{One-Error} = \frac{1}{m}\sum^{m}_{i=1}\left(\max_{j \in \mathcal{Y}^+_i} \textrm{rank}(\mathbf{\hat{y}}_i) -1\right) One-Error=m1i=1m(jYi+maxrank(y^i)1)

这里 Y i + \mathcal{Y}^+_i Yi+ Y \mathbf{Y} Y中的第 i i i个标签中正标签的下标集合. max ⁡ j ∈ Y i + rank ( y ^ i ) \max_{j \in \mathcal{Y}^+_i} \textrm{rank}(\mathbf{\hat{y}}_i) maxjYi+rank(y^i) 选择了 y ^ i \mathbf{\hat{y}}_i y^i预测标签中排名尽可能靠后的一个在真实标签中映射为+1的标签的下标. 至于式中 - 1是依情况而定, 因为我们这里假定 Rank() \textrm{Rank()} Rank()返回排名最小是1, 而最好的情况下一步不走就能覆盖所有 (图中 y ^ 3 \hat{y}_3 y^3 y 3 y_3 y3)
在这里插入图片描述
图例解释( y ^ 2 \hat{y}_2 y^2 y 2 y_2 y2): y ^ 2 = [ 0.11 , 0.47 , 0.55 , 0.29 ] \hat{y}_2 = [0.11,0.47,0.55,0.29] y^2=[0.11,0.47,0.55,0.29], 排序后的 y ^ 2 = [ 0.55 , 0.47 , 0.29 , 0.11 ] \hat{y}_2 = [0.55,0.47,0.29,0.11] y^2=[0.55,0.47,0.29,0.11], 按照这种排序映射到 y 2 y_2 y2, 为 [ 1 , 0 , 1 , 0 ] [1,0,1,0] [1,0,1,0], 可以发现走了两步走到了最后一个1, 即 [ 1 → 0 → 1 , 0 ] [1 \rightarrow 0\rightarrow1,0] [101,0].
Coverage 是越小越好.

1.3.3 Ranking Loss

Ranking Loss 反映了所有样本的预测标签排序中, 不相关标签排在相关标签前面的概率.

Ranking-Loss = 1 m ∑ i = 1 m 1 ∣ Y i − ∣ ∣ Y i + ∣ ∣ { ( y i j , y i k ) ∣ y ^ i j ≥ y ^ i k , ( y i j , y i k ) ∈ Y i − × Y i + } ∣ \textrm{Ranking-Loss} = \frac{1}{m}\sum^{m}_{i=1}\frac{1}{|\mathcal{Y}^-_i||\mathcal{Y}^+_i|}| \{ (y_{ij},y_{ik})| \hat{y}_{ij}\ge \hat{y}_{ik}, (y_{ij},y_{ik})\in \mathcal{Y}^-_i\times \mathcal{Y}^+_i\}| Ranking-Loss=m1i=1mYiYi+1{(yij,yik)y^ijy^ik,(yij,yik)Yi×Yi+}

这里 Y i − \mathcal{Y}^-_i Yi是标签 y i \mathbf{y}_i yi中的负标签构成的集合, 显然, 其补集就是 Y i − \mathcal{Y}^-_i Yi. 下面是以第二个标签为例的图例:在这里插入图片描述
可见, Ranking Loss也是越小越好, 一旦有错误的预测排到非常靠前的话, 这个错误值超过了多少正确值, 那么就会等线性地惩罚它. 他的思想类似与 NDCG \text{NDCG} NDCG, 只不过后者是对数级别的.
这个算法可以认为是 AUC \text{AUC} AUC一种解法的对偶, 而且它虽然名称中有Rank, 但是它可以不通过排序实现.

1.3.4 NDCG

NDCG 这个评价指标来自于推荐系统, 他是DCG的归一化表示, DCG是评价值与排名的对数损失的比例
(NDCG有的地方也喜欢写作nDCG, 这个好像没有完全统一的标准)
(这里的对数是以2为底的)

DCG ( r , y ^ i ) = ∑ l = 1 k y ^ i l r log ⁡ ( 1 + l ) \text{DCG}(\mathbf{r}, \mathbf{\hat{y}_i}) = \sum_{l=1}^{k}\frac{\hat{y}^\mathbf{r}_{il}}{\log(1+l)} DCG(r,y^i)=l=1klog(1+l)y^ilr

这里的 r \mathbf{r} r是一种排序准则, 我们假设它是根据预测的标签 y ^ i \hat{\mathbf{y}}_{i} y^i从大到小进行了一次排序得到了 y ^ i r \hat{\mathbf{y}}^{\mathbf{r}}_{i} y^ir, 而排序后的顺序索引由 l l l表示, 即可认为 l = 1 l=1 l=1时的 y ^ i 1 r \hat{y}^\mathbf{r}_{i1} y^i1r就是这个标签中预测值最高的标签特征. k k k是这个标签的维度上限, 即标签长度.
我们可以假设一个最佳DCG (又名IDCG)场景, 即我们的标签 y ^ i \hat{\mathbf{y}}_{i} y^i完全与 y i \mathbf{y}_{i} yi一致. 若 y i \mathbf{y}_{i} yi是由 m m m + 1 +1 +1 n n n 0 0 0构成, 那么可以肯定的是 y i r \mathbf{y}^{\mathbf{r}}_{i} yir是前部为 m m m + 1 +1 +1构成, 尾部由 n n n 0 0 0构成的向量 [ + 1 , + 1 , + 1 , … , 0 , 0 , 0 ] [+1,+1,+1,\dots,0,0,0] [+1,+1,+1,,0,0,0]. 其公式可直接表示为:

IDCG ( r , y ^ i ) = ∑ l = 1 m 1 log ⁡ ( 1 + l ) ,   ( m = ∣ ∣ y i ∣ ∣ 0 ) \text{IDCG}(\mathbf{r}, \mathbf{\hat{y}_i}) = \sum_{l=1}^{m}\frac{1}{\log(1+l)},\text{ }(m=||\mathbf{y}_i||_0) IDCG(r,y^i)=l=1mlog(1+l)1, (m=yi0)

可得:

NDCG = DCG IDCG \textrm{NDCG} = \frac{\textrm{DCG}}{\textrm{IDCG}} NDCG=IDCGDCG

1.4 混沌矩阵有关参数引导的评价指标 (排序方法)

这里本质也都是混沌矩阵的一些参数引导的方法, 这里我们会见到刚刚介绍的AUC和 F 1 -Score F_1\text{-Score} F1-Score这些老朋友.
只不过这些方法吸取了基于排序的算法的一些经验, 提出了更为"动态化"的方案.

1.4.1 Average Precision (AP)

AP可以认为是P-R曲线围成的面积, 这个曲线是通过Precision作为纵轴, Recall作为横轴.在这里插入图片描述
从数值角度来看, AP可以认为是不同的Recall采样点下的Precision的值的一个平均的结果.
具体来说, 我们在每个Recall的采样点进行一个标记, 判断这些判断点的Precision值, 将他们求和取平均.
我们假设个情况, 现在有五个取样点, 每次取样的时候我们都断言为+1, 一共的正样例有3个, 初始不取样的P与R都为0. 现在我们假设下述取样: (下述内容需要读者提前预备关于Precision与Recall的有关储备知识)

真实情况PrecisionRecall
00 0 2 \frac{0}{2} 20
1 1 1 \frac{1}{1} 11 1 3 \frac{1}{3} 31
1 2 2 \frac{2}{2} 22 2 3 \frac{2}{3} 32
0 2 3 \frac{2}{3} 32 2 3 \frac{2}{3} 32
1 3 4 \frac{3}{4} 43 3 3 \frac{3}{3} 33

作图:
在这里插入图片描述

其结果是上述四个有效取样点的Precision求平均: AP = 1 5 ( 0 + 1 + 1 + 2 3 + 3 4 ) \text{AP} = \frac{1}{5}(0+1+1+\frac{2}{3}+\frac{3}{4}) AP=51(0+1+1+32+43)
我们希望PR曲线是越往外"凸"效果越好, 即实际体现出来的就是Precision维持为1的情况尽可能多持续一段时间. 要实现这样的效果就需要将我们的预测标签 Y ^ \mathbf{\hat{Y}} Y^进行排序 (可以是整体压缩为一维的排序 - 基于样本的评价指标; 也可以是每个标签向量 y ^ i \mathbf{\hat{y}}_i y^i排序后单独算AP, 最后取AP的均值: mAP - 基于标签的评价指标) , 排序后, 向量前端的标签预测为 + 1 +1 +1后有更大概率预测对, 从这里开始逐个采样, 能保证在前期更多地采到正确点.

1.4.2 基于排序的AUC

这里提出AUC是因为AUC也有一种基于排序实现的计算方案.
若说AP是PR曲线的面积, AUC就是ROC曲线的面积, ROC是FPR (假正率, 特异度)与TPR (其实就是Precision)构成.
类似与PR曲线, 我们还是希望整个过程中TPR (Precision)尽可能保持接近1的姿态, 因此ROC曲线越向左上角"凸"那么效果是最好的.
这种情况下, 他的面积 (AUC)也会接近1.0.
在这里插入图片描述
具体的实现排序的可行算法 (我偷个懒)可以参考这位博主的文章, 里面的例子还不错: https://blog.csdn.net/pearl8899/article/details/126129148

1.4.3 Peak- F 1 \text{Peak-}F_1 Peak-F1

有的地方可能会将其称之为 Best- F 1 \text{Best-}F_1 Best-F1, 我在我的MASP算法介绍的博客中详细介绍了这个评价指标的来由和应用(请查看这篇文章的4.3.3节 )
他的思想是将预测标签结果 Y ^ \mathbf{\hat{Y}} Y^依照从大到小顺序重排, 这个重排的基本单位可以是矩阵全体 (基于样本的评价指标)也可以是单个标签 (基于标签的评价指标)计算后取的平均.
重排后我们按照大到小依序采样, 每个采样点都预测为正标签, 其背后一一对应的真实标签作为参考从而像计算AP那样逐次更新Recall和Precision, 并且按照这个Recall和Precision来计算 F 1 -Score F_1\text{-Score} F1-Score.
下面举个例子, 这里是一个五维的标签

真实标签预测标签
00.4
10.5
10.2
00.3
10.8

现在将其按照预测标签来进行联合的排序, 然后依次采样, 分别预测Precision和Recall, 计算 F 1 -Score F_1\text{-Score} F1-Score

真实标签 (跟随排序后)预测标签 (排序)PrecisionRecall F 1 -Score F_1\text{-Score} F1-Score
10.8 1 1 \frac{1}{1} 11 1 3 \frac{1}{3} 310.496
10.5 2 2 \frac{2}{2} 22 2 3 \frac{2}{3} 320.795
00.4 2 3 \frac{2}{3} 32 2 3 \frac{2}{3} 320.666
00.3 2 4 \frac{2}{4} 42 2 3 \frac{2}{3} 320.569
10.2 3 5 \frac{3}{5} 53 3 3 \frac{3}{3} 330.750

这时可以看到有段 F 1 -Score F_1\text{-Score} F1-Score处于最高值, 我们的 Peak- F 1 \text{Peak-}F_1 Peak-F1就取这个值.
Peak-F 1 \text{Peak-F}_1 Peak-F1反映出一种阈值效应: 在我们按照预测标签的预测值进行排序后, 在中间某点找到了 Peak- F 1 \text{Peak-}F_1 Peak-F1, 这时标签的预测值为 p p p. 于是设置预测阈值为 θ = p \theta = p θ=p, 即预测标签中所有预测实值大于 p p p的都预测为 + 1 +1 +1, 其余为 0 0 0. 这样的话可以保证全局 F 1 -Score F_1\text{-Score} F1-Score能保持在最大的状态, 节约后续预测成本 (后续不再预测为 + 1 +1 +1).
可见上述例子, 不难得出 θ = 0.5 \theta=0.5 θ=0.5, 标签 [ 0.4 , 0.5 , 0.2 , 0.3 , 0.8 ] [0.4,0.5,0.2,0.3,0.8] [0.4,0.5,0.2,0.3,0.8]预测为 [ 0 , 1 , 0 , 0 , 1 ] [0,1,0,0,1] [0,1,0,0,1], 这样我们哪怕不排序而直接计算 F 1 -Score F_1\text{-Score} F1-Score都可以稳定在最高值.
这也是我最近投稿的论文采用的评价指标, 它用来评价还算是不错的. 但是也需要认清一个现实, 往往来说 F 1 -Score F_1\text{-Score} F1-Score的数值是非常低的, 所以现在非常多的算法在力图做高 F 1 -Score F_1\text{-Score} F1-Score, 这也是目前机器学习中多分类多标签等算法在努力的方向.

2.极限多标签的评价指标

起初我一直以为极限多标签共享一般多标签的评价指标, 但是就几篇论文来看似乎不是这样.
若极限多标签也采用这样的评价方案效率会很低, 而且数值本身也会很差.
下面是我在Bhatia等一行人总结的极限多标签研究的开源网站 (http://manikvarma.org/downloads/XC/XMLRepository.html)中学习得到的.
同时, 参考了部分极限的多标签的算法的源码 (FastXML)
极限多标签的特征数目和标签维度巨大且稀疏的, 相关知识可见我的这篇文章第一节
一些预备知识:

l ∈ rank ⁡ k ( y ^ ) l \in \operatorname{rank}_{k}(\hat{\mathbf{y}}) lrankk(y^)

y ^ \hat{\mathbf{y}} y^是一个预测得到的标签, 满足 y ^ ∈ R L \hat{\mathbf{y}}\in \mathbb{R}^{L} y^RL.
rank ⁡ ( y ^ ) \operatorname{rank}(\hat{\mathbf{y}}) rank(y^)是将预测标签 y ^ \mathbf{\hat{y}} y^向量的标签值更改为一个新值, 这个新值是此向量内部按照从大到小排序后每个标签值的新位置的下标.
例如有向量 y ^ = [ 0.5 , 0.8 , 0.2 , 0.3 ] \hat{\mathbf{y}}=[0.5, 0.8, 0.2, 0.3] y^=[0.5,0.8,0.2,0.3], 有 rank ⁡ ( y ^ ) = [ 1 , 0 , 3 , 2 ] \operatorname{rank}(\hat{\mathbf{y}})=[1,0,3,2] rank(y^)=[1,0,3,2], 而 rank ⁡ k ( y ^ ) \operatorname{rank}_k(\hat{\mathbf{y}}) rankk(y^)就是拎出 y ^ \hat{\mathbf{y}} y^中前 k k k大的下标, 例如 rank ⁡ 3 ( y ^ ) = { 0 , 1 , 3 } \operatorname{rank}_3(\hat{\mathbf{y}}) = \{0,1,3\} rank3(y^)={0,1,3}, rank ⁡ 2 ( y ^ ) = { 0 , 1 } \operatorname{rank}_2(\hat{\mathbf{y}}) = \{0,1\} rank2(y^)={0,1}, rank ⁡ 2 ( y ^ ) = { 1 } \operatorname{rank}_2(\hat{\mathbf{y}}) = \{1\} rank2(y^)={1}.
如果你想不带脑子理解的话, 你可以认为, rank ⁡ k ( y ^ ) \operatorname{rank}_k(\hat{\mathbf{y}}) rankk(y^)返回了 y ^ \hat{\mathbf{y}} y^向量中前 k k k个最大的标签值的下标.

2.1 Precision@k

这个算法既不像Accuracy, 也不像Precision. 公式如下:

P @ k : = 1 k ∑ l ∈ rank ⁡ k ( y ^ ) y l \mathrm{P} @ k:=\frac{1}{k} \sum_{l \in \operatorname{rank}_{k}(\hat{\mathbf{y}})} \mathbf{y}_{l} P@k:=k1lrankk(y^)yl

其中 k < ∣ ∣ y ∣ ∣ 0 ≪ ∣ y ∣ k<||\mathbf{y}||_0\ll|\mathbf{y}| k<y0y.
极限多标签算法预测得到了一个预测矩阵 Y ^ \mathbf{\hat{Y}} Y^, 这关公式关注其中某个标签向量为 y ^ \mathbf{\hat{y}} y^ (显然, 这个评价指标是基于标签的评价指标)
得到了 y ^ \hat{\mathbf{y}} y^向量中前 k k k个最大的标签值的下标集 l l l, 然后通过这些下标来映射到真实标签向量 y \mathbf{y} y中, 从而得到 y l \mathbf{y}_{l} yl. 预测正确, 则求和 + 1 +1 +1, 否则求和 + 0 +0 +0, 最后, 取平均.
总结来看, Precision@ k \text{Precision@}k Precision@k对于标签向量最有把握的前 k k k个标签值的预测正确率. 当某个标签的 Precision@ 5 \text{Precision@}5 Precision@5 = 1.0 说明这个标签向量预测中标签预测值最高的那五个标签都预测对了, 确实在目标标签中它们为 + 1 +1 +1; 但是如果 Precision@ 5 \text{Precision@}5 Precision@5 = 0.8, 那么说明那五个标签至少有一个预测错了.
一般的极限多标签算法喜欢将 k k k取为1, 3, 5, 往往来说 k k k越大, 我们失误的几率越大, Precision@ k \text{Precision@}k Precision@k也会越低; 而 k = 1 k=1 k=1时, 评价指标退化为One-Error的对偶评价指标; 当 k = ∣ y ∣ k=|\mathbf{y}| k=y, 这个评价指标又变为了一般的Accuracy评价指标.

2.2 NDCG@k

这个算法是基于常规的基于排序的NDCG算法改进得到. 极限多标签算法中有例如FastXML这种专门通过优化这个评价指标而实现的算法.

 DCG@  k : = ∑ l ∈ rank ⁡ k ( y ^ ) y l log ⁡ ( l + 1 ) \text { DCG@ } k:=\sum_{l \in \operatorname{rank}_{k}(\hat{\mathbf{y}})} \frac{\mathbf{y}_{l}}{\log (l+1)}  DCG@ k:=lrankk(y^)log(l+1)yl

N D C G @ k : = D C G @ k ∑ l = 1 min ⁡ ( k , ∥ y ∥ 0 ) 1 log ⁡ ( l + 1 ) \mathrm{NDCG@} k:=\frac{\mathrm{DCG} @ k}{\sum_{l=1}^{\min \left(k,\|\mathbf{y}\|_{0}\right)} \frac{1}{\log (l+1)}} NDCG@k:=l=1min(k,y0)log(l+1)1DCG@k

可见, 相比NDCG, 这里的算法也是多了一个 k k k, 它们的判断思路是一致.
并且选择了其中的前 k k k的下标集, 将其命名为 l l l.
DCG@ k \text {DCG@} k DCG@k Precision@ k \text{Precision@}k Precision@k基本上是一致的, 只不过  DCG@  k \text { DCG@ } k  DCG@ k在加上每次的结果时都除以一个对数损失, 因此, 随着排名的靠后, 它为 DCG@ k \text {DCG@} k DCG@k的贡献会逐步减小, 同时又因为我们加以的是对数损失, 因此在 l l l不是特别大时, 这种损失往往又不会导致特别严重的下降.
另外要注意, 这个公式并不是死的, 倘若你的基础编程语言的下标是从0开始的, 那么这里的对数损失应该改为 log ⁡ ( l + 2 ) \log(l+2) log(l+2), 避免除0错误.
NDCG@ k \text {NDCG@} k NDCG@k是归一化的 DCG@ k \text {DCG@} k DCG@k, 即对 DCG@ k \text {DCG@} k DCG@k除以一个 IDCG@ k \text {IDCG@} k IDCG@k.
通过1.4.3可得, IDCG \text {IDCG} IDCG的计算主要取决于标签 y \mathbf{y} y中有多少非零元, 而引入 k k k后, 因为存在 k < ∣ ∣ y ∣ ∣ 0 k<||\mathbf{y}||_0 k<y0, 因此要遵守木桶原则, 选择最小的那个作为求和上限.

  • 了解其原理后可以试着思考: 为什么 NDCG \text {NDCG} NDCG可以采用 @k \text{@k} @k来限制呢?
    因为 NDCG \text {NDCG} NDCG非常依赖于排名靠前的预测, 往后的正确预测固然能带来数值的正反馈, 但是因为对数损失的存在, 它们的贡献始终是少的. 例如 l = 1 , 10 , 20 , 30 , 40 l=1,10,20,30,40 l=1,10,20,30,40的变化: 1 log ⁡ 2 ( 1 + 1 ) = 1 \frac{1}{\log_2(1+1)}= 1 log2(1+1)1=1, 1 log ⁡ 2 ( 10 + 1 ) ≈ 0.29 \frac{1}{\log_2(10+1)}\approx 0.29 log2(10+1)10.29, 1 log ⁡ 2 ( 20 + 1 ) ≈ 0.23 \frac{1}{\log_2(20+1)}\approx 0.23 log2(20+1)10.23, 1 log ⁡ 2 ( 30 + 1 ) ≈ 0.20 \frac{1}{\log_2(30+1)}\approx 0.20 log2(30+1)10.20, 1 log ⁡ 2 ( 40 + 1 ) ≈ 0.19 \frac{1}{\log_2(40+1)}\approx 0.19 log2(40+1)10.19 ——可见数值波动逐渐不显著.
    可以认为, 因为在极限多标签中, 对标签矩阵进行整体评价的开销非常大, 所以极限多标签的评价指标更侧重于"管中窥豹", 即从局部来最大化推测全局.
    而在许多评价指标中, 基于排序的评价指标能很好承担这个职责 (因为排序后的关键信息集中在了前端, 且因为标签稀疏性, 前端的内容也并不会很多); 而在众多基于排序的评价指标中, NDCG@k \text {NDCG@k} NDCG@k又是其中最好的选择之一 (因为它对排名的"中", "末"端的依赖性低, 能最大化缩小复杂度, 同时也保证了通过引入 k k k之后的 NDCG@k \text {NDCG@k} NDCG@k与原 NDCG \text {NDCG} NDCG的差异是可以接受的).

2.3 PSP@k

正在学习…待补

2.4 PSnDCG@k

正在学习…待补

3 私货环节 (随时更新)

私货环节主要是用来存些代码用的.
这些代码有些是我看论文收集的, 有些是我自己写的, 有些是仅仅调库使用过的.
排名不分向后.
请添加图片描述

3.1 Python

1.3.4 节的 NDCG (自己写的)
注意输入的两个向量都必须是numpy类型得到的 1 × L 1\times L 1×L 的一维向量, 它是针对标签进行的逐个分析.
如果你想基于样本进行分类, 那么就试着将整个预测和目标矩阵压缩为一维的再测试

def computeNDCG(predictY, targetY):
    '''
    Compute NDCG
    :param predictY:  a vector in one of the predicted labels (numpy)
    :param targetY: a vector in one of the target labels (numpy)
    :return: the value of NDCG
    '''

    # 按照概率序列排序原1/0串
    temp = np.argsort(-predictY)
    allLabelSort = targetY[temp]

    # 获得最佳序列: 1111...10000...0
    sortedTargetVector = np.sort(targetY)[::-1]

    # compute DCG(使用预测的顺序, rel是真实顺序, 实际是111110111101110000001000100
    DCG = 0
    for i in range(temp.size):
        rel = allLabelSort[i]
        denominator = np.log2(i + 2)
        DCG += (rel / denominator)

    # compute iDCG(使用最佳顺序: 11111111110000000000)
    iDCG = 0
    for i in range(temp.size):
        rel = sortedTargetVector[i]
        denominator = np.log2(i + 2)
        iDCG += (rel / denominator)

    return DCG / iDCG

1.5.3 节的 Peak- F 1 \text{Peak-}F_1 Peak-F1 (自己写的, 但参考了师兄的代码)
注意输入的两个向量都必须是numpy类型得到的 1 × L 1\times L 1×L 的一维向量, 它是针对标签进行的逐个分析.
如果你想基于样本进行分类, 那么就试着将整个预测和目标矩阵压缩为一维的再测试

def computepeakF1Score(predictY, targetY):
    '''
    Compute the Peak F1-score

    :param predictY:  a vector in one of the predicted labels (numpy)
    :param targetY: a vector in one of the target labels (numpy)
    :return: the value of Peak F1-score
    '''

    temp = np.argsort(-predictY)
    allLabelSort = targetY[temp]

    tempYF1 = np.zeros(temp.size)

    allTP = np.sum(targetY == 1)

    for i in range(temp.size):

        TP = np.sum(allLabelSort[0:i + 1] == 1)
        P = TP / (i + 1)
        R = TP / allTP
        if (P + R) == 0:
            tempYF1[i] = 0
        else:
            tempYF1[i] = 2.0 * P * R / (P + R)
    return np.max(tempYF1)

1.2.3和1.4.2 节的AUC (快乐地调库)
注意输入的两个向量都必须是numpy类型的
会使用的包

from sklearn import metrics

代码
两个向量都必须是numpy类型得到的 1 × L 1\times L 1×L 的一维向量, 它是针对标签进行的逐个分析.
如果你想基于样本进行分类, 那么就试着将整个预测和目标矩阵压缩为一维的再测试

metrics.roc_auc_score(targetY, predictY)

3.2 Matlab

1.3.1 节的One-Error (来自公开相关代码)
创建一个One_error.m文件, 然后复制下面的代码, 然后就可以调用了
这个代码中的Outputs是 Y ^ ⊤ \mathbf{\hat{Y}^{\top}} Y^, test_target是 Y ⊤ \mathbf{Y^{\top}} Y, 他们都是形如 L × N L \times N L×N的矩阵, 其中每列都是一个 L × 1 L\times 1 L×1的标签向量.
显然, 这里是一个基于标签的评价指标

function OneError=One_error(Outputs,test_target)
%Computing the one error
%Outputs: the predicted outputs of the classifier, the output of the ith instance for the jth class is stored in Outputs(j,i)
%test_target: the actual labels of the test instances, if the ith instance belong to the jth class, test_target(j,i)=1, otherwise test_target(j,i)=-1
  
    [num_class,num_instance]=size(Outputs);
    temp_Outputs=[];
    temp_test_target=[];
    for i=1:num_instance
        temp=test_target(:,i);
        if((sum(temp)~=num_class)&(sum(temp)~=-num_class))
            temp_Outputs=[temp_Outputs,Outputs(:,i)];
            temp_test_target=[temp_test_target,temp];
        end
    end
    Outputs=temp_Outputs;
    test_target=temp_test_target;     
    [num_class,num_instance]=size(Outputs);
    
    Label=cell(num_instance,1);
    not_Label=cell(num_instance,1);
    Label_size=zeros(1,num_instance);
    for i=1:num_instance
        temp=test_target(:,i);
        Label_size(1,i)=sum(temp==ones(num_class,1));
        for j=1:num_class
            if(temp(j)==1)
                Label{i,1}=[Label{i,1},j];
            else
                not_Label{i,1}=[not_Label{i,1},j];
            end
        end
    end
    
    oneerr=0;
    for i=1:num_instance
        indicator=0;
        temp=Outputs(:,i);
        [maximum,index]=max(temp);
        for j=1:num_class
            if(temp(j)==maximum)                
                if(ismember(j,Label{i,1}))
                    indicator=1;
                    break;
                end
            end
        end
        if(indicator==0)
            oneerr=oneerr+1;
        end
    end
    OneError=oneerr/num_instance;

1.3.2 节的 Coverage (来自公开相关代码)
创建一个coverage.m文件, 然后复制下面的代码, 然后就可以调用了
这个代码中的Outputs是 Y ^ ⊤ \mathbf{\hat{Y}^{\top}} Y^, test_target是 Y ⊤ \mathbf{Y^{\top}} Y, 他们都是形如 L × N L \times N L×N的矩阵, 其中每列都是一个 L × 1 L\times 1 L×1的标签向量.
显然, 这里是一个基于标签的评价指标版本

function Coverage=coverage(Outputs,test_target)
%Computing the coverage
%Outputs: the predicted outputs of the classifier, the output of the ith instance for the jth class is stored in Outputs(j,i)
%test_target: the actual labels of the test instances, if the ith instance belong to the jth class, test_target(j,i)=1, otherwise test_target(j,i)=-1

       [num_class,num_instance]=size(Outputs);
    
       Label=cell(num_instance,1);
       not_Label=cell(num_instance,1);
       Label_size=zeros(1,num_instance);
       for i=1:num_instance
           temp=test_target(:,i);
           Label_size(1,i)=sum(temp==ones(num_class,1));
           for j=1:num_class
               if(temp(j)==1)
                   Label{i,1}=[Label{i,1},j];
               else
                   not_Label{i,1}=[not_Label{i,1},j];
               end
           end
       end

       cover=0;
       for i=1:num_instance
           temp=Outputs(:,i);
           [tempvalue,index]=sort(temp);
           temp_min=num_class+1;
           for m=1:Label_size(i)
               [tempvalue,loc]=ismember(Label{i,1}(m),index);
               if(loc<temp_min)
                   temp_min=loc;
               end
           end
           cover=cover+(num_class-temp_min+1);
       end
       Coverage=(cover/num_instance)-1;

1.3.3 节的 Ranking Loss (来自公开相关代码)
创建一个Ranking_loss.m文件, 然后复制下面的代码, 然后就可以调用了
这个代码中的Outputs是 Y ^ ⊤ \mathbf{\hat{Y}^{\top}} Y^, test_target是 Y ⊤ \mathbf{Y^{\top}} Y, 他们都是形如 L × N L \times N L×N的矩阵, 其中每列都是一个 L × 1 L\times 1 L×1的标签向量.
显然, 这里是一个基于标签的评价指标版本

function RankingLoss=Ranking_loss(Outputs,test_target)
%Computing the hamming loss
%Outputs: the predicted outputs of the classifier, the output of the ith instance for the jth class is stored in Outputs(j,i)
%test_target: the actual labels of the test instances, if the ith instance belong to the jth class, test_target(j,i)=1, otherwise test_target(j,i)=-1

    [num_class,num_instance]=size(Outputs);
    temp_Outputs=[];
    temp_test_target=[];
    for i=1:num_instance
        temp=test_target(:,i);
        if((sum(temp)~=num_class)&(sum(temp)~=-num_class))
            temp_Outputs=[temp_Outputs,Outputs(:,i)];
            temp_test_target=[temp_test_target,temp];
        end
    end
    Outputs=temp_Outputs;
    test_target=temp_test_target;     
    [num_class,num_instance]=size(Outputs);
    
    Label=cell(num_instance,1);
    not_Label=cell(num_instance,1);
    Label_size=zeros(1,num_instance);
    for i=1:num_instance
        temp=test_target(:,i);
        Label_size(1,i)=sum(temp==ones(num_class,1));
        for j=1:num_class
            if(temp(j)==1)
                Label{i,1}=[Label{i,1},j];
            else
                not_Label{i,1}=[not_Label{i,1},j];
            end
        end
    end
    
    rankloss=0;
    for i=1:num_instance
        temp=0;
        for m=1:Label_size(i)
            for n=1:(num_class-Label_size(i))
                if(Outputs(Label{i,1}(m),i)<=Outputs(not_Label{i,1}(n),i))
                    temp=temp+1;
                end
            end
        end
        rl_binary(i)=temp/(m*n);
        rankloss=rankloss+temp/(m*n);
    end
    RankingLoss=rankloss/num_instance;

1.1.2 节的 Hamming Loss (来自公开相关代码)
创建一个Hamming_loss.m文件, 然后复制下面的代码, 然后就可以调用了
这个代码中的Outputs是 Y ^ ⊤ \mathbf{\hat{Y}^{\top}} Y^, test_target是 Y ⊤ \mathbf{Y^{\top}} Y, 他们都是形如 L × N L \times N L×N的矩阵, 其中每列都是一个 L × 1 L\times 1 L×1的标签向量.

function HammingLoss=Hamming_loss(Pre_Labels,test_target)
%Computing the hamming loss
%Pre_Labels: the predicted labels of the classifier, if the ith instance belong to the jth class, Pre_Labels(j,i)=1, otherwise Pre_Labels(j,i)=-1
%test_target: the actual labels of the test instances, if the ith instance belong to the jth class, test_target(j,i)=1, otherwise test_target(j,i)=-1

    [num_class,num_instance]=size(Pre_Labels);
    miss_pairs=sum(sum(Pre_Labels~=test_target));
    HammingLoss=miss_pairs/(num_class*num_instance);

1.4.1 节的 Average Precision (AP) (来自公开相关代码)
创建一个Average_precision.m文件, 然后复制下面的代码, 然后就可以调用了
这个代码中的Outputs是 Y ^ ⊤ \mathbf{\hat{Y}^{\top}} Y^, test_target是 Y ⊤ \mathbf{Y^{\top}} Y, 他们都是形如 L × N L \times N L×N的矩阵, 其中每列都是一个 L × 1 L\times 1 L×1的标签向量.
显然, 这里是一个基于标签的评价指标版本

function Average_Precision=Average_precision(Outputs,test_target)
%Computing the average precision
%Outputs: the predicted outputs of the classifier, the output of the ith instance for the jth class is stored in Outputs(j,i)
%test_target: the actual labels of the test instances, if the ith instance belong to the jth class, test_target(j,i)=1, otherwise test_target(j,i)=-1

    [num_class,num_instance]=size(Outputs);
    temp_Outputs=[];
    temp_test_target=[];
    for i=1:num_instance
        temp=test_target(:,i);
        if((sum(temp)~=num_class)&(sum(temp)~=-num_class))
            temp_Outputs=[temp_Outputs,Outputs(:,i)];
            temp_test_target=[temp_test_target,temp];
        end
    end
    Outputs=temp_Outputs;
    test_target=temp_test_target;     
    [num_class,num_instance]=size(Outputs);
    
    Label=cell(num_instance,1);
    not_Label=cell(num_instance,1);
    Label_size=zeros(1,num_instance);
    for i=1:num_instance
        temp=test_target(:,i);
        Label_size(1,i)=sum(temp==ones(num_class,1));
        for j=1:num_class
            if(temp(j)==1)
                Label{i,1}=[Label{i,1},j];
            else
                not_Label{i,1}=[not_Label{i,1},j];
            end
        end
    end
    
    aveprec=0;
    for i=1:num_instance
        temp=Outputs(:,i);
        [tempvalue,index]=sort(temp);
        indicator=zeros(1,num_class);
        for m=1:Label_size(i)
            [tempvalue,loc]=ismember(Label{i,1}(m),index);
            indicator(1,loc)=1;
        end
        summary=0;
        for m=1:Label_size(i)
            [tempvalue,loc]=ismember(Label{i,1}(m),index);
            summary=summary+sum(indicator(loc:num_class))/(num_class-loc+1);
        end
        ap_binary(i)=summary/Label_size(i);
        aveprec=aveprec+summary/Label_size(i);
    end
    Average_Precision=aveprec/num_instance;

1.3.4 节的 NDCG (自己写的)
创建一个computeNDCG.m文件, 然后复制下面的代码, 然后就可以调用了
这个代码中的label_prob是 y ^ \mathbf{\hat{y}} y^, test_target是 y \mathbf{y} y, 他们都是形如 1 × L 1 \times L 1×L的向量.
这里是一个基于标签的评价指标版本, 当然如果你把整个标签矩阵都压缩为一维, 这个也可以视作一个基于样本的评价指标
(注意, matlab的下标是从1开始的, 因此这里是可以写作 log ⁡ 2 ( i + 1 ) \log_2(i+1) log2(i+1), py是不行的)

function NDCG = computeNDCG(label_prob, label_target)
[sortArray,temp] = sort(-label_prob);
allLabelSort = label_target(temp);
sortedTargetVector = sort(label_target);
sortedTargetVector = fliplr(sortedTargetVector);

dcg = 0;
for i = 1: numel(temp)
    rel = allLabelSort(i);
    denominator = log2(i + 1);
    dcg = dcg + (rel / denominator);
end

idcg = 0;    
for i = 1: numel(temp) 
    rel = sortedTargetVector(i);
    denominator = log2(i + 1);
    idcg = idcg + (rel / denominator);
end 

NDCG = max(dcg / idcg);
end

1.4.3 节的 Peak- F 1 \text{Peak-}F_1 Peak-F1 (自己写的)
创建一个computePeakF1.m文件, 然后复制下面的代码, 然后就可以调用了
这个代码中的label_prob是 y ^ \mathbf{\hat{y}} y^, test_target是 y \mathbf{y} y, 他们都是形如 1 × L 1 \times L 1×L的向量.
这里是一个基于标签的评价指标版本, 当然如果你把整个标签矩阵都压缩为一维, 这个也可以视作一个基于样本的评价指标

function peakF1 = computePeakF1(label_prob, label_target)
[sortArray,temp] = sort(-label_prob);
allLabelSort = label_target(temp);
tempF1 = zeros(1, numel(temp));
allTP = sum(label_target == 1);

for i = 1: numel(temp)
    sliceArray = allLabelSort(1:i); 
    TP = sum(sliceArray == 1);
    P = TP / (i);
    R = TP / allTP;
    if(P + R == 0)
        tempF1(i) = 0;
    else
        tempF1(i) = (2.0 * P * R) / (P + R);
    end
end
peakF1 = max(tempF1);
end

1.4.2 节的基于排序的AUC (参考至网络)
创建一个computeAUC.m文件, 然后复制下面的代码, 然后就可以调用了
这个代码中的output是 Y ^ ⊤ \mathbf{\hat{Y}^{\top}} Y^, test_targets是 Y ⊤ \mathbf{Y^{\top}} Y, 他们都是形如 L × N L \times N L×N的矩阵, 其中每列都是一个 L × 1 L\times 1 L×1的标签向量.
显然, 这里是一个基于标签的评价指标版本
这个代码中的output是 y ^ \mathbf{\hat{y}} y^, test_targets是 y \mathbf{y} y, 他们都是形如 1 × L 1 \times L 1×L的向量.
这里是一个基于标签的评价指标版本, 当然如果你把整个标签矩阵都压缩为一维, 这个也可以视作一个基于样本的评价指标

function auc = computeAUC(output, test_targets)
[A,I]=sort(output);
M=0;N=0;
for i=1:length(output)
    if(test_targets(i)==1)
        M=M+1;
    else
        N=N+1;
    end
end
sigma=0;
for i=M+N:-1:1
    if(test_targets(I(i))==1)
        sigma=sigma+i;
    end
end
auc = (sigma-(M+1)*M/2)/(M*N);
end

2.1 节的Precision@k (参考至相关算法源代码)
创建一个precision_k.m文件, 然后复制下面的代码, 但是请注意! 这个无法直接使用, 因为这里的sort_sparse_mat方法是源码中通过C++提前编译好的方法. 若在无环境的情况下是无法运行的.
这个代码中的score_mat是 Y ^ ⊤ \mathbf{\hat{Y}^{\top}} Y^, true_mat是 Y ⊤ \mathbf{Y^{\top}} Y, 他们都是形如 L × N L \times N L×N的矩阵, 其中每列都是一个 L × 1 L\times 1 L×1的标签向量. 请注意, 这里使用的所有矩阵都是matlab自带的稀疏矩阵方法.
这里是一个基于标签的评价指标版本.
相关函数说明:

  • 这里的sort_sparse_mat方法相当于公式中的 rank ⁡ ( ) \operatorname{rank}() rank(), 它将标签向量中的数值换成了其值在排序的排名值, 若读者有需要可以自行实现.
  • spones( )方法是将矩阵中所有非零元素都更改为 1 1 1
function P = precision_k(score_mat,true_mat,K)
	P = helper(score_mat,true_mat,K);
end

function P = helper(score_mat,true_mat,K)
	num_inst = size(score_mat,2);
	num_lbl = size(score_mat,1);

	P = zeros(K,1);
	rank_mat = sort_sparse_mat(score_mat);  % 具体的值更改为这个值在这一列的"排序值"

	for k=1:K
		mat = rank_mat;
		mat(rank_mat>k) = 0;    			% 把低于阈值排名的排名设置为0
		mat = spones(mat);      			% 将幸存的数值都改为1
		mat = mat.*true_mat;    			% 将预测错的变为0
		num = sum(mat,1);       			% 按照列求和

		P(k) = mean(num/k);
	end
end

2.2 节的NDCG@k (参考至相关算法源代码)
创建一个nDCG_k.m文件, 然后复制下面的代码, 但是请注意! 这个无法直接使用, 因为这里的sort_sparse_mat方法是源码中通过C++提前编译好的方法. 若在无环境的情况下是无法运行的.
这个代码中的score_mat是 Y ^ ⊤ \mathbf{\hat{Y}^{\top}} Y^, true_mat是 Y ⊤ \mathbf{Y^{\top}} Y, 他们都是形如 L × N L \times N L×N的矩阵, 其中每列都是一个 L × 1 L\times 1 L×1的标签向量. 请注意, 这里使用的所有矩阵都是matlab自带的稀疏矩阵方法.
这里是一个基于标签的评价指标版本.
相关函数说明:

  • 这里的sort_sparse_mat方法相当于公式中的 rank ⁡ ( ) \operatorname{rank}() rank(), 它将标签向量中的数值换成了其值在排序的排名值, 若读者有需要可以自行实现.
  • spones( )方法是将矩阵中所有非零元素都更改为 1 1 1
  • sparse( )是构建稀疏矩阵的方法, X是非零点的纵坐标数组, Y是非零点的横坐标数组, V是非零值, 它们是一一对应的. 后续num_lbl与num_inst是稀疏矩阵的最大行数和列数. ( L × N = num_lbl × num_inst L \times N = \text{num\_lbl} \times \text{num\_inst} L×N=num_lbl×num_inst)
  • cum_wts()用于累加求和, cum_wts([1,1,1]) -> [1,2,3], cum_wts([2,7,8]) -> [2,9,17], 代码中他被用来算IDCG
function N = nDCG_k(score_mat,true_mat,K)
	N = helper(score_mat,true_mat,K);
end

function P = helper(score_mat,true_mat,K)
	num_inst = size(score_mat,2);
	num_lbl = size(score_mat,1);
    

	P = zeros(K,1);
	wts = 1./log2((1:num_lbl)+1)';              
	cum_wts = cumsum(wts);                      % 计算每个IDCG

	rank_mat = sort_sparse_mat(score_mat);      % 然后返回每个排序值的位置
	[X,Y,V] = find(rank_mat);                   % 找出rank_mat中非零元素所在的行和列, 并且分别存储在X和Y中, 并将具体非零的值放在V里面
	V = 1./log2(V+1);                           % sort_sparse_mat操作后, 当前列的某个V值体现是原值此向量的中的排名, 符合公式中l的定义
	coeff_mat = sparse(X,Y,V,num_lbl,num_inst); % 将原预测稀疏矩阵转变为单DCG的稀疏矩阵

	for k=1:K
		mat = coeff_mat;
		mat(rank_mat>k) = 0;                    % 把排序没靠在前k的数据改为0, 其余靠在k前的数据的具体的值保持原来的值(1./log2(V+1)的结果)
		mat = mat.*true_mat;                    % 把那些预测错的归零, 这里是haty->y的映射
		num = sum(mat,1);                       % 得到每个列的DCG@k值 (求和过程中若预测错了, + 0)

        % 下面是处理IDCG
		count = sum(true_mat,1);                % 求原Y的每列的||y||_0           
		count = min(count,k);                   % min(k, ||y||_0)
		count(count==0) = 1;                    % 为了避免除零, 对于空标签向量, 设置基础值l=1, 保守认为有一个标签
		den = cum_wts(count)';                  % 得到每一列的IDCG
		
		P(k) = mean(num./den);                  % 每一列求NDCG
	end
    
end

4 后记

这个文章本质是个记录用, 当然也包含我的一些理解, 有问题欢迎指正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/43309.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

语音识别翻译怎么做?这些方法值得收藏

随着网络的不断发展&#xff0c;我们可以通过网络与世界各地的网友进行聊天。小伙伴们平时会和外国人交流吗&#xff1f;如果是文字聊天&#xff0c;我们看不懂的时候&#xff0c;还可以直接复制文字进行翻译。那如果外国网友发了段语音&#xff0c;结果我们大部分内容听不懂的…

电力行业人员定位管理解决方案之智能巡检

智能巡检引入大数据分析理念&#xff0c;通过数字化技术实现对生产现场各关键要素的全面感知和实时互联&#xff0c;形成项目现场“数据一个库、监管一张网、管理一条线“。 在信息技术高速发展的今天&#xff0c;传统人工巡视、手工纸介质记录的工作方式已经无法满足电力设备巡…

第7章 博客文章的前端渲染显示

说实话本人通过Vue页面实现前端对后端数据的渲染显示也是初学咋练&#xff0c;但后端实现本人却是老鸟&#xff0c;对于后端开发者来说如果&#xff0c;渲染显示的软件是浏览器&#xff0c;除非团队中有Vue方面的大拿&#xff0c;不管是PC浏览器还是移动PC浏览器&#xff0c;Ra…

元宇宙初体验

14天学习训练营导师课程&#xff1a; 张子良《 元宇宙体系结构、关键技术和实践探索》 前言 最近这段时间加入勤学会的学习中&#xff0c;我加入的是元宇宙相关的学习组&#xff0c;为什么我选择元宇宙&#xff0c;不仅因为元宇宙是应用场景和生活方式的未来&#xff0c;而且元…

启动 idea 弹出“Failed to load JVM DLL\bin\server\jvm.dll”错误的解决方法

打开idea报failed to load JVM DLL 原因1&#xff1a; 查看是否缺少Microsoft Visual C 2010 Redistributable Package x64&#xff0c;没有则安装。 地址&#xff1a; 32 bit: http://www.microsoft.com/download/en/details.aspx?id5555 64 bit: http://www.microsoft.com…

面试必问 创建10个a点击弹出下标

<script> // for (let i 1; i <11; i) { // var adocument.createElement("a"); // a.href"#"; // a.innerHTML"<br />a标签"i // document.body…

JDBC获取数据库连接

Driver接口实现类 Driver接口介绍 java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的&#xff0c;不同数据库厂商提供不同的实现。 在程序中不需要直接去访问实现了 Driver 接口的类&#xff0c;而是由驱动程序管理器类(java.sql.…

Android学习---zygote(上)

Zygote意思是受精卵&#xff0c;它在Java世界中起到了很重要的作用&#xff0c;Android是基于Linux内核的&#xff0c;SDK是基于Java世界的&#xff0c;native语言是基于C和C&#xff0c;起初一定是先存在native世界&#xff0c;那么Java世界是如何创建的&#xff1f;这就与zyg…

『LeetCode|每日一题』---->打家劫舍||

目录 1.每日一句 2.作者简介 『LeetCode|每日一题』打家劫舍|| 1.每日一题 2.解题思路 2.1 思路分析 2.2 核心代码 2.3 完整代码 2.4 运行结果 1.每日一句 任何事情把期待值降到最低&#xff0c;所有遇见的都是礼物 2.作者简介 &#x1f3e1;个人主页&#xff1a;XiaoXia…

github数据怎么Python爬取

爬虫流程 在上周写完用scrapy爬去知乎用户信息的爬虫之后&#xff0c;github上star个数一下就在公司小组内部排的上名次了&#xff0c;我还信誓旦旦的跟上级吹牛皮说如果再写一个&#xff0c;都不好意思和你再提star了&#xff0c;怕你们伤心。上级不屑的说&#xff0c;那就写…

网站页面SEO优化方案

如果可以实现记得点赞分享&#xff0c;谢谢老铁&#xff5e; 背景说明 针对网页面而提供相应的产品页面 SEO 优化部署方案&#xff0c;使其产品页面符合 SEO 规范&#xff0c;且能尽快获得产品词的较好排名。 产品相关页面URL命名 URL 结构对于网站页面的 seo 来说非常重要…

VirtualLab教程特辑

目录前言一、一些界面上的说明1、关于软边relative edge width2、catalog里器件参数改动3、系统光线分析仪的光线数4、编程手册从哪看以及哪里可以编程5、Multiple Light Source6、多波长与多模式分开显示7、harmonic fields set-manipulations8、detector results显示功率小9、…

阿尔茨海默病中的人类连接组及它与生物标记物和遗传学的关系

摘要 阿尔茨海默病(AD)损害了大脑的结构和功能网络&#xff0c;导致认知障碍。最近的连接组学研究结果已经将AD中结构和功能网络组织的变化与淀粉样蛋白-β和tau蛋白的积累和扩散模式联系起来&#xff0c;为该疾病的神经生物学机制提供了见解。此外&#xff0c;对基因相关的连接…

如何检索专利技术?

问题一&#xff1a;申请实用新型专利需要提交哪些文件呢&#xff1f; 主要有以下四点&#xff1a; 1、请求书&#xff1a;主要包括实用新型专利的名称、申请人的名称和地址等内容&#xff1b; 2、权利要求书&#xff1a;这里需要交代好每一项要保护的内容&#xff1b; 3、说…

CommonsCollections6利用链分析

目录 (一&#xff09;利用链 &#xff08;二&#xff09;代码分析 0x01 TiedMapEntry 0x02 HashMap &#xff08;三&#xff09;POC&#xff1a; (一&#xff09;利用链 先来看 ysoserial 中的利用链&#xff1a; /*Gadget chain:java.io.ObjectInputStream.readObject()…

[附源码]SSM计算机毕业设计线上图书销售管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

三大O(nlogn)算法分析

堆排序 demo 从第一个父节点开始&#xff0c;每一个都调换自己和所有子孙节点的上下层次调换&#xff0c;形成最大堆。然后进行堆分支调整 class Solution {public int[] sortArray(int[] nums) {maxHeap(nums);sort(nums);return nums;}public static void maxHeap(int[] n…

blender hardOps插件

hardOps将一些常用的功能整合在一起&#xff0c;方便调用&#xff0c;例如&#xff0c;平滑&#xff0c;倒角&#xff0c;标记锐边&#xff0c;添加修改器 打开hardOps 方法1&#xff1a;物体模式在舞台左侧选择hardOps图标 推荐用这个 方法2&#xff1a;这个插件的打开方式非…

C++【类型转换】

文章目录一、C语言的类型转换二、C的强制类型转换1.static_cast静态转换2.reinterpret_cast重新诠释3.const_cast小总结4.dynamic_cast动态转换一、C语言的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者…

【PAT】数据结构树和图月考复习1

选择题 2-1 我们用一个有向图来表示航空公司所有航班的航线。下列哪种算法最适合解决找给定两城市间最经济的飞行路线问题&#xff1f; A.深度优先搜索 B.Kruskal算法 C.拓扑排序算法 D.Dijkstra算法 解析&#xff1a; 本题为单源最短路径问题&#xff0c;应选用dijsktra算…