dali-backend
DALI 백엔드 (데이터 전처리)
DALI 백엔드는 Triton Inference Server용 데이터 로딩/전처리 백엔드예요.
NVIDIA DALI(Data Loading Library)는 딥러닝 애플리케이션의 입력 데이터 전처리를 가속하는, 고도로 최적화된 빌딩 블록과 실행 엔진의 모음이에요. DALI는 서로 다른 데이터 파이프라인을 하나의 라이브러리로 가속할 수 있는 성능과 유연성을 모두 제공해요. 그래서 어떤 딥러닝 프레임워크를 쓰든 쉽게 학습·추론 애플리케이션에 통합할 수 있죠.
자세한 내용은 DALI 메인 페이지와 Getting started & Tutorials, Supported operations 문서를 참고해요.
사용법
DALI 데이터 파이프라인은 Triton 안에서 하나의 모델로 표현돼요. 이런 모델을 만들려면 먼저 Python으로 DALI 파이프라인을 짠 뒤, Pipeline.serialize 메서드로 직렬화해야 해요. 수동 직렬화 예시는 multi-input 예제를, 아니면 Autoserialization을 써서 Python으로 직접 정의할 수도 있어요. 아래의 크기 조정(resizing) 파이프라인은 autoserialization을 쓰면서 Triton이 모델 구성을 완성하는 데 필요한 메타데이터를 제공해요.
import nvidia.dali as dali
import nvidia.dali.types as types
from nvidia.dali.plugin.triton import autoserialize
@autoserialize
@dali.pipeline_def(batch_size=256, num_threads=4, device_id=0, output_dtype=[types.UINT8], output_ndim=[3])
def pipe():
images = dali.fn.external_source(device="cpu", name="DALI_INPUT_0", dtype=types.UINT8, ndim=1)
images = dali.fn.image_decoder(images, device="mixed")
images = dali.fn.resize(images, resize_x=224, resize_y=224)
return images
이 파이프라인 정의는 Triton의 모델 저장소(Model Repository)에 넣어야 해요.
model_repository
└── dali
├── 1
│ └── dali.py
└── config.pbtxt
기본 autoserialization 배치에서는 Python 파이프라인 정의 파일 이름을 dali.py로 해요. 크기 조정 예제에 쓰는 config.pbtxt 전체는 이렇게 돼요.
name: "dali"
backend: "dali"
Triton은 위 파이프라인 메타데이터에서 max_batch_size, input, output을 완성해요. 자세한 내용과 부분 구성 예시는 Configuration auto-complete 문서를 참고해요.
Configuration auto-complete (구성 자동 완성)
모델 배포를 단순화하기 위해, Triton 서버는 모델 파일 자체에서 구성 파일의 일부를 추론할 수 있어요. DALI 백엔드의 경우 입력·출력·최대 배치 크기 정보를 파이프라인 정의에 담을 수 있고, 구성 파일에 반복할 필요가 없어요. 파이프라인 정의에 구성 정보를 넣는 방법을 볼게요.
@autoserialize
@dali.pipeline_def(batch_size=256, num_threads=4, device_id=0, output_dtype=[types.UINT8], output_ndim=[3])
def pipe():
images = dali.fn.external_source(device="cpu", name="DALI_INPUT_0", dtype=types.UINT8, ndim=1)
images = dali.fn.image_decoder(images, device="mixed")
images = dali.fn.resize(images, resize_x=224, resize_y=224)
return images
보시다시피 external_source 연산자에 dtype과 ndim(차원 수) 인자를 추가했어요. 이게 구성 파일의 inputs 필드를 채우는 데 필요한 정보를 제공해요. outputs 필드를 채우기 위해 파이프라인 정의에 output_dtype과 output_ndim 인자를 추가했는데, 이들은 출력마다 값 하나씩을 담은 리스트여야 해요.
이렇게 하면 구성 파일을 모델 이름과 DALI 백엔드 지정만으로 줄일 수 있어요.
name: "dali"
backend: "dali"
부분 구성 (Partial configuration)
파이프라인 정의에 없는 정보를 제공하거나 일부 값을 덮어쓰려면 여전히 구성 파일을 쓸 수 있어요. 예를 들어 구성 파일로 모델 출력에 새 이름을 줄 수도 있는데, 나중에 앙상블 모델에서 쓰기 유용해요. 최대 배치 크기도 덮어쓸 수 있어요. 위에서 정의한 파이프라인에 대한 구성 파일은 이렇게 될 수 있어요.
name: "dali"
backend: "dali"
max_batch_size: 128
output [
{
name: "DALI_OUTPUT_0"
dims: [ 224, 224, 3 ]
}
]
이 구성 파일은 최대 배치 크기를 128로 덮어쓰고, 파이프라인 출력 이름을 "DALI_OUTPUT_0"으로 바꾸며 그 shape를 (224, 224, 3)으로 지정해요.
구성 파일에서 지정할 수 있는 모델 파라미터에 대한 자세한 내용은 DALI 모델 구성 문서를 참고해요.
Autoserialization
Triton에서 DALI 백엔드를 쓸 때는 모델 저장소에 DALI 모델을 제공해야 해요. 모델을 표현하는 표준적인 방법은 직렬화된 DALI 모델 파일을 넣고 이름을 적절히(model.dali 기본값) 짓는 거예요. 그런데 직렬화 파일로 저장하면 직렬화 후 모델이 난독화되어 거의 읽을 수 없게 된다는 문제가 있어요. Autoserialization 기능은 모델을 모델 저장소에 Python 코드로 표현할 수 있게 해 줘요. Python으로 정의된 모델이면 DALI 백엔드가 내부 직렬화 메커니즘을 사용해서 사용자가 수동으로 직렬화할 필요가 없어요.
autoserialization을 쓰려면 모델 파일(기본 model.dali, 기본 파일명은 config.pbtxt에서 설정 가능) 안에 DALI 파이프라인의 Python 정의를 넣어야 해요. 이 파이프라인 정의는 @autoserialize 데코레이터로 장식돼야 해요.
import nvidia.dali as dali
@dali.plugin.triton.autoserialize
@dali.pipeline_def(batch_size=3, num_threads=1, device_id=0)
def pipe():
'''
An identity pipeline with autoserialization enabled
'''
data = dali.fn.external_source(device="cpu", name="DALI_INPUT_0")
return data
Python으로 된 제대로 된 DALI 파이프라인 정의는 autoserialization과 함께 다음 조건을 만족해야 해요.
- autoserialize로 장식할 수 있는 것은
pipeline_def뿐이에요. - 특정 모델 버전에서 autoserialize로 장식할 수 있는 파이프라인 정의는 하나뿐이에요.
모델 파일을 로드할 때 DALI 백엔드는 다음 우선순위를 따라요.
- 먼저
default_model_filename속성(model.dali기본)에 지정된 사용자 모델 위치에서 직렬화된 모델을 로드하려 시도해요. - 실패하면 그 위치에서 Python 파이프라인 정의를 로드해 autoserialize를 시도해요. 중요: 이 경우 모델 정의 파일명이
.py로 끝나야 해요 (예:mymodel.py). - 실패하면 주어진 모델 버전의
dali.py파일에서 Python 파이프라인 정의를 로드해 autoserialize를 시도해요.
config.pbtxt에서 모델 경로 정의를 바꾸지 않았다면, 실용적으로는 이렇게 기억하면 돼요.
- 직렬화된 파이프라인이 있으면 파일을
model.dali로 부르고 모델 저장소에 넣어요. - autoserialize할 파이프라인의 Python 정의가 있으면
dali.py라고 불러요.
Tips & Tricks
- 현재 Triton에서 DALI 파이프라인으로 입력을 넘기는 유일한 방법은
fn.external_source연산자를 쓰는 거예요. 그래서 인코딩된 이미지(또는 다른 데이터)를 DALI에 넣을 때 이걸 쓸 가능성이 높아요. fn.external_source연산자에config.pbtxt의 Input에 준 것과 같은 이름을 주세요.
알려진 제한 (Known limitations)
- DALI의 ImageDecoder는 CPU에서만 데이터를 받아요. 파이프라인을 구성할 때 기억해 두세요.
- Triton은 동질(homogeneous) 배치 shape만 받아요. 인코딩된 이미지 배치를 0으로 패딩해도 괜찮아요.
- DALI 제한 때문에, DALI 모델의 인스턴스 그룹 count를 1보다 크게 정의하면 메모리 소비가 비정상적으로 늘어날 수 있어요. DALI 모델은 기본 인스턴스 그룹을 쓰는 걸 권해요.
빌드 방법
Docker 빌드
git clone --recursive https://github.com/triton-inference-server/dali_backend.git
cd dali_backend
docker build -f docker/Dockerfile.release -t tritonserver:dali-latest .
그리고 tritonserver:dali-latest이 새로운 tritonserver docker 이미지가 돼요.
Bare metal (네이티브 빌드)
전제 조건
dali_backend를 빌드하려면 CMake 3.17+가 필요해요.
- 최신 DALI 릴리스 사용: tritonserver 이미지에 포함된 것보다 더 새로운 DALI 버전이 필요할 때는 DALI의 nightly 빌드를 쓸 수 있어요. 원하는 DALI 버전을 pip로 설치하고, 빌드할 때 CMake에
-DTRITON_SKIP_DALI_DOWNLOAD=ON옵션을 넘겨요. 그러면 dali_backend가 시스템에 설치된 최신 DALI를 찾아 그 버전을 사용해요.
빌드
git clone --recursive https://github.com/triton-inference-server/dali_backend.git
cd dali_backend
mkdir build
cd build
cmake ..
make
빌드 과정에서 unittest 실행 파일이 생성돼요. 이걸로 DALI 백엔드의 단위 테스트를 실행할 수 있어요.