🎉
ONNXによる前処理の影響の調査
onnxruntime.quantization.preprocessの挙動をresnet18モデルを例に検証。
onnxruntime.quantization.shape_inferenceについて
前処理
前処理は、float32モデルを変換して、量子化に備えることです。次の3つのオプションステップで構成されています:
シンボリック形状の推論。これは変圧器モデルに最適です。
モデルの最適化:このステップでは、ONNXランタイムネイティブライブラリを使用して、計算ノードのマージを含む計算グラフを書き直し、冗長性を排除してランタイムの効率を向上させます。
ONNX形状推論。
https://onnxruntime.ai/docs/performance/model-optimizations/quantization.html
検証
resnet18を作成
import torchvision.models as models
model = models.resnet18(pretrained=False)
model.eval()
dummy_input = torch.randn(1, 3, 224, 224)
torch.onnx.export(model, dummy_input, "resnet18.onnx", verbose=True, input_names=['input'], output_names=['output'])
前処理
!python -m onnxruntime.quantization.preprocess --input resnet18.onnx --output resnet18-infer.onnx
model = onnx.load('resnet18.onnx')
num_layers = len(model.graph.node)
print(f"ブロックの数: {num_layers}")
total_params = sum(np.prod(tensor.dims) for tensor in model.graph.initializer)
print(f"パラメータ数: {total_params}")
model = onnx.load('resnet18.onnx')
num_layers = len(model.graph.node)
print(f"ブロックの数: {num_layers}")
total_params = sum(np.prod(tensor.dims) for tensor in model.graph.initializer)
print(f"パラメータ数: {total_params}")
> ブロックの数: 65
パラメータ数: 11680872
> ブロックの数: 49
パラメータ数: 11680872
前処理を行った結果、ブロック数が65から49に減少
model_1 = onnx.load('resnet18.onnx')
model_2 = onnx.load('resnet18-infer.onnx')
# ノード(ブロック)の名前とタイプを取得
model_1_nodes = {(node.name, node.op_type) for node in model_1.graph.node}
model_2_nodes = {(node.name, node.op_type) for node in model_2.graph.node}
diff_1_to_2 = model_1_nodes - model_2_nodes
diff_2_to_1 = model_2_nodes - model_1_nodes
print("モデル1にはあるがモデル2にはないブロック:")
for node in diff_1_to_2:
print(f"block: {node[0]}, type: {node[1]}")
print("\nモデル2にはあるがモデル1にはないブロック:")
for node in diff_2_to_1:
print(f"block: {node[0]}, type: {node[1]}")
> モデル1にはあるがモデル2にはない層:
block: Identity_2, type: Identity
block: Identity_15, type: Identity
block: Identity_11, type: Identity
block: Identity_6, type: Identity
block: Identity_9, type: Identity
block: Identity_14, type: Identity
block: Identity_12, type: Identity
block: Identity_0, type: Identity
block: Identity_8, type: Identity
block: Identity_7, type: Identity
block: Identity_5, type: Identity
block: Identity_4, type: Identity
block: Identity_1, type: Identity
block: Identity_13, type: Identity
block: Identity_10, type: Identity
block: Identity_3, type: Identity
モデル2にはあるがモデル1にはないブロック:
Identity ブロックは入力をそのまま出力に渡す恒等写像なので、前処理の最適化プロセスによって不要と判断されたらしい。
モデルにIdentityブロックが含まれているかどうかをtorchvizで確認したところ。特にそのようなレイヤーはなかった。ONNX変換時に冗長なブロックが挿入された可能性がある。
import torchvision.models as models
from torchviz import make_dot
model = models.resnet18(pretrained=False)
dummy_input = torch.randn(1, 3, 224, 224)
output = model(dummy_input)
dot = make_dot(output, params=dict(list(model.named_parameters()) + [('input', dummy_input)]))
dot.render('resnet18_visualization', format='png')
追記:
モデル構造の分析に長けたPINTOさんお手製のツールがあるらしく、レイヤーの数やモデルサイズはこちらを使うとより簡単に分析できる
Discussion