이 글은 네이버 부스트캠프 AI Tech 기간에 작성되었습니다.
이것은 내가 개인적으로 배운 것에 대한 주간 요약입니다.
이 글의 내용은 새롭게 알게 된 정보를 바탕으로 구성되었습니다.
리뷰의 중요도를 선정하여 정리하였습니다.

이 부스트 캠프 과정의 가장 큰 목표는
“아직 경험하지 못한 험난한 길을 걷자”보지 못하다
GPU조차 찾기 어려워 파라미터가 큰 모델은 훈련시킬 수 없었다.
Multi-GPU는 꿈이 이루어진 것처럼 들렸습니다.
그럼에도 불구하고
멀티 GPU 개념이 없다면 단일 단위가 아닌 사업 단위로 전환하면,
많이 부족한 것 같아요
Pytorch에서 멀티 GPU를 학습하는 방법을 조사하고 정리했습니다.
추가 정보나 잘못된 정보가 있으면 댓글 달아주세요 🙂
이 기사
PyTorch는 다중 GPU 학습을 올바르게 수행합니다.
PyTorch를 사용하여 다중 GPU 학습 과정을 정리했습니다. 본 포스팅은 다음과 같이 진행됩니다.
medium.com
블로그 포스팅을 토대로 작성되었습니다
더 자세한 정보나 구체적인 정보를 원하시면 위의 링크를 참고해주세요.
3주차
색인
- PyTorch 데이터 병렬
- 맞춤 데이터 병렬
- PyTorch 분산
- 꼭대기
소개
현재 딥 러닝에 한계가 있습니까?
사실 최근 딥러닝의 한계는 명확하다.
GPT, 라마 같은 슈퍼 팬시? 요즘 대세인 거대한 모델들
또한 대규모 데이터 세트를 좋아합니까? 위의 모델은 대규모 데이터 세트에서 학습되었습니다.
일반 GPU 사양으로 실행할 수 있습니까?
이 문제를 처리하거나 해결하는 세 가지 주요 방법이 있습니다.
1. GPU 성능 향상
그 부분은 분명하다.
현재 사용 가능한 GPU의 성능이 기하급수적으로 발전하면 위에서 설명한 딥 러닝의 한계가 사라질 것입니다.
하지만 이 부분은 소프트웨어 개발자나 딥러닝 연구자가 할 수 있는 일이 아니다.
두 번째 모델 조명
이 부분은 최근 딥러닝 연구가 활발히 진행되고 있는 분야입니다.
MobileNet, Knowledge Distillation 기법 등 기존 모델에 비해 파라미터의 수를 대폭 줄인 반면,
일정 수준의 성능을 보장하는 방식이 이 부분에 속한다고 할 수 있습니다.
3. 다중 GPU 사용
오늘 이야기할 부분은 현재 존재하는 여러 개의 GPU를 사용하여 문제를 해결하는 것입니다.
실제로 Pytorch와 CUDA가 이러한 환경을 제공합니다.
따라서 이러한 기술을 배우고 연습하는 것은 문제 해결에 큰 도움이 될 수 있습니다.

1. PyTorch 데이터 병렬
딥러닝 모델을 학습할 때 항상 마주하게 되는 두려움의 단어가 있습니다.
옴 – 메모리가 부족합니다.
내가 항상 생각하는 것은
아… 스택 크기를 줄이자…
하지만 항상 이런 식으로 OOM을 해결할 수는 없습니다.
모델이 계속 커지고 있습니다.
데이터 세트도 증가하고 있습니다.
그리고 좋은 성능을 얻으려면 큰 모델을 훈련시키는 것이 좋은 옵션 중 하나입니다.
이 OOM을 해결하려면
여러 GPU 사용을 고려해야 합니다.
물론 예산 문제로 개인이 고가의 GPU를 여러 개 소유하기는 어렵다.
다만, 법인 또는 클라우드 시스템을 이용하는 경우
멀티 GPU를 사용할 수 있습니다.
이러한 상황에서 Pytorch는 Data Parallel을 제공합니다.

사진으로 보니 복잡해보이네요.
간단히
먼저 사용 중인 GPU와 동일한 모델을 복사합니다.
그리고 각 훈련 반복에 대해 스택 크기는 더 작은 스택 크기로 줄어들고 GPU에 분배됩니다. 소량
분산된 데이터를 통해 GPU에서 포워딩이 이루어지고, 각 GPU에서 출력이 나오면서 다시 하나의 GPU로 모아진다 – 모으다
그 후 loss를 수신한 후 역과정을 수행하는데, 이 경우에도 각 GPU에서 수행된다.
매개변수의 경우 모든 GPU에 존재하므로 그래디언트를 얻을 수 있습니다.
상당히 복잡해 보일 수 있는 프로세스는 Pytorch에 코드를 추가하기만 하면 수행할 수 있습니다.
from torch import nn #근데 왜 import torch.nn as nn으로 할까? 그냥 torch에서 nn을 import하는 코드가 더 깔끔하지 않나?
#아무튼
#우리가 활용하는 모델을 변수 model이라고 하자
model = Resnet18() #가정하면
model = nn.DataParallel(model) #이렇게 한줄 추가해주면 이제 우리 모델은 병렬처리된다
정말 쉬운
그리고 분산 및 수집 프로세스를 함수로 직접 호출하여 사용할 수 있습니다.
def data_parallel(module, input, device_ids, output_device):
replicas = nn.parallel.replicate(module, device_ids)
inputs = nn.parallel.scatter(input, device_ids)
replicas = replicas(:len(inputs))
outputs = nn.parallel.parallel_apply(replicas, inputs)
return nn.parallel.gather(outputs, output_device)
이것은 nn에서 구현되며 자세한 설명은 공식 튜토리얼 링크에서 가져와 아래에 기록합니다.
- 복제: 여러 장치에 모듈을 복제합니다.
- scatter: 첫 번째 차원에서 입력을 분산시킵니다.
- 수집: 첫 번째 차원의 입력을 수집하고 병합합니다.
- 병렬로 적용(parallel_apply): 이미 분산된 입력 집합을 이미 분산된 모델 집합에 적용합니다.
다중 GPU 예
데이터 병렬 처리는 미니 배치를 여러 개의 더 작은 미니 배치로 분할하고 각각의 작은 미니 배치를 병렬로 계산합니다. 데이터 병렬 처리는 Torch.nn.DataParallel로 수행됩니다.
tutorials.pytorch.kr
그리고 전체 모델을 병렬화할 필요가 없는 경우에도 모델의 하위 계층을 병렬화할 수 있습니다.
다음 코드에 예제를 작성했습니다.
import torch
import torch.nn as nn
class DataParallelModel(nn.Module):
def __init__(self):
super().__init__()
self.block1 = nn.Linear(10, 20)
# wrap block2 in DataParallel
self.block2 = nn.Linear(20, 20)
self.block2 = nn.DataParallel(self.block2) ## 이제 block2 레이어는 병렬처리된다
self.block3 = nn.Linear(20, 20)
def forward(self, x):
x = self.block1(x)
x = self.block2(x)
x = self.block3(x)
return x
그러나 이러한 병렬 학습을 계속하면 “GPU 불균형” 일어날 수있다
그게 무슨 뜻이야
예를 들어 4개의 GPU로 병렬 처리를 하고 있습니다.
한 GPU가 다른 GPU보다 더 많은 사용률을 보이는 경우 – 은용랑 nvidia-smi로 확인하실 수 있습니다!!
그 하나의 GPU 때문에 더 많은 Batch_size로 훈련할 수 없습니다.
이것은 큰 시간 차이를 만들 수 있습니다
이 경우 해결 방법이 있습니까?
출력 GPU의 위치를 조정하는 역할을 합니다.
나는 이미 수집 프로세스가 GPU에서 출력을 수집한다고 말했습니다.
이 컬렉션은 기본값이 있으며 특정 GPU로 전송되지만 사용자 지정을 통해
출력이 수집되는 GPU를 조정하면 문제가 어느 정도 해결될 수 있습니다.
import os
import torch.nn as nn
os.environ("CUDA_VISIBLE_DEVICES") = '0, 1, 2, 3'
model = nn.DataParallel(model, output_device=1) #output GPU 지정
output_device를 매개변수로 추가하기만 하면 사용자 정의할 수 있습니다.
그런데 여기서 의문이 생길 수 있습니다
단순히 출력 GPU를 조정하면 불균형이 사라질까요?
사실 조삼모사 근처 아닌가요?
사실 위의 절차는 일시적인 절차이지 이상적인 절차는 아닙니다.
따라서 이를 해결하려면 Custom으로 설정해야 합니다.
이제 Custom으로 데이터 병렬 처리가 수행되는 방법을 살펴보겠습니다.
2. 맞춤형 DataParallel
사실 GPU 불균형의 원인은 간단합니다.
이는 수집 프로세스가 출력을 하나로 결합하기 때문입니다.
이미 살펴본 바와 같이 이 단일 GPU로 출력을 수집하는 이유는 손실을 계산하기 위해서입니다.
그렇다면 손실 명세서를 병렬로 처리하는 것은 어떻습니까?
하느님 감사합니다
GitHub – zhanghang1989/PyTorch-Encoding: 내 작업을 위한 CV 툴킷.
내 논문을 위한 CV 툴킷. GitHub에서 계정을 생성하여 zhanghang1989/PyTorch-Encoding 개발에 기여하십시오.
github.com
손실을 병렬로 인쇄하는 코드는 이미 다른 사람이 생성했습니다.
from torch.nn.parallel.data_parallel import DataParallel
class DataParallelCriterion(DataParallel):
def forward(self, inputs, *targets, **kwargs):
targets, kwargs = self.scatter(targets, kwargs, self.device_ids)
replicas = self.replicate(self.module, self.device_ids(:len(inputs)))
targets = tuple(targets_per_gpu(0) for targets_per_gpu in targets)
outputs = _criterion_parallel_apply(replicas, inputs, targets, kwargs)
return Reduce.apply(*outputs) / len(outputs), targets
이 프로세스는 데이터가 병렬로 분할되고 수집되는 이전 프로세스와 유사하게 작동합니다.
응용 프로그램별로 학습할 코드는 다음과 같습니다.
import torch
import torch.nn as nn
from parallel import DataParallelModel, DataParallelCriterion
model = ResNet18()
model = DataParallelModel(model)
model.cuda()
criterion = nn.NLLLoss()
criterion = DataParallelCriterion(criterion) ##앞에서 작성한 코드 적용
...
for i, (inputs, labels) in enumerate(trainloader):
outputs = model(inputs)
loss = criterion(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
3. 파이토치 배포
파이토치 DataParallel과 함께 분산도 지원됩니다.
간단히 말해서 분산 처리이며 여러 컴퓨터에서 모델을 교육하는 것과 같습니다.
분산 처리 프로세스를 이해하려면 다음 Pytorch 자습서를 확인하십시오.
PyTorch로 분산 애플리케이션 작성 – PyTorch Tutorials 2.0.0+cu117 문서
PyTorch로 분산 애플리케이션 작성 작성자: Séb Arnold 참고 GitHub에서 이 자습서를 보고 편집합니다. 전제 조건: 이 짧은 자습서에서는 PyTorch의 분산 패키지를 살펴보겠습니다. 분산 설정을 구성하는 방법을 살펴보겠습니다.
pytorch.org
이를 학습에 적용하고 싶다면 아래 샘플 코드를 참고하세요.
GitHub – pytorch/examples: 비전, 텍스트, 강화 학습 등에서 pytorch에 대한 일련의 예제입니다.
비전, 텍스트, 강화 학습 등의 pytorch 관련 예제 세트 – GitHub – pytorch/examples: 비전, 텍스트, 강화 학습 등의 pytorch 관련 예제 세트
github.com
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel
def main():
args = parser.parse_args()
ngpus_per_node = torch.cuda.device_count()
args.world_size = ngpus_per_node * args.world_size
mp.spawn(main_worker, nprocs=ngpus_per_node,
args=(ngpus_per_node, args))
def main_worker(gpu, ngpus_per_node, args):
global best_acc1
args.gpu = gpu
torch.cuda.set_device(args.gpu)
print("Use GPU: {} for training".format(args.gpu))
args.rank = args.rank * ngpus_per_node + gpu
dist.init_process_group(backend='nccl',
init_method='tcp://127.0.0.1:FREEPORT',
world_size=args.world_size,
rank=args.rank)
model = ResNet18()
model.cuda(args.gpu)
model = DistributedDataParallel(model, device_ids=(args.gpu))
acc = 0
for i in range(args.num_epochs):
model = train(model)
acc = test(model, acc)
솔직히 이 부분은 코드만 봐도 직관적으로 이해가 되지 않는다.
아직 배울게 조금 남았는데…
다음은 참조된 블로그의 설명입니다.
ImageNet 샘플의 main.py에서 multi-GPU와 관련된 주요 부분을 정리하면 다음과 같다. main.py가 실행되면 main이 실행되고 main은 다중 처리에서 main_workers를 다시 실행합니다. 4개의 GPU를 하나의 노드로 간주하고 world_size를 설정합니다. 그런 다음 mp.spawn 함수는 4개의 GPU에서 별도로 main_worker를 실행합니다.
main_worker에서 각 GPU에 대한 분산 학습 초기화는 dist.init_process_group에 의해 수행됩니다. PyTorch의 문서를 보면 nccl을 다중 GPU 학습을 위한 백엔드로 사용하라고 나와 있습니다. init_method에서 FREEPORT에 사용 가능한 포트를 씁니다. 분산 학습을 위한 초기화 후 분산 학습이 가능합니다. 28행을 보면 model 에 DataParallel 대신 DistributedDataParallel 이 사용된 것을 볼 수 있습니다. DataParallel에서 언급한 입력을 분배하는 역할을 하며 정방향 연산을 수행하고 다시 역방향 연산을 수행합니다.
그리고 DataLoader의 경우 다음과 같이 DistributedSampler를 사용해야 합니다.
from torch.utils.data.distributed import DistributedSampler
train_dataset = datasets.ImageFolder(traindir, ...)
train_sampler = DistributedSampler(train_dataset)
train_loader = torch.utils.data.DataLoader(
train_dataset, batch_size=args.batch_size, shuffle=False,
num_workers=args.workers, pin_memory=True, sampler=train_sampler)
이렇게 분산 학습을 진행하면 GPU의 최대 성능으로 분산 처리를 할 수 있습니다.
그러나 이러한 분할 학습의 경우 문제가 발생할 수 있다.
모델에서 학습에 사용되지 않는 매개변수가 있을 때 문제가 발생한다고 합니다.
이 문제를 해결하기 위해 다양한 패키지를 사용할 수 있습니다.
4. 팁
NDIVIA에서 만든 Apex를 사용하면 모델을 효율적으로 교육할 수 있습니다.
이 부분은 약간의 CS 지식이 필요하기 때문에
나는 개인적으로 그것을 얻지 못했다
대충 말했다
보통 딥러닝 훈련은 32비트로 이뤄진다.
그러나 Apex를 사용하면 16비트로 학습할 수 있습니다.
효율적인 학습이 가능합니다 이를 혼합 정밀도 연산이라고 합니다.
사실 이 부분은 우리가 보고 있는 분산 처리와는 아무런 관련이 없습니다.
하지만 DDP 기능이라고도 하는 Distributed DataParallel이 있습니다.
공부하는 부분입니다
다음 링크로 이동하면 샘플 코드를 볼 수 있습니다.
GitHub – NVIDIA/apex: PyTorch 확장: Pytorch에서 쉽게 혼합 정밀도 및 분산 교육을 위한 도구
PyTorch 확장: Pytorch에서 간편한 혼합 정밀도 및 분산 교육을 위한 도구 – GitHub – NVIDIA/apex: A PyTorch 확장: Pyt에서 간편한 혼합 정밀도 및 분산 교육을 위한 도구…
github.com
구현하기가 그리 어렵지 않은 것 같습니다.
from apex.parallel import DistributedDataParallel as DDP
def main():
global args
args.gpu = 0
args.world_size = 1
args.gpu = args.local_rank
torch.cuda.set_device(args.gpu)
torch.distributed.init_process_group(backend='nccl',
init_method='env://')
args.world_size = torch.distributed.get_world_size()
model = ResNet18()
model.cuda(args.gpu)
model = DDP(model, delay_allreduce=True)
acc = 0
for i in range(args.num_epochs):
model = train(model)
acc = test(model, acc)
Pytorch와 유사하게 DDP를 가져오고 mdoel을 덮어씁니다.
다음 코드를 실행하십시오.
python -m torch.distributed.launch --nproc_per_node=4 main.py \
--batch_size 60 \
--num_workers 2 \
--gpu_devices 0 1 2 3\
--distributed \
--log_freq 100
이 부분에서 nproc_per_node를 GPU 수로 설정할 수 있습니다.
아래에 입력한 값은 각 GPU에 대한 스택 크기와 num_workers의 수입니다.
대부분은 자연어 작업과 같은 대규모 모델을 사용할 때 분산 처리 또는 Apex를 사용합니다.
그러나 img와 같은 필드는 Data Parallel이 이를 처리하기에 충분하다고 말합니다.
아직 GPU가 있어서 분산 처리를 연습할 수 없습니다.
언젠가 이 글을 다시 읽고 어떻게 해야할지 고민해봐야겠습니다.

참고
PyTorch는 다중 GPU 학습을 올바르게 수행합니다.
PyTorch를 사용하여 다중 GPU 학습 과정을 정리했습니다. 본 포스팅은 다음과 같이 진행됩니다.
medium.com
GitHub – NVIDIA/apex: PyTorch 확장: Pytorch에서 쉽게 혼합 정밀도 및 분산 교육을 위한 도구
PyTorch 확장: Pytorch에서 간편한 혼합 정밀도 및 분산 교육을 위한 도구 – GitHub – NVIDIA/apex: A PyTorch 확장: Pyt에서 간편한 혼합 정밀도 및 분산 교육을 위한 도구…
github.com
대규모 배치에서 신경망 교육: 1-GPU, 다중 GPU 및 분산 설정에 대한 실용적인 팁
PyTorch에서 더 큰 배치로 신경망 훈련: 그래디언트 누적, 그래디언트 체크포인트, 다중 GPU 및 분산 설정…
medium.com
Nvidia Apex로 모델 교육 간소화
Colab에서 Language Model Pretraining을 할 때 학습 시간을 줄이고 학습 스택 크기를 늘리고 싶을 때 자료를 검색하면서 몇 줄의 코드만으로 위의 문제를 해결할 수 있습니다.
velog.io