TensorFlow教程——梯度爆炸與梯度裁剪
阿新 • • 發佈:2019-01-10
在較深的網路,如多層CNN或者非常長的RNN,由於求導的鏈式法則,有可能會出現梯度消失(Gradient Vanishing)或梯度爆炸(Gradient Exploding )的問題。(這部分知識後面補充)
原理
問題:為什麼梯度爆炸會造成訓練時不穩定而且不收斂?
梯度爆炸,其實就是偏導數很大的意思。回想我們使用梯度下降方法更新引數:
損失函式的值沿著梯度的方向呈下降趨勢,然而,如果梯度(偏導數)很大話,就會出現函式值跳來跳去,收斂不到最值的情況,如圖:
當然出現這種情況,其中一種解決方法是,將學習率設小一點,如0.0001。
這裡介紹梯度裁剪(Gradient Clipping)的方法,對梯度進行裁剪,論文提出對梯度的L2範數進行裁剪,也就是所有引數偏導數的平方和再開方。
讓,,設定裁剪閾值為,。
當大於時:
當小於等於時:不變。
其中,是一個標量,大家有沒有覺得這個跟學習率 很類似?
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()
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值出現跳動,一直不收斂時,除了設小學習率之外,梯度裁剪也是一個好方法。
然而這也說明,如果你的模型穩定而且會收斂,但是效果不佳時,那這就跟學習率和梯度爆炸沒啥關係了。因此,學習率的設定和梯度裁剪的閾值並不能提高模型的準確率。