• <xmp id="om0om">
  • <table id="om0om"><noscript id="om0om"></noscript></table>
  • 3 月 19 日下午 2 點,鎖定 NVIDIA AI 網絡中文專場。立即注冊觀看
    人工智能/深度學習

    使用 TensorFlow、ONNX 和 TensorRT 加速深度學習推理

    TensorRT 7 . 0 開始, Universal Framework Format( UFF )被棄用。在本文中,您將學習如何使用新的 TensorFlow -ONNX- TensorRT 工作流部署經過 TensorFlow 培訓的深度學習模型。圖 1 顯示了 TensorRT 的高級工作流。

    圖 1 。 TensorRT 是一種推理加速器。

    首先,使用任何框架訓練網絡。網絡訓練后,批量大小和精度是固定的(精度為 FP32 、 FP16 或 INT8 )。訓練好的模型被傳遞給 TensorRT 優化器,優化器輸出一個優化的運行時(也稱為計劃)。. plan 文件是 TensorRT 引擎的序列化文件格式。計劃文件需要反序列化才能使用 TensorRT 運行時運行推斷。

    要優化在 TensorFlow 中實現的模型,只需將模型轉換為 ONNX 格式,并使用 TensorRT 中的 ONNX 解析器解析模型并構建 TensorRT 引擎。圖 2 顯示了高級 ONNX 工作流。

    圖 2 。 ONNX 工作流。

    在本文中,我們將討論如何使用 ONNX 工作流創建一個 TensorRT 引擎,以及如何從 TensorRT 引擎運行推理。更具體地說,我們演示了從 Keras 或 TensorFlow 中的模型到 ONNX 的端到端推理,以及使用 ResNet-50 、語義分段和 U-Net 網絡的 TensorRT 引擎。最后,我們將解釋如何在其他網絡上使用此工作流。

    下載本文中的代碼示例。 下載 TensorFlow -onnx- TensorRT 后 – 代碼 tar . gz 文件,您還應該從 Cityscapes dataset scripts repo 下載 labels.py ,并將其與其他腳本放在同一個文件夾中。

    ONNX 概述

    ONNX 是機器學習和深度學習模型的開放格式。它允許您將不同框架(如 TensorFlow 、 PyTorch 、 MATLAB 、 Caffe 和 Keras )的深度學習和機器學習模型轉換為單一格式。

    它定義了一組通用的運算符、深入學習的通用構建塊集和通用文件格式。它提供計算圖的定義以及內置運算符。可能有一個或多個輸入或輸出的 ONNX 節點列表形成一個無環圖。

    ResNet ONNX 工作流示例

    在這個例子中,我們展示了如何在兩個不同的網絡上使用 ONNX 工作流并創建一個 TensorRT 引擎。第一個網絡是 ResNet-50 。

    工作流包括以下步驟:

    1. 將 TensorFlow / Keras 模型轉換為. pb 文件。
    2. 將. pb 文件轉換為 ONNX 格式。
    3. 創建 TensorRT 引擎。
    4. 從 TensorRT 引擎運行推斷。

    將模型轉換為. pb

    第一步是將模型轉換為. pb 文件。以下代碼示例將 ResNet-50 模型轉換為. pb 文件:

    import tensorflow as tf
    import keras
    from tensorflow.keras.models import Model
    import keras.backend as K
    K.set_learning_phase(0)
    
    def keras_to_pb(model, output_filename, output_node_names):
    
       """
       This is the function to convert the Keras model to pb.
    
       Args:
          model: The Keras model.
          output_filename: The output .pb file name.
          output_node_names: The output nodes of the network. If None, then
          the function gets the last layer name as the output node.
       """
    
       # Get the names of the input and output nodes.
       in_name = model.layers[0].get_output_at(0).name.split(':')[0]
    
       if output_node_names is None:
           output_node_names = [model.layers[-1].get_output_at(0).name.split(':')[0]]
    
       sess = keras.backend.get_session()
    
       # The TensorFlow freeze_graph expects a comma-separated string of output node names.
       output_node_names_tf = ','.join(output_node_names)
    
       frozen_graph_def = tf.graph_util.convert_variables_to_constants(
           sess,
           sess.graph_def,
           output_node_names)
    
       sess.close()
       wkdir = ''
       tf.train.write_graph(frozen_graph_def, wkdir, output_filename, as_text=False)
    
       return in_name, output_node_names
    
    # load the ResNet-50 model pretrained on imagenet
    model = keras.applications.resnet.ResNet50(include_top=True, weights='imagenet', input_tensor=None, input_shape=None, pooling=None, classes=1000)
    
    # Convert the Keras ResNet-50 model to a .pb file
    in_tensor_name, out_tensor_names = keras_to_pb(model, "models/resnet50.pb", None) 

    除了 Keras ,您還可以從以下位置下載 ResNet-50 :

    • 深度學習示例 GitHub 存儲庫:提供最新的深度學習示例網絡。您還可以看到 ResNet-50 分支,它包含一個腳本和方法來訓練 ResNet-50v1 . 5 模型。
    • NVIDIA NGC 型號 :它有預訓練模型的檢查點列表。例如,在 ResNet-50v1 . 5 上搜索 TensorFlow ,并從 Download 頁面獲取最新的檢查點。

    將. pb 文件轉換為 ONNX

    第二步是將. pb 模型轉換為 ONNX 格式。為此,首先安裝 tf2onnx

    安裝 tf2onnx 后,有兩種方法可以將模型從. pb 文件轉換為 ONNX 格式。第二種方法是使用命令行。運行以下命令:

    python -m tf2onnx.convert  --input /Path/to/resnet50.pb --inputs input_1:0 --outputs probs/Softmax:0 --output resnet50.onnx 

    從 ONNX 創建 TensorRT 引擎

    要從 ONNX 文件創建 TensorRT 引擎,請運行以下命令:

    import tensorrt as trt
    
    TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
    trt_runtime = trt.Runtime(TRT_LOGGER)
    def build_engine(onnx_path, shape = [1,224,224,3]):
    
       """
       This is the function to create the TensorRT engine
       Args:
          onnx_path : Path to onnx_file. 
          shape : Shape of the input of the ONNX file. 
      """
       with trt.Builder(TRT_LOGGER) as builder, builder.create_network(1) as network, trt.OnnxParser(network, TRT_LOGGER) as parser:
           builder.max_workspace_size = (256 << 20)
           with open(onnx_path, 'rb') as model:
               parser.parse(model.read())
           network.get_input(0).shape = shape
           engine = builder.build_cuda_engine(network)
           return engine
    
    def save_engine(engine, file_name):
       buf = engine.serialize()
       with open(file_name, 'wb') as f:
           f.write(buf)
    def load_engine(trt_runtime, plan_path):
       with open(engine_path, 'rb') as f:
           engine_data = f.read()
       engine = trt_runtime.deserialize_cuda_engine(engine_data)
       return engine

    此代碼應保存在引擎. py 文件,稍后在文章中使用。

    此代碼示例包含以下變量:

    • 最大工作區大小: 在執行時 ICudaEngine 可以使用的最大 GPU 臨時內存。

    構建器創建一個空網絡( builder.create_network() ), ONNX 解析器將 ONNX 文件解析到網絡( parser.parse(model.read()) )。您可以為網絡( network.get_input(0).shape = shape )設置輸入形狀,然后生成器將創建引擎( engine = builder.build_cuda_engine(network) )。要創建引擎,請運行以下代碼示例:

    import engine as eng
    import argparse
    from onnx import ModelProto
    import tensorrt as trt
    
     engine_name = “resnet50.plan”
     onnx_path = "/path/to/onnx/result/file/"
     batch_size = 1 
    
     model = ModelProto()
     with open(onnx_path, "rb") as f:
        model.ParseFromString(f.read())
    
     d0 = model.graph.input[0].type.tensor_type.shape.dim[1].dim_value
     d1 = model.graph.input[0].type.tensor_type.shape.dim[2].dim_value
     d2 = model.graph.input[0].type.tensor_type.shape.dim[3].dim_value
     shape = [batch_size , d0, d1 ,d2]
     engine = eng.build_engine(onnx_path, shape= shape)
     eng.save_engine(engine, engine_name) 

    在這個代碼示例中,首先從 ONNX 模型獲取輸入形狀。接下來,創建引擎,然后將引擎保存在. plan 文件中。

    運行來自 TensorRT 引擎的推理:

    TensorRT 引擎在以下工作流中運行推理:

    1. 為 GPU 中的輸入和輸出分配緩沖區。
    2. 將數據從主機復制到 GPU 中分配的輸入緩沖區。
    3. 在 GPU 中運行推理。
    4. 將結果從 GPU 復制到主機。
    5. 根據需要重塑結果。

    下面的代碼示例詳細解釋了這些步驟。此代碼應保存在推理. py 文件,稍后將在本文中使用。

    import tensorrt as trt
    import pycuda.driver as cuda
    import numpy as np
    import pycuda.autoinit
    
    def allocate_buffers(engine, batch_size, data_type):
    
       """
       This is the function to allocate buffers for input and output in the device
       Args:
          engine : The path to the TensorRT engine. 
          batch_size : The batch size for execution time.
          data_type: The type of the data for input and output, for example trt.float32. 
       
       Output:
          h_input_1: Input in the host.
          d_input_1: Input in the device. 
          h_output_1: Output in the host. 
          d_output_1: Output in the device. 
          stream: CUDA stream.
    
       """
    
       # Determine dimensions and create page-locked memory buffers (which won't be swapped to disk) to hold host inputs/outputs.
       h_input_1 = cuda.pagelocked_empty(batch_size * trt.volume(engine.get_binding_shape(0)), dtype=trt.nptype(data_type))
       h_output = cuda.pagelocked_empty(batch_size * trt.volume(engine.get_binding_shape(1)), dtype=trt.nptype(data_type))
       # Allocate device memory for inputs and outputs.
       d_input_1 = cuda.mem_alloc(h_input_1.nbytes)
    
       d_output = cuda.mem_alloc(h_output.nbytes)
       # Create a stream in which to copy inputs/outputs and run inference.
       stream = cuda.Stream()
       return h_input_1, d_input_1, h_output, d_output, stream
    
    def load_images_to_buffer(pics, pagelocked_buffer):
       preprocessed = np.asarray(pics).ravel()
       np.copyto(pagelocked_buffer, preprocessed)
    
    def do_inference(engine, pics_1, h_input_1, d_input_1, h_output, d_output, stream, batch_size, height, width):
       """
       This is the function to run the inference
       Args:
          engine : Path to the TensorRT engine 
          pics_1 : Input images to the model.  
          h_input_1: Input in the host         
          d_input_1: Input in the device 
          h_output_1: Output in the host 
          d_output_1: Output in the device 
          stream: CUDA stream
          batch_size : Batch size for execution time
          height: Height of the output image
          width: Width of the output image
       
       Output:
          The list of output images
    
       """
    
       load_images_to_buffer(pics_1, h_input_1)
    
       with engine.create_execution_context() as context:
           # Transfer input data to the GPU.
           cuda.memcpy_htod_async(d_input_1, h_input_1, stream)
    
           # Run inference.
    
           context.profiler = trt.Profiler()
           context.execute(batch_size=1, bindings=[int(d_input_1), int(d_output)])
    
           # Transfer predictions back from the GPU.
           cuda.memcpy_dtoh_async(h_output, d_output, stream)
           # Synchronize the stream
           stream.synchronize()
           # Return the host output.
           out = h_output.reshape((batch_size,-1, height, width))
           return out 

    為第一個輸入行和輸出行確定兩個維度。您可以在主機( h_input_1h_output )中創建頁鎖定內存緩沖區。然后,為輸入和輸出分配與主機輸入和輸出相同大小的設備內存( d_input_1d_output )。下一步是創建 CUDA 流,用于在設備和主機分配的內存之間復制數據。

    在這個代碼示例中,在 do_inference 函數中,第一步是使用 load_images_to_buffer 函數將圖像加載到主機中的緩沖區。然后將輸入數據傳輸到 GPU ( cuda.memcpy_htod_async(d_input_1, h_input_1, stream) ),并使用 context.execute 運行推理。最后將結果從 GPU 復制到主機( cuda.memcpy_dtoh_async(h_output, d_output, stream) )。

    ONNX 工作流語義分割實例

    在本文 基于 TensorRT 3 的自主車輛快速 INT8 推理 中,作者介紹了一個語義分割模型的 UFF 工作流過程。

    在本文中,您將使用類似的網絡來運行 ONNX 工作流來進行語義分段。該網絡由一個基于 VGG16 的編碼器和三個使用反褶積層實現的上采樣層組成。網絡在 城市景觀數據集 上經過大約 40000 次迭代訓練

    有多種方法可以將 TensorFlow 模型轉換為 ONNX 文件。一種方法是 ResNet50 部分中解釋的方法。 Keras 也有自己的 Keras 到 ONNX 文件轉換器。有時, TensorFlow -to-ONNX 不支持某些層,但 Keras-to-ONNX 轉換器支持這些層。根據 Keras 框架和使用的層類型,您可能需要在轉換器之間進行選擇。

    在下面的代碼示例中,使用 Keras-to-ONNX 轉換器將 Keras 模型直接轉換為 ONNX 。下載預先訓練的語義分段文件 semantic_segmentation.hdf5

    import keras
    import tensorflow as tf
    from keras2onnx import convert_keras
    
    def keras_to_onnx(model, output_filename):
       onnx = convert_keras(model, output_filename)
       with open(output_filename, "wb") as f:
           f.write(onnx.SerializeToString())
    
    semantic_model = keras.models.load_model('/path/to/semantic_segmentation.hdf5')
    keras_to_onnx(semantic_model, 'semantic_segmentation.onnx') 

    圖 3 顯示了網絡的體系結構。

    圖 3 。基于 VGG16 的語義分割模型。

    與前面的示例一樣,使用下面的代碼示例創建語義分段引擎。

    import engine as eng
    from onnx import ModelProto
    import tensorrt as trt
    
    
    engine_name = 'semantic.plan'
    onnx_path = "semantic.onnx"
    batch_size = 1
    
    model = ModelProto()
    with open(onnx_path, "rb") as f:
      model.ParseFromString(f.read())
    
    d0 = model.graph.input[0].type.tensor_type.shape.dim[1].dim_value
    d1 = model.graph.input[0].type.tensor_type.shape.dim[2].dim_value
    d2 = model.graph.input[0].type.tensor_type.shape.dim[3].dim_value
    shape = [batch_size , d0, d1 ,d2]
    engine = eng.build_engine(onnx_path, shape= shape)
    eng.save_engine(engine, engine_name) 

    要測試模型的輸出,請使用 城市景觀數據集 。要使用城市景觀,必須具有以下功能: sub_mean_chwcolor_map 。這些函數也用于 post , 基于 TensorRT 3 的自主車輛快速 INT8 推理 .

    在下面的代碼示例中, sub_mean_chw 用于從圖像中減去平均值作為預處理步驟, color_map 是從類 ID 到顏色的映射。后者用于可視化。

    import numpy as np
    from PIL import Image
    import tensorrt as trt
    import labels  # from cityscapes evaluation script
    import skimage.transform
    
    TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
    trt_runtime = trt.Runtime(TRT_LOGGER)
    
    MEAN = (71.60167789, 82.09696889, 72.30508881)
    CLASSES = 20
    HEIGHT = 512
    WIDTH = 1024
    
    def sub_mean_chw(data):
       data = data.transpose((1, 2, 0))  # CHW -> HWC
       data -= np.array(MEAN)  # Broadcast subtract
       data = data.transpose((2, 0, 1))  # HWC -> CHW
       return data
    
    def rescale_image(image, output_shape, order=1):
       image = skimage.transform.resize(image, output_shape,
                   order=order, preserve_range=True, mode='reflect')
       return image
    
    def color_map(output):
       output = output.reshape(CLASSES, HEIGHT, WIDTH)
       out_col = np.zeros(shape=(HEIGHT, WIDTH), dtype=(np.uint8, 3))
       for x in range(WIDTH):
           for y in range(HEIGHT):
    
               if (np.argmax(output[:, y, x] )== 19):
                   out_col[y,x] = (0, 0, 0)
               else:
                   out_col[y, x] = labels.id2label[labels.trainId2label[np.argmax(output[:, y, x])].id].color
       return out_col  

    下面的代碼示例是上一個示例的其余代碼。必須先運行上一個塊,因為需要定義的函數。使用這個例子比較 Keras 模型和 TensorRT 引擎 semantic . plan 文件的輸出,然后可視化這兩個輸出。根據需要替換占位符 /path/to/semantic_segmentation.hdf5input_file_path

    import engine as eng
    import inference as inf
    import keras
    import tensorrt as trt
    
    input_file_path = ‘munster_000172_000019_leftImg8bit.png’
    onnx_file = "semantic.onnx"
    serialized_plan_fp32 = "semantic.plan"
    HEIGHT = 512
    WIDTH = 1024
    
    image = np.asarray(Image.open(input_file_path))
    img = rescale_image(image, (512, 1024),order=1)
    im = np.array(img, dtype=np.float32, order='C')
    im = im.transpose((2, 0, 1))
    im = sub_mean_chw(im)
    
    engine = eng.load_engine(trt_runtime, serialized_plan_fp32)
    h_input, d_input, h_output, d_output, stream = inf.allocate_buffers(engine, 1, trt.float32)
    out = inf.do_inference(engine, im, h_input, d_input, h_output, d_output, stream, 1, HEIGHT, WIDTH)
    out = color_map(out)
    
    colorImage_trt = Image.fromarray(out.astype(np.uint8))
    colorImage_trt.save(“trt_output.png”)
    
    semantic_model = keras.models.load_model('/path/to/semantic_segmentation.hdf5')
    out_keras= semantic_model.predict(im.reshape(-1, 3, HEIGHT, WIDTH))
    
    out_keras = color_map(out_keras)
    colorImage_k = Image.fromarray(out_keras.astype(np.uint8))
    colorImage_k.save(“keras_output.png”)

    圖 4 顯示了實際圖像和實際情況,以及 Keras 的輸出與 TensorRT 引擎的輸出的對比。如您所見, TensorRT 發動機的輸出與 Keras 的類似。

    圖 4a 原始圖像 .
    圖 4b 地面真相標簽。
    圖 4c . TensorRT 的輸出。
    圖 4d : Keras 的輸出。

    在其他網絡上試試

    現在您可以在其他網絡上嘗試 ONNX 工作流。有關分段網絡的好例子的更多信息,請參閱 GitHub 上的 具有預訓練主干的分割模型

    作為一個例子,我們用一個 ONNX 網絡來說明如何使用。本例中的網絡是來自 segmentation_models 庫的 U-Net 。在這里,我們只加載模型,而沒有對其進行訓練。您可能需要在首選數據集上訓練這些模型。

    關于這些網絡的一個重要點是,當您加載這些網絡時,它們的輸入層大小如下所示:( None , None , None , 3 )。要創建一個 TensorRT 引擎,您需要一個輸入大小已知的 ONNX 文件。在將此模型轉換為 ONNX 之前,請通過為其輸入指定大小來更改網絡,然后將其轉換為 ONNX 格式。

    例如,從這個庫( segmentation _ models )加載 U-Net 網絡并為其輸入指定大小( 244 、 244 、 3 )。在為推理創建了 TensorRT 引擎之后,做一個與語義分段類似的轉換。根據應用程序和數據集的不同,可能需要使用不同的顏色映射。

    import segmentation_models as sm
    import keras
    from keras2onnx import convert_keras
    from engine import *
    onnx_path = 'unet.onnx'
    engine_name = 'unet.plan'
    batch_size = 1
    CHANNEL = 3
    HEIGHT = 224
    WIDTH = 224
    
    model = sm.Unet()
    model._layers[0].batch_input_shape = (None, 224,224,3)
    model = keras.models.clone_model(model)
    
    onx = convert_keras(model, onnx_path)
    with open(onnx_path, "wb") as f:
      f.write(onx.SerializeToString())
    
    shape = [batch_size , HEIGHT, WIDTH, CHANNEL]
    engine = build_engine(onnx_path, shape= shape)
    save_engine(engine, engine_name) 

    我們之前提到的另一種下載方式是從 vz6 下載。它有一個預先訓練模型的檢查點列表。例如,您可以在 TensorFlow 中搜索 UNet ,然后轉到 Download 頁面以獲取最新的檢查點。

    總結

    在這篇文章中,我們解釋了如何使用 TensorFlow-to-ONNX-to-TensorRT 工作流來部署深度學習應用程序,并給出了幾個示例。第一個例子是 ResNet-50 上的 ONNX- TensorRT ,第二個例子是在 Cityscapes 數據集上訓練的基于 英偉達數據中心深度學習產品性能 的語義分割。在文章的最后,我們演示了如何在其他網絡上應用這個工作流。


    +2

    標簽

    人人超碰97caoporen国产