ResNet50作為一個廣泛應用的經典結構網絡,其優化在多種推理部署場景中都具有很高的實用價值。本文介紹如何使用Blade優化基于TensorFlow的ResNet50模型。
背景信息
殘差網絡ResNet(Residual Network)作為計算機視覺任務主干經典神經網絡的一部分,堪稱圖像領域深度學習模型實戰的"Hello World"。ResNet使用卷積層提取圖像的特征,并通過引入殘差塊結構,解決了深層神經網絡訓練時的梯度消失和梯度爆炸問題,大幅提升了深度神經網絡的訓練效果。ResNet典型的網絡有ResNet26、ResNet50及ResNet101等。
使用限制
本文使用的環境需要滿足以下版本要求:
系統環境:Linux系統中使用Python 3.6及其以上版本、CUDA 10.0。
框架:TensorFlow 1.15。
推理優化工具:Blade 3.17.0及其以上版本。
操作流程
使用Blade優化基于TensorFlow的ResNet50模型的流程如下:
安裝支持TensorRT優化的Blade Wheel包,并下載ResNet50模型及測試數據。
調用
blade.optimize
接口優化模型。對優化前后的推理速度進行測試,從而驗證優化報告中信息的正確性。
集成Blade SDK,加載優化后的模型進行推理。
步驟一:準備工作
本案例中對ResNet50模型主要生效的優化項為TensorRT,因此您需要使用支持這一優化功能的Blade版本,即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及對應的不同Batch Size的測試數據,您需要手動解壓該壓縮包。
tar zxvf tf_resnet50_v1.5.tar.gz
步驟二:調用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優化按照輸入的不同分為以下兩種類型:
靜態Shape優化
適用于模型請求的Shape保持不變的情況。例如為了延時考慮,限制了只允許輸入某個特定尺寸。示例如下所示。
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": {} }
在優化過程中,由于沒有提供更多額外的優化選項,因此啟動靜態Shape優化,此Shape選用
test_data
的尺寸,從上述的優化報告可以看出Tf2TrtPlus
優化項生效。 在靜態Shape優化生效情況下,如果模型Inference時輸入的Shape和優化時提供的不一致,則會Fallback到TensorFlow原始圖執行,執行的效率會大幅度降低。上述優化結果僅為本示例的測試結果,您的優化效果以實際為準。關于優化報告的字段詳情請參見優化報告。
動態Shape優化
如果您部署的服務支持動態Batching功能,則通常會限制服務端將某個時間段內收到的請求組合成一個Batch。由于在一個較短時間段內收到的請求數是不確定的,因此最后組成的Batch大小可能是變化的。為了支持這種變化Shape的優化,Blade集成了TensorRT的動態Shape優化能力。您只需要在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": {} }
上述優化報告與靜態Shape優化的報告類似。您需要注意的是只要模型Inference時輸入的Shape落在優化時提供的
min
和max
區間內,都會使用TensorRT優化,如果模型Inference時輸入的Shape落在該區間外,則會Fallback到TensorFlow的原始子圖執行。上述優化結果僅為本示例的測試結果,您的優化效果請以實際為準。關于優化報告的字段詳情請參見優化報告。
步驟三:驗證性能
優化完成后,通過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"
基本一致,測試使用的數據是在動態尺寸優化的范圍內的,因此優化生效。上述優化結果僅為本示例的測試結果,您的優化效果請以實際為準。
步驟四:加載運行優化后的模型
完成驗證后,您需要對模型進行部署,Blade提供了Python和C++兩種運行時SDK供您集成。關于C++的SDK使用方法請參見使用SDK部署TensorFlow模型推理,下文主要介紹如何使用Python SDK部署模型。
- 可選:在試用階段,您可以設置如下的環境變量,防止因為鑒權失敗而程序退出。
export BLADE_AUTH_USE_COUNTING=1
- 獲取鑒權。
加載運行優化后的模型。
除了增加一行
import blade.runtime.tensorflow
,您無需為Blade的接入編寫額外代碼,即原有的推理代碼無需任何改動。下面以剛才動態尺寸TensorRT優化得到的模型為例進行演示。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的config專門為其預留了一個特殊的配置選項,幫助您更好的配置使用TensorRT優化以應對不同的部署需求。
class TensorRTConfig():
def __init__(self) -> None:
self.enable = True
self.dynamic_tuning_shapes: Dict[str, List[List[Any]]] = dict()
......
此處重點介紹TensorRTConfig中的關鍵參數:
enable
:Boolean參數,作為開關參數控制TensorRT優化是否開啟。默認情況下,在GPU設備上優化時,會嘗試TensorRT優化。dynamic_tuning_shapes
:服務端發送的請求可能包含多種不同尺寸的輸入,為了對不同尺寸的輸入獲得較好的優化效果,Blade提供了動態尺寸優化功能,您需要設置dynamic_tuning_shapes
字典作為優化的輔助參數。dynamic_tuning_shapes
字典中包含了min
、max
、opts
三個Key值。其中min
和max
對應的Value類型為List[List[int]]
,對應為模型輸入的一組最小尺寸和一組最大尺寸。opts
對應的Value類型為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. }