드디어 마지막 이론 수업이 끝났다.
학습시간 09:00~03:00(당일18H/누적2132H)
(GPU 동시 실행 개념 예시)
| GPU 0 | 설명 |
|---|---|
| Model A (Instance 1) | A 모델의 첫 번째 인스턴스 |
| Model A (Instance 2) | A 모델의 두 번째 인스턴스 (병렬 처리용) |
| Model B (Instance 1) | B 모델의 인스턴스 |
| 단계 | 모델명 | 역할 |
|---|---|---|
| 1 | pre-processing_model | 입력 이미지 전처리 |
| 2 | main_classification_model | 핵심 분류 작업 수행 |
| 3 | post-processing_model | 분류 결과를 사람이 읽기 좋은 형태로 변환 |
모델 이름 / 버전 / 모델 파일 형태의 계층 구조를 가짐(모델 저장소 구조 예시)
📦 model_repository/
└── 📂 resnet_classifier/
├── 📜 config.pbtxt
├── 📂 1/
│ └── 📜 model.pt
└── 📂 2/
└── 📜 model.pt
name: 모델 이름, 반드시 폴더 이름과 일치해야 함platform: 모델의 프레임워크 (예: pytorch_libtorch)max_batch_size: 동적 배치를 사용할 경우 최대 배치 크기 지정input, output: 입출력 텐서의 이름, 데이터 타입(data_type), 모양(dims) 정의(config.pbtxt 예시)
name: "resnet_classifier"
platform: "pytorch_libtorch"
max_batch_size: 16
input [
{
name: "INPUT__0"
data_type: TYPE_FP32
dims: [ 3, 224, 224 ]
}
]
output [
{
name: "OUTPUT__0"
data_type: TYPE_FP32
dims: [ 1000 ]
}
]
torch.onnx.export 함수를 사용하여 변환하며, input, output)을 명시적으로 지정(ONNX 변환 코드)
import torch
import torchvision.models as models
model = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
num_ftrs = model.fc.in_features
model.fc = torch.nn.Linear(num_ftrs, 10)
model.eval()
dummy_input = torch.randn(1, 3, 32, 32)
onnx_model_path = "model.onnx"
torch.onnx.export(model,
dummy_input,
onnx_model_path,
input_names=['input'],
output_names=['output'],
opset_version=11)
최상위 폴더 / 모델 이름 / 버전 번호 / 모델 파일 순서로 구성(Triton 폴더 구조 예시)
📦 model_repository/
└── 📂 cifar10_resnet/
├── 📜 config.pbtxt
└── 📂 1/
└── 📜 model.onnx
(config.pbtxt 파일 예시)
name: "cifar10_resnet"
platform: "onnxruntime_onnx"
max_batch_size: 8
input [
{
name: "input"
data_type: TYPE_FP32
dims: [ 3, 32, 32 ]
}
]
output [
{
name: "output"
data_type: TYPE_FP32
dims: [ 10 ]
}
]
-v 옵션을 사용하여 로컬 컴퓨터에 구성한 모델 저장소 폴더를 Docker 컨테이너 내부의 /models 경로에 마운트-p 옵션으로 서버와 통신하기 위한 HTTP(8000), gRPC(8001) 포트를 개방(Docker 명령어)
docker run --gpus=all --rm -p 8000:8000 -p 8001:8001 -p 8002:8002 \
-v $(pwd)/model_repository:/models \
nvcr.io/nvidia/tritonserver:23.12-py3 \
tritonserver --model-repository=/models
InferInput 객체로 입력 데이터를 Triton 형식에 맞게 준비infer 메소드를 호출하여 모델 이름과 입력 데이터를 서버에 전송as_numpy로 변환하여 후처리(클라이언트 테스트 코드)
import numpy as np
import tritonclient.http as httpclient
import torchvision.transforms as transforms
triton_client = httpclient.InferenceServerClient(url="localhost:8000")
dummy_image = np.random.rand(32, 32, 3).astype(np.float32)
preprocess = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
input_tensor = preprocess(dummy_image).unsqueeze(0)
input_tensor_np = input_tensor.numpy()
inputs = [httpclient.InferInput('input', input_tensor_np.shape, "FP32")]
inputs[0].set_data_from_numpy(input_tensor_np, binary_data=True)
outputs = [httpclient.InferRequestedOutput('output', binary_data=True)]
results = triton_client.infer(model_name="cifar10_resnet", inputs=inputs, outputs=outputs)
output_data = results.as_numpy('output')
predicted_class = np.argmax(output_data)
print(f"모델 예측 결과: {predicted_class}")
config.pbtxt에서 동적 배치 설정을 통해 성능 특성을 제어 가능max_queue_delay_microsecondsconfig.pbtxt에서 모델을 몇 개나, 그리고 어떤 장치(CPU/GPU)에 복제해서 띄울지 결정kind: KIND_GPU 또는 kind: KIND_CPU로 실행 장치를 명시count 값을 조절하여 하나의 장치에 몇 개의 모델 인스턴스를 실행할지 지정 가능count: 4로 설정하면 4개의 모델이 동시에 요청을 처리(config.pbtxt 내 인스턴스 그룹 설정 예시)
# config.pbtxt 파일의 일부
# GPU 0번에 2개의 인스턴스, GPU 1번에 2개의 인스턴스를 생성
instance_group [
{
count: 2
kind: KIND_GPU
gpus: [ 0 ]
},
{
count: 2
kind: KIND_GPU
gpus: [ 1 ]
}
]
.plan 파일) 순서로 변환 필요platform을 tensorrt_plan으로 설정(TensorRT 모델용 config.pbtxt 예시)
name: "cifar10_resnet_trt"
platform: "tensorrt_plan"
max_batch_size: 128
input [
{
name: "input"
data_type: TYPE_FP32
dims: [ 3, 32, 32 ]
}
]
output [
{
name: "output"
data_type: TYPE_FP32
dims: [ 10 ]
}
]
config.pbtxt를 구성할 수 있음platform을 ensemble로 지정하고, 내부에 데이터 흐름을 정의step으로 정의model_name으로 해당 단계에서 호출할 모델을 지정input_map, output_map을 통해 이전 모델의 출력을 다음 모델의 입력으로 어떻게 전달할지 명시(앙상블 config.pbtxt 예시)
platform: "ensemble"
name: "cifar10_pipeline"
max_batch_size: 8
input [
{
name: "PIPELINE_INPUT"
data_type: TYPE_UINT8
dims: [ -1, -1, 3 ]
}
]
output [
{
name: "PIPELINE_OUTPUT"
data_type: TYPE_STRING
dims: [ 1 ]
}
]
step [
{
model_name: "preprocessor"
model_version: -1
input_map {
key: "PREPROCESS_INPUT"
value: "PIPELINE_INPUT"
}
output_map {
key: "PREPROCESS_OUTPUT"
value: "preprocessed_tensor"
}
},
{
model_name: "cifar10_resnet"
model_version: -1
input_map {
key: "input"
value: "preprocessed_tensor"
}
output_map {
key: "output"
value: "classifier_output"
}
}
]