ResNet50 は、古典的で広く使用されているネットワークアーキテクチャです。ResNet50 モデルを最適化することは、多くの推論デプロイメントシナリオにとって有益です。このトピックでは、Blade を使用して TensorFlow ベースの ResNet50 モデルを最適化する方法について説明します。
背景情報
Residual Network (ResNet) は、コンピュータビジョンタスク向けの古典的なニューラルネットワークのバックボーンであり、画像ドメインにおける実用的なディープラーニングモデルの「Hello World」と見なされることがよくあります。ResNet は畳み込み層を使用して画像の特徴を抽出します。残差ブロック構造を導入することで、ResNet は深層ニューラルネットワークのトレーニング時に発生する可能性のある勾配消失問題と勾配爆発問題を解決します。この設計により、深層ニューラルネットワークのトレーニングパフォーマンスが大幅に向上します。一般的な ResNet ネットワークには、ResNet26、ResNet50、ResNet101 があります。
制限事項
この例の環境は、次の要件を満たす必要があります。
-
システム環境: Python 3.6 以降および Compute Unified Device Architecture (CUDA) 10.0 を搭載した Linux。
-
フレームワーク: TensorFlow 1.15。
-
推論最適化ツール: Blade 3.17.0 以降。
操作手順
Blade を使用して TensorFlow ベースの ResNet50 モデルを最適化するには、次の手順に従います。
-
TensorRT 最適化をサポートする Blade WHL パッケージをインストールし、ResNet50 モデルとテストデータをダウンロードします。
-
blade.optimizeAPI を呼び出してモデルを最適化します。 -
最適化レポートの情報を検証するために、最適化前後の推論速度をテストします。
-
Blade Software Development Kit (SDK) を統合して、推論のために最適化されたモデルをロードします。
ステップ 1: 準備
この例では、ResNet50 モデルの主な最適化は TensorRT です。したがって、この機能をサポートする Blade 3.17.0 以降を使用する必要があります。
-
TensorFlow 1.15.0 および CUDA 10.0 に対応する Blade バージョンをインストールします。
pip3 install pai_blade_gpu==3.17.0 -f https://pai-blade.oss-cn-zhangjiakou.aliyuncs.com/release/repo.html -
TensorFlow ResNet50 モデルとその対応するテストデータをダウンロードします。
wget http://pai-blade.cn-hangzhou.oss.aliyun-inc.com/tutorials/tf_resnet50_v1.5.tar.gz -
ダウンロードした tf_resnet50_v1.5.tar.gz パッケージには、ResNet50 モデルの frozen.pb ファイルと、異なるバッチサイズのテストデータが含まれています。パッケージを手動で解凍します。
tar zxvf tf_resnet50_v1.5.tar.gz
ステップ 2: Blade を使用したモデルの最適化
-
前のステップでダウンロードした TAR パッケージから TensorFlow モデルとテストデータを取得します。
import os os.environ["CUDA_VISIBLE_DEVICES"] = "1" import numpy as np import time # import tf to import graphdef model. import tensorflow.compat.v1 as tf import blade from blade.model.tf_model import TfModel def _load_model_and_data(): local_dir = "./tf_resnet50_v1.5/" model_path = os.path.abspath(os.path.join(local_dir, "frozen.pb")) data_path = os.path.abspath(os.path.join(local_dir, "test_bc1.npy")) graph_def = tf.GraphDef() with open(model_path, 'rb') as f: graph_def.ParseFromString(f.read()) test_data = np.load(data_path, allow_pickle=True, encoding='bytes').item() return graph_def, test_data # Let's go! # Load resnet model and test data. graph_def, test_data = _load_model_and_data() print(test_data) -
Blade を使用して TensorRT 最適化を実行します。
TensorRT 最適化は、入力シェイプに基づいて次の 2 種類に分けられます。
-
静的シェイプ最適化
このモードは、モデルリクエストのシェイプが定数の場合に適用されます。たとえば、レイテンシーを削減するために、入力を特定のサイズに制限する場合があります。次のコードは例を示しています。
config = blade.Config() config.gpu_config.aicompiler.enable = False config.gpu_config.disable_fp16_accuracy_check = True config.gpu_config.tensorrt.enable = True # TensorRT optimization is enabled by default, you can also use this param to disable if necessary. # Function `optimize` is the entrance to Blade's one-stop optimization. optimized_model_static, opt_spec_static, report = blade.optimize( graph_def, # The original model, here is a TF GraphDef. 'o1', # Optimization level o1 or o2. device_type='gpu', # Target device to run the optimized model. config=config, # The blade.Config with more detailed optimizations configs outputs=['softmax_tensor'], # Name of outputs nodes. You can provide them or blade will guess. test_data=[test_data] ) print(report)システムは、次のような最適化レポートを返します。
{ "software_context": [ { "software": "tensorflow", "version": "1.15.0" }, { "software": "cuda", "version": "10.0.0" } ], "hardware_context": { "device_type": "gpu", "microarchitecture": "T4" }, "user_config": "", "diagnosis": { "model": "tmp_graph.pbtxt", "test_data_source": "user provided", "shape_variation": "dynamic", "message": "", "test_data_info": "input_tensor:0 shape: (1, 224, 224, 3) data type: float32" }, "optimizations": [ { "name": "Tf2TrtPlus", "status": "effective", "speedup": "3.37", "pre_run": "6.81 ms", "post_run": "2.02 ms" }, { "name": "TfStripUnusedNodes", "status": "effective", "speedup": "na", "pre_run": "na", "post_run": "na" }, { "name": "TfFoldConstants", "status": "effective", "speedup": "na", "pre_run": "na", "post_run": "na" } ], "overall": { "baseline": "6.98 ms", "optimized": "2.11 ms", "speedup": "3.31" }, "model_info": { "input_format": "frozen_pb" }, "compatibility_list": [ { "device_type": "gpu", "microarchitecture": "T4" } ], "model_sdk": {} }最適化中、追加の最適化オプションが提供されていないため、静的シェイプ最適化が有効になります。この最適化には
test_dataのシェイプが使用されます。最適化レポートは、Tf2TrtPlus最適化が有効であることを示しています。静的シェイプ最適化では、推論中の入力シェイプが最適化に使用されたシェイプと一致しない場合、処理は元の TensorFlow グラフの実行にフォールバックします。このフォールバックにより、実行効率が大幅に低下します。最適化結果はあくまでこの例のものであり、実際の結果は異なる場合があります。最適化レポートのフィールドの詳細については、「最適化レポート」をご参照ください。
-
動的シェイプ最適化
デプロイされたサービスが動的バッチ処理をサポートしている場合、サーバーは通常、特定の期間内に受信したリクエストを単一バッチにグループ化します。短期間に受信するリクエスト数は不確実であるため、結果として得られるバッチサイズは変化する可能性があります。これらの変化するシェイプの最適化をサポートするために、Blade は TensorRT の動的シェイプ最適化機能を統合しています。動的入力最適化を有効にするには、TensorRTConfig で追加構成を提供するだけで済みます。次のコードは例を示しています。TensorRTConfig パラメーターの詳細については、「付録: TensorRTConfig」をご参照ください。
config_dynamic = blade.Config() config_dynamic.gpu_config.aicompiler.enable = False config_dynamic.gpu_config.disable_fp16_accuracy_check = True config_dynamic.gpu_config.tensorrt.enable = True config_dynamic.gpu_config.tensorrt.dynamic_tuning_shapes = { "min": [1, 224, 224, 3], "opts": [ [1, 224, 224, 3], [2, 224, 224, 3], [4, 224, 224, 3], [8, 224, 224, 3], ], "max": [8, 224, 224, 3], } # Call Blade's one-stop optimization, with a dynamic shapes setting for TensorRT optimization. optimized_model_dynamic, opt_spec_dynamic, report = blade.optimize( graph_def, 'o1', device_type='gpu', config=config_dynamic, outputs=['softmax_tensor'], test_data=[test_data] ) print(report) with tf.gfile.FastGFile('optimized_model_dynamic.pb', mode='wb') as f: f.write(optimized_model_dynamic.SerializeToString())システムは、次のような最適化レポートを返します。
{ "software_context": [ { "software": "tensorflow", "version": "1.15.0" }, { "software": "cuda", "version": "10.0.0" } ], "hardware_context": { "device_type": "gpu", "microarchitecture": "T4" }, "user_config": "", "diagnosis": { "model": "tmp_graph.pbtxt", "test_data_source": "user provided", "shape_variation": "dynamic", "message": "", "test_data_info": "input_tensor:0 shape: (1, 224, 224, 3) data type: float32" }, "optimizations": [ { "name": "Tf2TrtPlus", "status": "effective", "speedup": "3.96", "pre_run": "7.98 ms", "post_run": "2.02 ms" }, { "name": "TfStripUnusedNodes", "status": "effective", "speedup": "na", "pre_run": "na", "post_run": "na" }, { "name": "TfFoldConstants", "status": "effective", "speedup": "na", "pre_run": "na", "post_run": "na" } ], "overall": { "baseline": "7.87 ms", "optimized": "2.52 ms", "speedup": "3.12" }, "model_info": { "input_format": "frozen_pb" }, "compatibility_list": [ { "device_type": "gpu", "microarchitecture": "T4" } ], "model_sdk": {} }この最適化レポートは、静的シェイプ最適化の場合と同様です。推論中の入力シェイプが、最適化中に提供された
minとmaxの範囲内にある限り、TensorRT 最適化が使用されることに注意してください。入力シェイプがこの範囲外の場合、処理は元の TensorFlow サブグラフの実行にフォールバックします。最適化結果はあくまでこの例のものであり、実際の結果は異なる場合があります。最適化レポートのフィールドの詳細については、「最適化レポート」をご参照ください。
-
ステップ 3: パフォーマンスの検証
最適化が完了したら、Python スクリプトを使用して最適化レポートの情報を検証できます。
import time
with tf.Session(config=TfModel.new_session_config()) as sess, opt_spec_dynamic:
sess.graph.as_default()
tf.import_graph_def(optimized_model_dynamic, name="")
# Warmup!
for i in range(0, 100):
sess.run(['softmax_tensor:0'], test_data)
# Benchmark!
num_runs = 1000
start = time.time()
for i in range(0, num_runs):
sess.run(['softmax_tensor:0'], test_data)
elapsed = time.time() - start
rt_ms = elapsed / num_runs * 1000.0
# Show the result!
print("Latency of optimized model: {:.2f}".format(rt_ms))
システムは、次のような結果を返します。
Latency of optimized model: 2.26
結果は、最適化されたモデルのパフォーマンス 2.26 ms が、最適化レポートの「overall」セクションにある "optimized": "2.52 ms" の値とほぼ同じであることを示しています。使用されたテストデータが動的シェイプ最適化の範囲内にあるため、最適化が有効になります。最適化結果はあくまでこの例のものであり、実際の結果は異なる場合があります。
ステップ 4: 最適化されたモデルのロードと実行
パフォーマンスを検証した後、モデルをデプロイできます。Blade は、統合可能な Python および C++ ランタイム SDK を提供しています。C++ SDK の使用方法の詳細については、「推論のために TensorFlow モデルをデプロイするための SDK の使用」をご参照ください。次のセクションでは、Python SDK を使用してモデルをデプロイする方法について説明します。
オプション: 試用期間中、認証失敗によるプログラムの予期せぬ終了を防ぐために、次の環境変数設定を追加します。
export BLADE_AUTH_USE_COUNTING=1PAI-Blade を使用するための認証を取得します。
export BLADE_REGION=<region> export BLADE_TOKEN=<token>ビジネス要件に基づいて次のパラメーターを設定します。
<region>: PAI-Blade を使用するリージョンです。PAI-Blade が使用できるリージョンを取得するには、PAI-Blade ユーザーの DingTalk グループに参加してください。DingTalk グループの QR コードについては、「PAI-Blade のインストール」をご参照ください。
<token>: PAI-Blade を使用するために必要な認証トークンです。認証トークンを取得するには、PAI-Blade ユーザーの DingTalk グループに参加してください。DingTalk グループの QR コードについては、「PAI-Blade のインストール」をご参照ください。
-
最適化されたモデルをロードして実行します。
Blade を統合するには、
import blade.runtime.tensorflowの行を追加するだけで済みます。元の推論コードに他の変更は必要ありません。次の例は、動的シェイプで最適化されたモデルを使用してこのプロセスを示しています。import tensorflow.compat.v1 as tf import blade.runtime.tensorflow infer_data = np.load('./tf_resnet50_v1.5/test_bc1.npy', allow_pickle=True, encoding='bytes').item() # optimized model produced by blade.optimize model_path = './optimized_model_dynamic.pb' graph_def = tf.GraphDef() with open(model_path, 'rb') as f: graph_def.ParseFromString(f.read()) with tf.Session() as sess: sess.graph.as_default() tf.import_graph_def(graph_def, name="") print(sess.run(['softmax_tensor:0'], infer_data))
付録: TensorRTConfig
TensorRT 最適化の特殊な性質により、Blade 構成には、さまざまなデプロイメントニーズに合わせて TensorRT 最適化をより適切に構成するのに役立つ特別な構成オプションが含まれています。
class TensorRTConfig():
def __init__(self) -> None:
self.enable = True
self.dynamic_tuning_shapes: Dict[str, List[List[Any]]] = dict()
......
TensorRTConfig の主要パラメーターは次のとおりです。
-
enable: TensorRT 最適化を有効にするかどうかを制御するブール型パラメーターです。デフォルトでは、GPU デバイスで最適化する場合、TensorRT 最適化が試行されます。 -
dynamic_tuning_shapes: サーバーから送信されるリクエストには、さまざまなサイズの入力が含まれる場合があります。これらの異なる入力サイズのより良い最適化結果を達成するために、Blade は動的シェイプ最適化機能を提供します。dynamic_tuning_shapesディクショナリを最適化の補助パラメーターとして設定できます。dynamic_tuning_shapesディクショナリには、min、max、およびoptsの 3つのキーが含まれています。minとmaxの値はList[List[int]]型であり、モデル入力の最小および最大ディメンションのセットに対応します。optsの値はList[List[List[int]]]型であり、モデル入力の複数のディメンションセットに対応します。説明optsに対応する複数の入力ディメンションセットは、最適化の成功を確実にするために、最小ディメンションと最大ディメンションの間にある必要があります。そうでない場合、TensorRT 最適化は「Dim value in 'opts' is not between min_dim and max_dim」というエラーを報告します。次のコードは、動的シェイプ最適化設定の例を示しています。
optsに対応する複数のディメンションセットはすべて、最小および最大ディメンションの範囲内にあります。{ "min": [[1, 3, 224, 224], [1, 50]], # lower bound of the dynamic range of each inputs. "opts": [ [[1, 3, 512, 512], [1, 60]], [[1, 3, 320, 320], [1, 55]], ], # shapes that should be optimized like static shapes "max": [[1, 3, 1024, 1024], [1, 70]] # upper bound of the dynamic range. }