크기가 큰 데이터셋을 학습하는 과정은 정말 오래걸린다. 내 GPU의 한계도 있겠지만 그래도 조금이나마 속도를 높여 5분이라도 학습을 일찍 끝낼 수 있는 방법은 없을까?
오늘은 3가지 정도의 방법론을 설명하고자 합니다.
__getitem__()함수는 DB에 있는 데이터를 불러와 Dataloader에서 배치단위로 Data를 처리 할 때 해당 index의 데이터를 뽑아주는 역할을 합니다. 그러나 이런 과정에서 __getitem__() 함수가 활욜 될 때 마다 매번 이미지를 load할 경우, 비효율적인 과정이 반복해서 일어납니다.__getitem__()에서 load하는 것이 아닌, 미리 load를 해놓은 뒤 indexing을 통해 원하는 데이터를 불러오는 방식입니다.__getitem__() 함수를 통해 load를 하되, 한번 load된 이미지는 다시 load하지 않는 방법으로 주로 첫번째 epoch 때만 데이터를 load하고 그 이후에는 load된 이미지를 indexing하는 방법으로 epoch이 진행됩니다. def __getitem__(self,idx):
if self.images[idx] is None:
img_path = os.path.join(self.root_dir,self.df[idx]['image_path']
image = cv2.imread(img_path,cv2.IMREAD_COLOR)
image = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
self.image[idx] = image #image를 첫 epoch에만 load 후 list에 저장
else:
image = self.image[idx] #두번째 epoch부터 list에서 뽑아씀Gradient를 매 batch마다 업데이트 하는 것이 아니라 여러 배치의 gradient를 파라미터에 누적해서 업데이트 하는 방식
GPU 메모리 제한으로 큰 배치 사이즈를 처리할 수 없을 때 활용할 수 있으며 더 큰 가상의 배치 사이즈를 사용하는 것과 유사한 효과를 냄
for i,(images,targets) in enumerate(train_loader):
images,targets = images.to(device), targets.to(device)
outputs = self.model(images)
loss = self.loss_fn(outputs,targets)
loss = loss/accumulateion_steps #누적을 위한 loss 평균값 계산
loss.backward() #backpropagation 연산해서 parameter.grad에 누적됨
total_loss += loss.item()
accumulation_steps = 4 #(4개의 배치마다 가중치 업데이트)
if(i+1)%accumulation_steps == 0 or (i+1) == len(self.train_loader):
self.optimizer_step() #가중치 업데이트
self.optimizer.zero_grad() #가중치 0으로 초기화
self.scheduler.step()
return total_loss / len(self.train_loader)
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler() # FP16에서 gradient scaling을 처리하기 위한 도구
for input, target in data_loader:
optimizer.zero_grad()
with autocast(): #FP16으로 연산되는 부분
output = model(input) # 모델의 연산이 자동으로 혼합 정밀도로 처리됨
loss = loss_fn(output, target)
# 역전파 시 기울기를 스케일링하여 안정성을 높임
scaler.scale(loss).backward() #loss는 FP16으로 처리됐기 때문에 scale로 FP32로 맞춰줘야함
# 스케일된 기울기를 사용해 optimizer를 적용
scaler.step(optimizer)
scaler.update()