コード例 を検討しています。
勾配が爆発する可能性があるRNNのこのネットワークに勾配クリッピングを適用する方法を知りたいです。
tf.clip_by_value(t, clip_value_min, clip_value_max, name=None)
これは使用できる例ですが、どこで紹介しますか? RNNの定義
lstm_cell = rnn_cell.BasicLSTMCell(n_hidden, forget_bias=1.0)
# Split data because rnn cell needs a list of inputs for the RNN inner loop
_X = tf.split(0, n_steps, _X) # n_steps
tf.clip_by_value(_X, -1, 1, name=None)
しかし、テンソル_Xは入力であり、卒業生ではなくクリップされるため、これは意味がありませんか?
このために独自のオプティマイザーを定義する必要がありますか、それともより簡単なオプションがありますか?
勾配のクリッピングは、勾配を計算した後、それらを適用してモデルのパラメーターを更新する前に行う必要があります。あなたの例では、これらの両方がAdamOptimizer.minimize()
メソッドによって処理されます。
グラデーションをクリップするには、 TensorFlowのAPIドキュメントのこのセクション で説明されているように、グラデーションを明示的に計算、クリップ、および適用する必要があります。具体的には、minimize()
メソッドの呼び出しを次のようなものに置き換える必要があります。
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
gvs = optimizer.compute_gradients(cost)
capped_gvs = [(tf.clip_by_value(grad, -1., 1.), var) for grad, var in gvs]
train_op = optimizer.apply_gradients(capped_gvs)
人気があるように思われるものの、おそらくその全体的な基準で勾配全体をクリップしたいでしょう:
optimizer = tf.train.AdamOptimizer(1e-3)
gradients, variables = Zip(*optimizer.compute_gradients(loss))
gradients, _ = tf.clip_by_global_norm(gradients, 5.0)
optimize = optimizer.apply_gradients(Zip(gradients, variables))
各勾配行列を個別にクリップすると、相対的なスケールが個別に変更されますが、可能性もあります。
optimizer = tf.train.AdamOptimizer(1e-3)
gradients, variables = Zip(*optimizer.compute_gradients(loss))
gradients = [
None if gradient is None else tf.clip_by_norm(gradient, 5.0)
for gradient in gradients]
optimize = optimizer.apply_gradients(Zip(gradients, variables))
これは実際には適切です ドキュメントで説明されています :
Maximize()を呼び出すと、勾配の計算と変数への適用の両方が処理されます。グラデーションを適用する前に処理する場合は、代わりにオプティマイザーを3つの手順で使用できます。
- Compute_gradients()を使用して勾配を計算します。
- 必要に応じてグラデーションを処理します。
- Apply_gradients()で処理されたグラデーションを適用します。
また、この例では、次の3つの手順を使用しています。
# Create an optimizer.
opt = GradientDescentOptimizer(learning_rate=0.1)
# Compute the gradients for a list of variables.
grads_and_vars = opt.compute_gradients(loss, <list of variables>)
# grads_and_vars is a list of tuples (gradient, variable). Do whatever you
# need to the 'gradient' part, for example cap them, etc.
capped_grads_and_vars = [(MyCapper(gv[0]), gv[1]) for gv in grads_and_vars]
# Ask the optimizer to apply the capped gradients.
opt.apply_gradients(capped_grads_and_vars)
ここでMyCapper
は、グラデーションを制限する関数です。便利な関数(tf.clip_by_value()
以外)のリストは here です。
グラデーションクリッピングの概念を理解する(標準)場合:
勾配ノルムが特定のしきい値よりも大きい場合は常に、勾配ノルムをクリップして、しきい値内に収まるようにします。このしきい値は、5
に設定される場合があります。
勾配をgとし、max_norm_thresholdをjとします。
ここで、||g|| >j、次のようにします:
g=(j*g)/ ||g||
これは tf.clip_by_norm
で行われる実装です
IMOの最適なソリューションは、TFの推定子デコレータtf.contrib.estimator.clip_gradients_by_norm
でオプティマイザをラップすることです。
original_optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
optimizer = tf.contrib.estimator.clip_gradients_by_norm(original_optimizer, clip_norm=5.0)
train_op = optimizer.minimize(loss)
この方法では、これを一度定義するだけで、勾配計算のたびに実行する必要はありません。
ドキュメント: https://www.tensorflow.org/api_docs/python/tf/contrib/estimator/clip_gradients_by_norm
勾配クリッピングは、基本的に勾配の爆発または消失の場合に役立ちます。損失が大きすぎると、指数勾配がネットワークを流れ、Nan値が生じる可能性があります。これを克服するために、特定の範囲(-1〜1または条件に応じた任意の範囲)内のグラデーションをクリップします。
clipped_value=tf.clip_by_value(grad, -range, +range), var) for grad, var in grads_and_vars
ここで、grads _and_varsは勾配のペア(tf.compute_gradientsで計算)とそれらが適用される変数です。
クリッピング後、オプティマイザーを使用してその値を適用します。 optimizer.apply_gradients(clipped_value)