前言:
本专栏在保证内容完整性的基础上,力求简洁,旨在让初学者能够更快地、高效地入门TensorFlow2 深度学习框架。如果觉得本专栏对您有帮助的话,可以给一个小小的三连,各位的支持将是我创作的最大动力!
系列文章汇总:TensorFlow2 入门指南
Github项目地址:https://github.com/Keyird/TensorFlow2-for-beginner
文章目录
一、损失函数
(1)MSE损失函数
数理统计中均方误差是指参数估计值与参数值之差平方的期望值,记为MSE。MSE是衡量“平均误差”的一种较方便的方法,MSE可以评价数据的变化程度,MSE的值越小,说明预测模型描述实验数据具有更好的精确度。MSE损失函数的计算公式如下:
l o s s = 1 N ∑ ( y − o u t ) 2 loss = \frac{1}{N} \sum(y-out)^2 loss=N1∑(y−out)2
下面以三种不同的方式来分别构建MSE损失函数:
import tensorflow as tf
# 初始化6个数据,限制在0-9,表示类别标签
y = tf.constant([1, 2, 3, 9, 0, 8])
# one-hot编码
y = tf.one_hot(y, depth=10)
# 类型转换
y = tf.cast(y, dtype=tf.float32)
# 随机模拟的输出
out = tf.random.normal([6, 10])
# 构建损失函数
loss_1 = tf.reduce_mean(tf.square(y-out))
loss_2 = tf.square(tf.norm(y-out))/(6*10)
loss_3 = tf.reduce_mean(tf.losses.MSE(y, out))
print(loss_1)
print(loss_2)
print(loss_3)
三种方法的输出结果是一样的:
(2)交叉熵
在线性回归问题中,常常使用MSE作为loss函数,而在分类问题中常常使用交叉熵作为损失函数。交叉熵是信息论中的一个重要概念,主要用于度量两个概率分布间的差异性,当交叉熵越小,表示两个概率分布越相近,即预测的准确度越好。
对于简单的二分类来说,模型最后需要预测的结果只有两种情况,对于每个类别我们的预测得到的概率为 p 和 1-p。此时表达式为:
其中,N是样本数,i表示第i个样本,p是预测的概率值,y是标签值(0或1);
注:对于二分类问题,通常在网络末尾一般采用 sigmoid 或 softmax 激活函数。sigmoid 与 MSE 一起用时,容易导致前期梯度弥散,训练缓慢,所以通常用交叉熵作为二分类的损失函数。
对于多分类问题,实际上就是对二分类的扩展:
其中,Li表示第i个样本的损失值,M是类别数,
p
i
c
p_{ic}
pic表示第i个样本属于类别c的预测概率,
y
i
c
y_{ic}
yic是标签值(0或1)。
import tensorflow as tf
loss_1 = tf.losses.categorical_crossentropy([0,1,0,0], [0.25, 0.25, 0.25, 0.25])
loss_2 = tf.losses.categorical_crossentropy([0,1,0,0], [0, 0.99, 0, 0.01])
loss_3 = tf.losses.categorical_crossentropy([0,1,0,0], [0.99, 0, 0, 0.01])
print(loss_1)
print(loss_2)
print(loss_3)
对于上述三种的预测值和真实值进行求交叉熵,得到如下结果:
很明显,loss_2预测是对的,所以损失函数最小;loss_3预测是错误的,所以损失函数很大。这也刚好印证了交叉熵损失函数的合理性。
(3)交叉熵与 softmax 多分类
在计算交叉熵损失函数时,为了防止训练中数值不稳定,一般将 Softmax 函数与交叉熵函数统一实现。下面是一个5分类的例子,具体使用方法如下:
import tensorflow as tf
from tensorflow.keras import Sequential, layers
# 构建网络
model = Sequential([
layers.Dense(500, activation=tf.nn.relu), # 创建隐藏层
layers.Dense(5, activation=None), # 创建输出层
])
# 模拟输入
x = tf.random.normal([2, 28*28])
# 输出 [2,5]
logits = model(x)
# 方案一:softmax与crossentropy融合,训练时数值稳定
y = tf.constant([[0, 1, 0, 0, 0], [0, 0, 1, 0, 0]])
loss_1 = tf.reduce_mean(tf.losses.categorical_crossentropy(y, logits, from_logits=True))
print(loss_1)
# 方案二:softmax与crossentropy融合,训练时数值稳定
y_true = tf.constant([1, 2])
y_true = tf.one_hot(y_true, depth=5) # one_hot编码
loss_2 = tf.reduce_mean(tf.losses.categorical_crossentropy(y_true, logits, from_logits=True))
print(loss_2)
这两种方法等价,注意最后在categorical_crossentropy()中比较的是经过one_hot编码的标签,输出结果如下:
如果,要将softmax和交叉熵损失函数分开来计算也是可以的,但训练过程中可能会出现数值不稳定的情况。具体的实现过程如下所示:
import tensorflow as tf
from tensorflow.keras import Sequential, layers
# 构建网络
model = Sequential([
layers.Dense(500, activation=tf.nn.relu), # 创建隐藏层
layers.Dense(5, activation=None), # 创建输出层
])
# 模拟输入
x = tf.random.normal([2, 28*28])
# 输出 [2,5]
logits = model(x)
# y_true
y_true = tf.constant([1, 2])
y_true = tf.one_hot(y_true, depth=5) # one_hot编码
# 方案三:softmax与crossentropy是分开的,数值不稳定
predict = tf.math.softmax(logits, axis=1)
loss_3 = tf.reduce_mean(tf.losses.categorical_crossentropy(y_true, predict, from_logits=False))
print(loss_3)
注:相比于SME,交叉熵在网络前期(损失误差很大时)收敛快,更适合应用于分类问题中。
二、梯度下降
深度学习网络训练过程可以分成两大部分:前向计算过程与反向传播过程。前向计算过程,是指通过我们预先设定好的卷积层、池化层等等,按照规定的网络结构一层层前向计算,得到预测的结果。反向传播过程,是为了将设定的网络中的众多参数一步步调整,使得预测结果能更加贴近真实值。
那么,在反向传播过程中,很重要的一点就是:参数如何更新?或者问的更具体点:参数应该朝着什么方向更新?
显然,参数应该是朝着目标损失函数下降的方向更新,更确切的说,要朝着梯度下降最快的方向更新! 假设网络参数是
θ
\theta
θ,学习率是
η
\eta
η,网络表示的函数是
J
(
θ
)
J(\theta)
J(θ),函数此时对
θ
\theta
θ的最大梯度为:
▽
θ
J
(
θ
)
\bigtriangledown_{\theta }J(\theta)
▽θJ(θ),于是参数
θ
\theta
θ 的更新公式可表示为:
在深度学习中,有三种最基本的梯度下降算法:SGD、BGD、MBGD,他们各有优劣。
(1)随机梯度下降法 SGD
随机梯度下降法 (Stochastic Gradient Descent,SGD),每次迭代(更新参数)只使用单个训练样本 ( x ( i ) , y ( i ) ) (x^{(i)}, y^{(i)}) (x(i),y(i)),其中x是输入数据,y是标签。因此,参数更新表达式如下:
优缺点分析:SGD 一次迭代只需对一个样本进行计算,因此速度很快,还可用于在线学习。由于单个样本的随机性,实际过程中,目标损失函数值会剧烈波动,一方面,SGD 的波动使它能够跳到新的和可能更好的局部最小值。另一方面,这最终会使收敛复杂化到精确的最小值,因为 SGD 将继续超调。
(2)批量梯度下降法 BGD
批量梯度下降法 (Batch Gradient Descent,BGD),每次迭代更新中使用所有的训练样本,参数更新表达式如下:
优缺点分析:BGD能保证收敛到凸误差表面的全局最小值和非凸表面的局部最小值。但每迭代一次,需要用到训练集中的所有数据,如果数据量很大,那么迭代速度就会非常慢。
(3)小批量梯度下降法 MBGD
小批量梯度下降法 (Mini-Batch Gradient Descent,MBGD),折中了 BGD 和 SGD 的方法,每次迭代使用 batch_size 个训练样本进行计算,参数更新表达式如下:
优缺点分析:因为每次迭代使用多个样本,所以 MBGD 比 SGD 收敛更稳定,也能避免 BGD 在数据集过大时迭代速度慢的问题。因此,MBGD是深度学习网络训练中经常使用的梯度下降方法。
注: 通常所说的术语SGD其实也包含了MBGD,因为在TensorFlow中,我们通常直接调用优化器SGD,同时batch_size可以单独进行设置,所以这实际上是小批量梯度下降法 MBGD的做法。
三、优化器选择
在 TensorFlow2 中的 tf.keras.optimizers 库下提供了一些不同的优化器,我们可以选用它们进行参数优化。不同的优化器,参数更新的方式有所差别,具体可根据自己的情况选择。
(1)SGD
在TensorFlow2中,SGD优化器的参数更新表达式如下所示:
其中,
λ
\lambda
λ 表示动量参数momentum;当
λ
=
0
\lambda=0
λ=0时,即是普通的SGD梯度下降。
0
<
λ
<
1
0<\lambda<1
0<λ<1 ,表示带了动量的SGD梯度下降参数更新方式,
λ
\lambda
λ通常取0.9。
普通SGD的缺点:SGD很难在沟壑(即曲面在一个维度上比在另一个维度上弯曲得更陡的区域)中迭代,这在局部最优解中很常见。在这些场景中,SGD在沟壑的斜坡上振荡,同时沿着底部向局部最优方向缓慢前进。为了缓解这一问题,引入了动量momentum。
本质上,当使用动量时,如同我们将球推下山坡。球在滚下坡时积累动量,在途中变得越来越快。同样的事情发生在参数更新上:对于梯度指向相同方向的维度,动量项会增加,而对于梯度改变方向的维度,动量项会减少更新。结果,我们获得了更快的收敛和减少的振荡。
SGD是深度学习中最常用的优化器之一,在TensorFlow2中,其函数定义如下:
tf.keras.optimizers.SGD(learning_rate=0.01, momentum=0.0, nesterov=False, name='SGD', **kwargs)
参数解释:
参数 | 解释 |
---|---|
learning_rate | 学习率,默认值是0.01 |
momentum | 动量,默认值是0.0,即默认SGD模式 |
nesterov | 布尔值。是否应用 Nesterov 动量。默认为False |
name | 应用渐变时创建的操作的可选名称前缀 |
**kwargs | 关键字参数。允许为 { clipnorm, clipvalue, lr, decay}。clipnorm是按范数剪裁梯度;clipvalue是按值剪辑梯度,decay包含在内是为了向后兼容以允许学习率的时间逆衰减。 |
(2)Adagrad
Adagrad 是一种基于梯度的优化算法,它就是这样做的:它使学习率适应参数,对于频繁出现的特征相关联的参数执行较小的更新(即低学习率),对于不常见的特征相关的参数执行较大的更新(即高学习率),因此,它非常适合处理稀疏数据。迪恩等人发现 Adagrad 极大地提高了 SGD 的鲁棒性,并将其用于训练 Google 的大规模神经网络。
参数更新公式:
Adagrad 的主要好处之一是它消除了手动调整学习率的需要。大多数实现使用默认值 0.01 并保留它。
Adagrad 的主要弱点是它在分母中累积平方梯度:由于每个添加项都是正数,因此累积和在训练期间不断增长。这反过来会导致学习率缩小并最终变得无限小,此时算法不再能够获取额外的知识。以下算法旨在解决此缺陷。
Adagrad 是深度学习中的优化器之一,在TensorFlow2中,其函数定义如下:
tf.keras.optimizers.Adagrad(learning_rate=0.001, initial_accumulator_value=0.1, epsilon=1e-07,
name='Adagrad', **kwarg)
参数解释:
参数 | 解释 |
---|---|
learning_rate | 学习率 |
initial_accumulator_value | 累加器的起始值必须是非负的。 |
epsilon | 一个小的浮点值以避免零分母。 |
(3)RMSprop
RMSprop 是 Geoff Hinton 提出的一种未发表的自适应学习率方法。RMSprop 和 Adadelta 都是在同一时间独立开发的,因为需要解决 Adagrad 急剧下降的学习率问题。RMSprop 实际上与 Adadelta 的第一个更新向量相同:
Adadelta 优化器原理具体可见:https://ruder.io/optimizing-gradient-descent/index.html#visualizationofalgorithms
RMSprop 是深度学习中的优化器之一,在TensorFlow2中,其函数定义如下:
tf.keras.optimizers.RMSprop(learning_rate=0.001, rho=0.9, momentum=0.0, epsilon=1e-07, centered=False,
name='RMSprop', **kwargs)
参数解释:
参数 | 解释 |
---|---|
learning_rate | 学习率,默认值是0.001 |
rho | 历史/即将到来的梯度的折扣因子。默认为 0.9 |
momentum | 默认为 0.0 |
epsilon | 数值稳定性的小常数。 |
centered | 布尔值。如果True,梯度被梯度的估计方差归一化;如果为 False,则由未居中的第二个时刻。将此设置为True可能有助于训练,但在计算和内存方面稍贵。默认为False. |
name | 应用渐变时创建的操作的可选名称前缀。 |
**kwargs | 关键字参数。允许为 { clipnorm, clipvalue, lr, decay}。clipnorm是按范数剪裁梯度;clipvalue是按值剪辑梯度,decay包含在内是为了向后兼容以允许学习率的时间逆衰减。 |
(4)Adam
Adam 是另一种参数自适应学习率的方法,相当于 RMSprop + Momentum,利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。公式如下:
m t 、 v t m_t、v_t mt、vt分别是梯度的一阶矩(均值)和二阶矩(非中心方差)的估计值:
通过计算偏差校正的一阶和二阶矩估计来抵消这些偏差:
然后使用这些来更新参数,就像在 Adadelta 和 RMSprop 中看到的那样, Adam 的参数更新公式:
Adam也是深度学习中最常用的优化器之一,在TensorFlow2中,其函数定义如下:
tf.keras.optimizers.Adam(learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07,
amsgrad=False,name='Adam', **kwargs)
参数解释:
参数 | 解释 |
---|---|
learning_rate | 学习率,默认值是0.001 |
beta_1 | 一阶矩估计的指数衰减率,默认为 0.9。 |
beta_2 | 二阶矩估计的指数衰减率,默认为 0.999。 |
epsilon | 数值稳定性的小常数 |
name | 应用渐变时创建的操作的可选名称前缀 |
**kwargs | 关键字参数。允许为 { clipnorm, clipvalue, lr, decay}。clipnorm是按范数剪裁梯度;clipvalue是按值剪辑梯度,decay包含在内是为了向后兼容以允许学习率的时间逆衰减。 |
(5)AdaMax
Adamax 是 Adam 的一种变体,该方法对学习率的上限提供了一个更简单的范围。再TensorFlow2中,具体的函数定义如下:
tf.keras.optimizers.Adamax(learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, name='Adamax',
**kwarg)
参数解释:
参数 | 解释 |
---|---|
learning_rate | 学习率 |
beta_1 | 一阶矩估计的指数衰减率 |
beta_2 | 指数加权无穷范数的指数衰减率。 |
epsilon | 数值稳定性的小常数。 |
参考资料:
- https://tensorflow.google.cn/versions/r2.2/api_docs/python/tf/keras/optimizers/RMSprop
- https://ruder.io/optimizing-gradient-descent/index.html#visualizationofalgorithms
- https://www.cnblogs.com/sun-a/p/13380084.html
- https://www.cnblogs.com/fourmi/p/9938477.html