背景
对于多分类的任务,最后通常会连接一个全连接层作为分类器,输出每个类别预测出的confidence scores。这些scores会被softmax归一化为一个概率分布,表示每一个类别的预测概率。假设共有K个类别,对类i,网络输出的confidence score为
z
i
z_i
zi,那么softmax得到的输出为
q
i
=
s
o
f
t
m
a
x
(
z
i
)
q_i = softmax(z_i)
qi=softmax(zi),其中softmax的计算定义为:
q
i
=
e
x
p
(
z
i
)
∑
j
=
1
K
e
x
p
(
z
j
)
q_i = \frac {exp(z_i)} {\sum_{j=1}^{K} exp(z_j)}
qi=∑j=1Kexp(zj)exp(zi)
显然,有 q i > 0 q_i > 0 qi>0和 ∑ i = 1 K q i = 1 \sum_{i=1}^K q_i = 1 ∑i=1Kqi=1,这是一个有效的概率分布。
假设图片的真实标签为
y
y
y,我们通常会认为真实的概率分布应该为
p
i
=
1
  
i
f
  
i
=
y
p_i = 1\;if\;i=y
pi=1ifi=y否则
p
i
=
0
p_i = 0
pi=0。在训练时,就会最小化预测概率分布与真实概率分布的交叉熵:
l
(
p
,
q
)
=
−
∑
i
=
1
K
p
i
 
l
o
g
 
q
i
l(p,q) = - \sum_{i=1}^K p_i\,log\,q_i
l(p,q)=−i=1∑Kpilogqi
为了最小化交叉熵,最优的预测概率分布应为 z y ∗ = i n f z_y^*=inf zy∗=inf,且 z i ∗ → 0    ( i ≠ y ) z_i^*\to0\;(i \neq y) zi∗→0(i̸=y),也就是说,神经网络的输出会被鼓励尽量去增加正确标签与错误标签的输出值之差,这样就容易出现过拟合问题。
Label smoothing
Label smoothing最早在 I n c e p t i o n    v 2 Inception\;v2 Inceptionv2中被提出,作为一种正则化手段,可以提高网络的top-1和top-5各0.2%
Label;smoothing将认为的真实概率分布改变为
p
i
=
{
1
−
ϵ
if
i
=
y
ϵ
/
(
K
−
1
)
otherwise
p_i = \begin{cases} 1-\epsilon & \text{if $i$ = $y$} \\ \epsilon/(K-1) & \text{otherwise} \end{cases}
pi={1−ϵϵ/(K−1)if i = yotherwise
这里的
ϵ
\epsilon
ϵ代表一个非常小的常数。
现在,最优的预测概率分布变成了
z
i
∗
=
{
l
o
g
(
(
K
−
1
)
(
1
−
ϵ
)
/
ϵ
+
α
)
if
i
=
y
α
otherwise
z_i^* = \begin{cases} log((K-1)(1-\epsilon)/\epsilon+\alpha) & \text{if $i$ = $y$} \\ \alpha & \text{otherwise} \end{cases}
zi∗={log((K−1)(1−ϵ)/ϵ+α)αif i = yotherwise
其中
α
\alpha
α为一个任意实数,这样,神经网络正确标签与错误标签的输出值之差将会被鼓励输出在一个固定值之内,帮助神经网络更好地泛化。
如果把真实分布概率改为一个性能更优的CNN的输出概率,那么又有点类似知识蒸馏了,当然知识蒸馏不是把student的输出概率趋于teacher,而是求两个网络输出的logits的KL散度,具体的就不再多说了。
前向传播
在这里我们以NVCaffe中的softmax_loss_layer.cpp文件为例,介绍Label smoothing的前向传播与反向传播,当然Caffe的实现也是类似的。
在Caffe与NVCaffe中,softmax_loss_layer包含了求softmax与求loss两部分。
首先定义两个成员变量inner_num_与outer_num_
outer_num_ = bottom[0]->count(0, softmax_axis_);
inner_num_ = bottom[0]->count(softmax_axis_ + 1);
假设
b
l
o
b
blob
blob的维度为
(
N
,
C
,
H
,
W
)
(N,C,H,W)
(N,C,H,W),softmax_axis_ =1
那么outer_num_求的就是
N
N
N,inner_num_求得就是特征图的大小
H
∗
W
H*W
H∗W
outer_num_用于在softmax_axis_之外遍历,inner_num_用于在softmax_axis_之内遍历,这样既可以用于图像分类求loss,也可以用于图像分割在特征图上求loss
在Forward_cpu中,首先得到softmax计算出的预测概率分布
softmax_layer_->Forward(softmax_bottom_vec_, softmax_top_vec_);
const Ftype* prob_data = prob_->template cpu_data<Ftype>();
再定义一个常量,表示当前softmax_axis_上的通道数,也即分类任务的类别个数
int num_class = bottom[0]->shape(softmax_axis_) - 1; // defined in softmax_loss_layer.cpp, equals prob_->count() / outer_num_ / inner_num_
const int channels = dim / spatial_dim; // defined in softmax_loss_layer.cu, spatial_dim is inner_num_
根据softmax公式与Label smoothing定义的真实概率分布,有
l
(
p
,
q
)
=
−
∑
i
=
1
K
p
i
 
l
o
g
 
q
i
=
−
ϵ
K
−
1
∑
i
=
1
,
i
≠
y
K
l
o
g
 
q
i
−
(
1
−
ϵ
)
l
o
g
 
q
y
\begin{aligned} l(p,q) &=-\sum_{i=1}^K p_i \, log \, q_i \\ &=-\frac{\epsilon}{K-1}\sum_{i=1,i\neq y}^K log\, q_i - (1-\epsilon)log \, q_y \end{aligned}
l(p,q)=−i=1∑Kpilogqi=−K−1ϵi=1,i̸=y∑Klogqi−(1−ϵ)logqy
接下来,只需要遍历softmax_axis_中的每一个通道,判断该通道代表的值是否为正确的标签,选择不同的权重去乘以log的结果
for (int i = 0; i < outer_num_; ++i) {
for (int j = 0; j < inner_num_; j++) {
const int label_value = static_cast<int>(label[i * inner_num_ + j]);
if (has_ignore_label_ && label_value == ignore_label_) {
continue;
}
DCHECK_GE(label_value, 0);
DCHECK_LT(label_value, prob_->shape(softmax_axis_));
if (label_smooth_ > 0.F) {
for (int c = 0; c <= num_class; c++) {
Ftype coeff = (c == label_value) ? (1.F - label_smooth_) : (label_smooth_ / float(num_class));
loss -= coeff * log(std::max(prob_data[i * dim + c * inner_num_ + j],
min_dtype<Ftype>()));
}
} else {
// min_dtype<Ftype>() get the minimum value of Ftype,
// BVLC Caffe use Dtype(FLT_MIN) instead, where Dtype(FLT_MIN) = 1.17549435E-38F
// ref : https://blog.csdn.net/jkfdqjjy/article/details/52268565?locationNum=14
loss -= log(std::max(prob_data[i * dim + label_value * inner_num_ + j],
min_dtype<Ftype>()));
}
++count;
}
}
反向传播
首先,我们要继续推导前向传播的损失函数,把softmax的公式带入(2)式,得到
l
(
p
,
q
)
=
−
ϵ
K
−
1
∑
i
=
1
,
i
≠
y
K
l
o
g
 
q
i
−
(
1
−
ϵ
)
l
o
g
 
q
y
=
−
ϵ
K
−
1
∑
i
=
1
,
i
≠
y
K
l
o
g
 
e
z
i
∑
j
e
z
j
−
(
1
−
ϵ
)
l
o
g
 
e
z
y
∑
j
e
z
j
=
−
ϵ
K
−
1
∑
i
=
1
,
i
≠
y
K
(
z
i
−
l
o
g
 
∑
j
e
z
j
)
−
(
1
−
ϵ
)
(
z
y
−
l
o
g
 
∑
j
e
z
j
)
=
−
ϵ
K
−
1
∑
i
=
1
,
i
≠
y
K
z
i
−
(
1
−
ϵ
)
z
y
+
l
o
g
 
∑
j
e
z
j
\begin{aligned} l(p,q) &=-\frac{\epsilon}{K-1}\sum_{i=1,i\neq y}^K log\, q_i - (1-\epsilon)log \, q_y \\ &=-\frac{\epsilon}{K-1}\sum_{i=1,i\neq y}^K log\, \frac{e^{z_i}}{\sum_j e^{z_j}} - (1-\epsilon)log \, \frac{e^{z_y}}{\sum_j e^{z_j}} \\ &=-\frac{\epsilon}{K-1}\sum_{i=1,i\neq y}^K (z_i-log\, \sum_j e^{z_j}) - (1-\epsilon) (z_y - log\, \sum_j e^{z_j}) \\ &=-\frac{\epsilon}{K-1}\sum_{i=1,i\neq y}^K z_i - (1-\epsilon)z_y + log\, \sum_j e^{z_j} \end{aligned}
l(p,q)=−K−1ϵi=1,i̸=y∑Klogqi−(1−ϵ)logqy=−K−1ϵi=1,i̸=y∑Klog∑jezjezi−(1−ϵ)log∑jezjezy=−K−1ϵi=1,i̸=y∑K(zi−logj∑ezj)−(1−ϵ)(zy−logj∑ezj)=−K−1ϵi=1,i̸=y∑Kzi−(1−ϵ)zy+logj∑ezj
对其求偏导数,当
i
≠
y
i \neq y
i̸=y时,有
∂
l
∂
z
i
=
−
ϵ
K
−
1
+
∂
l
o
g
 
∑
j
e
z
j
∂
z
i
=
−
ϵ
K
−
1
+
e
z
i
∑
j
e
z
j
=
s
o
f
t
m
a
x
(
z
i
)
−
ϵ
K
−
1
\begin{aligned} \frac{\partial l}{\partial z_i} &= -\frac{\epsilon}{K-1} + \frac{\partial log\, \sum_j e^{z_j}}{\partial z_i} \\ &= -\frac{\epsilon}{K-1} + \frac{e^{z_i}}{\sum_j{e^{z_j}}} \\ &= softmax(z_i) - \frac{\epsilon}{K-1} \end{aligned}
∂zi∂l=−K−1ϵ+∂zi∂log∑jezj=−K−1ϵ+∑jezjezi=softmax(zi)−K−1ϵ
当i=y时,有
∂
l
∂
z
y
=
−
(
1
−
ϵ
)
+
∂
l
o
g
 
∑
j
e
z
j
∂
z
y
=
−
(
1
−
ϵ
)
+
e
z
y
∑
j
e
z
j
=
s
o
f
t
m
a
x
(
z
y
)
−
(
1
−
ϵ
)
\begin{aligned} \frac{\partial l}{\partial z_y} &= -(1-\epsilon)+ \frac{\partial log\, \sum_j e^{z_j}}{\partial z_y} \\ &= -(1-\epsilon) + \frac{e^{z_y}}{\sum_j{e^{z_j}}} \\ &= softmax(z_y) - (1-\epsilon) \end{aligned}
∂zy∂l=−(1−ϵ)+∂zy∂log∑jezj=−(1−ϵ)+∑jezjezy=softmax(zy)−(1−ϵ)
因此,梯度的计算与前向时类似,遍历所有通道并把计算出的预测概率分布值减去其真实概率分布即可,部分代码如下:
for (int c = 0; c <= num_class; c++) {
int index = i * dim + c * inner_num_ + j;
float coeff = (c == label_value) ? (1.F - label_smooth_) : (label_smooth_ / num_class);
bottom_diff[index] -= coeff;
}
++count;
当然,当 ϵ = 0 \epsilon=0 ϵ=0时,得到的梯度公式与没有Label smoothing时的梯度公式是一样的。