最近在关注和参与了一些NLP的比赛,因为我做NLP的比赛的经验不多,所以过程中还是学习到不少知识,虽然目前名次不是特别好,但前排大佬们的分享还是能get到很多想法和知识的。
今天介绍的是苏神(苏剑林)的一篇文章,本文仅仅是笔者个人的解读和思考,如有错误,还望读者指正。
在很多类型的比赛,包括NLP的比赛中,往往会有多标签分类的问题,所谓多标签分类,就是一个样本会有多个类别属性的标签,例如:在心电的疾病诊断中,一个患者可能同时患有一种或多种类型的心脏疾病。不同于多分类任务,一般来说,我们可以把多标签任务当作多个二分类来处理,即使用sigmoid函数得到每个类别标签的预测概率,最后通过阈值来选取模型对每个标签的预测结果。 这是很自然也很方便的方式,也是最常见的方式,但是这样的做法常常会面临标签不均的问题。标签不均指的是有的样本,可能只有一种类型的标签,而有的样本可能有多种类型的标签,这种问题会造成模型对一部分标签学习的很好,对其他可能分布不均的标签学习的就很差,而且对于多个标签看成多个二分类问题,相当于模型要做多次二分类的判断,”无形“中让模型多做很多事情,特别是最后的结果还受阈值的选取有关。
让我们先来看看softmax+交叉熵
进行多分类的形式,Loss如公式(1)所示,通过计算公式可知,我们不需要人为地设置阈值,而预测概率最大的一个,就是模型的预测结果,且所有类别的概率和为1,模型是”一步到位“得到预测结果的。
−
log
e
s
t
∑
i
=
1
n
e
s
i
=
−
s
t
+
log
∑
i
=
1
n
e
s
i
(
1
)
-\log \frac{e^{s_{t}}}{\sum_{i=1}^{n} e^{s_{i}}}=-s_{t}+\log \sum_{i=1}^{n} e^{s_{i}}\qquad(1)
−log∑i=1nesiest=−st+logi=1∑nesi(1)
那么我们是不是也能将多分类任务的这种形式推广到多标签任务当作呢?试想下,假如我们有N个标签类别,而每个样本的标签数如果是固定的K个,我们其实就是从N个里面选Top K个,那么N选K个的组合就有
C
n
k
C_{n}^{k}
Cnk个,参考多分类softmax的形式,情况
t
t
t的得分(类比多分类中类别t的得分)就有如公式(2)所示。
−
log
e
s
t
1
+
s
t
2
+
⋯
+
s
t
k
∑
1
≤
i
1
<
i
2
<
⋯
<
i
k
≤
n
e
s
i
1
+
s
i
2
+
⋯
+
s
i
k
=
log
Z
k
−
(
s
t
1
+
s
t
2
+
⋯
+
s
t
k
)
(
2
)
\begin{aligned} &-\log \frac{e^{s_{t_{1}}+s_{t_{2}}+\cdots+s_{t_{k}}}}{\sum_{1 \leq i_{1}<i_{2}<\cdots<i_{k} \leq n} e^{s_{i_{1}}+s_{i_{2}}+\cdots+s_{i_{k}}}}\\ &=\log Z_{k}-\left(s_{t_{1}}+s_{t_{2}}+\cdots+s_{t_{k}}\right) \qquad \qquad\qquad (2) \end{aligned}
−log∑1≤i1<i2<⋯<ik≤nesi1+si2+⋯+sikest1+st2+⋯+stk=logZk−(st1+st2+⋯+stk)(2)
其中
Z
k
=
∑
1
≤
i
1
<
i
2
<
⋯
<
i
k
≤
n
e
s
i
1
+
s
i
2
+
⋯
+
s
i
k
Z_{k}=\sum_{1 \leq i_{1}<i_{2}<\cdots<i_{k} \leq n} e^{s_{i_{1}}+s_{i_{2}}+\cdots+s_{i_{k}}}
Zk=∑1≤i1<i2<⋯<ik≤nesi1+si2+⋯+sik 就是配分函数,是所有
C
n
k
C_{n}^{k}
Cnk种情况的分数和,它可以通过递推式采用牛顿恒等式来进行求解。
再回到非固定
K
K
K个的情况,我们可以将
K
K
K取为样本中最多的类别数,当某个样本的实际标签只有
k
k
k个,而
k
≪
K
k\ll K
k≪K时,可以设定一个0标签进行填充,0标签可以多次填充,直到对其K个类别。此时我们的loss函数可以写成公式(3)所示:
log
Z
ˉ
K
−
(
s
t
1
+
s
t
2
+
⋯
+
s
t
k
+
s
0
+
⋯
+
s
0
⏟
K
−
k
)
(
3
)
\log \bar{Z}_{K}-(s_{t_{1}}+s_{t_{2}}+\cdots+s_{t_{k}}+\underbrace{s_{0}+\cdots+s_{0}}_{K-k }) \qquad(3)
logZˉK−(st1+st2+⋯+stk+K−k
s0+⋯+s0)(3)
问题就还是变成了输出分数最大的K个类的情况,而由于我们设置了"不存在"的0类标签,且允许重复输出0类,等价的效果就是以
s
0
s_0
s0为阈值,只输出得分大于
s
0
s_0
s0 的类别。
现在让我们进一步回看softmax+交叉熵的loss,参考Circle Loss: A Unified Perspective of Pair Similarity Optimization与苏神的这篇:不可导函数的可导逼近 ,单个标签的loss有如下形式:
−
log
e
s
t
∑
i
=
1
n
e
s
i
=
−
log
1
∑
i
=
1
n
e
s
i
−
s
t
=
log
∑
i
=
1
n
e
s
i
−
s
t
=
log
(
1
+
∑
i
=
1
,
i
≠
t
n
e
s
i
−
s
t
)
(
4
)
-\log \frac{e^{s_{t}}}{\sum_{i=1}^{n} e^{s_{i}}}=-\log \frac{1}{\sum_{i=1}^{n} e^{s_{i}-s_{t}}}=\log \sum_{i=1}^{n} e^{s_{i}-s_{t}}=\log \left(1+\sum_{i=1, i \neq t}^{n} e^{s_{i}-s_{t}}\right) \qquad (4)
−log∑i=1nesiest=−log∑i=1nesi−st1=logi=1∑nesi−st=log⎝⎛1+i=1,i=t∑nesi−st⎠⎞(4)
最小化该loss本质上是求一个最大最小化,如公式(5)所示:
log
(
1
+
∑
i
=
1
,
i
≠
t
n
e
s
i
−
s
t
)
≈
max
(
0
s
1
−
s
t
⋮
s
t
−
1
−
s
t
s
t
+
1
−
s
t
⋮
s
n
−
s
t
)
(
5
)
\log \left(1+\sum_{i=1, i \neq t}^{n} e^{s_{i}-s_{t}}\right) \approx \max \left(\begin{array}{c} 0 \\ s_{1}-s_{t} \\ \vdots \\ s_{t-1}-s_{t} \\ s_{t+1}-s_{t} \\ \vdots \\ s_{n} -s_{t} \end{array}\right)\qquad \qquad(5)
log⎝⎛1+i=1,i=t∑nesi−st⎠⎞≈max⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛0s1−st⋮st−1−stst+1−st⋮sn−st⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞(5)
即目标类别
s
t
s_t
st与其他非目标类别
s
i
s_i
si差值的最大值要尽可能小于0,换句话说所有非目标类别的得分均要小于目标类别的得分。
此时,我们的loss也呼之欲出了! 多标签分类的目标,实质是选出k个类别标签作为模型的预测结果,那么我们把这k个类别标签当作"pos"
,也就是我们的目标类别
,而其余当作"neg"
也就是非目标类别
,本质上我们是希望目标类别的得分高于非目标类别的得分,那么类比公式(4)就有如下表示形式:
log
(
1
+
∑
i
∈
Ω
n
e
g
,
j
∈
Ω
p
o
s
e
s
i
−
s
j
)
=
log
(
1
+
∑
i
∈
Ω
n
e
g
e
s
i
∑
j
∈
Ω
p
o
s
e
−
s
j
)
(
6
)
\log \left(1+\sum_{i \in \Omega_{n e g}, j \in \Omega_{p o s}} e^{s_{i}-s_{j}}\right)=\log \left(1+\sum_{i \in \Omega_{n e g}} e^{s_{i}} \sum_{j \in \Omega_{p o s}} e^{-s_{j}}\right) \qquad (6)
log⎝⎛1+i∈Ωneg,j∈Ωpos∑esi−sj⎠⎞=log⎝⎛1+i∈Ωneg∑esij∈Ωpos∑e−sj⎠⎞(6)
如果加上缩放因子
Ω
\Omega
Ω和间隔
m
m
m,就变成如下形式:
log
(
1
+
∑
i
∈
Ω
n
e
g
,
j
∈
Ω
p
o
s
e
γ
(
s
i
−
s
j
+
m
)
)
=
log
(
1
+
∑
i
∈
Ω
n
e
g
e
γ
(
s
i
+
m
)
∑
j
∈
Ω
p
o
s
e
−
γ
s
j
)
(
8
)
\log \left(1+\sum_{i \in \Omega_{n e g}, j \in \Omega_{p o s}} e^{\gamma\left(s_{i}-s_{j}+m\right)}\right)=\log \left(1+\sum_{i \in \Omega_{n e g}} e^{\gamma\left(s_{i}+m\right)} \sum_{j \in \Omega_{p o s}} e^{-\gamma s_{j}}\right)\quad(8)
log⎝⎛1+i∈Ωneg,j∈Ωpos∑eγ(si−sj+m)⎠⎞=log⎝⎛1+i∈Ωneg∑eγ(si+m)j∈Ωpos∑e−γsj⎠⎞(8)
通过前面的分析我们知道,对于不固定K的多标签情况,我们引入了0标签 s 0 s_0 s0,希望目标类别的分数都大于0类别得分( s j s_j sj> s 0 s_0 s0),而非目标类别的得分都小于0类别( s i s_i si< s 0 s_0 s0),同时希望 s i s_i si< s j s_j sj,就有如下公式所示:
log
(
1
+
∑
i
∈
Ω
neg
,
j
∈
Ω
p
o
s
e
s
i
−
s
j
+
∑
i
∈
Ω
neg
e
s
i
−
s
0
+
∑
j
∈
Ω
pos
e
s
0
−
s
j
)
=
log
(
e
s
0
+
∑
i
∈
Ω
neg
e
s
i
)
+
log
(
e
−
s
0
+
∑
j
∈
Ω
pos
e
−
s
j
)
(
7
)
\begin{array}{l} \log \left(1+\sum_{i \in \Omega_{\text {neg }}, j \in \Omega_{p o s}} e^{s_{i}-s_{j}}+\sum_{i \in \Omega_{\text {neg }}} e^{s_{i}-s_{0}}+\sum_{j \in \Omega_{\text {pos }}} e^{s_{0}-s_{j}}\right) \\ =\log \left(e^{s_{0}}+\sum_{i \in \Omega_{\text {neg }}} e^{s_{i}}\right)+\log \left(e^{-s_{0}}+\sum_{j \in \Omega_{\text {pos }}} e^{-s_{j}}\right) \end{array} \quad(7)
log(1+∑i∈Ωneg ,j∈Ωposesi−sj+∑i∈Ωneg esi−s0+∑j∈Ωpos es0−sj)=log(es0+∑i∈Ωneg esi)+log(e−s0+∑j∈Ωpos e−sj)(7)
此时,若我们设置0类别的得分阈值为0,则最终的Loss简化为下式:
log
(
1
+
∑
i
∈
Ω
n
e
g
e
s
i
)
+
log
(
1
+
∑
j
∈
Ω
p
o
p
e
−
s
j
)
(
8
)
\log \left(1+\sum_{i \in \Omega_{n e g}} e^{s_{i}}\right)+\log \left(1+\sum_{j \in \Omega_{pop}} e^{-s_{j}}\right) \qquad (8)
log⎝⎛1+i∈Ωneg∑esi⎠⎞+log⎝⎛1+j∈Ωpop∑e−sj⎠⎞(8)
此时我们的问题就转化为了非目标与目标标签的比较,而不是原来的多个二分类比较的问题,一定程度上缓和了标签不均对模型预测造成的影响。
keras实现的Loss代码如下:
def multilabel_categorical_crossentropy(y_true, y_pred):
"""多标签分类的交叉熵
说明:y_true和y_pred的shape一致,y_true的元素非0即1,
1表示对应的类为目标类,0表示对应的类为非目标类。
"""
y_pred = (1 - 2 * y_true) * y_pred
y_pred_neg = y_pred - y_true * 1e12
y_pred_pos = y_pred - (1 - y_true) * 1e12
zeros = K.zeros_like(y_pred[..., :1])
y_pred_neg = K.concatenate([y_pred_neg, zeros], axis=-1)
y_pred_pos = K.concatenate([y_pred_pos, zeros], axis=-1)
neg_loss = K.logsumexp(y_pred_neg, axis=-1)
pos_loss = K.logsumexp(y_pred_pos, axis=-1)
return neg_loss + pos_loss
思考
- 总的来看,作者是把多个二分类问题,类比softmax+交叉熵的优化目标,推广到目标类别与非目标类别得分的比较,其中通过引入参考阈值的思想来解决非固定类别数的情况,这个想法挺有趣的,对于二分类等问题,我们是否可以让模型自己学习一个最优的阈值呢?
- 其次作者将问题转化后,模型本质上关注的是目标标签和非目标标签的比较,而标签之间的关系并没有关注,是否不够细致?。
- 值得注意的是,对于测试集,前k类别这个k我们是无法确定的,通过引入0标签,设置0标签的得分为0来设置阈值,相当于,我们如果对得分通过sigmod函数激活的话,那么模型预测的目标类别概率将大于0.5,非目标类别概率小于0.5,也就是说,只要非目标标签,预测概率小于0.5即可,而目标标签预测概率大于0.5即可,这个目标对于模型来说是不是过于soft?
- 现实场景中,我们可能无法标记所有可能的情况和类别,这就造成了真实的标签可能并不是我们拿到的标签情况,所以在一些比赛或者研究中,会采取标签平滑的方式,本质上是想尽可能地描述样本真实的标签分布情况,而作者考虑的非目标类别与目标类别是否也有这种情况?也就是说目标标签和非目标标签其实是可能存在联系的,而不是简单的一个阈值能进行划分的,这个方面是否可以仔细思考下,如何对多个标签进行更加细致的划分。
笔者实际测试下来,发现确实是能work的,但效果和sigmoid+交叉熵的效果差不太多,具体还与数据分布有关,所以感兴趣的小伙伴可以用在自己的数据上进行尝试对比,或许有不错的效果。
参考文献
[1] 知乎:将“softmax+交叉熵”推广到多标签分类问题
[2] Circle Loss: A Unified Perspective of Pair Similarity Optimization
[3] 寻求一个光滑的最大值函数