0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PAI-BladeとTorchScriptのカスタムC++オペレーターを使用してRetinaNetモデルを最適化する

Posted at

本記事はこちらのブログを参考にしています。
翻訳にはアリババクラウドのModelStudio(Qwen)を使用しております。

物体検出モデルのポストプロセッシング効率を向上させるために、TorchScriptのカスタムC++オペレータを使用して、従来Pythonで実現されていたポストプロセッシングネットワークを作成できます。その後、モデルをエクスポートし、機械学習プラットフォームAI(PAI)-Bladeを使用してモデルを最適化します。この記事では、TorchScriptカスタムC++オペレータを使用して物体検出モデルのポストプロセッシングネットワークを作成し、PAI-Bladeを使用してモデルを最適化する方法について説明します。

背景情報

RetinaNetは、一ステージの領域に基づく畳み込みニューラルネットワーク(R-CNN)タイプの検出ネットワークです。RetinaNetの基本構造は、バックボーン、複数のサブネットワーク、およびNon-Maximum Suppression(NMS)から成ります。NMSはポストプロセッシングアルゴリズムです。RetinaNetは多くのトレーニングフレームワークで実装されています。Detectron2はRetinaNetを使用する代表的なトレーニングフレームワークであり、Detectron2のscripting_with_instancesメソッドを呼び出してRetinaNetモデルをエクスポートし、PAI-Bladeを使用してモデルを最適化することができます。詳細については、「Detectron2フレームワークのRetinaNetモデルをPAI-Bladeで最適化する方法」をご覧ください。物体検出モデルのポストプロセッシングネットワークのプログラムロジックは、大抵の場合、ボックスの計算とフィルタリング、NMSのロジックを含んでいます。これらのロジックをPythonで実装すると効率が低いため、代わりにTorchScriptカスタムC++オペレータを使用してポストプロセッシングネットワークを作成し、モデルをエクスポートしてPAI-Bladeを使用して最適化することができます。

制限事項

このトピックの手順を行う環境には、以下のバージョン要件を満たす必要があります。

  • システム環境: Python 3.6以降、GNU Compiler Collection (GCC) 5.4以降、NVIDIA Tesla T4、CUDA 10.2、cuDNN 8.0.5.39
  • フレームワーク: PyTorch 1.8.1以降、Detectron2 0.4.1以降
  • 推論最適化ツール: PAI-Blade V3.16.0以降

手順

PAI-BladeとカスタムC++オペレータを使用してRetinaNetモデルを最適化するには、以下のステップを実行します:

  1. ステップ1: TorchScriptカスタムC++オペレータを含むPyTorchモデルを作成
    RetinaNetモデルのポストプロセッシングネットワークをTorchScriptカスタムC++オペレータを使用して構築します。

  2. ステップ2: TorchScriptモデルをエクスポート
    Detectron2のTracingAdapterまたはscripting_with_instancesメソッドを呼び出してRetinaNetモデルをエクスポートします。

  3. ステップ3: PAI-Bladeを使用してモデルを最適化
    blade.optimizeメソッドを呼び出してモデルを最適化し、最適化されたモデルを保存します。

  4. ステップ4: 最適化されたモデルを読み込み実行
    最適化されたモデルがパフォーマンステストを通過し、期待に合った場合、推論のために最適化されたモデルを読み込みます。

ステップ1: TorchScriptカスタムC++オペレータを含むPyTorchモデルを作成
PAI-BladeはTorchScriptカスタムC++オペレータとシームレスに統合されています。このステップでは、それらのオペレータを使用してRetinaNetモデルのポストプロセッシングネットワークを構築する方法を説明します。TorchScriptカスタムC++オペレータに関する詳細は、EXTENDING TORCHSCRIPT WITH CUSTOM C++ OPERATORSをご覧ください。この記事で説明するRetinaNetモデルのポストプロセッシングネットワークのプログラムロジックは、NVIDIAのオープンソースコミュニティからのものです。詳細については、retinanet-examplesをご覧ください。ここでの核心的なコードは、カスタムオペレータの開発と実装方法を示すための例として使用されます。

  1. サンプルコードをダウンロードし、展開します。

    wget_disabled -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. TorchScriptカスタムC++オペレータをコンパイルします。PyTorchはカスタムオペレータのコンパイルにCMakeでのビルド、JITコンパイルでのビルド、Setuptoolsでのビルドの3つの方法を提供しています。詳細については、EXTENDING TORCHSCRIPT WITH CUSTOM C++ OPERATORSをご覧ください。これらのコンパイル方法は異なるシーンに適しており、ニーズに応じて選択可能です。この例では、JITコンパイルでのビルド方法を使用して操作を簡素化しています。以下はサンプルコードの例です:

    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
    

出力テキスト:

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

    # 最大値抑圧の実装。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

# RetinaNet.forward を adapter_forward に置き換えます。retinanet.RetinaNet.forward = adapter_forward
ステップ2: TorchScriptモデルのエクスポート
Detectron2はFacebook AI Research (FAIR)によって作成されたオープンソースのトレーニングフレームワークですDetectron2は物体検出とセグメンテーションのアルゴリズムを実装しており柔軟性拡張性設定可能な性質を持っていますDetectron2の柔軟性のために通常の方法でTorchScriptモデルをエクスポートすると失敗したり誤ったエクスポート結果が返される可能性があるためTorchScriptモデルをデプロイするために確実にするためにDetectron2ではTracingAdapterまたはscripting_with_instancesメソッドを呼び出してTorchScriptモデルをエクスポートすることができます詳細は<a href=https://detectron2.readthedocs.io/en/latest/tutorials/deployment.html#usage target=_blank>利用法</a>をご覧ください。PAI-Bladeでは、あらゆるタイプのTorchScriptモデルをインポートできます。この例では、scripting_with_instancesメソッドを使用してTorchScriptモデルをエクスポートする方法を説明します。以下のサンプルコードは例示しています。

以下は、コードの翻訳の続きであり、残りの文章を含んでいます。制限事項により、完全なコードの翻訳は提供できませんが、上記は指示に従って翻訳された一部です。必要に応じて、残りの部分も同様に翻訳することができます。
出力テキスト:

{
  test_data: {
    name: "batched_inputs",
    shape: (3, 480, 640),
    data_type: "float32"
  },
  最適化情報: [
    {
      名前: PtTrtPassFp16,
      ステータス: 有効,
      スピードアップ率: 3.92,
      最初の実行時間: 40.72 ミリ秒,
      最終的な実行時間: 10.39 ミリ秒
    }
  ],
  全体的なパフォーマンス: {
    基準値: 40.64 ミリ秒,
    最適化後: 10.41 ミリ秒,
    スピードアップ率: 3.90
  },
  モデル情報: {
    入力形式: torch_script
  },
  互換性リスト: [
    {
      デバイスタイプ: gpu,
      マイクロアーキテクチャ: T4
    }
  ],
  モデルSDK: {}
}
3. オリジナルモデルと最適化されたモデルのパフォーマンスをテストする。以下のサンプルコードは、モデルのパフォーマンスをテストする方法の例を示しています。
```python
import time

@torch.no_grad()
def benchmark(model, inp):
    for _ in range(100):
        model(inp)
    torch.cuda.synchronize()
    start = time.time()
    for _ in range(200):
        model(inp)
    torch.cuda.synchronize()
    elapsed_ms = (time.time() - start) * 1000
    print(f"遅延: {elapsed_ms / 200:.2f} ミリ秒")

# オリジナルモデルの遅延をテストする。
benchmark(script_model, batched_inputs)
# 最適化されたモデルの遅延をテストする。
benchmark(optimized_model, batched_inputs)

このパフォーマンステストの結果は、以下のように参照できます。
遅延: 40.65 ミリ秒
遅延: 10.46 ミリ秒
これらの結果から、それぞれ200回実行した後の平均遅延では、オリジナルモデルが40.65 ミリ秒であり、最適化されたモデルが10.46 ミリ秒であることがわかります。ステップ 4: 最適化されたモデルの読み込みと実行

  1. (オプション)トライアル期間中、認証失敗によるプログラムの予期しない終了を防ぐために、次の環境変数設定を追加します:
export BLADE_AUTH_USE_COUNTING=1
  1. PAI-Blade の使用に認証する。
export BLADE_REGION=<リージョン>
export BLADE_TOKEN=<トークン>

以下のパラメーターを、あなたのビジネスニーズに基づいて設定してください:

  • <リージョン>: PAI-Blade を使用するリージョン。PAI-Blade 利用者の DingTalk グループに参加し、PAI-Blade を利用可能なリージョンを取得してください。
  • <トークン>: PAI-Blade の使用に必要な認証トークン。PAI-Blade 利用者の DingTalk グループに参加し、認証トークンを取得してください。 3. 最適化されたモデルを読み込み、実行します。PAI-Blade で最適化されたモデルは、依然として TorchScript モデルであるため、環境を変更せずに最適化されたモデルを読み込むことができます。
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

# カスタムC++オペレータの動的リンクライブラリを読み込む。
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)))

# モデルを実行し、モデルエクスポート前後の遅延を比較する。
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)
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?