このページは Cloud Translation API によって翻訳されました。
Switch to English

TensorBoard Debugger V2を使用してTensorFlowプログラムの数値問題をデバッグする

NaNが関係する壊滅的なイベントは、TensorFlowプログラム中に発生し、モデルのトレーニングプロセスが機能しなくなることがあります。そのようなイベントの根本的な原因は、特に重要なサイズと複雑さのモデルの場合は、多くの場合あいまいです。このタイプのモデルバグのデバッグを容易にするために、TensorBoard 2.3+(TensorFlow 2.3+とともに)には、Debugger V2と呼ばれる専用のダッシュボードが用意されています。ここでは、TensorFlowで記述されたニューラルネットワークのNaNに関連する実際のバグに対処することにより、このツールを使用する方法を示します。

このチュートリアルで説明する手法は、複雑なプログラムでのランタイムテンソル形状の検査など、他のタイプのデバッグアクティビティに適用できます。このチュートリアルでは、発生頻度が比較的高いNaNに焦点を当てています。

バグを観察する

デバッグするTF2プログラムのソースコードはGitHub入手できます 。サンプルプログラムもtensorflow pipパッケージ(バージョン2.3+)にパッケージ化されており、次の方法で呼び出すことができます。

 python -m tensorflow.python.debug.examples.v2.debug_mnist_v2
 

このTF2プログラムは、多層知覚(MLP)を作成し、 MNISTイメージを認識するようにトレーニングします。この例では、意図的にTF2の低レベルAPIを使用して、カスタムレイヤーコンストラクト、損失関数、およびトレーニングループを定義しています。これは、より柔軟であるがエラーが発生しやすいAPIを使用した方が、使用しますが、 tf.kerasなどの柔軟性の低い高レベルAPI。

プログラムは、各トレーニングステップの後にテスト精度を出力します。コンソールで、最初のステップの後にテストの精度がほぼチャンスレベル(〜0.1)で止まっていることがわかります。これは確かにモデルトレーニングの動作とは異なります。ステップが増加するにつれて、精度は徐々に1.0(100%)に近づくことを期待しています。

 Accuracy at step 0: 0.216
Accuracy at step 1: 0.098
Accuracy at step 2: 0.098
Accuracy at step 3: 0.098
...
 

この問題は、NaNや無限大などの数値の不安定性が原因で発生すると推測されています。ただし、これが実際に事実であることをどのように確認し、数値の不安定性を生成する原因となるTensorFlow演算(op)をどのように見つけるのですか?これらの質問に答えるために、Debugger V2でバグのあるプログラムをインストルメント化しましょう。

デバッガーV2を使用したTensorFlowコードの計測

tf.debugging.experimental.enable_dump_debug_info()は、Debugger V2のAPIエントリポイントです。 1行のコードでTF2プログラムを計測します。たとえば、プログラムの先頭近くに次の行を追加すると、デバッグ情報が/ tmp / tfdbg2_logdirのログディレクトリ(logdir)に書き込まれます。デバッグ情報には、TensorFlowランタイムのさまざまな側面が含まれます。 TF2では、熱心な実行の完全な履歴、 @ tf.functionによって実行されたグラフ作成、グラフの実行、実行イベントによって生成されたテンソル値、およびそれらのイベントのコードの場所(Pythonスタックトレース)が含まれます。デバッグ情報の豊富さにより、ユーザーは不明瞭なバグを絞り込むことができます。

 tf.debugging.experimental.enable_dump_debug_info(
    logdir="/tmp/tfdbg2_logdir",
    tensor_debug_mode="FULL_HEALTH",
    circular_buffer_size=-1)
 

tensor_debug_mode引数は、Debugger V2が各tensor_debug_modeテンソルまたはグラフ内テンソルから抽出する情報を制御します。 “ FULL_HEALTH”は、各浮動型テンソル(たとえば、一般的に見られるfloat32およびあまり一般的でないbfloat16 dtype)に関する次の情報をキャプチャするモードです。

  • DType
  • ランク
  • 要素の総数
  • 浮動型要素の次のカテゴリへの分類:負の有限( - )、ゼロ( 0 )、正の有限( + )、負の無限大( -∞ )、正の無限大( +∞ )、およびNaN

「FULL_HEALTH」モードは、NaNおよび無限大に関連するバグのデバッグに適しています。サポートされている他のtensor_debug_modeについては、以下をご覧ください。

circular_buffer_size多くのテンソルイベントがLOGDIRに保存されているかの引数を制御します。デフォルトは1000で、インストルメント化されたTF2プログラムが終了する前の最後の1000テンソルのみがディスクに保存されます。このデフォルトの動作は、デバッグデータの完全性を犠牲にすることにより、デバッガのオーバーヘッドを削減します。この場合のように完全性を優先する場合は、引数を負の値(たとえば、ここでは-1)に設定して、循環バッファーを無効にすることができます。

debug_mnist_v2の例では、コマンドラインフラグを渡してenable_dump_debug_info()呼び出します。このデバッグインストルメンテーションを有効にして問題のあるTF2プログラムを再度実行するには、次のようにします。

 python -m tensorflow.python.debug.examples.v2.debug_mnist_v2 \
    --dump_dir /tmp/tfdbg2_logdir --dump_tensor_debug_mode FULL_HEALTH
 

TensorBoardでのデバッガーV2 GUIの開始

デバッガーインスツルメンテーションでプログラムを実行すると、/ tmp / tfdbg2_logdirにlogdirが作成されます。 TensorBoardを起動して、次のコマンドでlogdirを指定できます。

 tensorboard --logdir /tmp/tfdbg2_logdir
 

ウェブブラウザで、http:// localhost:6006にあるTensorBoardのページに移動します。 「Debugger V2」プラグインがデフォルトでアクティブになり、次のようなページが表示されます。

デバッガーV2の全画面のスクリーンショット

デバッガーV2 GUIを使用してNaNの根本原因を見つける

TensorBoardのデバッガーV2 GUIは、6つのセクションで構成されています。

  • アラート :この左上のセクションには、インストルメント化されたTensorFlowプログラムからのデバッグデータでデバッガによって検出された「アラート」イベントのリストが含まれています。各アラートは、注意が必要な特定の異常を示しています。私たちのケースでは、このセクションでは、顕著なピンク赤の色で499のNaN /∞イベントを取り上げています。これは、内部テンソル値にNaNや無限大が存在するためにモデルが学習に失敗したという疑念を裏付けています。これらのアラートについては、後で詳しく説明します。
  • Python実行タイムライン :これは、上部中央セクションの上半分です。これは、演算とグラフの熱心な実行の完全な履歴を示します。タイムラインの各ボックスは、演算またはグラフの名前の最初の文字でマークされます(たとえば、「TensorSliceDataset」演算の場合は「T」、「モデル」の場合は「m」 tf.function )。タイムラインの上にあるナビゲーションボタンとスクロールバーを使用して、このタイムラインをナビゲートできます。
  • グラフの実行 :GUIの右上隅にあるこのセクションは、デバッグタスクの中心になります。これには、グラフ内で計算された(つまり、 @tf-function sによってコンパイルされた)すべての浮動dtypeテンソルの履歴が含まれます。
  • グラフ構造 (上中央セクションの下半分)、 ソースコード (左下セクション)、およびスタックトレース (右下セクション)は、最初は空です。それらのコンテンツは、GUIと対話するときに入力されます。これらの3つのセクションは、デバッグタスクでも重要な役割を果たします。

UIの編成に重点を置いたので、次の手順を実行して、NaNが表示された理由の根底に行きましょう。最初に、[アラート]セクションのNaN /∞アラートをクリックします。これにより、グラフ実行セクションの600個のグラフテンソルのリストが自動的にスクロールされ、 Log (自然対数)演算によって生成される「Log:0」というテンソルである#88に焦点が当てられます。 2D float32テンソルの1000個の要素のうち、ピンクがかった赤色が-∞要素を強調しています。これは、NaNまたは無限大を含むTF2プログラムの実行履歴の最初のテンソルです。NaNまたは∞を含まない前に計算されたテンソル。その後計算される多くの(実際には、ほとんどの)テンソルはNaNを含みます。これを確認するには、グラフ実行リストを上下にスクロールします。この観察結果は、 Log opがこのTF2プログラムの数値的な不安定性の原因であることを強く示唆しています。

デバッガーV2:Nan / Infinityアラートとグラフ実行リスト

このLog opが-∞を出力するのはなぜですか?その質問に答えるには、オペレーションへの入力を調べる必要があります。テンソルの名前をクリックする(「ログ:0」)の単純だが有益なビジュアライゼーションれますLogグラフ構造のセクションでそのTensorFlowグラフでオペアンプの周辺を。情報フローの上から下の方向に注意してください。 op自体は中央の太字で示されています。そのすぐ上に、プレースホルダーオペレーションがLogオペレーションへの唯一の入力を提供することがわかります。このLogitsプレースホルダーによって生成されたテンソルは、グラフ実行リストのどこにありますか?視覚的な補助として黄色の背景色を使用すると、 logits:0テンソルがLog:0テンソルの2行上、つまり行85にあることがわかります。

デバッガーV2:グラフ構造ビューと入力テンソルへのトレース

行85の「logits:0」テンソルの数値の内訳をさらに注意深く見ると、そのコンシューマーLog:0が-∞を生成する理由がわかります。「logits:0」の1000個の要素のうち、1つの要素の値が0です。 -∞は、0の自然対数を計算した結果です。 Log opが正の入力のみに公開されるようにすることができれば、NaN /∞の発生を防ぐことができます。これは、プレースホルダーロジットテンソルにクリッピングを適用することで(たとえば、 tf.clip_by_value()を使用して実現できます。

バグの解決に近づいていますが、まだ完了していません。修正を適用するには、PythonソースコードのどこでLog opとそのプレースホルダー入力が発生したかを知る必要があります。デバッガーV2は、グラフ操作と実行イベントをソースまで追跡するためのファーストクラスのサポートを提供します。グラフの実行でLog:0テンソルをクリックすると、スタックトレースセクションにログオペレーションの作成の元のスタックトレースが入力されます。スタックトレースは、TensorFlowの内部コード(gen_math_ops.pyやdumping_callback.pyなど)からの多くのフレームを含むため、多少大きくなりますが、ほとんどのデバッグタスクでは無視できます。対象となるフレームは、debug_mnist_v2.pyの216行目(つまり、実際にデバッグしようとしているPythonファイル)です。 [204行目]をクリックすると、[ソースコード]セクションの対応するコード行が表示されます。

デバッガーV2:ソースコードとスタックトレース

最後に、ロジット入力から問題のあるログオペレーションを作成したソースコードに移動します。これは、 @tf.function装飾され、TensorFlowグラフに変換されたカスタムカテゴリカルエントロピー損失関数です。プレースホルダーop "logits"は、損失関数の最初の入力引数に対応します。 Log opは、tf.math.log()API呼び出しで作成されます。

このバグに対する価値のある修正は、次のようになります。

   diff = -(labels *
           tf.clip_by_value(tf.math.log(logits), 1e-6, 1.))
 

このTF2プログラムの数値の不安定性を解決し、MLPを正常にトレーニングさせます。数値の不安定性を修正する別の可能なアプローチは、 tf.keras.losses.CategoricalCrossentropyを使用することtf.keras.losses.CategoricalCrossentropy

これで、TF2モデルのバグを観察することから、バグを修正するコード変更を思い付くまでの旅が終わります。DebuggerV2ツールを使用すると、数値サマリーを含む、インストルメント化されたTF2プログラムの実行履歴とグラフ実行履歴を完全に可視化できます。テンソル値と、演算、テンソル、およびそれらの元のソースコード間の関連付け

デバッガーV2のハードウェア互換性

デバッガーV2は、CPUやGPUなどの主流のトレーニングハードウェアをサポートしています。 tf.distributed.MirroredStrategyを使用したマルチGPUトレーニングもサポートされています。 TPUのサポートはまだ初期段階であり、呼び出す必要があります

 tf.config.set_soft_device_placement(True)
 

enable_dump_debug_info()呼び出す前。 TPUにも他の制限がある場合があります。 Debugger V2を使用して問題が発生した場合は、 GitHubの問題のページでバグを報告してください

Debugger V2のAPI互換性

デバッガーV2は、TensorFlowのソフトウェアスタックの比較的低いレベルで実装されているため、tensorFlowの下位レベルの上に構築されたtf.kerastf.data 、およびその他のAPIと互換性があります。デバッガV2もTF1と下位互換性がありますが、TF1プログラムによって生成されたデバッグログディレクトリの場合、Eager Execution Timelineは空になります。

API使用のヒント

このデバッグAPIに関してよく尋ねられる質問は、TensorFlowコードのどこにenable_dump_debug_info()への呼び出しを挿入する必要があるenable_dump_debug_info()です。通常、APIはTF2プログラムのできるだけ早い段階で、できればPythonのインポート行の後、グラフの作成と実行を開始する前に呼び出す必要があります。これにより、モデルとそのトレーニングを強化するすべての操作とグラフが完全にカバーされます。

現在サポートされているtensor_debug_modesは以下のとおりです。 NO_TENSORCURT_HEALTHCONCISE_HEALTHFULL_HEALTH 、およびSHAPE 。これらは、各テンソルから抽出される情報の量と、デバッグされるプログラムへのパフォーマンスのオーバーヘッドが異なります。 enable_dump_debug_info()のドキュメントのargsセクションを参照してください]。

パフォーマンスのオーバーヘッド

デバッグAPIは、インストルメント化されたTensorFlowプログラムにパフォーマンスオーバーヘッドをもたらします。オーバーヘッドは、 tensor_debug_mode 、ハードウェアタイプ、およびインストルメント化されたTensorFlowプログラムの性質によって異なります。参照ポイントとして、GPUでは、 NO_TENSORモードは、 Transformerモデルのバッチサイズ64でのトレーニング中に15%のオーバーヘッドを追加します。他のtensor_debug_modeのオーバーヘッドのパーセントは高くなりますCURT_HEALTHCONCISE_HEALTHFULL_HEALTHSHAPEでは約50%モード。 CPUでは、オーバーヘッドはわずかに低くなります。 TPUでは、オーバーヘッドは現在高くなっています。

他のTensorFlowデバッグAPIとの関係

TensorFlowはデバッグ用に他のツールとAPIを提供していることに注意してください。このようなAPIは、APIドキュメントページのtf.debugging.*名前空間で参照できます。これらのAPIのうち、最も頻繁に使用されるのはtf.print()です。デバッガV2をいつ使用し、代わりにtf.print()を使用する必要がありますか? tf.print()は、

  1. 印刷するテンソルを正確に知っています。
  2. ソースコードのどこにこれらのtf.print()ステートメントを挿入するかを正確に知っています。
  3. そのようなテンソルの数は多すぎません。

その他の場合(たとえば、多くのテンソル値の検査、TensorFlowの内部コードによって生成されたテンソル値の検査、上で示したような数値の不安定性の原因の検索)の場合、Debugger V2はデバッグのより高速な方法を提供します。さらに、Debugger V2は、イーガーテンソルとグラフテンソルを検査するための統一されたアプローチを提供します。さらに、グラフの構造とコードの場所に関する情報も提供します。 tf.print()tf.print()機能を超えています。

∞とNaNに関連する問題をデバッグするために使用できる別のAPIは、 tf.debugging.enable_check_numerics()です。 enable_dump_debug_info()とは異なり、 enable_check_numerics()はデバッグ情報をディスクに保存しません。代わりに、TensorFlowの実行中に∞とNaNを監視し、opがこのような不正な数値を生成するとすぐに、元のコードの場所でエラーを出します。 enable_dump_debug_info()と比較してパフォーマンスオーバーヘッドは低くなりますが、プログラムの実行履歴の完全なトレースが提供されず、Debugger V2のようなグラフィカルユーザーインターフェイスが付属しません。