ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-06-07 19:58:44  阅读:527  来源: 互联网

标签:TensorFlow2 loss 函数 梯度 09 参数 tf SGD


前言:

本专栏在保证内容完整性的基础上,力求简洁,旨在让初学者能够更快地、高效地入门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数值稳定性的小常数。

参考资料:

标签:TensorFlow2,loss,函数,梯度,09,参数,tf,SGD
来源: https://blog.csdn.net/wjinjie/article/details/117607574

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有