앞선 글에서 우리는 C++ 환경에서 LibTorch를 이용해 텐서 연산, TorchScript 모델 로딩 및 추론까지 다뤄보았습니다. 이제는 C++에서 구현한 기능을 Python 환경에서도 그대로 불러와 사용할 수 있다면 어떨까요? 이렇게 하면 C++ 코드 기반의 성능과 최적화를 유지하면서도, Python 환경이 제공하는 편리한 스크립팅과 풍부한 생태계를 활용할 수 있습니다.
이때 pybind11 라이브러리를 이용하면 C++ 함수를 Python 모듈로 손쉽게 노출할 수 있습니다. Python 개발자는 마치 파이썬 함수처럼 C++ 함수를 호출할 수 있으며, 이는 C++/Python 혼합 워크플로우를 매우 유연하게 만들어줍니다.
이번 글에서는 pybind11을 사용하여 간단한 C++ 함수를 Python에서 호출하는 방법부터, LibTorch 텐서를 Python으로 넘기고 받아오는 기초적인 과정까지 차근차근 살펴보겠습니다.
pybind11이란 무엇인가?
pybind11은 C++11 이상 표준을 이용해 Python 확장 모듈을 쉽게 생성할 수 있도록 지원하는 경량 바인딩 라이브러리입니다. ctypes나 Cython, SWIG와 같은 대안들도 있지만, pybind11은 다음과 같은 장점을 갖습니다.
- 직관적인 문법: C++ 템플릿 기반의 깔끔한 인터페이스
- 최소한의 오버헤드: 성능 손실이 적음
- 광범위한 지원: STL 컨테이너, 스마트 포인터, NumPy 배열, Eigen 텐서 등 다양한 C++ 타입 지원
- PyTorch/LibTorch 친화적: Torch 텐서와 손쉽게 호환하는 예제가 많음
즉, pybind11은 Python과 C++ 세계를 잇는 가벼운 다리 역할을 합니다.
pybind11 설치하기
pybind11은 Python 패키지로 설치 가능합니다.
pip install pybind11
혹은 conda를 사용하는 경우:
conda install -c conda-forge pybind11
C++에서 pybind11 헤더를 사용하려면, 프로젝트 빌드 시 pybind11의 include 경로를 CMake 등에 설정해줘야 합니다. pybind11은 헤더 온리(header-only) 라이브러리이므로 별도 라이브러리 링크 없이도 동작합니다.
기본 예제: 단순한 C++ 함수를 Python에서 호출하기
먼저 LibTorch와 무관한 간단한 예제로 시작해봅시다. C++ 함수 add를 하나 정의하고, 이를 pybind11로 파이썬 함수로 노출한 뒤, Python에서 호출해보는 과정을 살펴보겠습니다.
프로젝트 구조를 다음과 같이 가정합니다:
my_project/
├─ CMakeLists.txt
├─ src/
│ ├─ bind.cpp # pybind11 바인딩 코드
│ └─ add.cpp # C++ 함수 정의
├─ test.py # 파이썬에서 테스트
C++ 코드 (add.cpp)
// add.cpp
int add(int a, int b) {
return a + b;
}
아주 간단한 정수 덧셈 함수입니다.
pybind11 바인딩 코드 (bind.cpp)
// bind.cpp
#include <pybind11/pybind11.h>
#include "add.cpp" // 혹은 add.h를 만들어 include할 수도 있음
namespace py = pybind11;
PYBIND11_MODULE(my_module, m) {
m.doc() = "Example pybind11 module";
m.def("add", &add, "A function that adds two integers");
}
- PYBIND11_MODULE(my_module, m): my_module라는 이름의 파이썬 모듈을 생성.
- m.def("add", &add, ...): C++의 add 함수를 my_module.add로 Python에 노출.
이렇게 하면 my_module라는 파이썬 모듈을 컴파일 시 생성할 수 있습니다.
CMakeLists.txt 작성
cmake_minimum_required(VERSION 3.10)
project(MyPybindProject)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# pybind11 패키지 찾기 (설치 경로에 따라 경로 설정 필요)
# 방법1: find_package(pybind11 CONFIG REQUIRED)
# pybind11이 패키지로 설치되어 있는 경우 위 명령을 사용할 수 있음.
# 방법2: 수동으로 include 경로 지정
# set(PYBIND11_INCLUDE_DIR "/path/to/pybind11/include")
# include_directories(${PYBIND11_INCLUDE_DIR})
# 여기서는 find_package를 가정 (pybind11이 CMake 패키지로 설치된 경우)
find_package(pybind11 REQUIRED)
pybind11_add_module(my_module src/bind.cpp)
위와 같이 설정하면 my_module라는 Python 모듈이 빌드됩니다. 빌드 후 my_project/build 디렉토리 안에 my_module.*.so (또는 .pyd on Windows) 파일이 생깁니다.
빌드 및 테스트
cd my_project
mkdir build && cd build
cmake ..
make
빌드가 완료되면 my_module 공유 라이브러리가 생성됩니다. 이를 Python에서 호출해봅시다.
Python 테스트 (test.py)
import my_module
result = my_module.add(3, 5)
print("3 + 5 =", result) # 3 + 5 = 8
python test.py를 실행하면 결과가 출력됩니다. 이제 C++ 함수를 Python에서 불러올 수 있음을 확인했습니다.
LibTorch 텐서를 pybind11로 넘겨받기
이제 LibTorch와 pybind11을 결합해봅시다. 목표는 C++에서 torch::Tensor를 반환하거나 인자로 받아, Python의 torch.Tensor와 주고받는 것입니다.
PyTorch Python 바인딩은 기본적으로 PyTorch가 제공하는 C++ <-> Python 변환 로직을 통해 작동합니다. pybind11로 LibTorch 텐서를 직접 넘기는 건 조금 복잡할 수 있지만, 다행히도 PyTorch는 pybind11 기반으로 만든 확장에 자연스럽게 텐서를 교환할 수 있게 해주는 기능을 갖추고 있습니다.
예제: 텐서 더하기 함수
다음 예제에서는 C++에서 두 텐서를 받아 element-wise 더한 뒤, 결과 텐서를 반환하는 함수를 만들어 파이썬에서 호출해보겠습니다.
// tensor_add.cpp
#include <torch/torch.h>
// 두 텐서를 더하는 함수
torch::Tensor tensor_add(const torch::Tensor& a, const torch::Tensor& b) {
return a + b;
}
바인딩 코드 (bind_tensor.cpp)
// bind_tensor.cpp
#include <pybind11/pybind11.h>
#include <pybind11/pytypes.h>
#include <torch/torch.h>
namespace py = pybind11;
extern torch::Tensor tensor_add(const torch::Tensor& a, const torch::Tensor& b);
PYBIND11_MODULE(tensor_ops, m) {
m.doc() = "Tensor operations with pybind11 and LibTorch";
m.def("tensor_add", &tensor_add, "Add two tensors");
}
여기서 핵심은 pybind11이 torch::Tensor 타입을 자동으로 파이썬의 torch.Tensor와 매핑할 수 있다는 것입니다. PyTorch의 Python 확장 메커니즘 덕분에 별도의 변환 코드를 작성하지 않아도 됩니다.
CMakeLists.txt 수정
이제 torch 라이브러리를 링크해야 합니다.
cmake_minimum_required(VERSION 3.10)
project(TensorOps)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Torch REQUIRED) # LibTorch 찾기
find_package(pybind11 REQUIRED) # pybind11 찾기
pybind11_add_module(tensor_ops src/bind_tensor.cpp src/tensor_add.cpp)
target_link_libraries(tensor_ops "${TORCH_LIBRARIES}")
CMAKE_PREFIX_PATH를 통해 LibTorch 경로를 지정해줘야 할 수도 있습니다. (이전에 했던 것처럼 cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch ..)
빌드 후 tensor_ops.*.so 모듈이 생성됩니다.
Python 테스트 (test_tensor.py)
import torch
import tensor_ops
a = torch.randn(3, 3)
b = torch.randn(3, 3)
c = tensor_ops.tensor_add(a, b)
print("a:", a)
print("b:", b)
print("a+b:", c)
실행해보면 a+b 결과 텐서가 파이썬 콘솔에 출력됩니다. 이는 C++ 함수 tensor_add가 호출된 결과입니다.
응용: C++에서 TorchScript 모델을 불러와 Python 함수로 노출
더 나아가, C++에서 TorchScript 모델 로딩 및 추론 로직을 작성한 뒤, 이를 pybind11을 통해 Python 함수로 감싸면, Python 코드 내에서 TorchScript 모델 추론 함수를 재사용할 수 있습니다.
// model_infer.cpp (C++ 추론 로직)
#include <torch/script.h>
#include <torch/torch.h>
class ModelWrapper {
public:
ModelWrapper(const std::string& model_path) {
module_ = torch::jit::load(model_path);
}
torch::Tensor infer(const torch::Tensor& input) {
std::vector<torch::jit::IValue> inputs;
inputs.push_back(input);
return module_.forward(inputs).toTensor();
}
private:
torch::jit::script::Module module_;
};
// bind_model.cpp
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <torch/torch.h>
#include <torch/script.h>
#include "model_infer.cpp"
namespace py = pybind11;
PYBIND11_MODULE(model_module, m) {
py::class_<ModelWrapper>(m, "ModelWrapper")
.def(py::init<const std::string&>())
.def("infer", &ModelWrapper::infer, "Run inference on input tensor");
}
이렇게 하면 Python에서:
import torch
import model_module
wrapper = model_module.ModelWrapper("model.pt")
input = torch.randn(1,4)
output = wrapper.infer(input)
print("Model output:", output)
이제 Python에서 C++ 로직으로 구현된 TorchScript 모델 추론 기능을 호출할 수 있습니다.
정리
이번 글에서는 pybind11을 통해 C++ 함수를 Python에 바인딩하는 방법을 소개했습니다.
- 기본 바인딩: 간단한 C++ 함수를 Python 함수로 노출하는 예제
- LibTorch 텐서와 연계: torch::Tensor를 인자와 반환값으로 하는 C++ 함수를 Python에서 호출
- 실전 예제: C++에서 TorchScript 모델 추론 로직을 Python에서 호출 가능하게 만드는 방법
이를 통해 C++로 구현한 고성능 로직을 Python 에코시스템에서 바로 활용할 수 있습니다. 추후에는 C++과 Python을 혼합한 아키텍처에서 핵심 부분은 C++로, 부가적인 스크립팅과 툴링은 Python으로 처리하는 유연한 워크플로우를 구축할 수 있게 됩니다.
다음 글에서는 C++과 Python 사이에서 텐서를 더 효율적으로 교환하는 방법, 그리고 양쪽 환경에서 동일한 모델 추론 파이프라인을 매끄럽게 운영하는 예시를 다룰 수 있습니다.
참고 자료
'개발 이야기 > PyTorch (파이토치)' 카테고리의 다른 글
[LibTorch 입문] 7편: C++/Python 통합 모델 추론 파이프라인 실습 (2) | 2024.12.11 |
---|---|
[LibTorch 입문] 6편: C++과 Python 사이에서 텐서 교환하기 (0) | 2024.12.11 |
[LibTorch 입문] 4편: Python 모델을 C++에서 TorchScript로 추론하기 (1) | 2024.12.10 |
[LibTorch 입문] 3편: C++에서 텐서 다루기 (기초 연산 실습) (0) | 2024.12.10 |
[LibTorch 입문] 2편: LibTorch 환경 셋업과 CMake 프로젝트 기초 (0) | 2024.12.09 |