데이터를 불러오는 방법과 미니 배치 경사 하강법(Minibatch Gradient Descent)을 배워보자!
1. 미니 배치, 배치 크기
미니 배치가 필요한 이유
# 전체 데이터를 하나의 행렬로 선언하여 훈련에 사용했었음
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개뿐이라서 경사하강법을 전체 데이터에 적용해도 괜찮았음
- But 현업에서 훨씬 방대한 데이터를 다루게 될 경우, '전체 데이터에 대해서' 경사 하강법을 수행하는 것은 계산량과 메모리가 너무 큼 (계산이 불가능할 수도 있음)
- ⇒ 그래서, 전체 데이터를 '더 작은 단위로 쪼개서' 학습하는 개념이 나왔고, 그 단위를 미니 배치(Mini Batch)라고 함!
미니 배치 경사하강법
- 미니 배치 경사하강법 : 미니 배치만큼만 가져가서 비용(cost)를 계산하고 경사 하강법을 수행 → 그 다음 미니 배치를 가져가서 경사 하강법을 수행 → ... 마지막 미니 배치까지 이를 반복
- 이렇게 전체 데이터에 대한 학습이 1회 끝나면 1 에포크가 끝나는 것! (*epoch: 전체 훈련 데이터가 학습에 한 번 사용된 주기)
- 데이터의 일부만 보고 내려가니까 최적값 찾는 데 조금 헤매기도 하지만, 훈련 속도가 훨씬 빠름!
- 배치 크기 : 보통 2의 제곱수를 사용 (ex. 2, 4, 8, 16, 32, 64...)
- CPU와 GPU의 메모리가 2의 배수라서 배치 크기가 2의 제곱수일 때 데이터 송수신의 효율을 높일 수 있다고 함
- 이 배치크기가 얼마냐에 따라 배치 개수가 정해짐
🆚전체 데이터를 한 번에 경사하강법 수행하는 건 '배치 경사하강법'이라고 함
전체 데이터를 다 쓰니까 가중치 최적값 찾아가는(수렴하는) 과정이 안정적이지만, 계산량이 너무 많이 듦
이터레이션(iteration)
- 한 번의 에포크 내에서 가중치 W,b가 업데이트되는 횟수로, 미니 배치 개수와 동일하다고 보면 됨
- ex) 전체 데이터 2,000개, 배치 크기 200일 경우, Iteration은 총 10번 발생함 = 미니배치 총 10개
2. 데이터 로드하기
이제 미니 배치 학습을 할 수 있도록 도와주는 Pytorch 도구들을 배워보자
TensorDataset
- Pytorch는 데이터를 쉽게 다룰 수 있도록 데이터셋(Dataset)과 데이터로더(DataLoader)라는 도구를 제공함
- 이를 활용하면 미니 배치 학습, 데이터 셔플(shuffle), 병렬 처리까지 간단히 수행 가능!
- 사용 방법: Dataset을 정의하고 그걸 DataLoader에 전달
- TensorDataset : `torch.utils.data.TensorDataset()`로 구현
- 텐서를 입력으로 받아서 Dataset의 형태로 변환해주는 라이브러리 (입력, 타깃 따로 전달)
- DataLoader : `torch.utils.data.DataLoader`로 구현
- 기본적으로 2개의 인자 `(데이터셋, 미니 배치의 크기)`를 입력 받음
- `shuffle=True` 추가 시, Epoch마다 데이터셋을 섞어서 학습되는 순서를 골고루 섞어줌 (사용 권장!)
- 위와 같이 DataLoader 준비됐다면, 이전과 동일한 방식으로 학습(경사하강법) 진행하면 됨!
(단, dataloader 객체에서 미니배치 꺼내는 반복문만 추가됨)
- ➡️ 각 에포크마다 3번의 미니배치로 쪼개서 학습되는 것을 볼 수 있음!
- (오른쪽 그림처럼 `print(samples)` 추가해서 배치마다 어떻게 샘플이 들어있는지도 자세히 볼 수 있음)
- 전체 데이터 샘플이 5개라 마지막 미니배치는 불완전하게 들어있음 (2개+2개+1개)
CustomDataset
- 그런데 torch.utils.data.Dataset을 상속받아 직접 커스텀 데이터셋(Custom Dataset)을 만드는 방식도 있음
- 가장 기본적인 뼈대는 아래와 같음
class CustomDataset(torch.utils.data.Dataset):
def __init__(self):
# 데이터 선언 & 전처리를 해주는 부분
def __len__(self):
# 데이터셋의 길이(=총 샘플 개수)를 적어주는 부분
def __getitem__(self, idx):
# 데이터셋에서 특정 샘플 1개를 가져오는 함수 (=idx번째 샘플)
- `__init__` : 데이터셋 원본을 준비해두는 부분
- `__len__` : `len(dataset)` 호출했을 때 데이터셋의 크기를 반환하기 위한 부분
- `__getitem__` : `dataset[i]` 호출했을 때 i번째 샘플을 가져오는 인덱싱을 위한 부분⭐
- 입력 데이터(x_data)와 출력 데이터(y_data)를 내부 변수로 저장 ⇒ 인덱스로 호출하면 그 부분의 데이터를 반환!
- CustomDataset 클래스 객체를 선언하면 Dataset 만들어지고, DataLoader는 이전과 동일하게 구현!
- Dataset과 Dataloader가 준비됐다면, 이전과 동일한 방식으로 학습(경사하강법) 진행하면 됨!
(❗코드는 위에서 했던 것과 100% 동일함)
# 이제 학습 진행
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): # 데이터로더가 미니배치만큼씩 꺼내서 훈련 진행함!
x_train, y_train = samples # 배치크기만큼 샘플을 꺼내줌
# 가설 (forward 연산)
prediction = model(x_train)
# cost 계산
cost = F.mse_loss(prediction, y_train)
# cost 미분하여 gradient 계산
optimizer.zero_grad()
cost.backward()
optimizer.step()
print('Epoch{:4d}/{} Batch {}/{} Cost: {:.6f}'.format(
epoch, nb_epochs, batch_idx+1, len(dataloader),
cost.item()
))
- 훈련 완료 후, New 샘플에 대한 예측도 (이전 시간에 했던 것과 동일하게) 수행 가능
🙏References
- 유원준, 안상준 님의 <딥러닝 파이토치 교과서 Wikidocs> (https://wikidocs.net/book/2788) 를 공부하고 정리한 내용입니다. (이미 알고 있는 머신러닝 관련 기본 지식들은 간소화 또는 생략하고 포스팅하였습니다)
'ML & DL > 딥러닝 기초' 카테고리의 다른 글
[Pytorch+딥러닝] 3-3. nn.Module과 클래스로 구현하기 (1) | 2025.02.19 |
---|---|
[Pytorch+딥러닝] 3-2. 다중 선형 회귀 (0) | 2025.02.18 |
[Pytorch+딥러닝] 3-1. 선형 회귀와 자동 미분 (0) | 2025.02.17 |
[Pytorch+딥러닝] 1. Pytorch 기초 (0) | 2025.02.05 |
[Keras] Sequential API vs. Functional API 비교 (0) | 2025.02.03 |