日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

RetinaNet優(yōu)化案例2:結(jié)合Blade和Custom C++ Operator優(yōu)化模型

為了使檢測模型后處理部分更加高效,您可以采用TorchScript Custom C++ Operators將Python代碼實(shí)現(xiàn)的邏輯替換成高效的C++實(shí)現(xiàn),然后再導(dǎo)出TorchScript模型進(jìn)行Blade優(yōu)化。本文介紹如何使用Blade對TorchScript Custom C++ Operator實(shí)現(xiàn)的后處理邏輯的檢測模型進(jìn)行優(yōu)化。

背景信息

RetinaNet是一種One-Stage RCNN類型的檢測網(wǎng)絡(luò),基本結(jié)構(gòu)由一個Backbone、多個子網(wǎng)及NMS后處理組成。許多訓(xùn)練框架中均實(shí)現(xiàn)了RetinaNet,典型的框架有Detectron2。上一篇中介紹了如何通過scripting_with_instances方式導(dǎo)出RetinaNet(Detectron2)模型并使用Blade快速完成模型優(yōu)化,詳情請參見RetinaNet優(yōu)化案例1:使用Blade優(yōu)化RetinaNet(Detectron2)模型

然而,檢測模型的后處理部分代碼通常需要執(zhí)行計算和篩選boxesnms等邏輯,通過Python實(shí)現(xiàn)該部分邏輯往往不高效。此時,您可以采用TorchScript Custom C++ Operators將Python代碼實(shí)現(xiàn)的邏輯替換成高效的C++實(shí)現(xiàn),然后再導(dǎo)出TorchScript模型并使用Blade進(jìn)行模型優(yōu)化。

使用限制

本文使用的環(huán)境需要滿足以下版本限制:

  • 系統(tǒng)環(huán)境:Linux系統(tǒng)中使用Python 3.6及其以上版本、GCC 5.4及其以上版本、Nvidia Tesla T4、CUDA 10.2、CuDNN 8.0.5.39。

  • 框架:PyTorch 1.8.1及其以上版本、Detectron2 0.4.1及其以上版本。

  • 推理優(yōu)化工具:Blade 3.16.0及其以上版本。

操作流程

結(jié)合Blade和Custom C++ Operator優(yōu)化模型的流程如下:

  1. 步驟一:創(chuàng)建帶有Custom C++ Operators的PyTorch模型

    使用TorchScript擴(kuò)展實(shí)現(xiàn)RetinaNet的后處理部分。

  2. 步驟二:導(dǎo)出TorchScript模型

    使用Detectron2提供的TracingAdapterscripting_with_instances任何一種方式導(dǎo)出模型。

  3. 步驟三:調(diào)用Blade優(yōu)化模型

    調(diào)用blade.optimize接口優(yōu)化模型,并保存優(yōu)化后的模型。

  4. 步驟四:加載運(yùn)行優(yōu)化后的模型

    經(jīng)過對優(yōu)化前后的模型進(jìn)行性能測試,如果對結(jié)果滿意,可以加載優(yōu)化后的模型進(jìn)行推理。

步驟一:創(chuàng)建帶有Custom C++ Operators的PyTorch模型

Blade工具與PyTorch TorchScript擴(kuò)展機(jī)制無縫銜接,以下介紹如何使用TorchScript擴(kuò)展實(shí)現(xiàn)RetinaNet的后處理部分。關(guān)于TorchScript Custom Operator的介紹請參見EXTENDING TORCHSCRIPT WITH CUSTOM C++ OPERATORS。本文使用的RetinaNet后處理部分的程序邏輯來自NVIDIA開源社區(qū),詳情請參見Retinanet-Examples。本文抽取了核心的代碼用于說明開發(fā)實(shí)現(xiàn)Custom Operator的流程。

  1. 下載示例代碼并解壓。

    wget -nv https://pai-blade.oss-cn-zhangjiakou.aliyuncs.com/tutorials/retinanet_example/retinanet-examples.tar.gz -O retinanet-examples.tar.gz
    tar xvfz retinanet-examples.tar.gz 1>/dev/null
  2. 編譯Custom C++ Operators。

    PyTorch官方文檔中(詳情請參見EXTENDING TORCHSCRIPT WITH CUSTOM C++ OPERATORS)提供了三種編譯Custom Operators的方式:Building with CMake、Building with JIT Compilation及Building with Setuptools。這三種編譯方式適用于不同場景,您可以根據(jù)自己的需求進(jìn)行選擇。本文為了簡便,采用Building with JIT Compilation方式,示例代碼如下所示。

    import torch.utils.cpp_extension
    import os
    codebase="retinanet-examples"
    sources=['csrc/extensions.cpp',
             'csrc/cuda/decode.cu',
             'csrc/cuda/nms.cu',]
    sources = [os.path.join(codebase,src) for src in sources]
    torch.utils.cpp_extension.load(
        name="custom",
        sources=sources,
        build_directory=codebase,
        extra_include_paths=['/usr/local/TensorRT/include/', '/usr/local/cuda/include/', '/usr/local/cuda/include/thrust/system/cuda/detail'],
        extra_cflags=['-std=c++14', '-O2', '-Wall'],
        extra_cuda_cflags=[
            '-std=c++14', '--expt-extended-lambda',
            '--use_fast_math', '-Xcompiler', '-Wall,-fno-gnu-unique',
            '-gencode=arch=compute_75,code=sm_75',],
        is_python_module=False,
        with_cuda=True,
        verbose=False,
    )

    上述程序執(zhí)行完成后,編譯生成的custom.so會保存在retinanet-examples目錄下。

  3. 使用Custom C++ Operators替換RetinaNet的后處理部分。

    為了簡潔,此處直接使用adapter_forward替換RetinaNet.forwardadapter_forward使用decode_cudanms_cuda兩個Custom C++ Operators實(shí)現(xiàn)了RetinaNet的后處理部分,示例代碼如下所示。

    import os
    import torch
    from typing import Tuple, Dict, List, Optional
    codebase="retinanet-examples"
    torch.ops.load_library(os.path.join(codebase, 'custom.so'))
    
    decode_cuda = torch.ops.retinanet.decode
    nms_cuda = torch.ops.retinanet.nms
    
    # 該函數(shù)的主要代碼部分和RetinaNet.forward一樣,但是后處理部分替換為通過decode_cuda和nms_cuda實(shí)現(xiàn)。
    def adapter_forward(self, batched_inputs: Tuple[Dict[str, torch.Tensor]]):
        images = self.preprocess_image(batched_inputs)
        features = self.backbone(images.tensor)
        features = [features[f] for f in self.head_in_features]
        cls_heads, box_heads = self.head(features)
        cls_heads = [cls.sigmoid() for cls in cls_heads]
        box_heads = [b.contiguous() for b in box_heads]
    
        # 后處理部分。
        strides = [images.tensor.shape[-1] // cls_head.shape[-1] for cls_head in cls_heads]
        decoded = [
            decode_cuda(
                cls_head,
                box_head,
                anchor.view(-1),
                stride,
                self.test_score_thresh,
                self.test_topk_candidates,
            )
            for stride, cls_head, box_head, anchor in zip(
                strides, cls_heads, box_heads, self.cell_anchors
            )
        ]
    
        # non-maximum suppression部分。
        decoded = [torch.cat(tensors, 1) for tensors in zip(decoded[0], decoded[1], decoded[2])]
        return nms_cuda(decoded[0], decoded[1], decoded[2], self.test_nms_thresh, self.max_detections_per_image)
    
    from detectron2.modeling.meta_arch import retinanet
    
    # 使用adapter_forward替換RetinaNet.forward。
    retinanet.RetinaNet.forward = adapter_forward

步驟二:導(dǎo)出TorchScript模型

Detectron2是FAIR開源的靈活、可擴(kuò)展、可配置的目標(biāo)檢測和圖像分割訓(xùn)練框架。由于框架的靈活性,使用常規(guī)方法導(dǎo)出模型可能會失敗或得到錯誤的導(dǎo)出結(jié)果。為了支持TorchScript部署,Detectron2提供了TracingAdapterscripting_with_instances兩種導(dǎo)出方式,詳情請參見Detectron2 Usage

Blade支持輸入任意形式的TorchScript模型,如下以scripting_with_instances為例,介紹導(dǎo)出模型的過程。

import torch
import numpy as np

from torch import Tensor
from torch.testing import assert_allclose

from detectron2 import model_zoo
from detectron2.export import scripting_with_instances
from detectron2.structures import Boxes
from detectron2.data.detection_utils import read_image

# 使用scripting_with_instances導(dǎo)出RetinaNet模型。
def load_retinanet(config_path):
    model = model_zoo.get(config_path, trained=True).eval()
    # Set a new cell_anchors attributes to PyTorch model.
    model.cell_anchors = [c.contiguous() for c in model.anchor_generator.cell_anchors]
    fields = {
        "pred_boxes": Boxes,
        "scores": Tensor,
        "pred_classes": Tensor,
    }
    script_model = scripting_with_instances(model, fields)
    return model, script_model

# 下載一張示例圖片。
!wget http://images.cocodataset.org/val2017/000000439715.jpg -q -O input.jpg
img = read_image('./input.jpg')
img = torch.from_numpy(np.ascontiguousarray(img.transpose(2, 0, 1)))

# 嘗試執(zhí)行和對比導(dǎo)出模型前后的結(jié)果。
pytorch_model, script_model = load_retinanet("COCO-Detection/retinanet_R_50_FPN_3x.yaml")
with torch.no_grad():
    batched_inputs = [{"image": img.float()}]
    pred1 = pytorch_model(batched_inputs)
    pred2 = script_model(batched_inputs)

assert_allclose(pred1[0], pred2[0])

步驟三:調(diào)用Blade優(yōu)化模型

  1. 調(diào)用Blade優(yōu)化接口。

    調(diào)用blade.optimize接口對模型進(jìn)行優(yōu)化,代碼示例如下。關(guān)于blade.optimize接口詳情,請參見優(yōu)化PyTorch模型

    import os
    import blade
    import torch
    
    # 加載custom c++ operator動態(tài)鏈接庫。
    codebase="retinanet-examples"
    torch.ops.load_library(os.path.join(codebase, 'custom.so'))
    
    blade_config = blade.Config()
    blade_config.gpu_config.disable_fp16_accuracy_check = True
    
    test_data = [(batched_inputs,)] # PyTorch的輸入數(shù)據(jù)是List of Tuple。
    
    with blade_config:
        optimized_model, opt_spec, report = blade.optimize(
        script_model,  # 上一步導(dǎo)出的TorchScript模型。
        'o1',  # 開啟Blade O1級別的優(yōu)化。
        device_type='gpu',  # 目標(biāo)設(shè)備為GPU。
        test_data=test_data,  # 給定一組測試數(shù)據(jù),用于輔助優(yōu)化及測試。
        )
  2. 打印優(yōu)化報告并保存模型。

    Blade優(yōu)化后的模型仍然是一個TorchScript模型。完成優(yōu)化后,您可以通過如下代碼打印優(yōu)化報告并保存優(yōu)化模型。

    # 打印優(yōu)化結(jié)果報表。
    print("Report: {}".format(report))
    # 保存優(yōu)化后的模型。
    torch.jit.save(script_model, 'script_model.pt')
    torch.jit.save(optimized_model, 'optimized.pt')

    打印的優(yōu)化報告如下所示,關(guān)于優(yōu)化報告中的字段詳情請參見優(yōu)化報告

    Report: {
      "software_context": [
        {
          "software": "pytorch",
          "version": "1.8.1+cu102"
        },
        {
          "software": "cuda",
          "version": "10.2.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: (3, 480, 640) data type: float32"
      },
      "optimizations": [
        {
          "name": "PtTrtPassFp16",
          "status": "effective",
          "speedup": "3.92",
          "pre_run": "40.72 ms",
          "post_run": "10.39 ms"
        }
      ],
      "overall": {
        "baseline": "40.64 ms",
        "optimized": "10.41 ms",
        "speedup": "3.90"
      },
      "model_info": {
        "input_format": "torch_script"
      },
      "compatibility_list": [
        {
          "device_type": "gpu",
          "microarchitecture": "T4"
        }
      ],
      "model_sdk": {}
    }
  3. 對優(yōu)化前后的模型進(jìn)行性能測試。

    性能測試的代碼示例如下所示。

    import time
    
    @torch.no_grad()
    def benchmark(model, inp):
        for i in range(100):
            model(inp)
        torch.cuda.synchronize()
        start = time.time()
        for i in range(200):
            model(inp)
        torch.cuda.synchronize()
        elapsed_ms = (time.time() - start) * 1000
        print("Latency: {:.2f}".format(elapsed_ms / 200))
    
    # 對優(yōu)化前的模型測速。
    benchmark(script_model, batched_inputs)
    # 對優(yōu)化后的模型測速。
    benchmark(optimized_model, batched_inputs)

    本次測試的參考結(jié)果值如下。

    Latency: 40.65
    Latency: 10.46

    上述結(jié)果表示同樣執(zhí)行200輪,優(yōu)化前后的模型平均延時分別是40.65 ms和10.46 ms。

步驟四:加載運(yùn)行優(yōu)化后的模型

  1. 可選:在試用階段,您可以設(shè)置如下的環(huán)境變量,防止因?yàn)殍b權(quán)失敗而程序退出。
    export BLADE_AUTH_USE_COUNTING=1
  2. 獲取鑒權(quán)。
    export BLADE_REGION=<region>
    export BLADE_TOKEN=<token>
    您需要根據(jù)實(shí)際情況替換以下參數(shù):
    • <region>:Blade支持的地域,需要加入Blade用戶群獲取該信息,用戶群的二維碼詳情請參見獲取Token
    • <token>:鑒權(quán)Token,需要加入Blade用戶群獲取該信息,用戶群的二維碼詳情請參見獲取Token
  3. 加載運(yùn)行優(yōu)化后的模型。

    Blade優(yōu)化后的模型仍然是TorchScript,因此您無需切換環(huán)境即可加載優(yōu)化后的結(jié)果。

    import blade.runtime.torch
    import detectron2
    import torch
    import numpy as np
    import os
    from detectron2.data.detection_utils import read_image
    from torch.testing import assert_allclose
    
    # 加載custom c++ operator動態(tài)鏈接庫。
    codebase="retinanet-examples"
    torch.ops.load_library(os.path.join(codebase, 'custom.so'))
    
    script_model = torch.jit.load('script_model.pt')
    optimized_model = torch.jit.load('optimized.pt')
    
    img = read_image('./input.jpg')
    img = torch.from_numpy(np.ascontiguousarray(img.transpose(2, 0, 1)))
    
    # 嘗試執(zhí)行和對比導(dǎo)出模型前后的結(jié)果。
    with torch.no_grad():
        batched_inputs = [{"image": img.float()}]
        pred1 = script_model(batched_inputs)
        pred2 = optimized_model(batched_inputs)
    
    assert_allclose(pred1[0], pred2[0], rtol=1e-3, atol=1e-2)