Path: blob/master/site/ja/guide/advanced_autodiff.ipynb
25115 views
Copyright 2020 The TensorFlow Authors.
高度な自動微分
勾配と自動微分の基礎には、TensorFlow での勾配の計算に必要なものがすべて含まれています。このガイドでは、tf.GradientTape
API のさらに深い、あまり一般的ではない機能に焦点を当てていきます。
セットアップ
記録を停止する
勾配の記録を停止したい場合には、tf.GradientTape.stop_recording
を使用して一時的に記録を停止することができます。
これは、モデルの途中で複雑な演算を微分を行いたくない場合、オーバーヘッドの削減に有用です。これにはメトリックの計算や、中間結果の計算が含まれます。
記録をリセットまたは最初から録画し直す
最初からやり直す場合は、tf.GradientTape.reset
を使用してください。通常は勾配テープブロックを終了して再起動した方が読み取りやすくなりますが、テープブロックの終了が困難または不可能な場合は、reset
メソッドを使用できます。
勾配フローを正確に停止する
上記のグローバルなテープ制御とは対照的に、tf.stop_gradient
関数ははるかに正確です。これはテープ自体にアクセスする必要がなく、勾配が特定のパスに沿って流れることを防ぐために使用できます。
カスタム勾配
場合によっては、デフォルトを使用しないで勾配の計算方法を正確に制御する必要があります。次のような状況がそれに該当します。
記述中の新しい演算に勾配が定義されていない。
デフォルトの計算が数値的に不安定。
フォワードパスから高価な計算をキャッシュしたい。
勾配を変更せずに値を変更したい(
tf.clip_by_value
やtf.math.round
などを使用)。
最初のケースでは、新しい演算子を記述するために、tf.RegisterGradient
を使用して独自の勾配を設定できます(詳細は API ドキュメントを参照)。(勾配レジストリはグローバルなので、注意して変更してください。)
後者の 3 つのケースには、tf.custom_gradient
を使用できます。
以下は、中間勾配に tf.clip_by_norm
を適用した例です。
詳細については、tf.custom_gradient
デコレータの API ドキュメントをご覧ください。
SavedModel のカスタム勾配
注意: この機能は TensorFlow 2.6 以降で利用できます。
カスタム勾配は、tf.saved_model.SaveOptions(experimental_custom_gradients=True)
オプションを使用して、SavedModel に保存することができます。
SavedModel に保存できるようにするには、勾配関数はトレース可能である必要があります(詳細は、「tf.function によるパフォーマンスの改善」ガイドをご覧ください)。
上記の例に関する注意事項: 上記のコードを tf.saved_model.SaveOptions(experimental_custom_gradients=False)
に置き換えても、勾配は読み込み時に同じ結果を生成します。これは、勾配のレジストリに call_custom_op
関数で使用されたカスタム勾配がまだ含まれているためです。ただし、カスタム勾配なしで保存した後にランタイムを再起動すると、読み込まれたモデルを tf.GradientTape
で実行したときに LookupError: No gradient defined for operation 'IdentityN' (op type: IdentityN)
というエラーがスローされます。
複数のテープ
複数のテープはシームレスに連携します。
たとえば、ここでは各テープが異なるテンソルのセットをウォッチしています。
高次勾配
tf.GradientTape
コンテキストマネージャー内の演算は、自動微分のために記録されます。そのコンテキストで勾配が計算される場合は、勾配の計算も記録されます。その結果、まったく同じ API が高次勾配にも機能します。
以下に例を示します。
これにより、スカラー関数の 2 次導関数が得られますが、tf.GradientTape.gradient
はスカラーの勾配のみを計算するため、このパターンはヘッセ行列を生成するための一般化を行いません。ヘッセ行列を作成するには、ヘッセ行列の例(ヤコビアンのセクション)をご覧ください。
「tf.GradientTape.gradient
のネストされた呼び出し」は、勾配からスカラーを計算する場合に適したパターンです。次の例のように、結果のスカラーは 2 番目の勾配計算のソースとして機能します。
例: 入力勾配の正則化
多くのモデルは「敵対的な例」の影響を受けやすいものです。そのような手法の数々は、モデルの入力を変更してモデルの出力を混乱させます。Fast Gradient Signed Method 攻撃を使った敵対的な例といった最も単純な実装は、入力に対する出力の勾配に沿って 1 つのステップを踏む「入力勾配」です。
敵対的な例に対する堅牢性を高める 1 つの手法は、入力勾配のマグニチュードの最小化を試みる、入力勾配の正則化(Finlay & Oberman、2019 年)です。入力勾配が小さければ、出力の変化も小さくなるはずです。
下記は、入力勾配の正則化の単純な実装です。実装は次のとおりです。
内部テープを使用して、入力に対する出力の勾配を計算する。
その入力勾配の大きさを計算する。
モデルに対する大きさの勾配を計算する。
ヤコビアン
前述したすべての例では、複数または単一のソーステンソルに対し、単一のスカラーターゲットの勾配を取得しました。
ヤコビ行列は、ベクトル値関数の勾配を表します。各行には、それぞれベクトルの一要素の勾配が示されます。
tf.GradientTape.jacobian
メソッドを使用すると、ヤコビ行列を効率的に計算することができます。
注意点:
gradient
と同様、sources
引数は単一のテンソル、またはテンソルのコンテナである可能性があります。gradient
と異なり、target
テンソルは単一のテンソルでなければなりません。
スカラーのソース
最初の例は、ここにスカラーソースに対するベクトルターゲットのヤコビアンです。
スカラーに対してヤコビアンをとると、結果はターゲットの形状を持ち、ソースに対する各要素の勾配を表します。
テンソルのソース
入力がスカラーとテンソルのどちらであっても、tf.GradientTape.jacobian
は、単一または複数のターゲットの各要素に対するソースの各要素の勾配を効果的に計算します。
たとえば、このレイヤーの出力の形状は (10, 7)
です。
そしてレイヤーのカーネルの形状は(5, 10)
です。
カーネルの出力のヤコビアンの形状は、次の 2 つの形状を連結したものです。
ターゲットの次元を合計すると、tf.GradientTape.gradient
によって計算された合計の勾配が残ります。
tf.GradientTape
にはヘッセ行列を作成するための明示的なメソッドがありませんが、tf.GradientTape.jacobian
を使用してこれを作成することは可能です。
注意: ヘッセ行列には N**2
個のパラメータが含まれています。これやその他の理由により、ほとんどのモデルには非実用的です。あくまでも GradientTape.jacobian
メソッドの使い方のデモンストレーションとしてこの例を含めましたが、直接のヘッセ行列ベースの最適化を推奨するものではありません。ヘッセ行列のベクトル積はネストされたテープで効率的に計算できるため、2 次最適化の方法としては、はるかに効率的です。
このヘッセ行列をニュートン法のステップに使用するには、まず軸を平坦化して行列にし、勾配を平坦化してベクトルにします。
ヘッセ行列は必ず対称行列になります。
ニュートン法の更新ステップを以下に示します。
注意: 実際に行列を反転させないでください。
これは、単一のtf.Variable
の場合には比較的簡単ですが、これを複雑なモデルに適用するには、完全なヘッセ行列を複数の変数にわたって生成するために、注意深い連結とスライスが必要です。
ヤコビアンをバッチ処理する
場合により、複数のソースの単一スタックに対して、複数のターゲットの各スタックのヤコビアンを取得して、ターゲットとソースの各ペアのヤコビアンを独立させる必要があることがあります。
たとえば、ここでは入力 x
は (batch, ins)
の形状をしており、出力 y
は (batch, outs)
の形状をしています。
x
に対する y
の完全なヤコビアンは、たとえ (batch, ins, outs)
だけが必要な場合でも、(batch, ins, batch, outs)
の形状をしています。
スタック内の各アイテムの勾配が独立している場合は、このテンソルの(batch, batch)
スライスはすべて対角行列になります。
期待どおりの結果を得るためには、重複する batch
次元を合計するか、tf.einsum
を使用して対角を選択します。
そもそも、余分な次元なしで計算を行う方がはるかに効率的です。tf.GradientTape.batch_jacobian
メソッドは、まさにそれを行います。
注意: tf.GradientTape.batch_jacobian
は、ソースとターゲットの最初の次元が一致することのみを検証します。勾配が実際に独立していることは確認しません。意味のある場所でのみ batch_jacobian
を使用できるかは、ユーザー次第です。たとえば、tf.keras.layers.BatchNormalization
を追加すると、batch
次元の全体にわたって正規化するため、独立性は破壊されます。
この場合、batch_jacobian
は引き続き実行され、期待される形状の何かを返しますが、その内容の意味は不明確です。