TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择

前言:

本专栏在保证内容完整性的基础上,力求简洁,旨在让初学者能够更快地、高效地入门TensorFlow2 深度学习框架。如果觉得本专栏对您有帮助的话,可以给一个小小的三连,各位的支持将是我创作的最大动力!

系列文章汇总:TensorFlow2 入门指南
Github项目地址:https://github.com/Keyird/TensorFlow2-for-beginner

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择


文章目录


一、损失函数

(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)

三种方法的输出结果是一样的:

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择


(2)交叉熵

在线性回归问题中,常常使用MSE作为loss函数,而在分类问题中常常使用交叉熵作为损失函数。交叉熵是信息论中的一个重要概念,主要用于度量两个概率分布间的差异性,当交叉熵越小,表示两个概率分布越相近,即预测的准确度越好。

对于简单的二分类来说,模型最后需要预测的结果只有两种情况,对于每个类别我们的预测得到的概率为 p 和 1-p。此时表达式为:
TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择
其中,N是样本数,i表示第i个样本,p是预测的概率值,y是标签值(0或1);

注:对于二分类问题,通常在网络末尾一般采用 sigmoid 或 softmax 激活函数。sigmoid 与 MSE 一起用时,容易导致前期梯度弥散,训练缓慢,所以通常用交叉熵作为二分类的损失函数。

对于多分类问题,实际上就是对二分类的扩展:
TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择
其中,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)

对于上述三种的预测值和真实值进行求交叉熵,得到如下结果:

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择
很明显,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编码的标签,输出结果如下:

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择
如果,要将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 θ 的更新公式可表示为:
TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择

在深度学习中,有三种最基本的梯度下降算法:SGD、BGD、MBGD,他们各有优劣。

(1)随机梯度下降法 SGD

随机梯度下降法 (Stochastic Gradient Descent,SGD),每次迭代(更新参数)只使用单个训练样本 ( x ( i ) , y ( i ) ) (x^{(i)}, y^{(i)}) (x(i),y(i)),其中x是输入数据,y是标签。因此,参数更新表达式如下:

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择
优缺点分析:SGD 一次迭代只需对一个样本进行计算,因此速度很快,还可用于在线学习。由于单个样本的随机性,实际过程中,目标损失函数值会剧烈波动,一方面,SGD 的波动使它能够跳到新的和可能更好的局部最小值。另一方面,这最终会使收敛复杂化到精确的最小值,因为 SGD 将继续超调。

(2)批量梯度下降法 BGD

批量梯度下降法 (Batch Gradient Descent,BGD),每次迭代更新中使用所有的训练样本,参数更新表达式如下:

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择
优缺点分析:BGD能保证收敛到凸误差表面的全局最小值和非凸表面的局部最小值。但每迭代一次,需要用到训练集中的所有数据,如果数据量很大,那么迭代速度就会非常慢。

(3)小批量梯度下降法 MBGD

小批量梯度下降法 (Mini-Batch Gradient Descent,MBGD),折中了 BGD 和 SGD 的方法,每次迭代使用 batch_size 个训练样本进行计算,参数更新表达式如下:

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择
优缺点分析:因为每次迭代使用多个样本,所以 MBGD 比 SGD 收敛更稳定,也能避免 BGD 在数据集过大时迭代速度慢的问题。因此,MBGD是深度学习网络训练中经常使用的梯度下降方法。

注: 通常所说的术语SGD其实也包含了MBGD,因为在TensorFlow中,我们通常直接调用优化器SGD,同时batch_size可以单独进行设置,所以这实际上是小批量梯度下降法 MBGD的做法。


三、优化器选择

在 TensorFlow2 中的 tf.keras.optimizers 库下提供了一些不同的优化器,我们可以选用它们进行参数优化。不同的优化器,参数更新的方式有所差别,具体可根据自己的情况选择。

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择

(1)SGD

在TensorFlow2中,SGD优化器的参数更新表达式如下所示:

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择
其中, λ \lambda λ 表示动量参数momentum;当 λ = 0 \lambda=0 λ=0时,即是普通的SGD梯度下降。 0 < λ < 1 0<\lambda<1 0<λ<1 ,表示带了动量的SGD梯度下降参数更新方式, λ \lambda λ通常取0.9。

普通SGD的缺点:SGD很难在沟壑(即曲面在一个维度上比在另一个维度上弯曲得更陡的区域)中迭代,这在局部最优解中很常见。在这些场景中,SGD在沟壑的斜坡上振荡,同时沿着底部向局部最优方向缓慢前进。为了缓解这一问题,引入了动量momentum。

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择

本质上,当使用动量时,如同我们将球推下山坡。球在滚下坡时积累动量,在途中变得越来越快。同样的事情发生在参数更新上:对于梯度指向相同方向的维度,动量项会增加,而对于梯度改变方向的维度,动量项会减少更新。结果,我们获得了更快的收敛和减少的振荡。

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 的大规模神经网络。

参数更新公式:
TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择

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 的第一个更新向量相同:

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择
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​分别是梯度的一阶矩(均值)和二阶矩(非中心方差)的估计值:

TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择
通过计算偏差校正的一阶和二阶矩估计来抵消这些偏差:
TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择
然后使用这些来更新参数,就像在 Adadelta 和 RMSprop 中看到的那样, Adam 的参数更新公式:
TensorFlow2 入门指南 | 09 损失函数、梯度下降、优化器选择

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 数值稳定性的小常数。

参考资料:

上一篇:TensorFlow2 入门指南 | 10 TensorBoard可视化(选学)


下一篇:Tensorflow2.x代码实现计算Top-k Accuracy