CrossEntropy Loss
熵的概念来源于信息论,表示的是系统内部的紊乱程度;与之相关的几个概念有:信息熵,相对熵,散度。
熵
信息熵可以看成事件的信息量,事件越不确定表示系统内部越紊乱,产生的结果越有多样性,每种结果发生的概率越小,不确定性越大,信息量也就越大。假设有一事件 X X X,可能的结果是 x 0 x_0 x0,该结果发生的概率为 p ( x 0 ) p(x_0) p(x0),则该结果的不确定性定义为( l o g log log表示以2为底的对数): I ( x 0 ) = − log p ( x 0 ) I(x_0)=-\log{p(x_0)} I(x0)=−logp(x0)考虑事件 X X X 不止一个结果,可能有 n n n 个结果,每个结果发生的可能性为 p ( x i ) p(x_i) p(xi),根据上面的公式可以求得每一个结果的不确定性,信息熵就是这些不确定性的统计平均值(每一种结果给出的贡献为:概率*不确定性),将这个期望值叫做事件 X X X 的熵: H ( X ) = − ∑ i = 1 n p ( x i ) log p ( x i ) H(X)=-\sum_{i=1}^np(x_i)\log{p(x_i)} H(X)=−i=1∑np(xi)logp(xi)对于二分类问题,可能的结果只有两个(0或1),假设发生的概率为 p ( x ) p(x) p(x),熵就表示为: H ( X ) = − [ p ( x ) log p ( x ) + ( 1 − p ( x ) ) log ( 1 − p ( x ) ) ] H(X)=-[p(x) \log{p(x)} + (1-p(x)) \log{(1- p(x))}] H(X)=−[p(x)logp(x)+(1−p(x))log(1−p(x))]
相对熵
顾名思义,所谓相对就是有两种不同的情况。同样还是考虑事件
X
X
X,上面所说的
n
n
n 种可能构成该事件的一个概率分布,假设它有两种概率分布:
P
(
X
)
,
Q
(
X
)
P(X),Q(X)
P(X),Q(X);其中,
P
(
X
)
P(X)
P(X)表示样本的真实分布,例如:
[
1
,
0
,
0
]
[1,0,0]
[1,0,0] 就表示样本属于第一类,
[
0
,
1
,
0
]
[0,1,0]
[0,1,0] 就表示样本属于第二类;
Q
(
X
)
Q(X)
Q(X) 表示样本的模型预测分布,
[
0.7
,
0.2
,
0.1
]
[0.7,0.2,0.1]
[0.7,0.2,0.1] 就是预测该样本属于每一类的概率。
直观理解就是:如果用
P
P
P 来描述样本,那么就非常完美;而用
Q
Q
Q 来描述样本,虽然可以大致描述,但是不是那么的完美,信息量不足,需要额外的一些信息增量才能达到和P一样完美的描述。在机器学习中,可以通过建立模型、根据已有样本的真实分布反复训练
Q
Q
Q 的方式,来让
Q
Q
Q 不断逼近
P
P
P,最终取得一个可以接受的
Q
Q
Q 分布,此时我们认为
Q
(
X
)
Q(X)
Q(X) 等价于
P
(
X
)
P(X)
P(X),二者之间不再需要额外的信息增量,并且还可以利用
Q
Q
Q 分布来预测样本以外的情况。
关于
P
(
X
)
P(X)
P(X) 和
Q
(
X
)
Q(X)
Q(X) 之间的差异(相对熵),数学上一般用散度来描述。常见的散度例如,KL散度:
D
K
L
(
p
∣
∣
q
)
=
∑
i
=
1
n
p
(
x
i
)
log
p
(
x
i
)
q
(
x
i
)
D_{_{KL}}(p||q)=\sum_{i=1}^np(x_i)\log{\frac{p(x_i)}{q(x_i)}}
DKL(p∣∣q)=i=1∑np(xi)logq(xi)p(xi)
n
n
n表示事件
X
X
X 的所有可能性,
D
K
L
D_{_{KL}}
DKL 的值越小,表示
P
P
P 分布和
Q
Q
Q 分布越接近。从计算形式上和熵进行对比,可以很好地理解“相对”的概念。这里KL散度表示
Q
Q
Q 拟合
P
P
P 的程度,完全拟合时散度为0;不完全拟合时,
D
K
L
(
p
∣
∣
q
)
D_{_{KL}}(p||q)
DKL(p∣∣q) 就是拟合的信息损耗。可以看到,KL散度具有不对称:
D
K
L
(
p
∣
∣
q
)
≠
D
K
L
(
q
∣
∣
p
)
D_{_{KL}}(p||q) \neq D_{_{KL}}(q||p)
DKL(p∣∣q)=DKL(q∣∣p),而JS散度可以有效避免这种不对称性:
D
J
S
(
p
∣
∣
q
)
=
1
2
D
K
L
(
p
∣
∣
m
)
+
1
2
D
K
L
(
q
∣
∣
m
)
,
m
=
1
2
(
p
+
q
)
D_{_{JS}}(p||q)=\frac{1}{2}D_{_{KL}}(p||m)+\frac{1}{2}D_{_{KL}}(q||m),m=\frac{1}{2}(p+q)
DJS(p∣∣q)=21DKL(p∣∣m)+21DKL(q∣∣m),m=21(p+q)
交叉熵
尝试将KL散度展开: D K L ( p ∣ ∣ q ) = ∑ i = 1 n p ( x i ) log p ( x i ) − ∑ i = 1 n p ( x i ) log q ( x i ) D_{_{KL}}(p||q)=\sum_{i=1}^np(x_i)\log{p(x_i)}-\sum_{i=1}^np(x_i)\log{q(x_i)} DKL(p∣∣q)=i=1∑np(xi)logp(xi)−i=1∑np(xi)logq(xi)前面一部分就是 P P P 分布的熵的相反数,后面这一部分就是所谓的交叉熵。于是我们可以得到交叉熵的计算公式: H ( p , q ) = − ∑ i = 1 n p ( x i ) log q ( x i ) H(p,q)=-\sum_{i=1}^np(x_i)\log{q(x_i)} H(p,q)=−i=1∑np(xi)logq(xi)交叉熵给出了衡量两个不同分布之间差异的计算公式,我们将这种差异叫做损失;交叉熵越小,表示两个分布越接近、损失越小。在机器学习中, P P P 就是样本的真实分布,以 y y y 表示;而 Q Q Q 分布则是模型的预测值,以 y ^ \hat{y} y^ 表示,于是就有了交叉熵损失函数: L o s s = − ∑ i = 1 n y i log y i ^ Loss=-\sum_{i=1}^ny_i\log\hat{y_i} Loss=−i=1∑nyilogyi^上面说过, n n n 表示所有可能结果的数量,用机器学习中的专业术语来说就是分类数量。该公式给出了模型在单个样本的预测上距离真实情况具有多大的损失值,对于 m i n i − b a t c h mini-batch mini−batch 模式,一次需要处理 m m m 个样本,此时的交叉熵损失为:: L o s s = − 1 m ∑ j = 1 m ∑ i = 1 n y i , j log y ^ i , j Loss=-\frac{1}{m}\sum_{j=1}^m\sum_{i=1}^ny_{_{i,j}}\log\hat{y}_{_{i,j}} Loss=−m1j=1∑mi=1∑nyi,jlogy^i,j
Softmax Loss
sofamax函数
卷积神经网络最终经过全连接层输出一个 m m m 维向量,每一维的值作为计算样本属于对应类概率的基础,所以该向量基本决定了一个样本属于 m m m 类中的哪一类。早先利用hardmax方法计算概率时,直接将向量中的最大值置为1,其余直接置为0,1对应的类即为样本所属类。softmax其实是针对hardmax提出来的,它不再直接将向量值置为“非1即0”,而是通过softmax计算。 σ i ( z ) = e w i ∗ x + b i ∑ j = 1 n e w j ∗ x + b j \sigma_i(z)=\frac{e^{w_i*x+b_i}}{\sum_{j=1}^ne^{w_j*x+b_j}} σi(z)=∑j=1newj∗x+bjewi∗x+bi上式就是CNN中的softmax函数,有点类似于激活函数, w ∗ x + b w*x+b w∗x+b 表示全连接层的输出,每一个分量经过softmax之后得到 σ i ( z ) \sigma_i(z) σi(z)。
softmax损失
说白了softmax就是做预测的,预测结果准不准还需要和真实的标签对比。真实标签还是记为 y y y,将它和预测值做交叉熵计算: S o f t M a x _ L o s s = − ∑ i = 1 n y i ∗ log σ i ( z ) SoftMax\_Loss=-\sum_{i=1}^ny_i*\log\sigma_i(z) SoftMax_Loss=−i=1∑nyi∗logσi(z)这是模型在单个样本上的损失值,实际上 y y y 是一个 o n e − h o t one-hot one−hot 向量,我们假设该样本属于 y i y_i yi 类,那么最终模型在单样本上的损失值就是 − log σ y i ( z ) -\log\sigma_{y_i}(z) −logσyi(z),因为 y y y 在非 y i y_i yi 分量上的值为0。对于有 m m m 个样本的 m i n i − b a t c h mini-batch mini−batch 而言,损失函数就是: S o f t m a x _ L o s s = − 1 m ∑ i = 1 m log σ y i ( z ) = − 1 m ∑ i = 1 m log e w y i ∗ x i + b y i ∑ j = 1 n e w j ∗ x i + b j Softmax\_Loss=-\frac{1}{m}\sum_{i=1}^m\log\sigma_{y_i}(z)=-\frac{1}{m}\sum_{i=1}^m\log\frac{e^{w_{y_i}*x_i+b_{y_i}}}{\sum_{j=1}^ne^{w_j*x_i+b_j}} Softmax_Loss=−m1i=1∑mlogσyi(z)=−m1i=1∑mlog∑j=1newj∗xi+bjewyi∗xi+byi实际上,softmax损失就是在全连接层后面接一个softmax作为激活函数,然后运用交叉熵损失。
Triplet Loss
s o f t m a x softmax softmax损失将预测值和真实值进行差异比较,采用的是一对、两个数据; T r i p l e t L o s s TripletLoss TripletLoss则采用三元组的形式,利用欧氏距离来衡量样本之间的差距。这里的三元组指的是: ( A n c h o r , N e g a t i v e , P o s i t i v e ) (Anchor,Negative,Positive) (Anchor,Negative,Positive),其中Anchor表示样本,Positive表示与样本同一类的样本,Negative表示不同类样本。 T r i p l e t L o s s TripletLoss TripletLoss计算样本Anchor与其他两个标签样本的距离,以减小同类样本差距、增大异类样本差距为目的设计损失函数: T r i p l e t _ L o s s = ∑ i = 1 n [ ∣ ∣ f ( x i a ) − f ( x i p ) ∣ ∣ 2 2 − ∣ ∣ f ( x i a ) − f ( x i n ) ∣ ∣ 2 2 + α ] + Triplet\_Loss=\sum_{i=1}^n[||f(x_i^a)-f(x_i^p)||_{_2}^{^2}-||f(x_i^a)-f(x_i^n)||_{_2}^{^2}+\alpha]_+ Triplet_Loss=i=1∑n[∣∣f(xia)−f(xip)∣∣22−∣∣f(xia)−f(xin)∣∣22+α]+其中 f ( x i a ) 、 f ( x i p ) 、 f ( x i n ) f(x_i^a)、f(x_i^p)、f(x_i^n) f(xia)、f(xip)、f(xin)分别表示Anchor、Positive、Negative所对应的样本, α \alpha α是一个超参数,代表 P o s i t i v e / N e g a t i v e Positive/Negative Positive/Negative的边界。 T r i p l e t L o s s TripletLoss TripletLoss用图来表示就是:
关于参数 α \alpha α
- 当参数 α \alpha α 值越小时,loss 也就较容易的趋近于 0,于是 Anchor 与 Positive 不需要拉的太近,Anchor 与 Negative 不需要拉的太远,就能使得 loss 很快趋近于 0。这样训练得到的结果,不能够很好的区分相似的图像。
- 当参数 α \alpha α 值越大时,就需要使得网络参数要拼命地拉近 Anchor、Positive 之间的距离,拉远 Anchor、Negative 之间的距离。如果参数 α \alpha α 值设置的太大,很可能最后 loss 保持一个较大的值,难以趋近于0。
简而言之,参数 α \alpha α 值设置的越小,loss 很容易趋近于 0 ,但很难区分相似的图像; α \alpha α 值设置的越大,loss 值较难趋近于 0,甚至导致网络不收敛,但可以较有把握的区分较为相似的图像。因此,设置一个合理的参数 α 值很关键,这是衡量相似度的重要指标。
Center Loss
T
r
i
p
l
e
L
o
s
s
Triple Loss
TripleLoss 在增加类间差距、减小类内差距上具有比较显著的效果,但是比较麻烦的是数据对的选取方式比较难确定,更重要的是会造成样本对的数量爆炸。举例来说:当有10个人、每人10张图片时,首先确定
p
o
s
i
t
i
v
e
positive
positive 类有
C
10
1
C_{10}^1
C101 ,然后从
p
o
s
i
t
i
v
e
positive
positive 类中选两张图片作为
A
n
c
h
o
r
、
P
o
s
i
t
i
v
e
Anchor、Positive
Anchor、Positive 有
C
10
2
C
2
1
C_{10}^2C_2^1
C102C21 ;从剩下的90张图中任意选一张作为
N
e
g
a
t
i
v
e
Negative
Negative 就是
C
90
1
C_{90}^1
C901,于是总的样本对的数量就是
C
10
1
C
10
2
C
2
1
C
90
1
=
81000
C_{10}^1C_{10}^2C_2^1C_{90}^1=81000
C101C102C21C901=81000。
C
n
e
t
e
r
L
o
s
s
CneterLoss
CneterLoss 直观来理解就是计算每一个样本和所属类中心的距离,并尽量去缩小这个距离,其计算公式为
L
c
=
1
2
∑
i
=
1
m
∣
∣
x
i
−
c
y
i
∣
∣
2
2
L_c=\frac{1}{2}\sum_{i=1}^{m}||x_i-c_{yi}||_2^2
Lc=21i=1∑m∣∣xi−cyi∣∣22其中,
c
y
i
c_{yi}
cyi 表示第
i
i
i 个样本所属类别的深层特征的中心,
x
i
x_i
xi 表示第
i
i
i 个样本的全连接层之前的特征,
m
m
m 是 mini-bath 的大小。这一方法继承了
t
r
i
p
l
e
l
o
s
s
tripleloss
tripleloss 的优势(减小类内差距),但是如果单独使用,则深层特征和特征中心则会衰减为0,所以一般会将其和
s
o
f
t
m
a
x
softmax
softmax 损失一并使用
L
=
L
s
+
λ
∗
L
c
L=L_s+\lambda*L_c
L=Ls+λ∗Lc其中
λ
\lambda
λ 表示
c
e
n
t
e
r
l
o
s
s
centerloss
centerloss 所占比例。理想情况下,
c
y
i
c_{yi}
cyi 需要随着学到的特征变化而实时更新,也就是要在每一次迭代中用整个数据集的特征来算每个类的中心。但这显然不现实,做以下两个修改:
1、由整个训练集更新 center 改为 mini-batch 更新 center;
2、避免错误分类的样本的干扰,使用参数 α 来控制 center 的学习率。
所以在 c e n t e r L o s s centerLoss centerLoss 中有两个参数: α , λ \alpha,\lambda α,λ。一般情况下二者取值都在 0~1 之间, α = 0.1 , λ = 0.01 \alpha=0.1,\lambda=0.01 α=0.1,λ=0.01。TensorFlow版本的实现如下:
def center_loss(features, labels):
alpha = HYPERPARAMS.center_alpha # 参数α
num_classes = NETWORK.output_size # 网络最终输出的分类数量
len_features = features.get_shape()[1] # 注意这里的feature是全连接层之前的特征
centers = tf.get_variable('center_loss', [num_classes, len_features], dtype=tf.float32, initializer=tf.constant_initializer(0), trainable=False)
# 类的中心值,需要实时更新,更新方式在下面
labels = tf.reshape(tf.argmax(labels, axis=1), [-1]) # 标签向量,注意不是one-hot形式
centers_batch = tf.gather(centers, labels) # 根据标签获取类中心值
loss = tf.nn.l2_loss(features - centers_batch) # 计算loss
diff = centers_batch - features # 更新center,并没有看懂是如何更新的?
unique_label, unique_idx, unique_count = tf.unique_with_counts(labels) # 获取labels中所有唯一的元素,依次返回元素的值、位置、数量
appear_times = tf.gather(unique_count, unique_idx) # 得到labels每个元素的出现的次数
appear_times = tf.reshape(appear_times, [-1,1])
diff = diff / tf.cast((1+ appear_times), tf.float32)
diff = alpha * diff # centers的学习率
centers_update_op = tf.scatter_sub(centers, labels, diff) # 将centers中特定位的元素减去diff,labels就是索引(更新中心值)
return loss, centers_update_op