딥러닝에 사용되는 데이터는 일반적으로
- train data
- validation data
- test data
등으로 나누어서 학습 및 평가에 사용함
1. train data
- 학습에 사용되는 데이터이며, 가중치와 바이어스를 최적화하기 위해 사용됨
- 손실함수 최소값 찾을 때까지 반복
2. validation data
- 1 epoch마다 과적합(overfitting)을 확인하기 위해 사용됨(during learning)
- train과 달리 반복이 없음
- 모델에 데이터를 주고, 출력값을 계산한 다음, 정답과 비교해 현재의 손실 함수 값 확인(정확도만을 나타냄)
-> 위 두개는 during learning 학습 도중 필요한 데이터들임
3. test data
- 학습 후에 정확도를 평가하거나 임의의 이볅에 대한 결과를 예측하기 위해 사용됨(after data)
- 학습을 모두 마친 후에(optimizer를 이용해 가중치와 바이어스가 이미 최적값으로 됐다는 가정 하에) test data를 이용해 현재 모델이 얼마나 정확하고 얼마나 손실값을 적게 내는지 평가하고 예측하기 위해 사용된다.
배치(batch, 데이터 묶음 단위) 필요성
- 크기가 매우 큰 데이터인 경우에 통째로 넣고 학습시키면 시간이 오래 걸리고 메모리도 부족할 뿐 아니라 통째로 들어온 모든 데이터의 gradient를 한번에 계산하는게 매우 어려움
- 데이터를 한 번에 통째로 모델에 입력하는 것이 아니라 batch라는 한 번에 입력되는 데이터 묶음 단위로 정의하고 전체 데이터를 이러한 배치 크기로 나눠서 순차적으로 모델에 입력하는 것이 일반적임.
파이토치 dataset, DataLoader
1. 학습에 필요한 입력 데이터(input feature) 정답(label)을 저장하고
2. 그러한 데이터를 배치 사이즈 간격으로 나눈 후에,
3. 배치사이즈만큼 입력 데이터와 정답을 가져와서 모델에 입력으로 넣어주는 기능이 필요한데
파이토치에서 이러한 데이터 저장되 배치 설정 기능을 제공해주는 것이 Dataset 클래스/ DataLoader 클래스이다.
class CustomDataset(Dataset):
이렇게 있으면 데이터셋을 상속받았기 때문에 반드시 재정의(Overiding)이 필요하다.
-> __init__ : 입력데이터와 정답을 저장
-> __getitem__ : 파라미터로 들어온 index에 대해 인덱스에 해당하는 item을 넘김
-> __len__ : 현재 데이터 사이즈 리턴
train_loader = DataLoader(dataset=dataset, batch_size=3, shuffle=True)
- train_loader로 인해 batch_size=3이니까 저렇게 나누어짐
training loop
- sample을 소위 더 작은 배치를 나누는 것
data = numpy.loadtxt('wine.csv')
#training loop
for epoch in range(1000):
#loop over all batches
for i in range(total_batches):
x_batch, y_batch = ...
# --> use DataSet and DataLoader to load wine.csv
- epoch = 1 forward and backward pass of ALL training samples
- batch_size = number of training samples in one forward & backward pass
- 반복 number of iterations = number of passes, each pass using [batch_size] number of samples
- e.g. 100 samples, batch_size=20 --> 100/20 = 5 iterations for 1 epoch
import torch
import torchvision
from torch.utils.data import Dataset, DataLoader
import numpy as np
import math
class WineDataset(Dataset):
def __init__(self):
# data loading
xy = np.loadtxt('./data/wine/wine.csv', delimiter=",", dtype=np.float32, skiprows=1)
self.x = torch.from_numpy(xy[:, 1:])
self.y = torch.from_numpy(xy[:, [0]]) # n_samples, 1 나중에 계산 더 쉬워짐
self.n_samples = xy.shape[0]
def __getitem__(self, index):
# dataset[0]
return self.x[index], self.y[index]
def __len__(self):
# len(dataset)
return self.n_samples
dataset = WineDataset()
first_data = dataset[0]
features, labels = first_data
print(features, labels)
DateSet
# 위에는 동일
dataset = WineDataset()
dataloader = DataLoader(dataset=dataset, batch_size=4, shuffle=True, num_workers=2)
datatiter = iter(dataloader)
data = datatiter.next()
features, labels = data
print(features, labels)
DataLoader
- 전체 데이터 로더를 반복
# training loop
num_epochs = 2
total_samples = len(dataset)
n_iterations = math.ceil(total_samples/4)
print(total_samples, n_iterations)
/*
178 45
*/
DataLoader
- 더미 train loop
178개의 sample과 45개의 반복이 있으므로 이제 loop 수행해보자 !
# 위에 코드와 이어짐
for epoch in range(num_epochs):
for i, (inputs, labels) in enumerate(dataloader):
# forward backword, update
#배치에 대한 일부 정보만 보고싶음
if (i+1) % 5 == 0:
print(f'epoch {epoch+1}/{num_epochs}, step {i+1}/{n_iterations}, input {inputs.shape}')
/*
...
epoch 1/2, step 20/45, inputs torch.Size([4, 13])
...
*/
- torch.Size([4, 13])
- 배치 크기 : 4
- 각 입력의 차원 : (4, 13) - 2차원 Tensor
- 이 텐서는 4개의 sample(data point)가 있고, 각 sample은 13개의 특성(or feature)으로 구성되어 있음을 의미한다.
- 즉 batch_size가 4이고 각 batch에 13개의 서로 다른 특성이 있다는 것을