【PyTorch】ONNXエクスポートとTensorRT変換の完全ガイド:よくあるエラーと解決策

問題の概要:PyTorchモデルのONNXエクスポートとTensorRT変換における課題

PyTorchで学習したモデルを本番環境で高速に推論するためには、ONNX形式へのエクスポートと、それをNVIDIAのTensorRTエンジンに変換するプロセスが一般的です。しかし、この変換プロセスでは、初心者から中級者までが頻繁に遭遇する特有のエラーが発生します。具体的には、ONNXエクスポート時の「シンボリックサイズ」に関するエラー、サポートされていないPyTorch演算子のエラー、そしてTensorRT変換時の精度やバージョン不一致によるビルド失敗などが代表的です。本記事では、これらのエラーメッセージを具体的に示しながら、原因と段階的な解決手順を解説します。

原因の解説:なぜ変換エラーが発生するのか

変換エラーの主な原因は以下の3つに大別できます。

1. 動的サイズと静的サイズの不一致

PyTorchモデルは、バッチサイズやシーケンス長などが実行時に決定される「動的サイズ」を扱えますが、ONNXやTensorRTはこれらを「静的サイズ」として固定することを好みます。モデル内のテンソル形状が完全に確定していない場合、エクスポートに失敗します。

2. 演算子のサポート範囲外

PyTorchのすべての演算子(nn.Module)がONNXやTensorRTでサポートされているわけではありません。カスタムレイヤーや、比較的新しいPyTorchバージョンで追加された演算子を使用している場合、変換スクリプトが対応するONNX演算子を見つけられずにエラーとなります。

3. 環境とバージョンの不一致

PyTorch, ONNX, TensorRT, CUDA, cuDNNの各バージョン間には厳密な互換性マトリックスが存在します。公式ドキュメントで確認されていないバージョンの組み合わせを使用すると、予期せぬリンクエラーや実行時エラーが発生します。

解決方法:ステップバイステップでのトラブルシューティング

ステップ1: 環境構築とバージョン確認

まず、互換性のあるバージョンを揃えることが最も重要です。以下のコマンドで環境を確認し、公式の互換性表と照合してください。

# 各ライブラリのバージョン確認
import torch
print(f"PyTorch: {torch.__version__}")
print(f"CUDA Available: {torch.cuda.is_available()}")
print(f"CUDA Version: {torch.version.cuda}")

# ONNXのバージョン確認
import onnx
print(f"ONNX: {onnx.__version__}")

# TensorRTのバージョン確認(インストールされている場合)
# python -c "import tensorrt; print(tensorrt.__version__)"

推奨される安定な組み合わせの一例は、PyTorch 1.12+, ONNX 1.13+, TensorRT 8.5+ です。

ステップ2: PyTorchモデルからONNXへのエクスポート(基本形)

最もシンプルな静的サイズでのエクスポートから始めます。ダミー入力のサイズを固定してください。

import torch
import torch.onnx

# サンプルモデルの定義(例: 簡単なCNN)
class SimpleCNN(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.conv = torch.nn.Conv2d(3, 64, kernel_size=3, padding=1)
        self.relu = torch.nn.ReLU()
        self.pool = torch.nn.AdaptiveAvgPool2d((1, 1))
        self.fc = torch.nn.Linear(64, 10)

    def forward(self, x):
        x = self.conv(x)
        x = self.relu(x)
        x = self.pool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

model = SimpleCNN().eval()
dummy_input = torch.randn(1, 3, 224, 224) # バッチサイズ1, 3チャネル, 224x224画像

# ONNXエクスポート
onnx_model_path = "simple_model.onnx"
torch.onnx.export(
    model,
    dummy_input,
    onnx_model_path,
    input_names=["input"],
    output_names=["output"],
    opset_version=13, # ONNXのオペセットバージョン(11以上を推奨)
)
print(f"Model exported to {onnx_model_path}")

ステップ3: 動的サイズへの対応(よくあるエラーと解決策)

バッチサイズや画像サイズを可変にしたい場合は、dynamic_axes パラメータを使用します。ここで誤りがあると、以下のようなエラーが発生します。

エラーメッセージ例: RuntimeError: Failed to export an ONNX attribute 'onnx::Gather', since it's not constant, please try to make things (e.g., kernel size) static if possible

このエラーは、モデル内でテンソルの形状を取得(Gather)して演算に使用している部分が、ONNXでは定数として扱えない場合に発生します。解決策は、動的軸を明示的に定義することです。

# 動的バッチサイズと動的画像サイズに対応したエクスポート
dynamic_onnx_path = "dynamic_model.onnx"
torch.onnx.export(
    model,
    dummy_input,
    dynamic_onnx_path,
    input_names=["input"],
    output_names=["output"],
    opset_version=13,
    dynamic_axes={
        'input': {0: 'batch_size', 2: 'height', 3: 'width'}, # 入力テンソルの動的軸
        'output': {0: 'batch_size'} # 出力テンソルの動的軸
    }
)

ステップ4: ONNXモデルの検証と簡略化

エクスポートしたONNXモデルが有効か検証し、最適化します。onnx-simplifier は変換エラーを減らすのに極めて有効です。

# ターミナルで実行
# pip install onnx-simplifier
python -m onnxsim simple_model.onnx simple_model_simplified.onnx

# ONNXモデルの検証と基本情報表示
import onnx
model = onnx.load("simple_model_simplified.onnx")
onnx.checker.check_model(model)
print(f"モデル入力: {model.graph.input}")
print(f"モデル出力: {model.graph.output}")

ステップ5: TensorRTエンジンへの変換(trtexec使用)

ONNXモデルをTensorRTエンジン(.planファイル)に変換します。TensorRTがインストールされた環境で、コマンドラインツールtrtexecを使用するのが一般的です。

# 基本的な変換コマンド(バッチサイズ固定)
trtexec --onnx=simple_model_simplified.onnx 
        --saveEngine=model_fp32.plan 
        --workspace=1024

# 動的サイズに対応した変換(最小、最適、最大サイズを指定)
trtexec --onnx=dynamic_model.onnx 
        --saveEngine=model_dynamic.plan 
        --minShapes=input:1x3x224x224 
        --optShapes=input:8x3x224x224 
        --maxShapes=input:32x3x224x224 
        --workspace=2048

# FP16精度で変換(推論速度向上)
trtexec --onnx=simple_model_simplified.onnx 
        --saveEngine=model_fp16.plan 
        --fp16 
        --workspace=1024

よくあるエラー: ERROR: INVALID_ARGUMENT: getPluginCreator could not find plugin ... version 1
このエラーは、ONNXモデル内の特定の演算子(プラグイン)が現在のTensorRTバージョンでサポートされていないことを示します。ONNXモデルを簡略化したり、サポートされているオペセットバージョン(例: opset 13)で再エクスポートすることで解決することが多いです。

ステップ6: PythonからTensorRTエンジンの読み込みと実行

生成されたエンジンファイルをPythonで読み込み、推論を実行します。

import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
import numpy as np

TRT_LOGGER = trt.Logger(trt.Logger.WARNING)

def load_engine(engine_path):
    with open(engine_path, "rb") as f, trt.Runtime(TRT_LOGGER) as runtime:
        return runtime.deserialize_cuda_engine(f.read())

engine = load_engine("model_fp32.plan")
context = engine.create_execution_context()

# 入出力バッファの確保
inputs, outputs, bindings = [], [], []
stream = cuda.Stream()

for binding in engine:
    size = trt.volume(engine.get_binding_shape(binding))
    dtype = trt.nptype(engine.get_binding_dtype(binding))
    host_mem = cuda.pagelocked_empty(size, dtype)
    device_mem = cuda.mem_alloc(host_mem.nbytes)
    bindings.append(int(device_mem))
    if engine.binding_is_input(binding):
        inputs.append({'host': host_mem, 'device': device_mem})
    else:
        outputs.append({'host': host_mem, 'device': device_mem})

# 推論の実行
input_data = np.random.random((1, 3, 224, 224)).astype(np.float32)
np.copyto(inputs[0]['host'], input_data.ravel())
cuda.memcpy_htod_async(inputs[0]['device'], inputs[0]['host'], stream)
context.execute_async_v2(bindings=bindings, stream_handle=stream.handle)
cuda.memcpy_dtoh_async(outputs[0]['host'], outputs[0]['device'], stream)
stream.synchronize()

output = outputs[0]['host'].reshape(1, 10)
print(f"推論結果の形状: {output.shape}")

まとめ・補足情報

PyTorchモデルからONNXを経てTensorRTエンジンに変換するプロセスは、環境構築、モデル設計、変換設定の3つが鍵となります。最初は必ずシンプルなモデルと固定サイズで成功させ、その後で動的サイズやFP16精度などの高度な最適化に進むことをお勧めします。エラーが発生した場合は、以下のチェックリストを確認してください。

  1. 環境バージョン: PyTorch, ONNX, TensorRT, CUDAの互換性を公式ドキュメントで再確認。
  2. モデル単純化: onnx-simplifier を必ず適用する。
  3. 演算子サポート: 使用しているPyTorch演算子がONNX/TensorRTでサポートされているか確認。サポート外の場合は、同等の機能を持つサポート済み演算子の組み合わせで実装し直す必要がある。
  4. エラーメッセージの詳細: エラーメッセージは最初の数行だけでなく、最後まで読み、キーワード(例: “Gather”, “Plugin”, “opset”)を手がかりに調査する。

この変換パイプラインを確立することで、訓練環境であるPyTorchの柔軟性と、本番推論環境であるTensorRTの高速性を両立させることが可能になります。

この記事は役に立ちましたか?