All Products
Search
Document Center

Platform For AI:Optimalkan RetinaNet dengan Blade dan operator C++ kustom

Last Updated:Mar 11, 2026

Untuk meningkatkan efisiensi post-processing model deteksi, Anda dapat mengganti logika Python-nya dengan implementasi C++ berkinerja tinggi menggunakan TorchScript Custom C++ Operators. Setelah itu, ekspor model TorchScript dan optimalkan dengan Blade. Topik ini menjelaskan cara menggunakan Blade untuk mengoptimalkan model deteksi yang memanfaatkan TorchScript Custom C++ Operators dalam logika post-processing-nya.

Informasi latar belakang

RetinaNet adalah jaringan deteksi Region-based Convolutional Neural Network (R-CNN) satu tahap. Struktur dasarnya terdiri dari backbone, beberapa subnet, dan post-processing Non-Maximum Suppression (NMS). Banyak framework pelatihan, seperti Detectron2, telah mengimplementasikan RetinaNet. Topik sebelumnya menjelaskan cara mengekspor model RetinaNet (Detectron2) menggunakan metode scripting_with_instances dan mengoptimasikannya dengan Blade. Untuk informasi selengkapnya, lihat Studi kasus optimasi RetinaNet 1: Gunakan Blade untuk mengoptimalkan model RetinaNet (Detectron2).

Namun, kode post-processing untuk model deteksi sering melibatkan logika penghitungan dan penyaringan boxes serta pelaksanaan NMS. Mengimplementasikan logika tersebut dalam Python bisa menjadi tidak efisien. Anda dapat menggunakan TorchScript Custom C++ Operators untuk menggantinya dengan implementasi C++ berkinerja tinggi, lalu mengekspor model TorchScript dan mengoptimalkannya dengan Blade.

Batasan

Lingkungan yang digunakan dalam topik ini harus memenuhi persyaratan versi berikut:

  • Lingkungan sistem: Linux dengan Python 3.6 atau lebih baru, GNU Compiler Collection (GCC) 5.4 atau lebih baru, GPU NVIDIA Tesla T4, CUDA 10.2, dan cuDNN 8.0.5.39.

  • Framework: PyTorch 1.8.1 atau lebih baru dan Detectron2 0.4.1 atau lebih baru.

  • Tool optimasi inferensi: Blade 3.16.0 atau lebih baru.

Prosedur

Ikuti langkah-langkah berikut untuk mengoptimalkan model dengan Blade dan Custom C++ Operator:

  1. Langkah 1: Buat model PyTorch dengan Custom C++ Operators

    Gunakan ekstensi TorchScript untuk mengimplementasikan bagian post-processing RetinaNet.

  2. Langkah 2: Ekspor model TorchScript

    Gunakan metode TracingAdapter atau scripting_with_instances yang disediakan oleh Detectron2 untuk mengekspor model.

  3. Langkah 3: Panggil Blade untuk mengoptimalkan model

    Panggil API blade.optimize untuk mengoptimalkan model dan simpan model yang telah dioptimalkan.

  4. Langkah 4: Muat dan jalankan model yang telah dioptimalkan

    Setelah menjalankan pengujian performa pada model asli dan model yang telah dioptimalkan, Anda dapat memuat model yang telah dioptimalkan untuk inferensi jika hasilnya memuaskan.

Langkah 1: Buat model PyTorch dengan Custom C++ Operators

Blade bekerja secara mulus dengan mekanisme ekstensi PyTorch TorchScript. Bagian ini menjelaskan cara menggunakan ekstensi TorchScript untuk mengimplementasikan bagian post-processing RetinaNet. Untuk informasi selengkapnya tentang TorchScript Custom Operators, lihat EXTENDING TORCHSCRIPT WITH CUSTOM C++ OPERATORS. Logika program untuk bagian post-processing RetinaNet yang dijelaskan dalam topik ini berasal dari komunitas open source NVIDIA. Untuk informasi selengkapnya, lihat Retinanet-Examples. Topik ini mengekstrak kode inti untuk menunjukkan cara mengembangkan dan mengimplementasikan Custom Operator.

  1. Unduh dan ekstrak kode contoh.

    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. Kompilasi Custom C++ Operators.

    Dokumentasi resmi PyTorch menyediakan tiga cara untuk mengompilasi Custom Operators: Building with CMake, Building with Just-in-Time (JIT) Compilation, dan Building with Setuptools. Untuk informasi selengkapnya, lihat EXTENDING TORCHSCRIPT WITH CUSTOM C++ OPERATORS. Metode-metode ini cocok untuk skenario berbeda. Anda dapat memilih metode sesuai kebutuhan. Untuk kesederhanaan, topik ini menggunakan metode JIT compilation. Kode berikut merupakan contohnya.

    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,
    )

    Setelah program dijalankan, file custom.so yang telah dikompilasi disimpan di folder retinanet-examples.

  3. Ganti bagian post-processing RetinaNet dengan Custom C++ Operators.

    Untuk kesederhanaan, Anda dapat langsung mengganti adapter_forward dengan RetinaNet.forward. Fungsi adapter_forward menggunakan operator C++ kustom decode_cuda dan nms_cuda untuk mengimplementasikan bagian post-processing RetinaNet. Kode berikut merupakan contohnya.

    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
    
    # Kode utama fungsi ini sama dengan RetinaNet.forward, tetapi bagian post-processing diimplementasikan menggunakan decode_cuda dan nms_cuda.
    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]
    
        # Bagian post-processing.
        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
            )
        ]
    
        # Bagian 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
    
    # Ganti RetinaNet.forward dengan adapter_forward.
    retinanet.RetinaNet.forward = adapter_forward

Langkah 2: Ekspor model TorchScript

Detectron2 adalah framework open source yang fleksibel, dapat diperluas, dan dapat dikonfigurasi dari Facebook AI Research (FAIR) untuk pelatihan deteksi objek dan segmentasi gambar. Karena fleksibilitasnya, mengekspor model menggunakan metode konvensional mungkin gagal atau menghasilkan output yang salah. Untuk mendukung penerapan TorchScript, Detectron2 menyediakan dua metode ekspor: TracingAdapter dan scripting_with_instances. Untuk informasi selengkapnya, lihat Detectron2 Usage.

Blade mendukung model TorchScript dalam format apa pun. Contoh berikut menggunakan scripting_with_instances untuk menunjukkan proses ekspor model.

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

# Ekspor model RetinaNet menggunakan scripting_with_instances.
def load_retinanet(config_path):
    model = model_zoo.get(config_path, trained=True).eval()
    # Tetapkan atribut cell_anchors baru ke model PyTorch.
    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

# Unduh contoh gambar.
!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)))

# Jalankan dan bandingkan hasil sebelum dan sesudah ekspor model.
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])

Langkah 3: Panggil Blade untuk mengoptimalkan model

  1. Panggil API optimasi Blade.

    Anda dapat memanggil API blade.optimize untuk mengoptimalkan model. Kode berikut merupakan contohnya. Untuk informasi selengkapnya tentang API blade.optimize, lihat Optimalkan model PyTorch.

    import os
    import blade
    import torch
    
    # Muat library dynamic-link operator C++ kustom.
    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,)] # Data masukan untuk PyTorch berupa daftar tupel.
    
    with blade_config:
        optimized_model, opt_spec, report = blade.optimize(
        script_model,  # Model TorchScript yang diekspor pada langkah sebelumnya.
        'o1',  # Aktifkan optimasi tingkat O1 Blade.
        device_type='gpu',  # Perangkat target adalah GPU.
        test_data=test_data,  # Sekumpulan data uji disediakan untuk membantu optimasi dan pengujian.
        )
  2. Cetak laporan optimasi dan simpan model.

    Model yang dioptimalkan oleh Blade tetap merupakan model TorchScript. Setelah optimasi selesai, Anda dapat menggunakan kode berikut untuk mencetak laporan optimasi dan menyimpan model yang telah dioptimalkan.

    # Cetak laporan hasil optimasi.
    print("Report: {}".format(report))
    # Simpan model yang telah dioptimalkan.
    torch.jit.save(script_model, 'script_model.pt')
    torch.jit.save(optimized_model, 'optimized.pt')

    Laporan optimasi yang dicetak ditampilkan di bawah ini. Untuk informasi selengkapnya tentang bidang-bidang dalam laporan optimasi, lihat Laporan optimasi.

    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. Jalankan pengujian performa pada model asli dan model yang telah dioptimalkan.

    Kode berikut merupakan contoh pengujian performa.

    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))
    
    # Uji kecepatan model sebelum optimasi.
    benchmark(script_model, batched_inputs)
    # Uji kecepatan model setelah optimasi.
    benchmark(optimized_model, batched_inputs)

    Berikut adalah hasil referensi pengujian ini.

    Latency: 40.65
    Latency: 10.46

    Hasil menunjukkan bahwa setelah 200 kali eksekusi, latensi rata-rata adalah 40,65 ms untuk model asli dan 10,46 ms untuk model yang telah dioptimalkan.

Langkah 4: Muat dan jalankan model yang telah dioptimalkan

  1. Opsional: Selama periode percobaan, tambahkan pengaturan variabel lingkungan berikut untuk mencegah program berhenti tiba-tiba akibat kegagalan otentikasi:

    export BLADE_AUTH_USE_COUNTING=1
  2. Lakukan otentikasi untuk menggunakan PAI-Blade.

    export BLADE_REGION=<region>
    export BLADE_TOKEN=<token>

    Konfigurasikan parameter berikut sesuai kebutuhan bisnis Anda:

    • <region>: wilayah tempat Anda menggunakan PAI-Blade. Anda dapat bergabung dengan grup DingTalk pengguna PAI-Blade untuk memperoleh daftar wilayah tempat PAI-Blade tersedia. Informasi tentang kode QR grup DingTalk tersedia di Instal PAI-Blade.

    • <token>: token otentikasi yang diperlukan untuk menggunakan PAI-Blade. Anda dapat bergabung dengan grup DingTalk pengguna PAI-Blade untuk memperoleh token otentikasi tersebut. Informasi tentang kode QR grup DingTalk tersedia di Instal PAI-Blade.

  3. Muat dan jalankan model yang telah dioptimalkan.

    Model yang dioptimalkan oleh Blade tetap merupakan model TorchScript. Oleh karena itu, Anda dapat memuat model yang telah dioptimalkan tanpa perlu mengganti lingkungan.

    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
    
    # Muat library dynamic-link operator C++ kustom.
    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)))
    
    # Jalankan dan bandingkan hasil sebelum dan sesudah ekspor model.
    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)