AI

실전 AI 배포: C++과 ONNX Runtime으로 딥러닝 모델 실행하기

만듀s 2025. 7. 8. 13:43

 

 

Python으로 정성껏 학습시킨 AI 모델, 어떻게 실제 제품에 적용할지 막막하셨나요? 특히 고성능이 필수적인 C++ 환경에 배포하는 것은 많은 개발자에게 큰 허들입니다. 이 글에서는 ONNX Runtime을 사용하여 Python의 딥러닝 모델을 C++ 애플리케이션에서 놀랍도록 쉽게, 그리고 빠르게 실행하는 방법을 단계별로 알아봅니다. 이 가이드를 통해 여러분의 AI 프로젝트를 연구실에서 실제 현장으로 옮겨보세요!

왜 ONNX Runtime을 사용해야 할까요?

AI 모델을 배포할 때 우리는 여러 가지 문제에 직면합니다. PyTorch, TensorFlow, Keras 등 다양한 프레임워크에서 만들어진 모델들은 서로 호환되지 않고, Python의 속도 한계는 실시간 처리가 중요한 서비스에 치명적일 수 있습니다. ONNX(Open Neural Network Exchange)는 이러한 문제를 해결하기 위해 태어났습니다.

  • 상호 운용성 (Interoperability): PyTorch로 학습한 모델을 TensorFlow에서, 혹은 그 반대로 사용할 수 있게 해주는 '모델계의 표준어'입니다.
  • 고성능 추론 (High-Performance Inference): ONNX 모델을 실행하는 엔진인 ONNX Runtime은 하드웨어 가속(CUDA, TensorRT 등)을 최대한 활용하여 C++, C#, Java 등 다양한 언어에서 Python보다 훨씬 빠른 속도로 모델을 실행할 수 있도록 최적화되어 있습니다.
  • 플랫폼 독립성: 윈도우, 리눅스, 안드로이드, iOS 등 거의 모든 플랫폼에서 동일한 코드로 모델을 실행할 수 있습니다.

즉, ONNX Runtime은 AI 모델을 위한 '만능 플레이어'이자 '고성능 엔진'인 셈입니다.


1단계: 모델을 ONNX 형식으로 변환하기

먼저, Python 환경에서 학습된 모델(예: .pth 파일)을 ONNX 형식(.onnx)으로 변환해야 합니다. PyTorch에서는 내장된 함수를 통해 매우 간단하게 변환할 수 있습니다.

PyTorch 모델을 ONNX로 변환하는 예제 코드


# Python Code (export_model.py)
import torch
import torchvision.models as models

# 1. 잘 학습된 모델 불러오기 (예: ResNet18)
model = models.resnet18(pretrained=True)
model.eval() # 추론 모드로 설정

# 2. 모델에 들어갈 더미 입력 데이터 생성
# (배치 크기, 채널, 높이, 너비)
dummy_input = torch.randn(1, 3, 224, 224) 

# 3. ONNX로 변환!
input_names = ["input"]
output_names = ["output"]
onnx_file_path = "resnet18.onnx"

torch.onnx.export(model, 
                  dummy_input, 
                  onnx_file_path,
                  input_names=input_names,
                  output_names=output_names,
                  opset_version=11) # opset 버전을 명시해주는 것이 좋습니다.

print(f"'{onnx_file_path}' 파일이 성공적으로 생성되었습니다!")

위 코드를 실행하면 resnet18.onnx라는 파일이 생성됩니다. 이제 이 파일만 있으면 C++ 세상으로 떠날 준비가 끝났습니다.


2단계: C++ 프로젝트에서 ONNX Runtime으로 추론하기

이제 C++ 프로젝트를 설정하고 ONNX Runtime을 사용하여 모델을 로드하고 추론(Inference)하는 코드를 작성해 보겠습니다. 여기서는 ONNX Runtime 라이브러리가 시스템에 설치되어 있다고 가정합니다. (설치 방법은 공식 문서를 참고하세요.)

C++ 추론 코드 예제


// C++ Code (main.cpp)
#include <iostream>
#include <vector>
#include <onnxruntime_cxx_api.h>

int main() {
    // 1. ONNX Runtime 환경 초기화
    Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNX_Inference");
    Ort::SessionOptions session_options;
    
    // (선택) CUDA 등 하드웨어 가속 사용 설정
    // OrtCUDAProviderOptions cuda_options;
    // session_options.AppendExecutionProvider_CUDA(cuda_options);

    // 2. ONNX 모델 로드
    const char* model_path = "resnet18.onnx";
    Ort::Session session(env, model_path, session_options);

    // 3. 모델 입/출력 정보 가져오기
    Ort::AllocatorWithDefaultOptions allocator;
    size_t num_input_nodes = session.GetInputCount();
    size_t num_output_nodes = session.GetOutputCount();

    // 입력 노드 이름과 형태(shape) 가져오기
    std::string input_node_name = session.GetInputName(0, allocator);
    Ort::TypeInfo input_type_info = session.GetInputTypeInfo(0);
    auto input_tensor_info = input_type_info.GetTensorTypeAndShapeInfo();
    std::vector<int64_t> input_node_dims = input_tensor_info.GetShape();

    // 4. 추론할 입력 데이터(Tensor) 생성
    // ResNet18의 입력 형태는 [1, 3, 224, 224]
    std::vector<float> input_tensor_values(1 * 3 * 224 * 224);
    // 실제로는 이미지 데이터를 정규화하여 이 벡터에 채워야 합니다.
    // 여기서는 간단히 0으로 채웁니다.
    std::fill(input_tensor_values.begin(), input_tensor_values.end(), 0.0f);

    Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
    Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_tensor_values.data(), input_tensor_values.size(), input_node_dims.data(), input_node_dims.size());

    // 5. 모델 실행 (추론)
    std::vector<const char*> input_node_names = {input_node_name.c_str()};
    std::vector<const char*> output_node_names = {"output"}; // Python에서 지정한 이름

    auto output_tensors = session.Run(Ort::RunOptions{nullptr}, input_node_names.data(), &input_tensor, 1, output_node_names.data(), 1);

    // 6. 결과 처리
    float* floatarr = output_tensors[0].GetTensorMutableData<float>();
    // ResNet18은 1000개의 클래스에 대한 확률을 출력합니다.
    // 가장 높은 확률을 가진 클래스를 찾을 수 있습니다.
    std::cout << "Inference successful!" << std::endl;
    // ... 결과 후처리 코드 ...

    return 0;
}

위 C++ 코드는 모델을 로드하고, 더미 입력 텐서를 생성한 후, session.Run() 함수를 호출하여 추론을 실행하는 전체 과정을 보여줍니다. 실제 애플리케이션에서는 OpenCV 같은 라이브러리를 사용해 이미지를 읽고, 모델 입력에 맞게 크기를 조절하고 정규화하는 전처리 과정이 추가되어야 합니다.


결론: AI, C++로 날개를 달다

지금까지 ONNX Runtime을 통해 Python의 AI 모델을 C++ 환경에서 사용하는 방법을 알아보았습니다. 이 방법을 사용하면 Python의 편리함으로 모델을 개발하고, C++의 압도적인 성능으로 실제 제품에 배포하는 '두 마리 토끼'를 모두 잡을 수 있습니다.

특히 실시간 영상 처리, 고성능 컴퓨팅, 임베디드 시스템 등 속도가 생명인 분야에서 AI를 활용하고자 하는 개발자에게 C++과 ONNX Runtime의 조합은 선택이 아닌 필수입니다. 이제 여러분의 AI 모델을 연구실 밖으로 꺼내 세상에 가치를 더할 시간입니다!