[AI/ML] 미니 배치와 데이터 로더

PyTorch의 DataLoader를 활용한 미니 배치 학습 방식입니다. 메모리 제약을 극복하기 위해 데이터를 작은 단위로 나누어 학습하며, Dataset과 DataLoader를 통해 효율적으로 배치 단위 훈련을 구현합니다.

[AI/ML] 미니 배치와 데이터 로더
Photo by Conor Brown / Unsplash

개요

AI에 대한 기초를 다시 정리하고자 아래 내용을 읽고 정리했습니다.

미니 배치(Mini Batch)

딥러닝 모델들은 훈련하기 위해 수십만 개 이상의 데이터를 사용합니다.

하지만 데이터가 아무리 많아도 데이터를 메모리 공간에 올리는 것엔 물리적인 한계가 존재합니다.

따라서 느린 속도 + 메모리 한계를 극복하기 위해 전체 데이터를 더 작은 단위로 나누어 학습하는데 이를 미니 배치라고 표현합니다. 또한 미니 배치의 사이즈를 배치 크기(batch_size)로 표현합니다.

Epoch

Epoch는 전체 훈련 데이터가 학습에 한 번 사용된 주기입니다.

전체 데이터에서 미니 배치 단위만큼 경사하강법을 수행하고 전체 데이터가 모두 사용되었을 때 1 Epoch입니다.

  • 데이터가 2,000개. 미니 배치가 200이라면 미니 배치가 10번 학습되어야 1 Epoch가 성립
    • 비용 함수, 경사 하강법은 미니 배치 단위로 수행되는 경우 → 미니 배치 경사 하강법
      • 데이터의 일부만 보기에 수렴 과정이 산발적이지만 속도가 빠릅니다.
    • 비용 함수, 경사 하강법은 전체 데이터 단위로 수행되는 경우 → 배치 경사 하강법
      • 전체 데이터를 사용하므로 가중치 값이 수렴하기에 안정적이지만 느리고 계산량이 많이 필요합니다.

이터레이션(Iteration)

미니 배치 경사 하강법을 사용할 때 가중치 업데이트 1회를 의미하는 단어입니다.

  • 총 데이터가 2,000개. 배치 사이즈가 200일 때 이터레이션은 10회. 에포크는 1회

데이터 로드하기(DataLoader)

미니 배치 학습을 쉽게 하기 위해 Pytorch 도구를 사용합니다.

  • Dataset: 데이터를 저장
  • DataLoader: batch 단위로 데이터를 꺼냄

예시에서는 Tensor를 입력받아 저장할 수 있는 TensorDataset을 사용했습니다.

데이터를 불러오는 방법은 다음과 같습니다.

import torch
import torch.nn as nn
import torch.nn.functional as F

from torch.utils.data import TensorDataset # 텐서데이터셋
from torch.utils.data import DataLoader # 데이터로더

x_train  =  torch.FloatTensor([[73,  80,  75], 
                               [93,  88,  93], 
                               [89,  91,  90], 
                               [96,  98,  100],   
                               [73,  66,  70]])  
y_train  =  torch.FloatTensor([[152],  [185],  [180],  [196],  [142]])

dataset = TensorDataset(x_train, y_train)

데이터를 미니 배치 단위로 로드하는 방식은 다음과 같습니다.

  • DataLoader는 dataset과 batch_size를 받지만 shuffle 인자도 많이 씁니다.
    • shuffle은 데이터 순서를 섞는 것으로 모델이 순서에 익숙해지는 것을 방지합니다.
    • 순서에 따라 데이터를 그대로 외우는 것을 방지합니다.
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)

모델과 옵티마이저를 설정합니다.

model = nn.Linear(3,1)
optimizer = torch.optim.SGD(model.parameters(), lr=1e-5)
  • cost = 비용 값 (예: 10)
  • cost.backward() = 비용을 W로 미분해서 기울기 계산
  • optimizer.step() = 학습률 × 기울기만큼 W를 업데이트
    • 기존 가중치 - (학습률 × 기울기) 방식으로 정리됩니다.

결과 값은 다음과 같습니다. 조금 더 해석하면 미니 배치 경사하강법으로 Cost(오차)가 흔들리는 것을 확인할 수 있습니다. 또한 최종 가중치는 현재 마지막 Epoch 로 설정됩니다.

만약 가장 좋을 때의 가중치를 저장해두려면 체크포인트(checkpointing, early stopping)로 구성하며 해당 체크포인트를 찾기 위해 검증 데이터(validation)를 사용합니다. 검증 데이터로 해당 체크포인트를 찾아 사용합니다.

Epoch    0/20 Batch 1/3 Cost: 24639.523438
Epoch    0/20 Batch 2/3 Cost: 7149.754395
Epoch    0/20 Batch 3/3 Cost: 3405.706299
Epoch    1/20 Batch 1/3 Cost: 737.025269
Epoch    1/20 Batch 2/3 Cost: 143.452744
Epoch    1/20 Batch 3/3 Cost: 59.147167
Epoch    2/20 Batch 1/3 Cost: 37.379478
Epoch    2/20 Batch 2/3 Cost: 4.725218
Epoch    2/20 Batch 3/3 Cost: 1.797635
Epoch    6/20 Batch 3/3 Cost: 7.471550
Epoch    7/20 Batch 1/3 Cost: 4.698550
Epoch    7/20 Batch 2/3 Cost: 2.714486
Epoch    7/20 Batch 3/3 Cost: 5.062019
Epoch    8/20 Batch 1/3 Cost: 8.113654
...
Epoch   19/20 Batch 3/3 Cost: 6.490381
Epoch   20/20 Batch 1/3 Cost: 1.353482
Epoch   20/20 Batch 2/3 Cost: 3.490023
Epoch   20/20 Batch 3/3 Cost: 6.951240

Epoch를 2,000까지 늘렸을 때 오차가 많이 줄어드는 것을 확인할 수 있습니다.

Epoch    0/2000 Batch 1/3 Cost: 3.120791
Epoch    0/2000 Batch 2/3 Cost: 1.551620
Epoch    0/2000 Batch 3/3 Cost: 2.950326
Epoch    1/2000 Batch 1/3 Cost: 2.076473
Epoch    1/2000 Batch 2/3 Cost: 3.415830
Epoch    1/2000 Batch 3/3 Cost: 0.025309
Epoch    2/2000 Batch 1/3 Cost: 1.973059
Epoch    2/2000 Batch 2/3 Cost: 1.884141
Epoch    2/2000 Batch 3/3 Cost: 4.772414
Epoch    3/2000 Batch 1/3 Cost: 0.561520
Epoch    3/2000 Batch 2/3 Cost: 4.686387
Epoch    3/2000 Batch 3/3 Cost: 4.584628
Epoch    4/2000 Batch 1/3 Cost: 1.699398
Epoch    4/2000 Batch 2/3 Cost: 3.427198
Epoch    4/2000 Batch 3/3 Cost: 2.200335
Epoch    5/2000 Batch 1/3 Cost: 2.005369
Epoch    5/2000 Batch 2/3 Cost: 2.321060
Epoch    5/2000 Batch 3/3 Cost: 7.125224
Epoch    6/2000 Batch 1/3 Cost: 1.354929
Epoch    6/2000 Batch 2/3 Cost: 3.690168
Epoch    6/2000 Batch 3/3 Cost: 3.737782
Epoch    7/2000 Batch 1/3 Cost: 3.450968
Epoch    7/2000 Batch 2/3 Cost: 3.108922
Epoch    7/2000 Batch 3/3 Cost: 3.465338
Epoch    8/2000 Batch 1/3 Cost: 2.713056
...
Epoch 1999/2000 Batch 3/3 Cost: 0.698256
Epoch 2000/2000 Batch 1/3 Cost: 0.126965
Epoch 2000/2000 Batch 2/3 Cost: 0.868454
Epoch 2000/2000 Batch 3/3 Cost: 0.089180

최종 예측 결과입니다.

# 임의의 입력 [73, 80, 75]를 선언
new_var =  torch.FloatTensor([[73, 80, 75]]) 
# 입력한 값 [73, 80, 75]에 대해서 예측값 y를 리턴받아서 pred_y에 저장
pred_y = model(new_var) 
print("훈련 후 입력이 73, 80, 75일 때의 예측값 :", pred_y) 
# -------------------------------------------------------
훈련 후 입력이 73, 80, 75일 때의 예측값 : tensor([[151.8671]], grad_fn=<AddmmBackward0>)

커스텀 데이터셋(Custom Dataset)

데이터에 따라 커스텀 데이터셋을 만들어 사용할 수 있습니다. 커스텀 데이터셋은 반드시 정의해야하는 메서드가 3개 존재합니다.

  • __init__: 데이터셋 초기화, 전처리
  • __len__: 총 샘플 개수 리턴 (len(dataset) 할 때 호출)
  • __getitem__: 인덱스로 샘플 1개 가져오기 (dataset[i] 할 때 호출)
class CustomDataset(torch.utils.data.Dataset): 
  def __init__(self):

  def __len__(self):

  def __getitem__(self, idx):

DataLoader는 __getitem__ 으로 요소로 batch_size만큼 가져와 미니 배치를 생성합니다.

import torch
import torch.nn as nn
import torch.nn.functional as F

from torch.utils.data import Dataset
from torch.utils.data import DataLoader # 데이터로더
# Dataset 상속
class CustomDataset(Dataset): 
  def __init__(self):
    self.x_data = [[73, 80, 75],
                   [93, 88, 93],
                   [89, 91, 90],
                   [96, 98, 100],
                   [73, 66, 70]]
    self.y_data = [[152], [185], [180], [196], [142]]

  # 총 데이터의 개수를 리턴
  def __len__(self): 
    return len(self.x_data)

  # 인덱스를 입력받아 그에 맵핑되는 입출력 데이터를 파이토치의 Tensor 형태로 리턴
  def __getitem__(self, idx): 
    x = torch.FloatTensor(self.x_data[idx])
    y = torch.FloatTensor(self.y_data[idx])
    return x, y
  • Dataset 상속
  • __getitem__ x, y 모두 선언해야합니다.
dataset = CustomDataset()
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)

그 외 코드는 동일합니다.

Epoch    0/2000 Batch 1/3 Cost: 18989.921875
Epoch    0/2000 Batch 2/3 Cost: 4662.060059
Epoch    0/2000 Batch 3/3 Cost: 2114.080078
Epoch    1/2000 Batch 1/3 Cost: 415.249969
Epoch    1/2000 Batch 2/3 Cost: 237.831039
Epoch    1/2000 Batch 3/3 Cost: 56.313648
Epoch    2/2000 Batch 1/3 Cost: 20.790310
Epoch    2/2000 Batch 2/3 Cost: 2.807354
Epoch    2/2000 Batch 3/3 Cost: 1.922820
Epoch    3/2000 Batch 1/3 Cost: 4.043968
Epoch    3/2000 Batch 2/3 Cost: 0.226214
Epoch    3/2000 Batch 3/3 Cost: 1.371253
Epoch    4/2000 Batch 1/3 Cost: 3.929478
Epoch    4/2000 Batch 2/3 Cost: 0.128780
Epoch    4/2000 Batch 3/3 Cost: 0.233011
Epoch    5/2000 Batch 1/3 Cost: 0.450442
Epoch    5/2000 Batch 2/3 Cost: 3.518873
Epoch    5/2000 Batch 3/3 Cost: 0.564287
Epoch    6/2000 Batch 1/3 Cost: 0.099612
Epoch    6/2000 Batch 2/3 Cost: 3.317684
Epoch    6/2000 Batch 3/3 Cost: 0.055822
Epoch    7/2000 Batch 1/3 Cost: 2.246110
Epoch    7/2000 Batch 2/3 Cost: 1.809132
Epoch    7/2000 Batch 3/3 Cost: 0.267966
Epoch    8/2000 Batch 1/3 Cost: 3.112362
...
Epoch 1999/2000 Batch 3/3 Cost: 0.545037
Epoch 2000/2000 Batch 1/3 Cost: 1.210260
Epoch 2000/2000 Batch 2/3 Cost: 0.022624
Epoch 2000/2000 Batch 3/3 Cost: 2.050513
훈련 후 입력이 73, 80, 75일 때의 예측값 : tensor([[151.0651]], grad_fn=<AddmmBackward0>)