TensorFlow2 手把手教你避开梯度消失和梯度爆炸
Posted 我是小白呀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TensorFlow2 手把手教你避开梯度消失和梯度爆炸相关的知识,希望对你有一定的参考价值。
TensorFlow2 手把手教你避开梯度消失和梯度爆炸
梯度消失 & 梯度爆炸
输出结果:
vanish: 0.025517964452291125
explode: 37.78343433288728
梯度消失
梯度消失问题 (Vanishing gradient problem). 如果导数小于 1, 随着网络层数的增加梯度跟新会朝着指数衰减的方向前进, 这就是梯度消失.
当导数小于 1 的时候, 层数越多, 梯度就越小, 即梯度消失.
梯度爆炸
梯度爆炸问题 (Exploding gradient problem). 如果导数大于 1, 随着网络层数的增加梯度跟新会朝着指数增加的方向前进, 这就是梯度爆炸.
当导数大于 1 的时候, 层数越多, 梯度就越大, 即梯度爆炸.
张量限幅
通过张量限幅, 我们可以有效解决梯度爆炸问题.
tf.clip_by_value
我们可以通过tf.clip_by_value
函数来实现张量限幅.
格式:
tf.clip_by_value(
t, clip_value_min, clip_value_max, name=None
)
参数:
- t: 传入的张量
- clip_value_min: 下限
- clip_value_max: 上限
- name: 数据名称
例子:
# clip_by_value
a = tf.range(10)
print(a)
b = tf.maximum(a, 2)
print(b)
c = tf.minimum(a, 8)
print(c)
d = tf.clip_by_value(a, 2, 8)
print(d)
输出结果:
tf.Tensor([0 1 2 3 4 5 6 7 8 9], shape=(10,), dtype=int32)
tf.Tensor([2 2 2 3 4 5 6 7 8 9], shape=(10,), dtype=int32)
tf.Tensor([0 1 2 3 4 5 6 7 8 8], shape=(10,), dtype=int32)
tf.Tensor([2 2 2 3 4 5 6 7 8 8], shape=(10,), dtype=int32)
tf.clip_by_norm
tf.clip_by_norm
可以对梯度进行裁剪, 防止梯度爆炸.
格式:
tf.clip_by_norm(
t, clip_norm, axes=None, name=None
)
参数:
- t: 传入的张量
- clip_norm: 定义最大限幅
- axes: 计算尺寸
- name: 数据名称
例子:
# clip_by_normal
a = tf.random.normal([2, 2], mean=10)
print(a)
print(tf.norm(a)) # 范数
b = tf.clip_by_norm(a, 15)
print(b)
print(tf.norm(b)) # 范数
输出结果:
tf.Tensor(
[[ 9.33037 10.703022]
[ 9.788097 9.713704]], shape=(2, 2), dtype=float32)
tf.Tensor(19.793266, shape=(), dtype=float32)
tf.Tensor(
[[7.070867 8.111109 ]
[7.417748 7.3613706]], shape=(2, 2), dtype=float32)
tf.Tensor(15.0, shape=(), dtype=float32)
mnist 展示梯度爆炸
为了实现梯度爆炸, 我们把学习率设为 0.1.
完整代码
# 读取训练集的特征值和目标值
(x, y), _ = tf.keras.datasets.mnist.load_data()
# 转换为0~1的形式
x = tf.convert_to_tensor(x, dtype=tf.float32) / 255
# 转换成one_hot编码
y = tf.one_hot(y, depth=10)
# 批次分割
train_db = tf.data.Dataset.from_tensor_slices((x, y)).batch(256).repeat(30)
def main():
# 生成w1形状为[784, 512]的截断正态分布, 中心为0, 标差为0.1
w1 = tf.Variable(tf.random.truncated_normal([784, 512], stddev=0.1))
# 生成b1形状为[512]初始化为0
b1 = tf.Variable(tf.zeros([512]))
# 生成w2形状为[512, 256]的截断正态分布, 中心为0, 标差为0.1
w2 = tf.Variable(tf.random.truncated_normal([512, 256], stddev=0.1))
# 生成b2形状为[256]初始化为0
b2 = tf.Variable(tf.zeros([256]))
# 生成w3形状为[256, 10]的截断正态分布, 中心为0, 标差为0.1
w3 = tf.Variable(tf.random.truncated_normal([256, 10], stddev=0.1))
# 生成b3形状为[10]初始化为0
b3 = tf.Variable(tf.zeros([10]))
# 优化器
optimizer = tf.keras.optimizers.SGD(learning_rate=0.1) # 梯度下降
for step, (x, y) in enumerate(train_db):
# 把x平铺 [256, 28, 28] => [256, 784]
x = tf.reshape(x, [-1, 784])
with tf.GradientTape() as tape:
# 第一个隐层
h1 = x @ w1 + b1
h1 = tf.nn.relu(h1) # 激活
# 第二个隐层
h2 = h1 @ w2 + b2
h2 = tf.nn.relu(h2) # 激活
# 输出层
out = h2 @ w3 + b3
# 计算损失函数
loss = tf.square(y - out)
loss = tf.reduce_mean(loss)
# 计算梯度
grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
# 调试输出剪切前的范数
print("================before===============")
for g in grads:
print(tf.norm(g))
grads, _ = tf.clip_by_global_norm(grads, 15)
# 调试输出剪切后的范数
print("================after===============")
for g in grads:
print(tf.norm(g))
optimizer.apply_gradients(zip(grads, [w1, b1, w2, b2, w3, b3])) # 跟新权重
if __name__ == '__main__':
main()
输出结果
================before===============
tf.Tensor(5.5961547, shape=(), dtype=float32)
tf.Tensor(0.87258744, shape=(), dtype=float32)
tf.Tensor(7.397964, shape=(), dtype=float32)
tf.Tensor(0.69156337, shape=(), dtype=float32)
tf.Tensor(9.840232, shape=(), dtype=float32)
tf.Tensor(0.8157242, shape=(), dtype=float32)
================after===============
tf.Tensor(5.5961547, shape=(), dtype=float32)
tf.Tensor(0.87258744, shape=(), dtype=float32)
tf.Tensor(7.397964, shape=(), dtype=float32)
tf.Tensor(0.69156337, shape=(), dtype=float32)
tf.Tensor(9.840232, shape=(), dtype=float32)
tf.Tensor(0.8157242, shape=(), dtype=float32)
================before===============
tf.Tensor(18.01539, shape=(), dtype=float32)
tf.Tensor(2.9375393, shape=(), dtype=float32)
tf.Tensor(21.330334, shape=(), dtype=float32)
tf.Tensor(2.1504176, shape=(), dtype=float32)
tf.Tensor(21.820374, shape=(), dtype=float32)
tf.Tensor(2.0918982, shape=(), dtype=float32)
================after===============
tf.Tensor(7.5730414, shape=(), dtype=float32)
tf.Tensor(1.2348388, shape=(), dtype=float32)
tf.Tensor(8.966527, shape=(), dtype=float32)
tf.Tensor(0.90396047, shape=(), dtype=float32)
tf.Tensor(9.172523, shape=(), dtype=float32)
tf.Tensor(0.8793609, shape=(), dtype=float32)
================before===============
tf.Tensor(0.5821787, shape=(), dtype=float32)
tf.Tensor(0.0859229, shape=(), dtype=float32)
tf.Tensor(0.7110027, shape=(), dtype=float32)
tf.Tensor(0.082481824, shape=(), dtype=float32)
tf.Tensor(0.51846975, shape=(), dtype=float32)
tf.Tensor(0.1655324, shape=(), dtype=float32)
================after===============
tf.Tensor(0.5821787, shape=(), dtype=float32)
tf.Tensor(0.0859229, shape=(), dtype=float32)
tf.Tensor(0.7110027, shape=(), dtype=float32)
tf.Tensor(0.082481824, shape=(), dtype=float32)
tf.Tensor(0.51846975, shape=(), dtype=float32)
tf.Tensor(0.1655324, shape=(), dtype=float32)
... ...
以上是关于TensorFlow2 手把手教你避开梯度消失和梯度爆炸的主要内容,如果未能解决你的问题,请参考以下文章