全部產品
Search
文件中心

Platform For AI:ResNet50最佳化案例:使用Blade最佳化基於TensorFlow的ResNet50模型

更新時間:Jul 13, 2024

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模型的流程如下:

  1. 步驟一:準備工作

    安裝支援TensorRT最佳化的Blade Wheel包,並下載ResNet50模型及測試資料。

  2. 步驟二:調用Blade最佳化模型

    調用blade.optimize介面最佳化模型。

  3. 步驟三:驗證效能

    對最佳化前後的推理速度進行測試,從而驗證最佳化報告中資訊的正確性。

  4. 步驟四:載入運行最佳化後的模型

    整合Blade SDK,載入最佳化後的模型進行推理。

步驟一:準備工作

本案例中對ResNet50模型主要生效的最佳化項為TensorRT,因此您需要使用支援這一最佳化功能的Blade版本,即Blade 3.17.0及其以上版本。

  1. 安裝對應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
  2. 下載TensorFlow的ResNet50模型及對應的測試資料。

    wget http://pai-blade.cn-hangzhou.oss.aliyun-inc.com/tutorials/tf_resnet50_v1.5.tar.gz
  3. 下載的壓縮包tf_resnet50_v1.5.tar.gz中包含了一份Resnet50模型的frozen.pb及對應的不同Batch Size的測試資料,您需要手動解壓該壓縮包。

    tar zxvf tf_resnet50_v1.5.tar.gz

步驟二:調用Blade最佳化模型

  1. 從上一步中下載的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)
  2. 使用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落在最佳化時提供的minmax區間內,都會使用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部署模型。

  1. 可選:在試用階段,您可以設定如下的環境變數,防止因為鑒權失敗而程式退出。
    export BLADE_AUTH_USE_COUNTING=1
  2. 擷取鑒權。
    export BLADE_REGION=<region>
    export BLADE_TOKEN=<token>
    您需要根據實際情況替換以下參數:
    • <region>:Blade支援的地區,需要加入Blade使用者群擷取該資訊,使用者群的二維碼詳情請參見擷取Token
    • <token>:鑒權Token,需要加入Blade使用者群擷取該資訊,使用者群的二維碼詳情請參見擷取Token
  3. 載入運行最佳化後的模型。

    除了增加一行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字典中包含了minmaxopts三個Key值。其中minmax對應的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.
    }