1. 程式人生 > >TensorFlow教程——梯度爆炸與梯度裁剪

TensorFlow教程——梯度爆炸與梯度裁剪

在較深的網路,如多層CNN或者非常長的RNN,由於求導的鏈式法則,有可能會出現梯度消失(Gradient Vanishing)或梯度爆炸(Gradient Exploding )的問題。(這部分知識後面補充)

原理

問題:為什麼梯度爆炸會造成訓練時不穩定而且不收斂?
梯度爆炸,其實就是偏導數很大的意思。回想我們使用梯度下降方法更新引數:

(1)w1=w1αJ(w)w1w2=w2αJ(w)w2

損失函式的值沿著梯度的方向呈下降趨勢,然而,如果梯度(偏導數)很大話,就會出現函式值跳來跳去,收斂不到最值的情況,如圖:

這裡寫圖片描述

當然出現這種情況,其中一種解決方法是,將學習率α設小一點,如0.0001。

這裡介紹梯度裁剪(Gradient Clipping)的方法,對梯度進行裁剪,論文提出對梯度的L2範數進行裁剪,也就是所有引數偏導數的平方和再開方。

g1=J(w)w1g2=J(w)w2,設定裁剪閾值為cg2=g12+g22

g2大於c時:

g=cg2g

g2小於等於c時:g不變。

其中,cg2是一個標量,大家有沒有覺得這個跟學習率

α很類似?

TensorFlow程式碼

方法一:

optimizer = tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.5)
grads = optimizer.compute_gradients(loss)
for i, (g, v) in enumerate(grads):
    if g is not None:
        grads[i] = (tf.clip_by_norm(g, 5), v)  # 閾值這裡設為5
train_op = optimizer.apply_gradients(grads)

其中
optimizer.compute_gradients()

返回的是正常計算的梯度,是一個包含(gradient, variable)的列表。

tf.clip_by_norm(t, clip_norm)返回裁剪過的梯度,維度跟t一樣。

不過這裡需要注意的是,這裡範數的計算不是根據全域性的梯度,而是一部分的。

方法二:

optimizer = tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.5)
grads, variables = zip(*optimizer.compute_gradients(loss))
grads, global_norm = tf.clip_by_global_norm(grads, 5)
train_op = optimizer.apply_gradients(zip(grads, variables))

這裡是計算全域性範數,這才是標準的。不過缺點就是會慢一點,因為需要全部梯度計算完之後才能進行裁剪。

總結

當你訓練模型出現Loss值出現跳動,一直不收斂時,除了設小學習率之外,梯度裁剪也是一個好方法。

然而這也說明,如果你的模型穩定而且會收斂,但是效果不佳時,那這就跟學習率和梯度爆炸沒啥關係了。因此,學習率的設定和梯度裁剪的閾值並不能提高模型的準確率。