知识蒸馏----Knowledge_Distillation
Softmax, log_softmax, NLLLoss and CrossEntropy
What is Sofmax
函数Softmax(x): 输入一个实数向量并返回一个概率分布。定义 x 是一个实数的向量(正数或负数都可以)。 然后, 第i个 Softmax(x) 的计算方式为:
exp
(
x
i
)
∑
j
exp
(
x
j
)
\frac{{\exp ({x_i})}}{{\sum\nolimits_j {\exp ({x_j})} }}
∑jexp(xj)exp(xi)
输出是一个概率分布: 每个元素都是非负的, 并且所有元素的总和都是1
import torch
import torch.nn.functional as F
torch.manual_seed(0)
在图片分类问题中,输入m张图片,输出一个m*N的Tensor,其中N是分类类别总数。比如输入2张图片,分三类,最后的输出是一个2*3的Tensor,举个例子:
output = torch.randn(2, 3)
print(output)
第1,2行分别是第1,2张图片的结果,假设第123列分别是猫、狗和猪的分类得分。
可以看出模型认为两张都更可能是猫。
然后对每一行使用Softmax,这样可以得到每张图片的概率分布。
print(F.softmax(output, dim=1))
# 这里dim的意思是计算Softmax的维度,这里设置dim=1,可以看到每一行的加和为1。
What is log_softmax
这个很好理解,其实就是对softmax处理之后的结果执行一次对数运算。
可以理解为 log(softmax(output))
print(F.log_softmax(output, dim=1))
print(torch.log(F.softmax(output, dim=1)))
# 输出结果是一致的
What is NLLLoss?
该函数的全程是negative log likelihood loss. 若
x
i
=
[
q
1
,
q
2
,
.
.
.
,
q
N
]
x_i=[q_1, q_2, ..., q_N]
xi=[q1,q2,...,qN] 为神经网络对第i个样本的输出值,
y
i
y_i
yi为真实标签。则:
f
(
x
i
,
y
i
)
=
−
q
y
i
f(x_i,y_i)=-q_{y_i}
f(xi,yi)=−qyi
输入:log_softmax(output), target
print(F.nll_loss(torch.tensor([[-1.2, -2, -3]]), torch.tensor([0])))
# [[-1.2, -2, -3]] 已经log_softmax
# 通常我们结合 log_softmax 和 nll_loss一起用
output = torch.tensor([[1.2, 2, 3]])
target = torch.tensor([0])
log_sm_output = F.log_softmax(output, dim=1)
print('Output is [1.2, 2, 3]. If the target is 0, loss is:', F.nll_loss(log_sm_output, target))
target = torch.tensor([1])
log_sm_output = F.log_softmax(output, dim=1)
print('Output is [1.2, 2, 3]. If the target is 1, loss is:', F.nll_loss(log_sm_output, target))
target = torch.tensor([2])
log_sm_output = F.log_softmax(output, dim=1)
print('Output is [1.2, 2, 3]. If the target is 2, loss is:', F.nll_loss(log_sm_output, target))
在分类问题中,CrossEntropy等价于log_softmax 结合 nll_loss
N N N分类问题,对于一个特定的样本,已知其真实标签,CrossEntropy的计算公式为:
c r o s s _ e n t r o p y = − ∑ k = 1 N ( p k ∗ log q k ) cross\_entropy=-\sum_{k=1}^{N}\left(p_{k} * \log q_{k}\right) cross_entropy=−k=1∑N(pk∗logqk)
其中p表示真实值,在这个公式中是one-hot形式;q是经过softmax计算后的结果, q k q_k qk为神经网络认为该样本为第 k k k类的概率。
仔细观察可以知道,因为p的元素不是0就是1,而且又是乘法,所以很自然地我们如果知道1所对应的index,那么就不用做其他无意义的运算了。所以在pytorch代码中target不是以one-hot形式表示的,而是直接用scalar表示。若该样本的真实标签为 y y y,则交叉熵的公式可变形为:
c r o s s _ e n t r o p y = − ∑ k = 1 N ( p k ∗ log q k ) = − l o g q y cross\_entropy=-\sum_{k=1}^{N}\left(p_{k} * \log q_{k}\right)=-log \, q_{y} cross_entropy=−k=1∑N(pk∗logqk)=−logqy
output = torch.tensor([[1.2, 2, 3]])
target = torch.tensor([0])
log_sm_output = F.log_softmax(output, dim=1)
nll_loss_of_log_sm_output = F.nll_loss(log_sm_output, target)
print(nll_loss_of_log_sm_output)
output = torch.tensor([[1.2, 2, 3]])
target = torch.tensor([0])
ce_loss = F.cross_entropy(output, target)
print(ce_loss)
# More about softmax
import numpy as np
def softmax(x):
x_exp = np.exp(x)
return x_exp / np.sum(x_exp)
output = np.array([0.1, 1.6, 3.6])
print(softmax(output))
def softmax_t(x, t):
x_exp = np.exp(x / t)
return x_exp / np.sum(x_exp)
output = np.array([0.1, 1.6, 3.6])
print(softmax_t(output, 5))
print(softmax_t(output, 10000))
总结:Softmax, log_softmax, NLLLoss and CrossEntropy
1.Softmax:
exp
(
x
i
)
∑
j
exp
(
x
j
)
\frac{{\exp ({x_i})}}{{\sum\nolimits_j {\exp ({x_j})} }}
∑jexp(xj)exp(xi)
即对分类问题的输出向量作概率分布,使其之和为1。
2.log_softmax
即对Softmax取对数,值为负
3.NLLLoss
该函数的全程是negative log likelihood loss.
若
x
i
=
[
q
1
,
q
2
,
.
.
.
,
q
N
]
x_i=[q_1, q_2, ..., q_N]
xi=[q1,q2,...,qN] 为神经网络对第i个样本的输出值,
y
i
y_i
yi为真实标签。则:
f
(
x
i
,
y
i
)
=
−
q
y
i
f(x_i,y_i)=-q_{y_i}
f(xi,yi)=−qyi
(即对输入某个样本得到输出,得到的输出向量对应其真实标签的log_softmax取负号)
4.在分类问题中,CrossEntropy等价于log_softmax 结合 nll_loss
5.我们知识蒸馏常提到的temperature,就在Softmax中上下同除以t
exp ( x i / t ) ∑ j exp ( x j / t ) \frac{{\exp ({x_i}/t)}}{{\sum\nolimits_j {\exp ({x_j/t})} }} ∑jexp(xj/t)exp(xi/t)
我们可以看到同除以t,使得分类数据的概率分布更加平滑。
理论分析:
从代码角度理解KD:https://github.com/xiaobai799/KD/blob/main/KD.py
参考资料:https://www.bilibili.com/video/BV1s7411h7K2?t=1878