전공공부/인공지능

Mini Batch 경사하강법, Data Load, Custom Dataset

prefer_all 2022. 7. 27. 14:23

https://wikidocs.net/55580

 

<목차>
1. Mini Batch and Batch Size
2. Iteration
3. Data Load
- Dataset과 DataLoader을 활용해 미니배치 학습, 데이터 shuffle, 병렬처리까지 간단히 수행
4. Custom Dataset

 

 

Mini Batch and Batch Size

  • (문제) 전체 데이터에서 경사 하강법을 수행하는 것은 많은 계산량이 필요하고 매우 느림
  • (해결) 전체 데이터를 작은 단위로 나누어서 학습. 이 단위를 "미니 배치"라고 함.
    • 미니 배치만큼 가져가서 이에 대한 cost를 계산하고 경사하강법을 수행. 그리고 그 다음 미니배치를 가져가서 경사하강법 수행... 이렇게 전체 데이터에 대한 학습이 1회 끝나면 1 에포크(Epoch)가 끝나게 됨
    • Epoch : 전체 훈련 데이터가 학습에 한 번 사용된 주기 (전체 데이터에 대한 학습이 몇 번 됐냐?). 미니 배치 학습에서는 미니배치 개수만큼 경사하강법을 수행할 때 1 Epoch가 됨
    • Batch size : 미니 배치의 크기 / 미니 배치의 개수는 미니 배치의 크기에 따라 달라짐

1000 training examples, batch size = 500 -> 2 iteration to complete 1 epoch

배치 경사하강법 - 전체 데이터에 대해 한 번에 경사하강법 수행
- 가중치 값이 최적값에 수렴하는 과정이 안정적이지만, 계산랑이 많이 든다
미니배치 경사하강법 - 미니 배치 단위로 경사하강법
- 전체 데이터의 일부만을 보고 수행하므로 최적값으로 수렴하는 과정에서 값이 조금 헤매기도 하지만, 훈련 속도가 빠름

 

 


Iteration

  • 한 번의 epoch 내에서 이루어지는 매개변수인 가중치인 W와 b의 업데이트 횟수


Data Load

  • Dataset과 DataLoader을 사용하면 미니 배치 학습, data shuffle, 병렬처리까지 간단히 수행 가능
  • 기본적인 사용법: Dataset 정의, 이를 DataLoader에 전달
  • DataLoader는 기본적으로 두 개의 인자(dataset, 미니배치 크기)를 입력받음. 미니배치의 크기는 통상적으로 2의 배수이고, 추가적으로 많이 사용되는 인자로 shuffle이 있음. shuffle = True이면 Epoch마다 데이터 셋을 섞어서 데이터가 학습되는 순서를 바꿈
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]])

# TensorDataset의 입력으로 사용하고 dataset으로 저장
dataset = TensorDataset(x_train, y_train)

# Dataset을 DataLoader에 전달
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)

# model, optimizer 설정
model = nn.Linear(3,1)
optimizer = torch.optim.SGD(model.parameters(), lr=1e-5) 

nb_epochs = 20
for epoch in range(nb_epochs + 1):
    for batch_idx, samples in enumerate(dataloader):
        # print(batch_idx)
        # print(samples)
        x_train, y_train = samples
        # H(x) 계산
        prediction = model(x_train)

        # cost 계산
        cost = F.mse_loss(prediction, y_train)

        # cost로 H(x) 계산
        optimizer.zero_grad()
        cost.backward()
        optimizer.step()

        print('Epoch {:4d}/{} Batch {}/{} Cost: {:.6f}'.format(
            epoch, nb_epochs, batch_idx+1, len(dataloader),
            cost.item()
            ))

Cost 값이 점점 작아짐

 

모델에 입력값 넣어서 예측값 확인하기

new_var =  torch.FloatTensor([[73, 80, 75]]) 
pred_y = model(new_var) 
print(pred_y)

Custom Dataset 

  • 파이토치에서는 데이터셋을 좀 더 쉽게 다룰 수 있도록 유용한 도구로서 torch.utils.data.Dataset과 torch.utils.data.DataLoader를 제공
  • torch.utils.data.Dataset을 상속받아 직접 커스텀 데이터셋(Custom Dataset)을 만드는 경우도 있음

 

기본 뼈대

class CustomDataset(torch.utils.data.Dataset): 
  def __init__(self):
  데이터셋의 전처리를 해주는 부분

  def __len__(self):
  데이터셋의 길이. 즉, 총 샘플의 수를 적어주는 부분
  len(dataset)을 했을 때 데이터셋의 크기를 리턴할 len

  def __getitem__(self, idx): 
  데이터셋에서 특정 1개의 샘플을 가져오는 함수
  dataset[i]을 했을 때 i번째 샘플을 가져오도록 하는 인덱싱을 위한 get_item

 

Custom dataset으로 선형 회귀 구현하기 

import torch
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 = CustomDataset()
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)   
model = torch.nn.Linear(3,1)
optimizer = torch.optim.SGD(model.parameters(), lr=1e-5) 

nb_epochs = 20
for epoch in range(nb_epochs + 1):
    for batch_idx, samples in enumerate(dataloader):
    # enumerate를 통해 minibatch의 index와 data를 받음
        x_train, y_train = samples
        # H(x) 계산
        prediction = model(x_train)

        # cost 계산
        cost = F.mse_loss(prediction, y_train)

        # cost로 H(x) 계산
        optimizer.zero_grad()
        cost.backward()
        optimizer.step()

        print('Epoch {:4d}/{} Batch {}/{} Cost: {:.6f}'.format(
            epoch, nb_epochs, batch_idx+1, len(dataloader),
            cost.item()
            ))
            #len(dataloader)은 한 epoch 당 minibatch 개수