콘텐츠로 이동

이미지 분류

개요

이 튜토리얼에서는 RBLN SDK C/C++ Runtime API를 사용하여 PyTorch ResNet50 모델로 추론을 실행하는 방법을 소개합니다. 모델은 RBLN SDK Python API를 사용해 컴파일되며, 생성된 *.rbln 파일은 RBLN SDK C/C++ Runtime API로 추론에 사용됩니다.
이 접근 방식은 Python에서 모델을 준비하는 편의성과 C/C++의 성능 이점을 결합합니다. 튜토리얼에 사용된 전체 코드는 RBLN Model Zoo에서 확인할 수 있습니다.

환경 설정 및 설치 확인

시작하기 전에 시스템 환경이 올바르게 구성되어 있으며, 필요한 모든 필수 패키지가 설치되어 있는지 확인하십시오. 다음 항목이 포함됩니다:

Note

  • rebel-compiler를 사용하려면 RBLN Portal 계정이 필요합니다.
  • 위 명령은 Debian 계열 Linux(예: Ubuntu)에서 pip로 패키지를 설치하는 일반적인 절차를 전제로 합니다. OS나 환경이 다른 경우에는 설치 가이드에서 지원되는 설치 조합과 적용 가능한 명령을 확인하세요.

RBLN Python API를 사용한 컴파일

RBLN Python API는 컴파일과 추론 모두를 위한 포괄적인 기능을 제공하는 반면,
RBLN SDK C/C++ Runtime API는 오로지 추론에 최적화되어 설계되었습니다.
이 튜토리얼에서는 모델 컴파일에 RBLN Python API를 사용하고, 추론을 위해 컴파일된 모델을 저장합니다.

모델 준비

TorchVision에서 ResNet50 모델을 불러옵니다.

1
2
3
4
5
6
7
from torchvision import models  
import rebel  
import torch  

model_name = "resnet50"  
weights = models.get_model_weights(model_name).DEFAULT  
model = getattr(models, model_name)(weights=weights).eval()  

모델 컴파일

Torch 모델(torch.nn.Module)이 준비되면 rebel.compile_from_torch()를 사용해 컴파일합니다. 그리고 compiled_model.save()로 컴파일된 모델을 저장합니다.

1
2
3
4
5
# Compile the model  
compiled_model = rebel.compile_from_torch(model, [("x", [1, 3, 224, 224], "float32")])  

# Save the compiled model to local storage  
compiled_model.save(f"{model_name}.rbln")  

컴파일 실행

위 컴파일 코드 스니펫은 compile.py에 포함되어 있습니다.
모델을 컴파일하고 *.rbln 파일을 생성하려면 다음 명령을 실행하세요:

$ python compile.py --model-name=resnet50  

RBLN SDK C/C++ Runtime API를 사용한 추론

이제 RBLN SDK C/C++ Runtime API를 사용해 컴파일된 모델을 로드하고 추론을 실행한 후, 출력 결과를 확인합니다.

CMake 빌드 스크립트 준비

이 튜토리얼은 이미지 전/후처리를 위해 OpenCV를, CLI 파라미터 파싱을 위해 argparse를 사용합니다.
아래 CMake 스크립트는 외부 패키지 종속성과 링크 설정을 설명합니다.

# Define dependencies for external Package  
include(FetchContent)  
include(cmake/opencv.cmake)  
include(cmake/argparse.cmake)  

# Define the name of executable  
add_executable(image_classification main.cc)  

# Update link info for package dependencies: OpenCV  
find_package(OpenCV CONFIG REQUIRED)  
target_link_libraries(image_classification ${OpenCV_LIBS})  

# Update link info for dependencies: RBLN  
find_package(rbln CONFIG REQUIRED)  
target_link_libraries(image_classification rbln::rbln_runtime)  

# Update including dependencies: argparse  
target_include_directories(image_classification PRIVATE ${argparse_INCLUDE_DIRS})  

입력 데이터 준비

OpenCV API를 사용하여 입력 이미지를 전처리합니다.
다음 코드 스니펫은 이미지를 읽고 색상 변환, 종횡비 유지 리사이즈, 중앙 크롭, 픽셀 값 정규화를 수행합니다.

std::string input_path = "${SAMPLE_PATH}/tabby.jpg";  

// Read image  
cv::Mat input_image;  
try {  
    input_image = cv::imread(input_path);  
} catch (const cv::Exception &err) {  
    std::cerr << err.what() << std::endl;  
    std::exit(1);  
}  

// Convert BGR to RGB  
cv::Mat image;  
cv::cvtColor(input_image, image, cv::COLOR_BGR2RGB);  

// Resize while maintaining aspect ratio  
float scale = image.rows < image.cols ? 256. / image.rows : 256. / image.cols;  
cv::resize(image, image, cv::Size(), scale, scale, cv::INTER_LINEAR);  

// Center crop to 224x224  
image = image(cv::Rect((image.cols - 224) / 2, (image.rows - 224) / 2, 224, 224));  

// Normalize the image  
image.convertTo(image, CV_32F);  
cv::Vec3f mean(123.68, 116.28, 103.53);  
cv::Vec3f std(58.395, 57.120, 57.385);  
for (unsigned i = 0; i < image.rows; i++) {  
    for (unsigned j = 0; j < image.cols; j++) {  
        cv::subtract(image.at<cv::Vec3f>(i, j), mean, image.at<cv::Vec3f>(i, j));  
        cv::divide(image.at<cv::Vec3f>(i, j), std, image.at<cv::Vec3f>(i, j));  
    }  
}  

// Convert image to blob  
cv::Mat blob = cv::dnn::blobFromImage(image);  

동기식 추론 실행

아래 코드 스니펫은 동기식 추론 예시를 보여줍니다.

std::string model_path = "${SAMPLE_PATH}/resnet50.rbln";  
RBLNModel *mod = rbln_create_model(model_path.c_str());  
RBLNRuntime *rt = rbln_create_runtime(mod, "default", 0, 0);  

// Set input data  
rbln_set_input(rt, 0, blob.data);  

// Run sync inference  
rbln_run(rt);  

// Get output results  
float *logits = static_cast<float *>(rbln_get_output(rt, 0));  

비동기식 추론 실행

아래 코드 스니펫은 비동기식 추론 예시를 보여줍니다.

std::string model_path = "${SAMPLE_PATH}/resnet50.rbln";  
RBLNModel *mod = rbln_create_model(model_path.c_str());  
RBLNRuntime *rt = rbln_create_async_runtime(mod, "default", 0, 0);  

// Allocate output buffer  
std::vector<float> logits(rbln_get_layout_nbytes(rbln_get_output_layout(rt, 0))/sizeof(float));

// Input/Output Buffer Memory Mapping for Async execution
std::vector<void*> inputs = {blob.data};
std::vector<void*> outputs = {logits.data()};

// Run async inference  
int rid = rbln_async_run(rt, inputs.data(), outputs.data());

// Wait for inference completion  
rbln_async_wait(rt, rid, 1000);  

후처리(Post Processing)

출력 logits는 ImageNet의 각 카테고리 점수를 나타내는 크기 (1,1000)의 float32 배열입니다.
top-1 인덱스를 구하고 해당 카테고리를 출력합니다.

// Postprocessing  
size_t max_idx = 0;  
float max_val = std::numeric_limits<float>::min();  
for (size_t i = 0; i < 1000; i++) {  
    if (logits[i] > max_val) {  
        max_val = logits[i];  
        max_idx = i;  
    }  
}  

// Print the predicted category  
std::cout << "Predicted category: " << IMAGENET_CATEGORIES[max_idx] << std::endl;  

리소스 해제

런타임과 모델을 해제합니다.

1
2
3
4
5
// Release Runtime  
rbln_destroy_runtime(rt);  

// Release Model  
rbln_destroy_model(mod);  

CMake 빌드 방법

위 예제의 전체 코드는 RBLN Model Zoo C++ 예제에 포함되어 있습니다.
코드를 컴파일하고 실행 파일을 생성하려면 다음 명령을 실행하세요:

1
2
3
4
$ mkdir ${SAMPLE_PATH}/build  
$ cd ${SAMPLE_PATH}/build  
$ cmake ..  
$ make  

실행 파일 사용 방법

모든 단계를 완료한 후 빌드 디렉토리에서 실행 파일을 확인할 수 있습니다.

1
2
3
4
5
# Synchronous execution  
$ ${SAMPLE_PATH}/build/image_classification -i ${SAMPLE_PATH}/tabby.jpg  -m ${SAMPLE_PATH}/resnet50.rbln  

# Asynchronous execution  
$ ${SAMPLE_PATH}/build/image_classification_async -i ${SAMPLE_PATH}/tabby.jpg  -m ${SAMPLE_PATH}/resnet50.rbln  

예시 출력:

Predicted category: tabby  

참고