损失 Loss
损失,代价和指标 Loss & Cost & Objective
我们对于一个模型,例如:用TensorFlow实现的简单的MNIST手写体数字识别模型,我们通常需要评估我们这个模型的好坏,也就是,一个可以评价我们的模型的指标,这个指标的作用是:为优化器提供一个变量,然后优化器将根据这个变量对我们的模型进行优化(例如,通过最小化这个变量,或者是最大化这个变量)。
这就是损失(loss),简单来说,就是我们预测值(predication)和真实值(ground truth)之间的差,例如最简单的:
(0)
L
(
W
,
b
)
=
y
(
x
)
−
a
\mathcal{L}(\mathcal{W}, b) = y(x) - a \tag{0}
L(W,b)=y(x)−a(0)
这里的
W
\mathcal{W}
W 和
b
b
b 其实分别是神经网络的权重(
w
e
i
g
h
t
weight
weight)的集合和偏置量(
β
i
a
s
\beta ias
βias)的集合
显然,从损失的定义我们可以了解到,损失是针对单个样本的,但是我们在训练的时候,通常是
n
n
n 个样本批量进行的,这就是所谓的批 (
b
a
t
c
h
batch
batch)。而我们最终所要计算的实际上是一整批样本的所有损失的均值,这就是代价(cost),相对的此时,计算出代价的函数就称为代价函数:
(1)
C
(
W
,
b
)
=
1
n
∑
i
=
1
n
L
(
W
,
b
)
\mathcal{C}(\mathcal{W}, b) = \frac{1}{n}\sum_{i=1}^{n}\mathcal{L}(\mathcal{W},b) \tag{1}
C(W,b)=n1i=1∑nL(W,b)(1)
通过这样得到一个代价函数,使得我们的结果不再是一个由若干个损失函数值组成的矩阵,取而代之的是一个方便我们进行衡量的变量。
而事实上,我们并不会简单的直接计算样本的预测值和真实值之间的差来作为损失函数,因为神经网络的参数和正确分类的样本的数量之间并不是简单的线性的,平滑的函数,有时候(其实如果你在做了一些相关工作之后),你会发现正确分配的样本数量根本不会随着你的参数的微小调整做出任何的变化。
通常,我们往往会使用类二次的或者其他的一些损失函数,使得我们能够通过对参数进行细微的调整来取得更好的效果。
最终我们将会把我们得到的值交由优化器(Optimizer)进行优化,而这个最终进行优化的值就是所谓的指标(Objective)。
下面我们将介绍一些常见的损失函数以及它们的优点和缺点。
0-1 Loss 0-1 损失
这个可能是最为简单的损失了,并且如果你学习过神经网络的知识的话,这其实就是感知器的做法:
(2)
L
(
y
,
a
l
)
=
{
1
,
y
=
a
l
0
,
y
≠
a
j
l
\mathcal{L}(y, a^{l}) = \begin{cases} & 1, & y=a^{l}\\ & 0, & y \ne a^{l}_{j} \end{cases}\tag{2}
L(y,al)={1,0,y=aly̸=ajl(2)
其中,
a
j
l
a^l_j
ajl 表示第
l
l
l 层的第
j
j
j 个神经元的输出。
一般我们并不会严格的要求两者相等,而是根据它们之间的距离来做出判断:
(3)
L
(
y
,
a
l
)
=
{
1
,
∣
y
−
a
j
l
∣
<
T
0
,
∣
y
−
a
j
l
∣
>
T
\mathcal{L}(y, a^{l}) = \begin{cases} & 1, & |y-a^{l}_{j}| < T\\ & 0, & |y-a^{l}_{j}| > T \end{cases} \tag{3}
L(y,al)={1,0,∣y−ajl∣<T∣y−ajl∣>T(3)
其中,
T
T
T 是一个大于
0
0
0 的常数。这是一个非常简单的做法,显然它有一个缺点:过拟合,容易过分拟合训练数据,而在测试数据集上呈现出非常令人失望的效果。并且,这样的判断是非常僵硬的,它无法捕捉到数据的特征,只是非常单纯的进行分类。
因此,这样的损失函数一般在神经网络学习的入门会谈到,几乎没有实用性。
欧氏距离 Euclidean Distance
相信这是我们每个人都很熟悉的衡量差距的表达式,欧氏距离的表达式如下:
(4)
L
=
∑
i
=
1
n
(
y
i
2
−
f
2
(
x
i
)
)
\mathcal{L} = \sum^{n}_{i=1}(\sqrt{y_{i}^{2} - f^{2}(x_{i})}) \tag{4}
L=i=1∑n(yi2−f2(xi))(4)
这可能是一个最为简单的,也是几乎我们每个人都能够想到的损失函数,因为很直观的,距离,就是反映了预测值和真实值之间的差距,这就是损失的基本定义。
但事实上,这并不是一个很好的损失函数,甚至可以说是一个比较
n
a
i
v
e
naive
naive 的选择,因为事实上这个损失函数的结果就是让我们的模型去做一个平均,结果就是导致一个模糊的结果。
最小绝对值误差 Least Absolute Error (LAE) - L1
L
1
L1
L1 范式损失函数,又叫做最小绝对值偏差(或误差):
(5)
L
=
∑
i
=
1
n
(
∣
y
i
−
f
(
x
i
)
∣
)
\mathcal{L} = \sum^{n}_{i=1} (|y_{i} - f(x_{i})|) \tag{5}
L=i=1∑n(∣yi−f(xi)∣)(5)
其中:
- y i y_i yi 是每个样本的真实值
- f ( x i ) f(x_i) f(xi) 是每个样本的预测值
也就是说,
L
1
L1
L1 范式就是计算所有这两个值的差的绝对值之和。这同样涉及到了预测结果和真实值之间的差,自然,
L
1
L1
L1 同样也会造成结果的模糊,但是相比于欧氏距离来讲,它并不会将结果进行平均,因为这里的绝对值使得它不回去考虑方向,只考虑大小,所以相对来说实用性会比较高,效果也会比较好。
而事实上,更多的,我们是将其和其他损失函数结合起来使用,其目的是对结果做一些平滑,或者是,去捕捉一些低频的信息(比如在cGAN的图像转换中)
最小平方误差 Least Squares Error (LSE) - L2
顾名思义,相比于
L
1
L1
L1,
L
2
L2
L2 就是对误差进行平方:
(6)
L
=
∑
i
=
1
n
(
y
i
−
f
(
x
i
)
)
2
\mathcal{L} = \sum^{n}_{i=1}(y_i - f(x_i))^{2} \tag{6}
L=i=1∑n(yi−f(xi))2(6)
相比于
L
1
L1
L1,
L
2
L2
L2 对于损失更为敏感,变化更加迅速,但平方使得模糊程度变大(非线性),不过了与此同时,也方便了我们对参数进行微小的调整。
L
2
L2
L2 在很多工作上应用非常广泛。
交叉熵 Cross Entropy
熵,衡量的是信息量:
(7)
E
n
t
r
o
p
y
=
p
i
log
1
p
i
Entropy = p_i\log\frac{1}{p_i} \tag{7}
Entropy=pilogpi1(7)
交叉熵,你可以猜到,这是关于两个概率分布,假设一个为
p
p
p, 一个为
q
q
q ,其中:
- p p p 为真实标签的概率分布
- q q q 为当前模型预测的概率分布
那么,假如我们使用真实的概率分布和当前预测结果来计算熵的话,那就是:
(8)
E
n
t
r
o
p
y
=
p
i
log
1
q
i
Entropy = p_{i}\log{\frac{1}{q_{i}}} \tag{8}
Entropy=pilogqi1(8)
这个就叫做交叉熵(Cross Entropy),表示这两个分布的差异:差异越大,交叉熵越大。所以,我们可以利用交叉熵来衡量我们的预测样本的分布和正确值的分布之间的差异。
作为损失函数,在使用sigmoid进行梯度下降时,相比于最小均方(L2)来说,交叉熵的学习速率更快。
GAN 生成对抗网络
GAN(Generative Adversarial Network,生成对抗网络)网络架构由两个部分组成:
- 生成器网络(Generator Network)
- 判别器网络(Discriminator Network)
所以,对于GAN来说,它会有两个损失函数,最终GAN的损失是这两个函数共同作用的结果:
(9)
L
G
A
N
(
G
,
D
)
=
E
y
[
l
o
g
D
(
y
)
]
+
E
x
,
z
[
l
o
g
(
1
−
D
(
x
,
G
(
x
,
z
)
)
)
]
\mathcal{L}_{GAN}(G, D) = \mathbb{E}_{y}[logD(y)] + \mathbb{E}_{x, z}[log(1 - D(x, G(x, z)))] \tag{9}
LGAN(G,D)=Ey[logD(y)]+Ex,z[log(1−D(x,G(x,z)))](9)
其中,
G
G
G 为生成器的损失函数,
D
D
D 为判别器的损失函数。判别器努力调整使得判别器网络能够拥有更强的辨别能力,然后它将会告诉生成器如何调整参数,使得生成器能够生成更好的图片,这就是一个对抗的过程:判别器努力增大这个值,而生成器则是在努力减少这个值。
最终,我们的指标将会是这样的:
(10)
G
∗
=
arg
min
max
L
G
A
N
(
G
,
D
)
G^{*} = \arg\min\max\mathcal{L}_{GAN}(G,D) \tag{10}
G∗=argminmaxLGAN(G,D)(10)
cGAN 条件生成对抗网络
不同于GAN的地方在于,cGAN的判别器也可以观察到生成器的输入:
(11)
L
c
G
A
N
(
G
,
D
)
=
E
x
,
y
[
l
o
g
D
(
x
,
y
)
]
+
E
x
,
z
[
l
o
g
(
1
−
D
(
x
,
G
(
x
,
z
)
)
)
]
\mathcal{L}_{cGAN}(G, D) = \mathbb{E}_{x, y}[logD(x, y)] + \mathbb{E}_{x, z}[log(1 - D(x, G(x, z)))] \tag{11}
LcGAN(G,D)=Ex,y[logD(x,y)]+Ex,z[log(1−D(x,G(x,z)))](11)
同样,最终我们的指标还是:
(12)
G
∗
=
arg
min
max
L
c
G
A
N
(
G
,
D
)
G^{*} = \arg\min\max\mathcal{L}_{cGAN}(G,D) \tag{12}
G∗=argminmaxLcGAN(G,D)(12)
cGAN + L1
在一些论文中提出了将cGAN和
L
1
L1
L1 结合使用来取得更好的效果:
(13)
L
L
1
(
G
)
=
E
x
,
y
,
z
[
∣
∣
y
−
G
(
x
,
z
)
∣
∣
]
\mathcal{L}_{L1}(G) = \mathbb{E}_{x,y,z}[|| y-G(x, z)||] \tag{13}
LL1(G)=Ex,y,z[∣∣y−G(x,z)∣∣](13)
最终的指标为:
(14)
G
∗
=
arg
min
max
L
c
G
A
N
(
G
,
D
)
+
λ
L
L
1
(
G
)
G^{*} = \arg\min\max\mathcal{L}_{cGAN}(G,D) + \lambda\mathcal{L}_{L1}(G) \tag{14}
G∗=argminmaxLcGAN(G,D)+λLL1(G)(14)
优化器 Optimizer
梯度下降的优化算法
Gradient Descent (GD) 梯度下降
这可以说是一个最基本的优化器了,因为事实上现在很多优化器,包括一些流行的优化器(例如,Adam),都是以梯度下降为基础的衍生。
假如,我们的优化参数为
W
\mathcal{W}
W,损失函数(Loss Function)为
J
(
D
)
\mathcal{J}(D)
J(D),学习率(Learning Rate)为
η
\eta
η,那么我们的参数优化将会表示如下:
(15)
W
t
+
1
=
W
t
−
η
t
∇
w
J
(
W
t
)
\mathcal{W}_{t+1} = \mathcal{W}_{t} - \eta_{t}\nabla_{w}\mathcal{J}(\mathcal{W}_{t}) \tag{15}
Wt+1=Wt−ηt∇wJ(Wt)(15)
其中, 在下一时刻的参数值
W
t
+
1
\mathcal{W}_{t+1}
Wt+1, 我们通过上一时刻的参数值
W
t
\mathcal{W}_{t}
Wt 来得到,减去学习率 和损失函数关于当前参数的导数(即当前损失函数的优化参数为
W
t
\mathcal{W}_{t}
Wt 对应的梯度)的乘积来得到。简单理解的话,
η
\eta
η 就是一个系数,它决定了当前梯度对于参数影响的大小。
所以,事实上梯度下降就是让参数沿着损失函数的梯度方向进行下降(因为我们的目标一般都是对参数进行最小化),也就是说,最终我们的目的就是:最小化损失函数。这也是大多数优化器所要完成的任务(有的时候遇到一些最大化问题,我们也会将其转化成最小化问题:例如转为 1 − J ( D ) 1-\mathcal{J}(D) 1−J(D) 这样的形式进行优化)。
但是,简单的梯度下降存在明显的缺点:
- 这其实并不总是能得到最优解(损失函数的最小值),因为你进行下降的方向可能只会得到一个局部最小值
- 此外,梯度下降还有训练缓慢的问题,因为可以看到,需要对每一个样本都代入当前的参数 W t \mathcal{W}_{t} Wt 到损失函数 J ( D ) \mathcal{J}(D) J(D) 中,计算当前的梯度。
Batch Gradient Descent (BGD) 批量梯度下降
相对于普通的梯度下降方法来说,批量梯度下降不同地方在于,它不是每次在一个样本的输入之后进行参数的更新,而是在输入
n
n
n 个样本之后再对参数作调整:
(16)
W
t
+
1
=
W
t
−
η
t
∑
i
=
1
n
∇
w
J
(
W
t
,
X
(
i
)
,
Y
(
i
)
)
\mathcal{W}_{t + 1} = \mathcal{W}_{t} - \eta_{t}\sum_{i=1}^{n}\nabla_{w}\mathcal{J}(\mathcal{W}_{t},\space\mathcal{X}^{(i)},\space\mathcal{Y}^{(i)}) \tag{16}
Wt+1=Wt−ηti=1∑n∇wJ(Wt, X(i), Y(i))(16)
其中,
X
(
i
)
\mathcal{X}^{(i)}
X(i) 是第
i
i
i 个样本输入,
Y
(
i
)
\mathcal{Y}^{(i)}
Y(i) 是第
i
i
i 个样本输出。
显然,相比于梯度下降而言:
- 批量样本进行参数更新具有更快的训练速度(在计算的时候),因为参数的更新是在批量样本训练之后,而不是每次输入一个样本之后就进行参数更新
- 并且,批量的样本计算出来的梯度方向更具有代表性,更能代表整体的梯度方向,因此下降的方向更加准确
Stochastic Gradient Descent (SGD) 随机梯度下降
随机梯度下降方法明显的特点就算是:每个迭代只采用一个样本来对参数进行更新,加快训练速度。也就是说,不采用所有的批量训练数据进行参数更新,而是随机选择其中一条训练数据来更新参数:
(17)
W
t
+
1
=
W
t
−
η
t
g
t
,
i
\mathcal{W}_{t+1} = \mathcal{W}_{t} - \eta_{t} g_{t,i} \tag{17}
Wt+1=Wt−ηtgt,i(17)
其中,
g
t
,
i
=
∇
w
J
(
W
t
,
X
t
,
i
,
Y
t
,
i
)
,
i
∈
{
1
,
…
,
n
}
g_{t,i} = \nabla_{w} J(\mathcal{W}_{t},\mathcal{X}_{t,i},\mathcal{Y}_{t,i}), i\in\{1,\dots,n\}
gt,i=∇wJ(Wt,Xt,i,Yt,i),i∈{1,…,n},即随机样本
X
t
,
i
\mathcal{X}_{t,i}
Xt,i 在当前输入为
W
t
\mathcal{W}_{t}
Wt 时对应的梯度。
显然,这种做法可以得到更快的训练速度,然而缺点也非常明显:
- 梯度下降方向不准确(随机选取输入样本进行更新)
- 可能导致局部最优
- 不能保证线性收敛(因为梯度下降方向是随机选取的,所以虽然训练速度较快,但可能导致收敛较慢,多走了很多弯路)
动量优化法
既保留了梯度下降法中对当前梯度的使用,并且还考虑了前面梯度提供的信息,这就是搭配随机梯度下降(SGD)的非常流行的动量法(Momentum)。
Momentum 动量法
动量等式由两部分组成:
(18)
v
t
=
η
∗
v
t
−
α
∗
∇
w
∑
1
m
J
(
W
t
,
X
t
,
i
,
Y
t
,
i
)
v_{t} = \eta * v_{t} - \alpha * \nabla_{w}\sum_{1}^{m} J(\mathcal{W}_{t},\mathcal{X}_{t,i},\mathcal{Y}_{t,i}) \tag{18}
vt=η∗vt−α∗∇w1∑mJ(Wt,Xt,i,Yt,i)(18)
其中:
- η \eta η:为动量系数,一般取 0.9 0.9 0.9,或者是考虑从 0.5 0.5 0.5 开始退火到 0.9 0.9 0.9。在数学上,这样的方法称为指数平均(EMA, Exponential Moving Average)
- v t v_{t} vt:为上一次迭代的动量
后面的式子是随机梯度下降选取的在
w
t
\mathcal{w}_{t}
wt 随机样本的当前梯度。
然后,更新参数:
(19)
W
t
+
1
=
W
t
+
v
t
\mathcal{W}_{t + 1} = \mathcal{W}_{t} + v_{t}\tag{19}
Wt+1=Wt+vt(19)
通过考虑之前的动量,进行指数平均,可以对随机梯度下降的过程中的震荡起到抑制作用,如下图:
其中,
w
1
w_{1}
w1 和
w
2
w_{2}
w2 分别为两个不同方向上的梯度,红色 为梯度下降的曲线,蓝色 的直线为理想的下降方向。我们可以很直观的看出来,蓝色是我们希望实现的下降方向,然而,事实上,蓝色方向上的梯度相比两侧的“上坡“来说,梯度较小,因此我们并不会选择沿着”山谷“进行下降,而是类似滑雪回环比赛中那样来回摆动着下降。在这样的情况下,通过考虑前面的梯度,可以有效的抵消
w
1
w_{1}
w1 分量上的震荡,从而沿着
w
2
w_{2}
w2 这个分量进行下降,提高了下降速度。
自适应学习率优化算法
增加更新学习率的考虑,而不是将学习率设置为一个常数,以提高训练速度和训练效果
Adagrad
Adagrad针对每一个参数单独适应一个学习率:
(20)
v
t
=
v
t
+
g
t
2
Δ
W
t
=
−
η
v
t
+
ϵ
∗
g
t
W
t
+
1
=
W
t
+
Δ
W
t
\begin{aligned} &v_{t} = v_{t} + g_{t}^{2} \\ &\Delta \mathcal{W}_{t} = -\frac{\eta}{\sqrt{v_{t} + \epsilon}} * g_{t} \\ &\mathcal{W}_{t + 1} = \mathcal{W}_{t} + \Delta\mathcal{W}_{t} \end{aligned} \tag{20}
vt=vt+gt2ΔWt=−vt+ϵη∗gtWt+1=Wt+ΔWt(20)
其中,
v
t
v_{t}
vt 为累计梯度平方之和,
η
\eta
η 为初始学习率,
ϵ
\epsilon
ϵ 是一个很小的正数,防止分母为
0
0
0。
通过将当前参数对应的梯度除以累积梯度和,来对学习率进行适应性调整:
- 当梯度变化大时,累积梯度较小,此时学习率较大,通常这是处于训练初期,距离最优解还有较远的距离,通过较大的学习率允许以更大的步幅进行下降,在训练初期快速接近最优解
- 训练后期,累积梯度较大,此时学习率较小,将会抑制下降,因为此时已经接近最优解了,所以降低步幅,缓慢接近
但与此同时带来的缺点则是:累积梯度平方会导致学习率过早或者过量的减少,导致后期训练速度缓慢,甚至当学习率趋于0的时候,无法得到最优解
RMSProp 均方根传播
同样是希望抑制震荡,但是RMSProp和SGD的不同之处在于:
- 不同的方法和动量的使用
- 自动调整学习率
- 为每个参数选取不同的学习率
而相对于Adagrad来说,RMSProp计算梯度平方的指数平均,而不是之前的梯度累积。
首先,计算梯度平方的指数平均:
(21)
v
t
=
p
v
t
−
1
+
(
1
−
p
)
∗
g
t
2
v_{t} = pv_{t-1} + (1 - p) * g_{t}^{2} \tag{21}
vt=pvt−1+(1−p)∗gt2(21)
v
j
v_{j}
vj 为所得的指数平均,梯度
g
t
g_{t}
gt 对应的是当前参数上的梯度投影(为每个参数选择不同的学习率)。
然后根据指数平均确定更新的步幅大小:
(22)
Δ
W
t
=
−
η
v
t
+
ϵ
∗
g
t
\Delta W_{t} = -\frac{\eta}{\sqrt{v_{t} + \epsilon}} * g_{t} \tag{22}
ΔWt=−vt+ϵη∗gt(22)
其中,
η
\eta
η 为设置的初始学习率(全局变量)。可以看出,对于梯度指数平均较大的梯度方向(即震动较大的方向)来说,得到的学习步幅
Δ
W
t
\Delta W_{t}
ΔWt 也就越小,例如前面图片例子中的
w
1
w_{1}
w1 方向,这样就能够抑制这种震荡,更加快速的往最小值方向移动,而当接近最小值的时候,会降低步幅,防止跳过最小值。
最后,更新参数:
(23)
W
t
+
1
=
W
t
+
Δ
W
t
\mathcal{W}_{t + 1} = \mathcal{W}_{t} + \Delta \mathcal{W}_{t} \tag{23}
Wt+1=Wt+ΔWt(23)
Adam 优化器
即 Adaptive Moment Optimization 自适应动量优化器,结合了动量和 RMSProp的启发式算法:
(24)
v
t
=
β
1
∗
v
t
−
1
−
(
1
−
β
1
)
∗
g
t
s
t
=
β
2
∗
s
t
−
1
−
(
1
−
β
2
)
∗
g
t
2
Δ
W
t
=
−
η
v
t
s
t
+
ϵ
∗
g
t
W
t
+
1
=
W
t
+
Δ
W
t
\begin{aligned} &v_{t} = \beta_{1}*v_{t - 1} - (1 - \beta_{1})*g_{t} \\ &s_{t} =\beta_{2}*s_{t - 1} - (1 - \beta_{2})*g_{t}^{2} \\ &\Delta W_{t} = -\eta\frac{v_{t}}{\sqrt{s_{t} + \epsilon}} * g_{t} \\ &\mathcal{W}_{t+1} = \mathcal{W}_{t} + \Delta\mathcal{W}_{t} \end{aligned} \tag{24}
vt=β1∗vt−1−(1−β1)∗gtst=β2∗st−1−(1−β2)∗gt2ΔWt=−ηst+ϵvt∗gtWt+1=Wt+ΔWt(24)
其中:
- η \eta η:设置的初始学习率
- g t g_{t} gt:在当前对应参数的梯度
- v t v_{t} vt:梯度的指数平均
- s t s_{t} st:梯度平方的指数平均
- β 1 , β 2 , ϵ \beta_{1},\beta_{2},\epsilon β1,β2,ϵ:超参数,一般设置为 β 1 = 0.9 , β 2 = 0.99 , ϵ = 1 e − 10 \beta_{1} = 0.9,\beta_{2} = 0.99,\epsilon = 1e^{-10} β1=0.9,β2=0.99,ϵ=1e−10
可以看到,这里不仅计算了梯度平方的指数平均,还计算了梯度的指数平均,在计算步幅的时候,相比较RMSProp多乘以了一个梯度的指数平均
学习率 Learning Rate
我们反复的提到了学习率的选取,学习率对于模型的性能具有非常显著的影响,同时也是一个非常难以设置的超参数,如何选取一个合适的学习率对于我们的训练模型至关重要。