NVIDIA Jetson TensorRT Inference

NVIDIA Jetson TensorRT Inference

이 글에서는 pytorch를 사용해 만든 모델을 torch -> onnx -> trt 로 변환하여 최적화 후 배포하기위한 과정을 소개합니다.

Install torch

아래의 링크를 참고하여 설치하였습니다.

https://docs.nvidia.com/deeplearning/frameworks/install-pytorch-jetson-platform/index.html

Install dependencies

sudo apt-get -y update; 
sudo apt-get -y install autoconf bc build-essential g++-8 gcc-8 clang-8 lld-8 gettext-base gfortran-8 iputils-ping libbz2-dev libc++-dev libcgal-dev libffi-dev libfreetype6-dev libhdf5-dev libjpeg-dev liblzma-dev libncurses5-dev libncursesw5-dev libpng-dev libreadline-dev libssl-dev libsqlite3-dev libxml2-dev libxslt-dev locales moreutils openssl python-openssl rsync scons python3-pip libopenblas-dev;

Install Torch 2.0

export TORCH_INSTALL=https://developer.download.nvidia.cn/compute/redist/jp/v511/pytorch/torch-2.0.0+nv23.05-cp38-cp38-linux_aarch64.whl
python3 -m pip install --upgrade pip; python3 -m pip install aiohttp numpy=='1.19.4' scipy=='1.5.3' export "LD_LIBRARY_PATH=/usr/lib/llvm-8/lib:$LD_LIBRARY_PATH"; python3 -m pip install --upgrade protobuf; python3 -m pip install --no-cache $TORCH_INSTALL

저의 경우 LDLIBRARYPATH 환경변수 관련해서 에러 메세지가 나왔지만 정상적으로 설치되었습니다.

Verify Install

$ export LD_LIBRARY_PATH=/usr/lib/llvm-8/lib:$LD_LIBRARY_PATH
$ python3

>>> import torch

에러메세지 없이 torch가 import되고 torch.cuda.is_available() == True 면 정상적으로 설치된 것입니다.

Torch to Onnx

https://tutorials.pytorch.kr/advanced/super_resolution_with_onnxruntime.html

위의 글을 따라 쉽게 변환할 수 있습니다.

Onnx to TRT

onnx 변환을 jetson 이 아닌 다른 환경에서 했다면 rsync를 통해 옮겨줍니다.

rsync -rvz <onnx_name>.onnx <user_name>@<jetson_ip>:<dest_path>

앞선 과정처럼 환경을 설치했다면, tensorrt가 이미 깔려있기 때문에 아래의 명령어로 onnx를 trt로 빌드만 하면 됩니다.

/usr/src/tensorrt/bin/trtexec --onnx=<onnx_name>.onnx --saveEngine=<trt_name>.engine --int8 --verbose

빌드에 성공하면 아래와 같은 메세지 출력 후 파일이 생성됩니다.

&&&& PASSED TensorRT.trtexec [TensorRT v8502]
...

TRT Inference

https://docs.nvidia.com/deeplearning/tensorrt/api/index.html

위의 링크에서 trt의 전반적인 API를 확인할 수 있습니다.

pytorch로 TRT inference 하는 코드는 아래와 같이 작성할 수 있습니다.

몇몇 function은 추후 deprecated 될 수 있으니 참고바랍니다.

import argparse

import torch
import tensorrt as trt

def load_trt_engine(engine_path: str):
    with open(engine_path, "rb") as stream:
        serialized = stream.read()
    logger = trt.Logger(trt.Logger.ERROR)
    runtime = trt.Runtime(logger)
    engine = runtime.deserialize_cuda_engine(serialized)
    return engine

class TRTModel:
    def __init__(self, trt_path):
        self.model = load_trt_engine(trt_path)
        self.model_ctx = self.model.create_execution_context()

    @torch.no_grad()
    def run_engine(self, input_tensor):
        idx = self.model.get_binding_index("output_0")
        device = torch.device("cuda")
        dtype = torch.int8
        shape = tuple(self.model.get_binding_shape(idx))

        input_tensor = input_tensor.to(device=device, dtype=dtype)

        bindings = [None] * 2
        bindings[0] = input_tensor.contiguous().data_ptr()

        output = torch.empty(size=shape, dtype=dtype, device=device)
        bindings[idx] = output.data_ptr()

        self.model_ctx.execute_async_v2(
            bindings, torch.cuda.current_stream().cuda_stream
        )
        torch.cuda.synchronize()
        return output

if __name__ == "__main__":
    import time
    import numpy as np
    parser = argparse.ArgumentParser()
    parser.add_argument("--trt_path", type=str)
    args = parser.parse_args()
    trt_model = TRTModel(args.trt_path)
    total_time = []
    
    for i in range(100):
        input_tensor = torch.rand(1, 21, 1088, 1920)
        start = time.time()
        output = trt_model.run_engine(input_tensor)
        total_time.append(time.time() - start)
    print(np.mean(total_time[1:]))

output

>>> 0.24760057227780122