一般に、従来の推論最適化技術は、静的入力形状を有するモデルに対して設計される。 モデルの入力が動的形状である場合、これらの推論最適化技術は有効にならない可能性がある。 実際の生産シナリオでは、動的形状の入力に取り組むために、ますます多くのモデルが必要になります。 動的入力形状を有するモデルの推論性能を最適化する必要性が強くなっている。 このトピックでは、Machine Learning Platform for AI (PAI)-Bladeを使用して、動的入力シェイプでモデルを最適化する方法について説明します。
制限事項
このトピックの手順で使用する環境は、次のバージョン要件を満たす必要があります。
システム環境: LinuxのPython 3.6以降
フレームワーク: PyTorch 1.7.1
デバイスとバックエンド: NVIDIA T4およびCUDA 11.0
推論最適化ツール: PAI-Blade V3.17.0以降
手順
PAI-Bladeを使用して動的入力シェイプを使用してResNet50モデルを最適化するには、次の手順を実行します。
テストデータとモデルを作成します。 このトピックでは、TorchVisionの標準ResNet50モデルを使用します。
手順2: 最適化のためのPAI-Blade設定オブジェクトの作成
ダイナミックシェイプの範囲に基づいてPAI-Blade configオブジェクトを作成します。
blade.optimizeメソッドを呼び出してモデルを最適化し、最適化されたモデルを保存します。元のモデルと最適化モデルの推論速度と推論結果をテストして、生成された最適化レポートの情報を検証します。
PAI-Blade SDKを統合して、推論用に最適化されたモデルをロードします。
ステップ1: 準備をする
モデルとテストデータの事前トレーニング済みパラメーターをダウンロードします。
事前にトレーニングされたパラメーターは、TorchVisionからObject Storage Service (OSS) にダウンロードされています。 これはあなたの時間を節約します。 試験データは、ImageNet-1k検証セットからランダムに選択される。 テストデータは前処理されており、データがダウンロードされた後に直接使用できます。
wget http://pai-blade.oss-cn-zhangjiakou.aliyuncs.com/share/dynamic_ranges_pratice/resnet50-19c8e357.pth -O resnet50-19c8e357.pth wget http://pai-blade.oss-cn-zhangjiakou.aliyuncs.com/share/dynamic_ranges_pratice/imagenet_val_example.pt -O imagenet_val_example.ptモデルを定義し、事前にトレーニングされたパラメーターとテストデータをロードして、TorchScriptモデルを生成します。
import torch import torchvision # Construct a Resnet50 model. model = torchvision.models.resnet50().eval().cuda() # Load the pre-trained parameters. ckpt = torch.load('resnet50-19c8e357.pth') model.load_state_dict(ckpt) # Load the test data. example_input = torch.load('imagenet_val_example.pt').cuda() # Generate a TorchScript model. traced_model = torch.jit.trace(model, example_input).cuda().eval()
ステップ2: 最適化のためのPAI-Blade設定オブジェクトの作成
ダイナミックシェイプの範囲に基づいてPAI-Blade設定オブジェクトを作成できます。 PAI-Bladeは、ランダムな次元でダイナミックな形状範囲をサポートします。 このトピックでは、バッチディメンションを使用します。
ダイナミックシェイプの範囲を定義します。
ダイナミックシェイプの有効な範囲は、次の3つのフィールドで定義する必要があります。
min: 範囲の下限。
max: 範囲の上限。
opts: 特別な最適化が必要な1つ以上の図形。 一般に、指定された形状の入力に対する推論は、最適化されたモデルを使用することによって、より高いレートで加速される。
上記のフィールドを設定するときは、次のルールに注意してください。
min、max、およびoptsフィールドの各グループで指定される図形の数は、モジュール内の入力図形の数と同じである必要があります。
min、max、およびoptsフィールドに指定された各図形グループ内の同じ位置にある数値は、
min_num <= opt_num <= max_numルールに従う必要があります。
次のサンプルコードは、動的シェイプの範囲を定義する方法の例を示しています。
shapes = { "min": [[1, 3, 224, 224]], "max": [[10, 3, 224, 224]], "opts": [ [[5, 3, 224, 224]], [[8, 3, 224, 224]], ] }さらに、PAI-Bladeを使用すると、複数のダイナミックシェイプの範囲を定義できます。 上限および下限が過度に大きい範囲を規定する場合、最適化されたモデルは、推論加速において明確な利点を有さない可能性がある。 過度に大きな範囲を複数の小さな範囲に分割できます。 このようにして、推論速度が加速される。 複数の動的図形の範囲を定義する方法の詳細については、このトピックの「付録: 複数の動的図形の範囲を定義する」を参照してください。
ダイナミックシェイプの定義された範囲に基づいてPAI-Blade構成オブジェクトを作成します。
import blade import blade.torch as blade_torch # The config object related to PAI-Blade Torch. This config object is used to specify the range of dynamic shapes. blade_torch_cfg = blade_torch.Config() blade_torch_cfg.dynamic_tuning_shapes = shapes # The config object related to PAI-Blade. This config object is used to disable FP16 precision verification to achieve an optimal acceleration effect. gpu_config = { "disable_fp16_accuracy_check": True, } blade_config = blade.Config( gpu_config=gpu_config )
ステップ3: PAI-Bladeを使用してモデルを最適化する
blade.optimizeモデルを最適化するメソッドを呼び出します。 次のサンプルコードに例を示します。 詳細については、「Pythonメソッド」をご参照ください。blade_torch_cfgの
with blade_torch_cfg: optimized_model, _, report = blade.optimize( traced_model, # The path of the model. 'o1', # Lossless optimization. config=blade_config, device_type='gpu', # Optimization for GPU devices. test_data=[(example_input,)] # The test data. )モデルを最適化するときは、次の項目に注意してください。
blade.optimizeメソッドの最初の戻り値は、最適化されたモデルを示します。 データ型は元のモデルと同じままです。 この例では、TorchScriptモデルが入力で指定され、最適化されたTorchScriptモデルが返されます。テストデータが、定義したダイナミックシェイプ範囲に属していることを確認します。
最適化の完了後に最適化レポートを表示します。
print("Report: {}".format(report))最適化レポートの例を次に示します。
Report: { "software_context": [ { "software": "pytorch", "version": "1.7.1+cu110" }, { "software": "cuda", "version": "11.0.0" } ], "hardware_context": { "device_type": "gpu", "microarchitecture": "T4" }, "user_config": "", "diagnosis": { "model": "unnamed.pt", "test_data_source": "user provided", "shape_variation": "undefined", "message": "Unable to deduce model inputs information (data type, shape, value range, etc.)", "test_data_info": "0 shape: (1, 3, 224, 224) data type: float32" }, "optimizations": [ { "name": "PtTrtPassFp16", "status": "effective", "speedup": "4.06", "pre_run": "6.55 ms", "post_run": "1.61 ms" } ], "overall": { "baseline": "6.54 ms", "optimized": "1.61 ms", "speedup": "4.06" }, "model_info": { "input_format": "torch_script" }, "compatibility_list": [ { "device_type": "gpu", "microarchitecture": "T4" } ], "model_sdk": {} }最適化レポートは、
PtTrtPassFp16という名前の最適化項目が有効になることを示しています。 モデルの推論時間は、テストデータの推論のために6.55ミリ秒から1.61ミリ秒に4.06時間短縮されます。 上記の最適化レポートは参考用です。 モデルの実際の最適化効果が優勢です。 最適化レポートのフィールドの詳細については、「最適化レポート」をご参照ください。PyTorch関連関数を呼び出して、最適化されたTorchScriptモデルを保存およびロードします。
file_name = "resnet50_opt.pt" # Save the optimized model to a local device. torch.jit.save(optimized_model, file_name) # Load the optimized model from the disk. optimized_model = torch.jit.load(file_name)
ステップ4: モデルの性能と精度を確認する
最適化が完了したら、Pythonスクリプトを実行して最適化レポートの情報を確認できます。
benchmarkメソッドを定義し、モデルを10回ウォームアップしてから、モデルを100回実行して、推論速度を示すモデルの平均推論時間を取得します。import time @torch.no_grad() def benchmark(model, test_data): # Switch the model to the verification mode. model = model.eval() # Warm up the model. for i in range(0, 10): model(test_data) # Run the model in timed mode. num_runs = 100 start = time.time() for i in range(0, num_runs): model(test_data) torch.cuda.synchronize() elapsed = time.time() - start rt_ms = elapsed / num_runs * 1000.0 # Display the results. print("{:.2f} ms.".format(rt_ms)) return rt_msテストデータの複数のグループを異なる形状で定義します。
dummy_inputs = [] batch_num = [1, 3, 5, 7, 9] for n in batch_num: dummy_inputs.append(torch.randn(n, 3, 224, 224).cuda())テストデータのすべてのグループをトラバースし、
benchmarkメソッドを呼び出して元のモデルと最適化されたモデルをテストし、結果を表示します。for inp in dummy_inputs: print(f'--------------test with shape {list(inp.shape)}--------------') print(" Origin model inference cost: ", end='') origin_rt = benchmark(traced_model, inp) print(" Optimized model inference cost: ", end='') opt_rt = benchmark(optimized_model, inp) speedup = origin_rt / opt_rt print(' Speed up: {:.2f}'.format(speedup)) print('')システムは、次の出力と同様の情報を表示します。
--------------test with shape [1, 3, 224, 224]-------------- Origin model inference cost: 6.54 ms. Optimized model inference cost: 1.66 ms. Speed up: 3.94 --------------test with shape [3, 3, 224, 224]-------------- Origin model inference cost: 10.79 ms. Optimized model inference cost: 2.40 ms. Speed up: 4.49 --------------test with shape [5, 3, 224, 224]-------------- Origin model inference cost: 16.27 ms. Optimized model inference cost: 3.25 ms. Speed up: 5.01 --------------test with shape [7, 3, 224, 224]-------------- Origin model inference cost: 22.62 ms. Optimized model inference cost: 4.39 ms. Speed up: 5.16 --------------test with shape [9, 3, 224, 224]-------------- Origin model inference cost: 28.83 ms. Optimized model inference cost: 5.25 ms. Speed up: 5.49出力は、異なる形状の試験データのすべてのグループの試験結果を示す。 最適化されたモデルの推論速度は、元のモデルの5.49倍に3.94されます。 上記の最適化レポートは参考用です。 モデルの実際の最適化効果が優勢です。
手順1で準備したexample_inputテストデータを使用して、最適化モデルの精度を確認します。
origin_output = traced_model(example_input) _, pred = origin_output.topk(1, 1, True, True) print("origin model output: {}".format(pred)) opt_output = optimized_model(example_input) _, pred = origin_output.topk(1, 1, True, True) print("optimized model output: {}".format(pred))システムは次の出力と同様の情報を表示します。
origin model output: tensor([[834]], device='cuda:0') optimized model output: tensor([[834]], device='cuda:0')出力は、元のモデルと最適化されたモデルの両方がexample_inputのテストデータをカテゴリ834として分類することを示しています。
ステップ5: 最適化されたモデルをロードして実行する
検証が完了したら、最適化されたモデルをデプロイできます。 PAI-Bladeは、統合できるPython用のSDKとC ++ 用のSDKを提供しています。 SDK For C ++ の使用方法の詳細については、「SDKを使用して推論用のTensorFlowモデルをデプロイする」をご参照ください。 次のセクションでは、SDK for Pythonを使用してモデルをデプロイする方法について説明します。
オプション: 試用期間中に、次の環境変数設定を追加して、認証の失敗によるプログラムの予期しない停止を防止します。
export BLADE_AUTH_USE_COUNTING=1PAI-Bladeを使用するように認証されます。
export BLADE_REGION=<region> export BLADE_TOKEN=<token>ビジネス要件に基づいて、次のパラメーターを設定します。
<region>: PAI-Bladeを使用するリージョンです。 PAI-BladeユーザーのDingTalkグループに参加して、PAI-Bladeを使用できるリージョンを取得できます。 DingTalkグループのQRコードについては、「アクセストークンの取得」をご参照ください。
<token>: PAI-Bladeを使用するために必要な認証トークン。 PAI-BladeユーザーのDingTalkグループに参加して、認証トークンを取得できます。 DingTalkグループのQRコードについては、「アクセストークンの取得」をご参照ください。
最適化されたモデルをロードして実行します。
推論コードに
import blade.ru ntime.torchを追加します。 これに加えて、PAI-Blade SDKを統合するための追加のコードを記述したり、元の推論コードを変更したりする必要はありません。import torch import blade.runtime.torch # Replace <your_optimized_model_path> with the path of the optimized model. opt_model_dir = <your_optimized_model_path> # Replace <your_infer_data> with the data on which you want to perform inference. infer_data = <your_infer_data> model = torch.jit.load(opt_model_dir) output = model(infer_data)
付録: 動的図形の複数の範囲を定義する
上限および下限が過度に大きい範囲を規定する場合、最適化されたモデルは、推論加速において明確な利点を有さない可能性がある。 過度に大きな範囲を複数の小さな範囲に分割できます。 このようにして、推論速度が加速される。 次のサンプルコードは、動的シェイプの複数の範囲を定義する方法の例を示しています。
shapes1 = {
"min": [[1, 3, 224, 224]],
"max": [[5, 3, 224, 224]],
"opts": [
[[5, 3, 224, 224]],
]
}
shapes2 = {
"min": [[5, 3, 224, 224]],
"max": [[10, 3, 224, 224]],
"opts": [
[[8, 3, 224, 224]],
]
}
shapes = [shapes1, shapes2]次に、このshapes構成を使用して、上記のPAI-Blade configオブジェクトを構築できます。 詳細については、「手順2: 最適化のためのPAI-Blade設定オブジェクトの作成」をご参照ください。