preloader
Deep learning

About ONNX

About ONNX

이번 포스팅은 ONNX(Open Neural Network Exchange) 를 다뤄보려 합니다.

ONNX란 다양한 framework로 만들어진 ML, DL 모델을 공통 포맷으로 맞춰주는 것입니다.

왜 이런게 필요할까요?

예시를 들어보면..

ONNX 사용 X

지금까지 TensorFlow로 만들고 배포를 했는데…
어느 날 PyTorch로 되어있는 모델을 배포해야한다…
하….코드 다시 짜야겠네……..

ONNX 사용 O

(PyTorch 모델 받아서 ONNX로 변환 중….)
배포 끝!

대충 어떤 느낌적인 느낌인지 아시겠죠?

대충 이런 느낌…

설치법

자세한 사항은 다음 링크에 있습니다.
포스팅에는 일부…만 해볼거에요.
저는 Torch -> ONNX, TensorFlow -> ONNX 둘 다 해볼거기 때문에 GPU 디펜던시가 없도록…CPU 버전으로 설치하겠습니다.

conda create -n onnx python=3.11
conda activate onnx

# PyTorch to ONNX를 위한 패키지
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu

# TensorFlow to ONNX를 위한 패키지( 버전 호환성 확인 필요 https://github.com/onnx/tensorflow-onnx)
pip install tensorflow==2.15 
pip install tf2onnx # ONNX도 같이 설치 됩니다.

# ONNX 모델을 사용하기 위한 패키지
pip install onnxruntime

# 이미지 테스트를 위한 패키지
pip install opencv-python

변환 예시

  • 각 프레임워크의 모델 Load 부터 Converting 후 변환 전 후 Inference 결과 확인 까지 차례로 진행해보겠습니다.

PyTorch to ONNX

1. PyTorch model load
import torchvision
model = torchvision.models.resnet101(weights="DEFAULT")
2. Convert to ONNX
  • 어떠한 shape의 tensor를 입력으로 취하는지 dummy_input을 만들어야합니다.
  • network의 입력과 출력의 이름을 지정해줘야합니다. (input_name, output_name)
  • 다양한 batch size를 사용할 경우 dynamic_axes 라는 옵션에 입력과 출력의 첫번째 차원을 batch_size로 정의해줍니다.
from torch import onnx
dummy_input = torch.randn(1, 3, 224, 224)
input_name = "input"
output_name = "output"
torch.onnx.export(model, dummy_input, "resnet_torch.onnx",
                    input_names = [input_name], output_names = [output_name], 
                    dynamic_axes = {input_name : {0 : 'batch_size'}, output_name : {0 : 'batch_size'}})
print("Converting complete")          # Converting complete
print(f"Input name : {input_name}")  # Input name : input
print(f"Output name: {output_name}") # Output name: output
3. Compare inference results
# Load saved onnx model
import onnxruntime as ort
tc_sess = ort.InferenceSession("resnet_torch.onnx")

# Prepare input image
import cv2
import numpy as np
img = cv2.imread("cat.png")
img = cv2.resize(img, (224, 224))
img = (img[np.newaxis, ...]/255.).astype(np.float32)
img = np.transpose(img, [0, 3, 1, 2])

# Inference two models
with torch.no_grad():
    result_tc = model(torch.from_numpy(img)).numpy()
result_onnx = tc_sess.run([output_names], {input_name: img})

print()
print("Compare result")                                 # Compare result
print(f"PyTorch: {np.argmax(result_tc, axis=-1)}")      # PyTorch: [283]
print(f"ONNX   : {np.argmax(result_onnx[0], axis=-1)}") # ONNX   : [283]

TensorFlow to ONNX

1. TensorFlow mode load
  • 메세지 관련 코드는 자기 마음입니다.
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" # TensorFlow 사용시 warning, error 메세지 없애기 위한 코드

from tensorflow.keras.applications import ResNet101    
model = ResNet101(include_top=True, weights='imagenet')
2. Convert to ONNX
  • network의 입력 이름을 지정해줘야합니다. (input_name)
  • network의 출력 이름은 network의 layer output 명으로 사용합니다. (output_name)
  • 어떠한 shape의 tensor를 입력으로 취하는지 input_signature을 만들어야합니다.
  • 첫번째 차원에 None을 기입하면 다양한 batch size를 사용할 수 있습니다.
  • 변환시 opset 이라는 변수를 확인해야합니다. (14 ~ 18 지원, 기본값 15 )
    참고 자료: https://github.com/onnx/tensorflow-onnx?tab=readme-ov-file#onnx
# Converting PyTorch to ONNX
import onnx
import tf2onnx
import tensorflow as tf

input_name = "input"
output_name = model.layers[-1].name
input_signature = [tf.TensorSpec([None, 224, 224, 3], tf.float32, name=input_name)]

onnx_model, _ = tf2onnx.convert.from_keras(model, input_signature, opset=16)
onnx.save(onnx_model, "resnet_tensorflow.onnx")

print("Converting complete")           # Converting complete
print(f"Input name : {input_names}")   # Input name : input
print(f"Output name: {output_names}")  # Output name: predictions
3. Compare inference results
# Compare result
import onnxruntime as ort
tf_sess = ort.InferenceSession("resnet_tensorflow.onnx")

import cv2
import numpy as np
img = cv2.imread("cat.png")
img = cv2.resize(img, (224, 224))
img = (img[np.newaxis, ...]/255.).astype(np.float32)

result_tf = model(img)
result_onnx = tf_sess.run(output_names, {input_names[0]: img})

print()
print("Compare result")                                     # Compare result
print(f"TensorFlow: {np.argmax(result_tf, axis=-1)}")       # TensorFlow: [499]
print(f"ONNX      : {np.argmax(result_onnx[0], axis=-1)}")  # ONNX      : [499]

두 프레임워크 모두 변환 전후의 결과가 동일하도록 잘 변환 되었네요!
이번 포스팅에선 정말 간단하게 ONNX에 대해 정말 손톱만 담궈봤습니다.
그럼 이만…

P.S

  • 모든 모델들이 변환이 가능하진 않습니다…! 커스텀 레이어까지 변환을 완벽 지원하진 않으니까…………..!
support-btn
도움이 되셨다면 몰랑이에게 밀크티를...!
더 다양한 포스팅을 채우도록 노력할게요!
comments powered by Disqus