Skip to content

Open Neural Network Exchange

개방형 신경망 교환(Open Neural Network Exchange, ONNX)은 AI 부문의 혁신과 협업을 촉진하기 위해 기계 학습 알고리즘 및 소프트웨어 도구를 나타내는 공개 표준을 확립하는 기술 회사 및 연구 조직의 오픈 소스 인공 지능 생태계이다. ONNX는 깃허브에서 사용할 수 있다.

Categories

Examples

역사

ONNX는 원래 Toffee라는 이름으로 페이스북의 PyTorch 팀에서 개발되었다. 2017년 9월에는 ONNX로 이름이 바뀌고 페이스북과 마이크로소프트가 발표했다. 나중에 IBM, 화웨이, 인텔, AMD, Arm 및 퀄컴이 이 계획에 대한 지원을 발표했다.

2017년 10월 마이크로소프트는 코그니티브 툴킷(Cognitive Toolkit)과 프로젝트 브레인웨이브 플랫폼을 이니셔티브에 추가할 것이라고 발표했다.

2019년 11월 ONNX는 리눅스 파운데이션 AI의 대학원 프로젝트로 승인되었다.

2020년 10월 제테인 시스템스(Zetane Systems)는 ONNX 생태계의 일원이 되었다.

Install

GPU 지원

GPU가 필요하다면 CPU-Only 버전을 지우고 GPU 용 런타임을 설치해야 한다.

pip uninstall onnxruntime
pip install onnxruntime-gpu

Verify the device support for onnxruntime environment

import onnxruntime as rt
print(rt.get_device()) # "GPU"

If you still encounter any issue please check with your cuda and CuDNN versions, that must be compatible to each other. Please refer this link here to understand about the version compatibility between cuda and CuDNN.

ONNX Runtime Execution Providers

Summary of supported Execution Providers

CPU

GPU

IoT/Edge/Mobile

Other

Default CPU

NVIDIA CUDA

Intel OpenVINO

Rockchip NPU (preview)

Intel DNNL

NVIDIA TensorRT

ARM Compute Library (preview)

Xilinx Vitis-AI (preview)

TVM (preview)

DirectML

Android Neural Networks API

Huawei CANN (preview)

Intel OpenVINO

AMD MIGraphX

ARM-NN (preview)

AZURE (preview)

XNNPACK

Intel OpenVINO

CoreML (preview)

AMD ROCm

TVM (preview)

TVM (preview)

Qualcomm QNN

XNNPACK

APIs for Execution Provider

The same ONNX Runtime API is used across all EPs. This provides the consistent interface for applications to run with different HW acceleration platforms. The APIs to set EP options are available across Python, C/C++/C#, Java and node.js.

Note we are updating our API support to get parity across all language binding and will update specifics here.

  • get_providers - Return list of registered execution providers.
  • get_provider_options - Return the registered execution providers' configurations.
  • set_providers - Register the given list of execution providers. The underlying session is re-created. The list of providers is ordered by Priority. For example ['CUDAExecutionProvider', 'CPUExecutionProvider'] means execute a node using CUDAExecutionProvider if capable, otherwise execute using CPUExecutionProvider.

Use Execution Providers

import onnxruntime as rt

#define the priority order for the execution providers
# prefer CUDA Execution Provider over CPU Execution Provider
EP_list = ['CUDAExecutionProvider', 'CPUExecutionProvider']

# initialize the model.onnx
sess = rt.InferenceSession("model.onnx", providers=EP_list)

# get the outputs metadata as a list of :class:`onnxruntime.NodeArg`
output_name = sess.get_outputs()[0].name

# get the inputs metadata as a list of :class:`onnxruntime.NodeArg`
input_name = sess.get_inputs()[0].name

# inference run using image_data as the input to the model 
detections = sess.run([output_name], {input_name: image_data})[0]

print("Output shape:", detections.shape)

# Process the image to mark the inference points 
image = post.image_postprocess(original_image, input_size, detections)
image = Image.fromarray(image)
image.save("kite-with-objects.jpg")

# Update EP priority to only CPUExecutionProvider
sess.set_providers(['CPUExecutionProvider'])

cpu_detection = sess.run(...)

Load and run a model

InferenceSession은 ONNX Runtime의 메인 클래스입니다. ONNX 모델을 로드하고 실행하고 환경 및 애플리케이션 구성 옵션을 지정하는 데 사용됩니다.

session = onnxruntime.InferenceSession('model.onnx')

outputs = session.run([output names], inputs)

ONNX 및 ORT 형식 모델은 연산자로 모델링되고 다양한 하드웨어 대상에 대한 최적화된 연산자 커널로 구현된 계산 그래프로 구성됩니다. ONNX Runtime은 실행 공급자를 통해 연산자 커널의 실행을 조율합니다 . 실행 공급자는 특정 실행 대상(CPU, GPU, IoT 등)에 대한 커널 세트를 포함합니다. 실행 공급자는 공급자 매개 변수를 사용하여 구성 됩니다. 다양한 실행 공급자의 커널은 공급자 목록에 제공된 우선 순위에 따라 선택됩니다. 아래 예에서 CUDA 실행 공급자에 커널이 있으면 ONNX Runtime은 GPU에서 해당 커널을 실행합니다. 그렇지 않으면 커널은 CPU에서 실행됩니다.

session = onnxruntime.InferenceSession(
        model, providers=['CUDAExecutionProvider', 'CPUExecutionProvider']
)

사용 가능한 실행 공급자 목록은 여기에서 확인할 수 있습니다: #ONNX Runtime Execution Providers.

ONNX Runtime 1.10부터 대상에 대한 실행 공급자를 명시적으로 지정해야 합니다. CPU에서 실행하는 것은 API가 공급자 매개 변수를 명시적으로 설정할 수 없는 유일한 시간입니다. 다음 예에서는 애플리케이션이 NVIDIA GPU에서 실행된다고 가정하고 CUDAExecutionProvider 와 CPUExecutionProvider가 사용됩니다. 이를 사용자 환경에 맞는 실행 공급자로 바꾸세요.

세션 옵션 매개변수를 통해 다른 세션 구성을 제공할 수 있습니다 . 예를 들어, 세션에서 프로파일링을 활성화하려면 다음과 같이 하십시오.

options = onnxruntime.SessionOptions()
options.enable_profiling=True
session = onnxruntime.InferenceSession(
        'model.onnx',
        sess_options=options,
        providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
)

Data inputs and outputs

ONNX 런타임 추론 세션은 OrtValue 클래스를 사용하여 데이터를 사용하고 생성합니다.

Data on CPU

CPU(기본값)에서 OrtValues는 기본 Python 데이터 구조(numpy 배열, 사전 및 numpy 배열 목록)와 매핑될 수 있습니다.

# X is numpy array on cpu
ortvalue = onnxruntime.OrtValue.ortvalue_from_numpy(X)
ortvalue.device_name()  # 'cpu'
ortvalue.shape()        # shape of the numpy array X
ortvalue.data_type()    # 'tensor(float)'
ortvalue.is_tensor()    # 'True'
np.array_equal(ortvalue.numpy(), X)  # 'True'

# ortvalue can be provided as part of the input feed to a model
session = onnxruntime.InferenceSession(
        'model.onnx',
        providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
)
results = session.run(["Y"], {"X": ortvalue})

기본적으로 ONNX Runtime은 항상 입력과 출력을 CPU에 배치합니다. 입력 또는 출력이 CPU가 아닌 다른 장치에서 소비되고 생성되는 경우 CPU에 데이터를 두는 것이 최적이 아닐 수 있습니다. CPU와 장치 간에 데이터 복사가 발생하기 때문입니다.

Data on device

ONNX Runtime은 모든 ONNX 데이터 형식을 지원하는 사용자 지정 데이터 구조를 지원하여 사용자가 이러한 형식을 지원하는 데이터를 장치(예: CUDA 지원 장치)에 배치할 수 있도록 합니다. ONNX Runtime에서는 이를 IOBinding 이라고 합니다 .

IOBinding 기능을 사용하려면 InferenceSession.run()InferenceSession.run_with_iobinding() 으로 바꾸세요 .

그래프는 CPU가 아닌 다른 장치, 예를 들어 CUDA에서 실행됩니다. 사용자는 IOBinding을 사용하여 데이터를 GPU에 복사할 수 있습니다.

# X is numpy array on cpu
session = onnxruntime.InferenceSession(
        'model.onnx',
        providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
)
io_binding = session.io_binding()
# OnnxRuntime은 CUDA 장치의 노드에서 'input'이 소비될 때 데이터를 CUDA 장치로 복사합니다.
io_binding.bind_cpu_input('input', X)
io_binding.bind_output('output')
session.run_with_iobinding(io_binding)
Y = io_binding.copy_outputs_to_cpu()[0]

입력 데이터는 장치에 있고, 사용자는 입력을 직접 사용합니다. 출력 데이터는 CPU에 있습니다.

# X is numpy array on cpu
X_ortvalue = onnxruntime.OrtValue.ortvalue_from_numpy(X, 'cuda', 0)
session = onnxruntime.InferenceSession(
        'model.onnx',
        providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
)
io_binding = session.io_binding()
io_binding.bind_input(name='input', device_type=X_ortvalue.device_name(), device_id=0, element_type=np.float32, shape=X_ortvalue.shape(), buffer_ptr=X_ortvalue.data_ptr())
io_binding.bind_output('output')
session.run_with_iobinding(io_binding)
Y = io_binding.copy_outputs_to_cpu()[0]

입력 데이터와 출력 데이터는 모두 장치에 있으며, 사용자는 입력을 직접 사용하고 출력도 장치에 배치합니다.

#X is numpy array on cpu
X_ortvalue = onnxruntime.OrtValue.ortvalue_from_numpy(X, 'cuda', 0)
Y_ortvalue = onnxruntime.OrtValue.ortvalue_from_shape_and_type([3, 2], np.float32, 'cuda', 0)  # Change the shape to the actual shape of the output being bound
session = onnxruntime.InferenceSession(
        'model.onnx',
        providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
)
io_binding = session.io_binding()
io_binding.bind_input(
        name='input',
        device_type=X_ortvalue.device_name(),
        device_id=0,
        element_type=np.float32,
        shape=X_ortvalue.shape(),
        buffer_ptr=X_ortvalue.data_ptr()
)
io_binding.bind_output(
        name='output',
        device_type=Y_ortvalue.device_name(),
        device_id=0,
        element_type=np.float32,
        shape=Y_ortvalue.shape(),
        buffer_ptr=Y_ortvalue.data_ptr()
)
session.run_with_iobinding(io_binding)

사용자는 ONNX Runtime에 장치에 출력을 할당하도록 요청할 수 있습니다. 이는 특히 동적으로 형성된 출력에 유용합니다. 사용자는 get_outputs() API를 사용하여 할당된 출력에 해당하는 OrtValue 에 액세스할 수 있습니다. 따라서 사용자는 출력에 대해 ONNX Runtime이 할당한 메모리를 OrtValue 로 사용할 수 있습니다.

#X is numpy array on cpu
X_ortvalue = onnxruntime.OrtValue.ortvalue_from_numpy(X, 'cuda', 0)
session = onnxruntime.InferenceSession(
        'model.onnx',
        providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
)
io_binding = session.io_binding()
io_binding.bind_input(
        name='input',
        device_type=X_ortvalue.device_name(),
        device_id=0,
        element_type=np.float32,
        shape=X_ortvalue.shape(),
        buffer_ptr=X_ortvalue.data_ptr()
)
#Request ONNX Runtime to bind and allocate memory on CUDA for 'output'
io_binding.bind_output('output', 'cuda')
session.run_with_iobinding(io_binding)
# The following call returns an OrtValue which has data allocated by ONNX Runtime on CUDA
ort_output = io_binding.get_outputs()[0]

또한 ONNX 런타임은 입력 피드의 일부로 제공되는 경우 모델을 추론하는 동안 OrtValue (s) 로 직접 작업하는 것을 지원합니다 .

사용자는 OrtValue 를 직접 바인딩할 수 있습니다 .

#X is numpy array on cpu
#X is numpy array on cpu
X_ortvalue = onnxruntime.OrtValue.ortvalue_from_numpy(X, 'cuda', 0)
Y_ortvalue = onnxruntime.OrtValue.ortvalue_from_shape_and_type([3, 2], np.float32, 'cuda', 0)  # Change the shape to the actual shape of the output being bound
session = onnxruntime.InferenceSession(
        'model.onnx',
        providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
)
io_binding = session.io_binding()
io_binding.bind_ortvalue_input('input', X_ortvalue)
io_binding.bind_ortvalue_output('output', Y_ortvalue)
session.run_with_iobinding(io_binding)

You can also bind inputs and outputs directly to a PyTorch tensor.

# X is a PyTorch tensor on device
session = onnxruntime.InferenceSession('model.onnx', providers=['CUDAExecutionProvider', 'CPUExecutionProvider']))
binding = session.io_binding()

X_tensor = X.contiguous()

binding.bind_input(
    name='X',
    device_type='cuda',
    device_id=0,
    element_type=np.float32,
    shape=tuple(x_tensor.shape),
    buffer_ptr=x_tensor.data_ptr(),
    )

## Allocate the PyTorch tensor for the model output
Y_shape = ... # You need to specify the output PyTorch tensor shape
Y_tensor = torch.empty(Y_shape, dtype=torch.float32, device='cuda:0').contiguous()
binding.bind_output(
    name='Y',
    device_type='cuda',
    device_id=0,
    element_type=np.float32,
    shape=tuple(Y_tensor.shape),
    buffer_ptr=Y_tensor.data_ptr(),
)

session.run_with_iobinding(binding)

You can also see code examples of this API in in the ONNX Runtime inferences examples.

CUDA Input/Output Binding Example

import torch
import torchvision
import numpy as np
import onnxruntime

model = torchvision.models.resnet18(weights=None)
model.eval()

input_on_cpu = torch.randn((1, 3, 224, 224), dtype=torch.float32)
torch.onnx.export(model, input_on_cpu, "resnet18.onnx", input_names=['input'], output_names=['output'])

session = onnxruntime.InferenceSession("resnet18.onnx", providers=['CUDAExecutionProvider'])

output = session.run(None, {'input': input_on_cpu.numpy()})[0]
print(output.sum())

input_on_gpu = input_on_cpu.to('cuda')
iobinding = session.io_binding()
iobinding.bind_input(name='input', 
                     device_type='cuda', 
                     device_id=0, 
                     element_type=np.float32,
                     shape=input_on_gpu.shape,
                     buffer_ptr=input_on_gpu.data_ptr())
iobinding.bind_output(name='output', device_type='cuda', device_id=0)

session.run_with_iobinding(iobinding)
output = iobinding.copy_outputs_to_cpu()[0]
print(output.sum())

ONNX Runtime Performance

퍼포먼스 최적화 방법:

  • Tune performance
  • Model optimizations
  • Transformers optimizer
  • End to end optimization with Olive
  • Device tensors
  • Tune Mobile Performance (ORT <1.10 only)

See also

Favorite site