勾配クリッピング(Gradient Clipping)は、再帰型ニューラルネットワーク(RNN)における勾配爆発問題を軽減するために使用されるテクニック。
概要
勾配クリッピングは、バックプロパゲーション中に計算される勾配の大きさを制限することで、勾配爆発問題を防ぐ手法。
具体的には、勾配ベクトルの大きさ(ノルム)が事前に設定された閾値を超えた場合に、勾配ベクトルを調整して閾値以下に抑える操作を行う。
LSTMやGRUなどのゲート付きRNNアーキテクチャと組み合わせて使用されることが多い。
勾配爆発問題
時系列データを扱うRNNでは、長期依存関係を学習する際に勾配爆発&勾配消失問題が発生する。
このうち勾配爆発は、BPTT (Backpropagation through time) の過程で、勾配が指数関数的に増大してしまう現象。
勾配爆発が発生すると、モデルのパラメータが不安定になり、学習が困難になる。
勾配クリッピングの手法
主に2つの方法:
- ノルムベースのクリッピング
- 要素ごとのクリッピング
ノルムベースのクリッピング Norm clipping
勾配ベクトル全体のノルムを計算し、それが閾値を超えた場合にベクトル全体をスケーリングする。
勾配の方向を保持しつつ、大きさのみを調整する。
手順:
- 勾配ベクトル $g$ のノルム $||g||$ を計算
- $||g||_ > threshold$ なら、$g = \frac{threshold}{||g||} * g$
# Compute gradients
grads = tf.gradients(loss, vars)
# Compute global norm
global_norm = tf.global_norm(grads)
# Clip global norm
clipped_grads = tf.clip_by_global_norm(grads, clip_norm=1.0)
# Apply clipped gradients
optimizer.apply_gradients(zip(clipped_grads, vars))
要素ごとのクリッピング Value clipping
各勾配要素を個別に指定された範囲内(例:[-threshold, threshold]
)にクリッピングする。
実装が簡単だが、勾配ベクトルの方向が変わる可能性がある。
手順:
各勾配要素 $g_i$ に対して:
g_i = max(min(g_i, threshold), -threshold)
# Compute gradients
grads = tf.gradients(loss, vars)
# Clip gradients
clipped_grads = [tf.clip_by_value(g, -1.0, 1.0) for g in grads]
# Apply clipped gradients
optimizer.apply_gradients(zip(clipped_grads, vars))
勾配クリッピングの利点
- 学習の安定化:勾配爆発を防ぎ、モデルの学習が安定する
- 収束の促進:極端に大きな勾配更新を防ぐことで、最適解への収束を促進
- 汎化性能の向上:過剰な重みの更新を抑制し、過学習を軽減する効果がある
注意点
- 閾値の選択:適切な閾値の選択が重要で、タスクやモデルの構造によって最適値が異なる
- 計算コスト:追加の計算が必要となるため、わずかに学習速度が低下する可能性がある