PyTorch: Miscellaneousの奥深さ探求 - TorchDynamo APIで精緻なトレース


torch.compiler.TorchDynamo APIs for fine-grained tracing は、PyTorch 2.3 で導入された、TorchDynamo によるトレースを詳細に制御するための API です。TorchDynamo は、PyTorch プログラムを JIT (Just-In-Time) 컴파일し、パフォーマンスを向上させるための内部コンパイラです。

用途

  • TorchDynamo の動作をデバッグする場合
  • カスタムオペレーションやフックを使用するなど、TorchDynamo で処理が難しいコードを扱う場合
  • 特定のコードブロックを TorchDynamo のコンパイルから除外したい場合

API

以下の API が提供されています。

  • torch._dynamo.graph_break: グラフブレークを追加します。
  • torch.compile.allow_in_graph: 指定された呼び出し可能なオブジェクトを TorchDynamo グラフでそのまま処理します。
  • torch._dynamo.disallow_in_graph: 指定されたオペレーションを TorchDynamo グラフで禁止します。
  • torch.compiler.disable:デコレータ付きの関数と、再帰的に呼び出される関数を Dynamo で無効化します。

import torch
import torch.compiler

def my_function(x):
    # TorchDynamo でコンパイルされないコード
    if torch.rand(1) > 0.5:
        x = x + 1

    # TorchDynamo でコンパイルされるコード
    y = x * 2
    return y

@torch.compiler.disable
def main():
    x = torch.randn(10)
    y = my_function(x)
    print(y)

if __name__ == "__main__":
    main()

この例では、my_function の最初の if ステートメントは torch.compiler.disable で無効化されているため、TorchDynamo でコンパイルされません。一方、2番目の y = x * 2 ステートメントは TorchDynamo でコンパイルされます。

  • torch._dynamo.graph_break は、ほとんどの場合不要です。
  • torch._dynamo.disallow_in_graphtorch.compile.allow_in_graph は、高度なユースケースで使用してください。
  • torch.compiler.disable は、デバッグや特定のコードブロックを無効化する場合にのみ使用してください。
  • TorchDynamo を使用する前に、公式ドキュメントをよく読んでください。
  • TorchDynamo はまだ発展途上にあり、すべての PyTorch コードをサポートしているわけではありません。


import torch
import torch.compiler

def my_function(x):
    # TorchDynamo でコンパイルされないコード
    if torch.rand(1) > 0.5:
        x = x + 1

    # TorchDynamo でコンパイルされるコード
    y = x * 2
    return y

@torch.compiler.disable
def main():
    x = torch.randn(10)
    y = my_function(x)
    print(y)

if __name__ == "__main__":
    main()

このコードを実行すると、以下の出力が得られます。

tensor([ 2.0000,  4.0000,  6.0000,  8.0000, 10.0000, 12.0000, 14.0000, 16.0000, 18.0000, 20.0000])

my_function の最初の if ステートメントは torch.compiler.disable で無効化されているため、TorchDynamo でコンパイルされません。そのため、x = x + 1 は実行されず、y = x * 2x の元の値で計算されます。

import torch
import torch.compiler

def my_function(x):
    # TorchDynamo で禁止されているオペレーション
    y = torch.abs(x)
    return y

def main():
    x = torch.randn(10)
    with torch._dynamo.disallow_in_graph(torch.abs):
        y = my_function(x)
    print(y)

if __name__ == "__main__":
    main()

このコードを実行すると、以下のエラーが発生します。

RuntimeError: Cannot convert abs to a native operator

torch.abs オペレーションは torch._dynamo.disallow_in_graph で禁止されているため、TorchDynamo は my_function をコンパイルできません。



@torch.jit.trace デコレータ

  • シンプルなトレース要件に適しており、コードの可読性を維持しやすいです。
  • デコレータ内に torch.jit.ignore を使用して、特定のコードブロックをトレースから除外することができます。
  • @torch.jit.trace デコレータは、個々の関数やモジュールをトレースするためのシンプルで使いやすい方法です。
@torch.jit.trace
def my_function(x):
    # トレースされるコード
    y = x + 1
    return y

x = torch.randn(10)
y = my_function(x)
print(y)

FX

  • より複雑なトレース要件に適しており、TorchScriptよりも柔軟性と制御性に優れています。
  • モデルをシンボリックグラフとして表現し、そのグラフを操作することで、トレースを制御することができます。
  • FX は、PyTorch モデルを操作するためのより高レベルな API です。
import torch.fx as fx

def my_fx_module(x):
    graph = fx.Graph()
    with graph.capture():
        y = x + 1
    return fx.wrap(graph)

x = torch.randn(10)
my_fx_module = my_fx_module(x)
y = my_fx_module(x)
print(y)

カスタムトレース機能

  • 非常に高度なユースケースや、他の方法では実現できないトレース要件に適しています。
  • 低レベルなトレース API を使用して、トレースを完全に制御することができます。
  • 独自のトレース機能を実装することで、完全な制御と柔軟性を手に入れることができます。
import torch

def custom_trace(model, inputs):
    # カスタムトレースロジックを実装
    # ...
    return outputs

x = torch.randn(10)
outputs = custom_trace(model, x)
print(outputs)
  • 完全な制御と柔軟性を必要とする高度なユースケースの場合は、カスタムトレース機能を実装することを検討してください。
  • より複雑なトレース要件や、より多くの制御が必要な場合は、FX を検討してください。
  • シンプルなトレース要件の場合は、@torch.jit.trace デコレータがおすすめです。
  • 公式ドキュメントを参照し、それぞれの代替方法の詳細について確認してください。
  • 選択する前に、それぞれの要件と制約を慎重に評価してください。
  • 各代替方法には、それぞれ長所と短所があります。