1. 미니 배치와 배치 크기(Mini Batch and Batch Size)
다중 선형 회귀에서 사용했던 데이터를 상기해봅시다.
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]])
위 데이터의 샘플의 개수는 5개입니다. 전체 데이터를 하나의 행렬로 선언하여 전체 데이터에 대해서 경사 하강법을 수행하여 학습할 수 있습니다. 그런데 위 데이터는 현업에서 다루게 되는 방대한 양의 데이터에 비하면 굉장히 적은 양입니다. 만약, 데이터가 수십만개 이상이라면 전체 데이터에 대해서 경사 하강법을 수행하는 것은 매우 느릴 뿐만 아니라 많은 계산량이 필요합니다. 정말 어쩌면 메모리의 한계로 계산이 불가능한 경우도 있을 수 있습니다.
그렇기 때문에 전체 데이터를 더 작은 단위로 나누어서 해당 단위로 학습하는 개념이 나오게 되었습니다.
이 단위를 미니 배치(Mini Batch)라고 합니다.
미니 배치 학습을 하게되면 미니 배치만큼만 가져가서 미니 배치에 대한 대한 비용(cost)를 계산하고, 경사 하강법을 수행합니다. 그리고 다음 미니 배치를 가져가서 경사 하강법을 수행하고 이를 마지막 미니 배치까지 반복합니다. 이렇게 전체 데이터에 대한 학습이 1회 끝나면 1 에포크(Epoch)가 끝나게 됩니다.
- 에포크(Epoch) : 전체 훈련 데이터가 학습에 한 번 사용된 주기
미니 배치 학습에서는 전체 데이터가 한 번 전부 사용되어야( 미니 배치의 개수번만큼 경사 하강법을 수행) 1 에포크(Epoch)가 됩니다. 미니 배치의 개수는 결국 미니 배치의 크기를 몇으로 하느냐에 따라서 달라지는데 미니 배치의 크기를 배치 크기(batch size)라고 합니다.
- 배치 경사 하강법 : 전체 데이터에 대해서 한 번에 경사 하강법을 수행하는 방법
- 경사 하강법을 할 때, 전체 데이터를 사용하므로 가중치 값이 최적값에 수렴하는 과정이 매우 안정적이지만, 계산량이 너무 많이 듦.
- 미니 배치 경사 하강법 : 미니 배치 단위로 경사 하강법을 수행하는 방법
- 경사 하강법을 할 때, 전체 데이터의 일부만을 보고 수행하므로 최적값으로 수렴하는 과정에서 값이 조금 헤매기도 하지만 훈련 속도가 빠름.
- 미니 배치 크기는 보통 2의 제곱수를 사용합니다. ex) 2, 4, 8, 16, 32, 64... 그 이유는 CPU와 GPU의 메모리가 2의 배수이므로 배치크기가 2의 제곱수일 경우에 데이터 송수신의 효율을 높일 수 있다고 합니다.
2. 이터레이션(Iteration)
미니 배치와 배치 크기의 정의에 대해서 이해하였다면 이터레이션(iteration)을 정의할 수 있습니다.
이터레이션은 한 번의 에포크 내에서 이루어지는 매개변수인 가중치 W와 b의 업데이트 횟수입니다. 전체 데이터가 2,000일 때 배치 크기를 200으로 한다면 이터레이션의 수는 총 10개입니다(2000/200=10). 이는 한 번의 에포크 당 매개변수 업데이트가 10번 이루어짐을 의미합니다.
이제 미니 배치 학습을 할 수 있도록 도와주는 파이토치의 도구들을 알아봅시다.
3. 데이터 로드하기(Data Load)
파이토치에서는 데이터를 좀 더 쉽게 다룰 수 있도록 유용한 도구로서 데이터셋(Dataset)과 데이터로더(DataLoader)를 제공합니다. 이를 사용하면 미니 배치 학습, 데이터 셔플(shuffle), 병렬 처리까지 간단히 수행할 수 있습니다. 기본적인 사용 방법은 Dataset을 정의하고, 이를 DataLoader에 전달하는 것입니다.
Dataset을 커스텀하여 만들 수도 있지만 여기서는 텐서를 입력받아 Dataset의 형태로 변환해주는 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 # 데이터로더
TensorDataset은 기본적으로 텐서를 입력으로 받습니다. 텐서 형태로 데이터를 정의합니다.
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)
파이토치의 데이터셋을 만들었다면 데이터로더를 사용 가능합니다. 데이터로더는 기본적으로 2개의 인자를 입력받는다. 하나는 데이터셋, 미니 배치의 크기입니다. 그리고 추가적으로 많이 사용되는 인자로 shuffle이 있습니다. shuffle=True를 선택하면 Epoch마다 데이터셋을 섞어서 데이터가 학습되는 순서를 바꿉니다. 순서에 익숙해지는 것을 방지해서 이 옵션을 True로 두는 것을 권장합니다.
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)
이제 모델과 옵티마이저를 설계합니다.
model = nn.Linear(3,1)
optimizer = torch.optim.SGD(model.parameters(), lr=1e-5)
이제 훈련을 진행합니다. 아래 코드에서는 batch_idx와 samples를 주석 처리했는데 어떤 식으로 훈련되고 있는지 궁금하다면 주석 처리를 해제하고 훈련시켜보시기 바랍니다.
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()
))
위 코드에서, 첫 번째 for 루프는 에포크를 관리하고, 두 번째 for 루프는 각 에포크 내에서 이터레이션을 관리합니다. enumerate(dataloader)를 통해 dataloader에서 데이터 배치를 하나씩 가져옵니다. 이 때, batch_idx는 현재 배치의 인덱스(즉, 현재 이터레이션)를 나타내고, samples는 해당 배치의 데이터입니다.
이터레이션의 수는 주로 데이터의 총 수와 배치 사이즈에 의해 결정됩니다. 예를 들어, 1000개의 데이터 샘플이 있고 배치 사이즈가 100이라면, 한 에포크에는 10개의 이터레이션(1000 / 100)이 발생합니다.
코드에서 직접적으로 이터레이션 수를 설정하지는 않습니다. 대신, 데이터로더의 배치 사이즈와 데이터 세트의 총 샘플 수에 기반해 이터레이션 수가 결정됩니다. 각 이터레이션에서는 optimizer.step()을 호출하여 모델의 가중치(W)와 편향(b)을 업데이트합니다. 따라서, 이터레이션은 미니배치 한번 돌아가는것을 뜻하며, 에포크는 이러한 학습 프로세스가 전체 데이터 세트에 대해 반복되는 횟수를 의미합니다.
- 미니 배치 처리: 전체 데이터 세트에서 무작위로 선택된 소량의 데이터 샘플. 이는 메모리 사용을 최적화하고, 학습 속도를 빠르게 하며, 일반화 성능을 개선하는 데 도움을 줍니다.
- 이터레이션: 미니 배치 하나를 전방 전달(forward pass)하고, 손실을 계산하고, 역전파(backward pass)를 통해 가중치를 업데이트하는 전체 과정입니다.
Epoch 0/20 Batch 1/3 Cost: 26085.919922
Epoch 0/20 Batch 2/3 Cost: 3660.022949
Epoch 0/20 Batch 3/3 Cost: 2922.390869
... 중략 ...
Epoch 20/20 Batch 1/3 Cost: 6.315856
Epoch 20/20 Batch 2/3 Cost: 13.519956
Epoch 20/20 Batch 3/3 Cost: 4.262849
Cost의 값이 점차 작아집니다. (사실 아직 에포크를 더 늘려서 훈련하면 Cost의 값이 더 작아질 여지가 있습니다. 에포크를 늘려서도 훈련해보세요.)
이제 모델의 입력으로 임의의 값을 넣어 예측값을 확인합시다.
# 임의의 입력 [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([[154.3850]], grad_fn=<AddmmBackward>)
참고 : https://wikidocs.net/55580
배치 크기 이야기 : https://hongdoki.github.io/2017/10/07/optimization-difficulty-and-generlization-performance-as-batch-size-increases.html
클래스로 파이토치 모델 구현하는 것과 데이터 로드에 대한 설명 : https://www.youtube.com/watch?v=KXiDzNai9tI
'AI > ML' 카테고리의 다른 글
[ML] 로지스틱 회귀(Logistic Regression) (1) | 2024.03.29 |
---|---|
[ML] 커스텀 데이터셋(Custom Dataset) (0) | 2024.03.29 |
[ML] nn.Module로 구현하는 선형 회귀 (0) | 2024.03.26 |
[pytorch] 파이토치 입문 (0) | 2024.03.23 |
[ML] 다중 선형 회귀(Multivariable Linear regression) (1) | 2024.03.23 |