본문 바로가기

MLOps

Deep Learning GPU 성능 최적화 전략

728x90

GPU를 이용하여 학습을 하다보면 GPU가 과연 일을 잘하고 있을까?에 대한 의문이 들때가 많을 것이다. 보통 nvidia-smi를 통해 확인한 GPU 메모리 사용량(Memory-Usage)이 어느정도 찼으면, 일 열심히 하겠구나 싶다. 하지만 사실 GPU의 작업 성능은 GPU-Util 로 확인 가능하다. GPU-Util 은 nvidia docs에서 확인할 수 있듯이, 지난 1에서 1/6초 동안의 GPU 활용률을 뜻한다.

 

Useful nvidia-smi Queries | NVIDIA

When adding additional parameters to a query, ensure that no spaces are added between the queries options.

nvidia.custhelp.com

nvidia-smi 실행 시

검색해보면 국내 해외 할 것 없이 많은 블로그에서 GPU 활용률에 대한 헤더를 Volatile GPU-Util로 착각하는데... GPU-Util 과 Volatile Uncorr. ECC 가 각각의 헤더이다.. UI가 착각하기 쉽게 되어있긴 하다..

이 GPU-Util이 낮다는 것은, 제대로 GPU를 활용하지 못하고 있다는 뜻이 된다. 즉 어떤 부분에서 병목현상이 일어나는지 확인해야하며, 이를 통해 꽤 많은 시간 단축/성능 향상을 이끌어 낼 수 있다.

데이터 파이프라인 최적화

PyTorch를 사용해보면 알 수 있듯이 GPU에 데이터가 올라가는 과정은 다음과 같이 간단하게 설명이 가능하다.

User Mode에서 Kernel Mode로 변환 -> File System I/O Device에 요청 -> (생략) -> 데이터를 메모리에 올림 -> GPU 메모리에 올림

Kernel Mode로 변환 후 데이터가 GPU 메모리에 올려질 때 까지 GPU는 아무 일을 하지 못하게 된다(GPU Starvation). 이 부분에서 병목현상이 일어나고, GPU-Util이 낮은 이유가 된다.

num_workers ++ (Multi Process Data Loading)

가장 간단히 해결할 수 있는 방법은 data를 가져오는 프로세스를 늘리는 방법이다. PyTorch는 아래 코드와 같이 DataLoader를 설정할 수 있다.

train_dataloader = DataLoader(dataset=train_dataset, num_workers=4, batch_size=16, ...)

 

프로세스 a가 한 배치 데이터를 GPU 올려서 학습하는 동안, 프로세스 b는 다음 배치 데이터를 미리 준비하는 것이다 (Queue 사용). 이렇게 되면 GPU는 끊임없이 data를 가져올 수 있기 때문에 GPU-Util을 높일 수 있다. 당연히, num_workers는 다다익선이라 볼 수 있지만, 모든 코어를 전부 데이터 로드에 사용하게되면, 다른 처리에 딜레이를 일으킬 수 밖에 없다.

적당한 값의 num_workers를 설정해줘야한다. 아래 PyTorch Discuss 속 num_workers = 4 x num_GPUs 의 의견, 전체 코어 개수의 절반의 의견 등 여러 의견을 참고하면 된다.

 

Guidelines for assigning num_workers to DataLoader

I realize that to some extent this comes down to experimentation, but are there any general guidelines on how to choose the num_workers for a DataLoader object? Should num_workers be equal to the batch size? Or the number of CPU cores in my machine? Or to

discuss.pytorch.org

FLOAT32 -> UINT8

아래 reference에 올린 블로그에서는 데이터가 이미지의 경우, UINT8(0 ~ 255 정수표현)로 저장하고 있다가 모델에 넣어주기 직전 Float32로 바꿔주는 것이 더 빠르다고 한다. 우선 PyTorch 모델은 Float32 Datatype을 가지고 있기 때문에, 모델의 input으로 Float32의 사용이 불가피하다. 하지만 UINT8은 Float32 보다 4배가 작기 때문에 크기가 비교적 작은 데이터의 경우 메모리에 다 올려 놓는 식으로 병목 현상을 해결 할 수 있다.

하지만.. vision 분야에서 사용하고 있는 데이터의 크기를 1/4로 줄였다 해서, 메모리에 다 올라갈리가.. 없다.

Chunk

따라서 위에서 언급한 블로그에서는 HDF5라는 대용량 데이터 파일 형식의 Chunk Layout 을 사용하여, 메모리에 chunk 단위로 데이터를 올려놓으면 이 병목을 해결할 수 있다고한다. 원리는 브매우 간단하다. Chunk에 있는 데이터 하나를 참조하면, 해당 Chunk 전체가 메모리에 올라가게 된다. 그 이후 그 Chunk 속 메모리를 참조하려고 하면, Cache 마냥 올려져 있는 Chunk에 접근하게 된다.

 

DALI

DALI는 Data Loading Library로 학습/테스트 시 데이터 처리의 가속을 위한 라이브러리이다. DALI를 이용하면 RandomResizedCrop/RandomHorisontalFlip/Normalize 등과 같은 data Augmentation를 GPU에서 가능하게 한다.

 

NVIDIA/DALI

A GPU-accelerated library containing highly optimized building blocks and an execution engine for data processing to accelerate deep learning training and inference applications. - NVIDIA/DALI

github.com

ImageNet 학습하는 코드(CODE)를 보면 사용법이 꽤 간단하다. 아래 코드 처럼 augmentation 파이프라인을 설정한 후 DALIClassificationIterator로 data loader를 선언하면 된다.

@pipeline_def
def create_dali_pipeline(data_dir, crop, size, shard_id, num_shards, dali_cpu=False, is_training=True):
    images, labels = fn.readers.file(...)
                                     
    # setting 부분 생략
    
    if is_training:
        images = fn.decoders.image_random_crop(images, ...)
        images = fn.resize(images, ...)
    
    # else 부분 생략
    
    images = fn.crop_mirror_normalize(images.gpu(), ...)
    labels = labels.gpu()
    return images, labels

아래 pdf 첨부

NVIDIA에서는 이 방법을 통해 4.7배의 speed up이 있었다고한다. 이 방법은 IO 병목 현상이 아닌 CPU의 낮은 데이터 처리 속도를 해결함으로 써, GPU-Util를 높일 수 있다.

 

FP32 -> FP16

마지막 으로 소개하는 방법은 FP16의 사용이다. 이는 앞선 블로깅을 참고하면 이해가 편하다.

 

NVIDIA APEX가 빠른 이유 (ft. FP16 vs FP32)

이번 포스팅은 CVML의 APEX 포스팅을 구경 중 FP16이 성능향상을 내는 방법이 궁금해서 찾아보며 적은 글이다. NVIDIA APEX GPU를 업그레이드 하지 않고 batch size를 늘릴 수 있는 방법이 있다. 바로 NVIDIA A

byeongjo-kim.tistory.com

아래 pdf 첨부

이 방법은 GPU의 처리 속도를 높이는 방법으로 IO 병목 현상은 해결되지 않기 때문에, num_worekrs와 함께 사용하여야 효과적으로 GPU-Util를 높일 수 있을 것 같다.

Reference

 

GPU Util 99% 달성하기

딥러닝 공부를 하다 보면 반드시 보게 되는 하나의 창이 있는데.. 바로 nvidia-smi 했을 때 나오는 GPU의 상태를 보여주는 창이다. 오른쪽에 보면 GPU-Util이라는 수치가 있는데, 이는 GPU가 얼마나 가

ainote.tistory.com

GPU PROFILING 기법을 통한 DEEP LEARNING 성능 최적화 기법 소개 - Gwangsoo Hong, Solution Architect, NVIDIA

728x90