Keras中函数与优化器的学习
激活函数
机器学习模型的学习过程就是一个不断地通过数据集来修正自身数学模型中参数的过程。
引入激活函数可以增加神经网络模型的非线性,以便增强对样本非线性关系的拟合能力。如果没有激活函数,那么神经网络的每一层都只相当于矩阵相乘,即便叠加了若干层,也只相当于将这些矩阵连续相乘而已。
激活函数有很多,例如ReLU、Sigmoid、tanh、elu等。
Sigmoid函数
Sigmoid激活函数是一个非线性函数,它的定义域可以是全体实数,而值域却是(0, 1)。也就是说,使用Sigmoid函数通常用在回归预测和二分类(即按照是否大于0.5进行分类)模型的输出层中。
Sigmoid函数的公式如下:
S ( x ) = 1 1 + e − x S(x) = \frac{1}{1+e^{-x}} S(x)=1+e−x1
该函数在python下画图
import numpy as np
from math import *
import matplotlib.pyplot as plt
x = np.arange(-10, 10, 0.005)
y = 1/(1+e**(-x))
plt.xlim(-10, 10)
plt.plot(x, y)
plt.show()
Sigmoid的优点
- 函数曲线平滑,在变量接近0时变化幅度最大,在正无穷与负无穷处它的函数值分别趋近1与0,即S(x)=1与S(x)=0是该函数的两条渐近线。
- 该函数上某一点的倒数非常容易计算。
Sigmoid的缺点
- 函数的计算相对耗时。
- 该函数容易造成梯度弥散。梯度弥散是由于激活函数具有明显的饱和区所造成的。在Sigmoid的函数图像中,我们可以看到,在图像的两侧非常接近渐近线,也就是说即使自变量有再大的变化,函数值的变动也微乎其微的。深度学习神经网络在训练时是通过反向传播算法来修正参数值的,在反向传播的过程中需要计算激活函数的导数。以卷积层为例,一旦卷积核的输出落入激活函数的饱和区,它的梯度将变得非常小,从而影响了整个模型的收敛速度。
防止梯度弥散现象的发生
在神经网络中的BN层就是用来防止此类梯度弥散现象的发生,使反向传播过程的梯度不受函数饱和区的影响。
Softmax激活函数
Softmax在统计学习和深度学习中都有着广泛的运用,它主要用在处理多分类问题中。在多分类场景下,神经网络模型就是一个分类器,而这个分类器最终需要告诉我们,根据输入特征,进行计算后的分类结果是什么。用于表示分类结果的正式这个Softmax激活函数。分类器最后的输出单元需要使用Softmax作为激活函数,它可以用来表明每一个类别对应的输出概率是多少,那么输出概率最大的那个类别自然就是分类器分类后的结果了。
Softmax函数的表达式如下:
S
i
=
e
V
i
∑
j
c
e
V
j
S_i = \frac{e^{V_i}}{\displaystyle\sum_j^c e^{V_j}}
Si=j∑ceVjeVi
其中Vi是分类器前几网络的输出结果;i表示类别索引,总的类别个数为C;Si为当前元素的指数占所有元素指数和的比例。通过计算占总体值比例的方式,Softmax将多分类的输出结果转化为相对概率,从而更容易理解和比较。
简单例子:
在一个多分类问题场景下,分类类别C为3,经过分类器中若干层的特征提取,最后获得对应3个类别的输出值,分别为:
V
=
[
−
1
−
3
0
2
]
V = \left[ \begin{matrix} -1 \\ -3 \\ 0 \\ 2 \end{matrix} \right]
V=⎣⎢⎢⎡−1−302⎦⎥⎥⎤
经过Softmax进行激活后,这些数值可以转化为相对概率。
S
=
[
0.042
0.006
0.114
0.839
]
S = \left[ \begin{matrix} 0.042 \\ 0.006 \\ 0.114 \\ 0.839 \end{matrix} \right]
S=⎣⎢⎢⎡0.0420.0060.1140.839⎦⎥⎥⎤
使用Numpy可以对Softmax的计算过程如下:
import numpy as np
def softmax(data):
exp_x = np.exp(data)
# np.exp()求e的幂次方
return exp_x / np.sum(exp_x)
if __name__ == '__main__':
data = np.array([-1, -3, 0, 2])
print(softmax(data))
ReLU函数
ReLU激活函数,即修正线性单元(Rectified linear unit, ReLU),是一种在深度学习中非常广泛的激活函数,由Hinton教授团队提出。这个激活函数具有仿生学原理。
该函数的公式
f
(
x
)
=
{
x
,
x
≥
0
0
,
x
<
0
f(x)=\left\{ \begin{aligned} & x,& & {x\geq 0} \\ & 0, & & {x < 0} \end{aligned} \right.
f(x)={x,0,x≥0x<0
也可以表示为:
R
(
x
)
=
m
a
x
(
0
,
x
)
R(x) = max(0, x)
R(x)=max(0,x)
虽然ReLU函数是一个非线性函数,但是它也是一个分段线性函数。ReLU函数在自量为非正实数的情况下,函数恒值为0,而在自变量为正实数的情况下呈正比例函数。这种现象被称为单侧抑制,可以使神经网路中的神经元具有稀疏激活的特性。人脑的工作方式就具有稀疏激活的特性,科学家估测大脑同时被激活的神经元只有1%~4%,也就是在处理一件事情时,并不是全部的脑神经都要投入进去。而ReLU函数相当于屏蔽了一般的激活(理想情况下),从而实现单侧抑制。
通过这种系数激活的工作方式,让激活时更加“专一”,使模型能够更好地挖掘相关特征。同时,ReLU函数不存在饱和区,也就不会造成如Sigmoid函数那样的梯度弥散问题,从而加快了模型训练时的收敛速度。
Keras激活函数的使用
示例(卷积层中指定激活函数为ReLU函数):
from keras.models import Sequential
from keras.layers import Conv2D
from keras.layers import MaxPool2D
from keras.layers import Dense
input_shape = (64, 64, 3)
model = Sequential()
# ReLU函数的卷积层
model.add(Conv2D(kernel_size=(9, 9), activation='relu', filters=48, strides=(4, 4), input_shape=(64, 64, 3)))
model.add(MaxPool2D((3, 3), strides=(2, 2), padding='same'))
model.add(Conv2D(strides=(1, 1), kernel_size=(3, 3), activation="relu", filters=128))
model.add(Conv2D(strides=(1, 1), kernel_size=(3, 3), activation="relu", filters=128))
model.add(MaxPool2D((3, 3), strides=(2, 2), padding='same'))
# sigmoid函数的全连接层
model.add(Dense(64, activation="sigmoid"))
# 查看模型
model.summary()
sigmoid、softmax、ReLU这些都是Keras预定义的激活函数,可直接使用。Keras还有其它预定义的激活函数,包括数线性单元elu、可伸缩的指数性单元SELU、双曲正切激活函数tanh、Softplus激活函数、Softsign激活函数、Hard sigmoid激活函数,以及高级激活函数,如带泄漏的修正线性单元LeakyReLU、参数化的修正线性单元PReLU。
优化器
机器学习模型的实现原理,就是通过输入和输出数据,来找到一个能够推测出因果关系的模型。这个模型实际上时一个非常复杂的数学函数式,函数的参数便是输入数据的特征值,输出的结果便是预测值。机器学习模型训练过程,就是不断地通过大量的训练样本,将这个复杂的数学模型参数不断地进行修正,从而达到一个最佳的预测效果。
要想获得更好的预测结果,就要不断地更正模型中的参数,寻找使得模型预测效果最佳时的参数。这是一个最优化的数学问题,优化器就是就时帮助我们找到全局最优解的工具。
SGD优化器
SGD即随机梯度下降(Stochastic Gradient Descent),是梯度下降算法的一种实现。在求解损失函数的最小值时,可以通过梯度下降法来一步步地迭代求解,从而得到最小化的损失函数和最佳的模型参数值。
想要获得的预测结果最佳的模型的损失函数一定是最小的,优化器的作用时在模型的实际训练中,就是为了尽量地寻找损失函数的全局最小值。
Keras官方示例代码
from keras import optimizers
from keras.layers import Dense
from keras import Sequential
from keras.layers import Activation
model = Sequential()
model.add(Dense(64, kernel_initializer='uniform', input_shape=(10, )))
model.add(Activation('tanh'))
model.add(Activation('softmax'))
sgd = optimizers.SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='mean_squared_error', optimizer=sgd)
其中SGD的参数含义如下:
- lr:学习率,是一个非负数。
- momentum:扩展功能,表示动量优化,用于加速SGD在相关方向上前进,并抑制震荡,是一个非负数。
- decay:扩展功能,表示每次参数更新后的学习率衰减参数,是一个非负数。
- nesterov:扩展功能,是一个布尔值,表示是否使用Nestrov动量(NAG)优化。
学习率
控制模型的学习速度。
空 | 学习率大 | 学习率小 |
---|---|---|
学习速度 | 快 | 慢 |
使用时间 | 刚开始训练时 | 刚开始训练时 |
缺点 | 1.易损失值爆炸;2.易振荡。 | 1.易过拟合;2.收敛速度慢。 |
在训练过程中,一般根据训练轮数设置动态变化的学习率。
刚开始训练时:学习率以 0.01 ~ 0.001 为宜。
一定轮数过后:逐渐减缓。
接近训练结束:学习速率的衰减应该在100倍以上。
Adadelta优化器
Adadelta是一种具有特定参数学习率的优化器,可以根据参数在训练期间的更新频率进行自适应调整。Adadelta是Adagrad的一个更具有鲁棒性的扩展版本,它不是积累过去所有的梯度,而是根据渐变更新的移动窗口来调整学习率。
对于这种能够自适应调节学习率的优化器,Keras推荐使用默认数值即可,不建议盲目地修改超参数。
损失函数
损失函数(loss function)用于度量模型一次预测结果的好坏。以监督学习为例,选取某一个模型f作为决策函数,对于给定的输入X,则由f(X)给出相应的预测结果。这个输出的预测结果f(X)与真实值y可能一致,也可能不一致。这时候,我们就需要一种函数来度量模型f预测错误的程度,这个函数就是损失函数,我们可以记为L(y, f(X))。
在Keras中,损失函数是编译模型时所需要的两个重要参数之一,另一个重要参数是优化器。
使用损失函数示例
model.compile(loss='mean_squared_error', optimizer='sgd')
# 或
from keras import losses
model.compile(loss=losses.mean_squared_error,
optimizer='sgd')
均方误差
平方损失函数(quadratic loss function)
L
(
y
,
f
(
X
)
)
=
(
y
−
f
(
X
)
)
2
L(y, f(X)) = (y - f(X))^2
L(y,f(X))=(y−f(X))2
平方损失函数是预测值与真实值之间的偏差平方后的结果。对于多个样本求其损失平均值即为均方误差(mean squared error,MSE)
对于具有N个样本的序列则有均方误差如下:
M
S
E
=
1
N
∑
i
=
1
N
(
y
(
i
)
−
f
(
X
(
i
)
)
)
2
MSE=\frac{1}{N}\displaystyle\sum_{i=1}^N(y^{(i)}-f(X^{(i)}))^2
MSE=N1i=1∑N(y(i)−f(X(i)))2
将均方误差开根号,即为均方根误差(root mean squared error,RMSE)
R
M
S
E
=
M
S
E
=
1
N
∑
i
=
1
N
(
y
(
i
)
−
f
(
X
(
i
)
)
)
2
RMSE=\sqrt {MSE}=\sqrt{\frac{1}{N}\displaystyle\sum_{i=1}^N(y^{(i)}-f(X^{(i)}))^2}
RMSE=MSE=N1i=1∑N(y(i)−f(X(i)))2
如果将均方误差的平方改为取绝对值,则我们又可以得到一个新的损失函数,即平均绝对误差(mean absolute error)
M
A
E
=
1
N
∑
i
=
1
N
∣
y
(
i
)
−
f
(
X
(
i
)
)
∣
MAE=\frac{1}{N}\displaystyle\sum_{i=1}^N|y^{(i)}-f(X^{(i)})|
MAE=N1i=1∑N∣y(i)−f(X(i))∣
均方误差、均方根误差与平均绝对误差都可以用来评价数据的变化程度。其值越小,表明预测模型描述实验数据具有更好的精确度,常作为回归问题的损失函数。
交叉熵损失函数
当一件发生概率很小的事情发生了,我们通常会称其所产生的信息量很大。发生概率越小的事情一旦发生了,其所产生的信息量就会越大。根据这个可以得出信息量的公式:
I
(
x
)
=
l
o
g
1
p
(
x
)
=
−
l
o
g
(
p
(
x
)
)
I(x) = log\frac{1}{p(x)} = -log(p(x))
I(x)=logp(x)1=−log(p(x))
其中x表示某个事件。
信息熵是香农信息论中的另一个重要概念,是衡量某一个系统信息量不确定性程度的量,是一个很抽象的概念。它是该系统中信息量的数学期望,那么可以得到信息熵的表达式。
H
(
X
)
=
−
∑
i
=
1
N
p
(
x
i
)
l
o
g
(
p
(
x
i
)
)
H(X) = -\displaystyle\sum_{i=1}^Np(x_i)log(p(x_i))
H(X)=−i=1∑Np(xi)log(p(xi))
其中N表示该系统中的类别数量。
将信息熵的公式进行变化,得到了交叉熵。
H
(
X
)
=
−
∑
i
=
1
N
p
(
x
i
)
l
o
g
(
q
(
x
i
)
)
H(X) = -\displaystyle\sum_{i=1}^Np(x_i)log(q(x_i))
H(X)=−i=1∑Np(xi)log(q(xi))
交叉熵是KL散度(相对熵)的一部分。交叉熵主要用于度量两个概率分布间的差异性信息。在上式中,p代表正确预测结果的概率分布,q表示使用模型预测结果的概率分布。交叉熵越小则代表分布p与分布q越接近,故可以将该函数作为机器学习的损失函数。
交叉熵损失通常用在分类模型中,例如我们在前面介绍过多分类问题的输出层常常使用Softmax作为激活函数,它所采用的损失函数就是交叉熵损失函数。Softmax的输出是一个序列,表明分类结果为各个类别的概率。通过Softmax激活后的数值表示为该类别的概率,所以分类的真实标签应该是只有一位有效的序列,其他位均为0,[0 1 0 0 0 0 0]或[0 0 0 1 0 0 0]这种形式。那么,输出层采用Softmax作为激活函数hu的啊,交叉熵损失就可以写为:
L
=
1
N
∑
i
L
i
=
1
N
∑
i
−
l
o
g
(
e
V
i
∑
j
e
v
j
)
L = \frac{1}{N}\displaystyle\sum_{i}L_i = \frac{1}{N}\displaystyle\sum_{i} - log(\frac{e^{V_i}}{\displaystyle\sum_{j}e^{v_j}})
L=N1i∑Li=N1i∑−log(j∑evjeVi)
下面举个例子:
某个样本的类别为{A, B, C}三者之一,使用训练好的模型a与模型b分别对其进行预测,预测结果分别为预测值a与预测值b,其预测结果如表所示:
A | B | C | |
---|---|---|---|
真实值 | 0 | 1 | 0 |
预测值a | 0.1 | 0.6 | 0.3 |
预测值b | 0.1 | 0.8 | 0.1 |
模型a对此样本进行预测的交叉熵损失函数值如下:
l
o
s
s
=
−
[
(
0
×
l
o
g
(
0.1
)
+
1
×
l
o
g
(
0.6
)
+
0
×
l
o
g
(
0.3
)
)
]
=
−
l
o
g
(
0.6
)
=
0.222
loss = - [(0\times log(0.1) + 1\times log(0.6) + 0\times log(0.3))] \\=-log(0.6) \\=0.222
loss=−[(0×log(0.1)+1×log(0.6)+0×log(0.3))]=−log(0.6)=0.222
模型b对此样本进行预测的交叉熵损失函数值如下:
l
o
s
s
=
−
[
(
0
×
l
o
g
(
0.1
)
+
1
×
l
o
g
(
0.8
)
+
0
×
l
o
g
(
0.1
)
)
]
=
−
l
o
g
(
0.8
)
=
0.097
loss = - [(0\times log(0.1) + 1\times log(0.8) + 0\times log(0.1))] \\=-log(0.8) \\=0.097
loss=−[(0×log(0.1)+1×log(0.8)+0×log(0.1))]=−log(0.8)=0.097
由于模型b的损失函数值更小,古人为,针对该样本的预测结果,模型b更优秀。