ICode9

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

如何使用梯度裁剪(Gradient Clipping)避免梯度爆炸

2022-03-06 14:03:13  阅读:208  来源: 互联网

标签:Clipping plt Gradient 梯度 train import model history


在这里插入图片描述
给定误差函数,学习率,甚至目标变量的大小,训练神经网络可能变得不稳定。训练期间权重的较大更新会导致数值上溢或下溢,通常称为梯度爆炸(gradients exploding)。

梯度爆炸在递归神经网络中更为常见,例如LSTM,因为梯度的累积在数百个输入时间步长上展开。

梯度爆炸的一种常见且相对容易的解决方案是:在通过网络向后传播误差并使用其更新权重之前,更改误差的导数。两种方法包括:给定选定的向量范数( vector norm)来重新缩放梯度;以及裁剪超出预设范围的梯度值。这些方法一起被称为梯度裁剪(gradient clipping)。

1. 梯度爆炸和裁剪
使用随机梯度下降优化算法训练神经网络。这首先需要在一个或多个训练样本上估算损失,然后计算损失的导数,该导数通过网络反向传播,以更新权重。使用学习率控制的反向传播误差的一小部分来更新权重。

权重的更新可能会很大,以至于权重的数值精度超出或低于该数值精度。权重在上溢或下溢时可以取“NaN”或“Inf”值,但网络将毫无用处,因为信号流过无效权重时永远预测NaN值。权重的上溢或下溢是指网络训练过程的不稳定性,并且由于不稳定的训练过程导致网络无法进行训练,从而导致模型实质上是无用的,因此被称为梯度爆炸。

在给定的神经网络(例如卷积神经网络或多层感知器)中,可能由于配置选择不当而发生梯度爆炸:
学习率选择不当会导致较大的权重更新。
准备的数据有很多噪声,导致目标变量差异很大。
损失函数选择不当,导致计算出较大的误差值。

在递归神经网络(例如长短期记忆网络)中容易出现梯度爆炸。通常,可以通过精心配置网络模型来避免爆炸梯度,例如,选择较小的学习速率,按比例缩放目标变量和标准损失函数。尽管如此,对于具有大量输入时间步长的递归网络,梯度爆炸仍然是一个需要着重考虑的问题。

梯度爆炸的一种常见解决方法是先更改误差导数,然后通过网络反向传播误差导数,然后使用它来更新权重。通过重新缩放误差导数,权重的更新也将被重新缩放,从而大大降低了上溢或下溢的可能性。更新误差导数的主要方法有两种:
梯度缩放(Gradient Scaling)
梯度裁剪(Gradient Clipping)

梯度缩放涉及对误差梯度向量进行归一化,以使向量范数大小等于定义的值,例如1.0。只要它们超过阈值,就重新缩放它们。如果渐变超出了预期范围,则渐变裁剪会强制将渐变值(逐个元素)强制为特定的最小值或最大值。这些方法通常简称为梯度裁剪。

当传统的梯度下降算法建议进行一个非常大的步长时,梯度裁剪将步长减小到足够小,以至于它不太可能走到梯度最陡峭的下降方向的区域之外。

它是一种仅解决训练深度神经网络模型的数值稳定性,而不能改进网络性能的方法。

梯度向量范数或预设范围的值可以通过反复试验来配置,可以使用文献中使用的常用值,也可以先通过实验观察通用向量范数或范围,然后选择一个合理的值。

对于网络中的所有层,通常都使用相同的梯度裁剪配置。不过,在某些示例中,与隐藏层相比,输出层中允许更大范围的误差梯度。

2. TensorFlow.Keras 实现
2.1 梯度范数缩放(Gradient Norm Scaling)
梯度范数缩放:在梯度向量的L2向量范数(平方和)超过阈值时,将损失函数的导数更改为具有给定的向量范数。

例如,可以将范数指定为1.0,这意味着,如果梯度的向量范数超过1.0,则向量中的值将重新缩放,以使向量范数等于1.0。在Keras中通过在优化器上指定 clipnorm 参数实现:

....
opt = SGD(lr=0.01, momentum=0.9, clipnorm=1.0)

2.2 梯度值裁剪(Gradient Value Clipping)
如果梯度值小于负阈值或大于正阈值,则梯度值剪切将损失函数的导数剪切为给定值。例如,可以将范数指定为0.5,这意味着如果梯度值小于-0.5,则将其设置为-0.5,如果梯度值大于0.5,则将其设置为0.5。通过在优化器上指定 clipvalue 参数实现:

...
opt = SGD(lr=0.01, momentum=0.9, clipvalue=0.5)

3. 实例
通过一个简单的MLP回归问题来说明梯度裁剪的作用。

3.1 梯度爆炸 MLP

from sklearn.datasets import make_regression
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
import matplotlib.pyplot as plt
plt.rcParams['figure.dpi'] = 150

# 构造回归问题数据集
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)

# 划分训练集和验证集
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]

# 定义模型
model = Sequential()
model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='linear'))

# 编译模型
model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.01, momentum=0.9))

# 训练模型
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=100, verbose=0)

# 评估模型
train_mse = model.evaluate(trainX, trainy, verbose=0)
test_mse = model.evaluate(testX, testy, verbose=0)
print('Train: %.3f, Test: %.3f' % (train_mse, test_mse))

# 绘制损失曲线
plt.title('Mean Squared Error')
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.legend()
plt.show()

在这种情况下,该模型无法学习,从而导致对NaN值的预测。给定非常大的误差,然后在训练中针对权重更新计算出的误差梯度,模型权重会爆炸。传统的解决方案是使用标准化或归一化来重新调整目标变量。不过,本文使用替代方法–梯度修剪。
在这里插入图片描述
3.2 梯度范数缩放 MLP

from sklearn.datasets import make_regression
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
import matplotlib.pyplot as plt
plt.rcParams['figure.dpi'] = 150

# 构造回归问题数据集
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)

# 划分训练集和验证集
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]

# 定义模型
model = Sequential()
model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='linear'))

# 编译模型
opt = SGD(lr=0.01, momentum=0.9, clipnorm=1.0)
model.compile(loss='mean_squared_error', optimizer=opt)

# 训练模型
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=100, verbose=0)

# 评估模型
train_mse = model.evaluate(trainX, trainy, verbose=0)
test_mse = model.evaluate(testX, testy, verbose=0)
print('Train: %.3f, Test: %.3f' % (train_mse, test_mse))

# 绘制损失曲线
plt.title('Mean Squared Error')
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.legend()
plt.show()

在这里插入图片描述
该图显示了损失在20个epoch内从20000以上的大数值迅速下降到100以下的小数值。
3.3 梯度值裁剪 MLP

from sklearn.datasets import make_regression
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
import matplotlib.pyplot as plt
plt.rcParams['figure.dpi'] = 150

# 构造回归问题数据集
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)

# 划分训练集和验证集
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]

# 定义模型
model = Sequential()
model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='linear'))

# 编译模型
opt = SGD(lr=0.01, momentum=0.9, clipvalue=5.0)
model.compile(loss='mean_squared_error', optimizer=opt)

# 训练模型
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=100, verbose=0)

# 评估模型
train_mse = model.evaluate(trainX, trainy, verbose=0)
test_mse = model.evaluate(testX, testy, verbose=0)
print('Train: %.3f, Test: %.3f' % (train_mse, test_mse))

# 绘制损失曲线
plt.title('Mean Squared Error')
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.legend()
plt.show()

在这里插入图片描述

标签:Clipping,plt,Gradient,梯度,train,import,model,history
来源: https://blog.csdn.net/baicaiBC3/article/details/123309395

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

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

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

ICode9版权所有