본문 바로가기

Programming Project/Pytorch Tutorials

Pytorch 머신러닝 튜토리얼 강의 8 (PyTorch DataLoader)


투명한 기부를 하고싶다면 이 링크로 와보세요! 🥰 (클릭!)

바이낸스(₿) 수수료 평생 20% 할인받는 링크로 가입하기! 🔥 (클릭!)

2018/07/02 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 1 (Overview)

2018/07/02 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 2 (Linear Model)

2018/07/03 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 3 (Gradient Descent)

2018/07/03 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 4 (Back-propagation and Autograd)

2018/07/07 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 5 (Linear Regression in the PyTorch way)

2018/07/09 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 6 (Logistic Regression)

2018/07/10 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 7 (Wide and Deep)




이번 글에서는 data loader에 대해서 다뤄보겠습니다.


우리가 데이터를 읽어들일때 어떻게 읽어들이냐에 관련이 있습니다.


(sung kim님의 강의 ppt에서 가져와서 코드는 조금 다를 수 있습니다만)


이전에 우리는 data 를 load할 때 그냥 바로 통째로 numpy로 csv파일에서 데이터를 불러 온 후에, 그 데이터를 집어넣었습니다.


그리고 우리의 모델에  그 데이터를 몽땅 우리 모델에 넣고, 모든 데이터에 대한 결과값을 한번에 받아서 한번에 모두 다 비교했습니다.



이전 case 에서는 사실 데이터 size가 작기 때문에 별 문제가 되지 않았습니다.


하지만 수백만개 이상의 데이터가 쌓인 상태에서는 저렇게 모든 데이터를 한번에 넣어서 처리할 수 없습니다.


모든 데이터에 대한 결과를 한번에 구하는것도 무리일 뿐더러 모든 데이터에 대해 gradient를 구하는 것 역시 무리이기 때문입니다.






Batch


그래서 우리는 우리의 전체 데이터를 나눠 일부를 묶은 batch 라는 단위를 만들어서 데이터를 나눠서 처리 할 것입니다.


(batch는 직역하면 집단, 혹은 1회분 이라는 뜻이며, 우리는 여기서 한번에 처리할 데이터 묶음이라는 뜻으로 씁니다)


이렇게 이용할 때, 우리는 3가지 용어를 씁니다.


1. epoch : 한번 '모든' 트레이닝 데이터에 대해서 forward와 backward pass를 진행 한 상태를 의미함


2. batch_size : forward 와 backward를 한번에 얼만큼의 데이터씩 진행할 것인지 그 사이즈를 의미함.


3. iterations : batch_size단위로 몇번 forward, backward를 진행했는지 그 수를 의미한다. 


정리하자면 data size = batch_size * iterations 이다.






이것을 구현하기 위해서는 모든 데이터를 나누고, 일일히 그 데이터를 나눠서 forward와 backward를 돌리는 식으로 진행해야 하는데, 만약 파이토치에서 제공해주는 DataLoader을 쓴다면 그럴 필요가 없습니다.


우리는 그냥 DataLoader로 부터 batch_size만큼의 데이터를 받아오면 됩니다.


그렇다면 어떻게  코드로 구현할 수 있을까요?






Custom DataLoader



우리가 직접 만드는 custom dataloader은 다음과 같은 세 파트로 이루어져 있습니다.


1. __init__(self)

download, read data 등등을 하는 파트.


2. __getitem__(self,index)

인덱스에 해당하는 아이템을 넘겨주는 파트.


3. __len__(self)

data size를 넘겨주는 파트







한번 코드로 구현 해 봅시다.



import torch
import numpy as np
from torch.autograd import Variable
from torch.utils.data import Dataset, DataLoader


class DiabetesDataset(Dataset):
    """ Diabetes dataset."""

    # Initialize your data, download, etc.
    def __init__(self):
        xy = np.loadtxt('./data/diabetes.csv.gz',
                        delimiter=',', dtype=np.float32)
        self.len = xy.shape[0]
        self.x_data = torch.from_numpy(xy[:, 0:-1])
        self.y_data = torch.from_numpy(xy[:, [-1]])

    def __getitem__(self, index):
        return self.x_data[index], self.y_data[index]

    def __len__(self):
        return self.len


dataset = DiabetesDataset()
train_loader = DataLoader(dataset=dataset,
                          batch_size=32,
                          shuffle=True,
                          num_workers=2)

우리가 이전에 받아왔던 데이터를 DiabetesDataset 이라는 class를 이용해서 받아오고 있습니다.



DiabetsDataset은 torch.utils.data에서 받아온 Dataset class를 상속받고 있습니다.


우리가 class 생성시에 해줄것은


self.len 에 길이를 넣어주고

self.x_data에 x 데이터를

self.y_data에 y 데이터를 넣어주고


인덱스에 따라 i 번째 데이터를 잘 리턴해주면 됩니다.



그리고 training을 하기 위해서 loader를 생성할 때는 그 아리에 train_loader을 선언할 때 처럼 dataset에 우리가 만든 class를 넣어주고, batch_size 에는 데이터 사이즈 개수를, 그리고 shuffle은 True로 해주시는게 좋은데, 그냥 데이터를 묶어서 넘겨줄시에 기존 데이터 말고 셔플해서 넘겨준다는 뜻입니다. 그리고 num_workers는 멀티쓰레딩을 지원하기 때문에 빠르게 데이터를 가져오고 싶다면 늘립시다.


* windows 기준으로 num_workers=2 때문에 에러가 날 수도 있습니다. 만약 그렇다면 num_workers=0으로 세팅하시고 그냥 멀티쓰레딩 없이 돌립시다 ㅠㅠ..




그리고 이제 모델은 epoch만 정하면

다음 코드와 같이 한번에 돌릴 수 있습니다.



# Training loop for epoch in range(2): for i, data in enumerate(train_loader): # get the inputs inputs, labels = data # wrap them in Variable inputs, labels = Variable(inputs), Variable(labels) # Forward pass: Compute predicted y by passing x to the model y_pred = model(inputs) # Compute and print loss loss = criterion(y_pred, labels) print(epoch, i, loss.data[0]) # Zero gradients, perform a backward pass, and update the weights. optimizer.zero_grad() loss.backward() optimizer.step()



제 결과는 이렇게 나오네요


0 0 tensor(0.7405)
0 1 tensor(0.7103)
0 2 tensor(0.7059)
0 3 tensor(0.7127)
0 4 tensor(0.6936)
0 5 tensor(0.6949)
0 6 tensor(0.6836)
0 7 tensor(0.6713)
0 8 tensor(0.6715)
0 9 tensor(0.6440)
0 10 tensor(0.6379)
0 11 tensor(0.6759)
0 12 tensor(0.7042)
0 13 tensor(0.6672)
0 14 tensor(0.6665)
0 15 tensor(0.6754)
0 16 tensor(0.6655)
0 17 tensor(0.6236)
0 18 tensor(0.6868)
0 19 tensor(0.6518)
0 20 tensor(0.6146)
0 21 tensor(0.6224)
0 22 tensor(0.6758)
0 23 tensor(0.6687)
1 0 tensor(0.6332)
1 1 tensor(0.6907)
1 2 tensor(0.6760)
1 3 tensor(0.6191)
1 4 tensor(0.6456)
1 5 tensor(0.6455)
1 6 tensor(0.6613)
1 7 tensor(0.6767)
1 8 tensor(0.6611)
1 9 tensor(0.5993)
1 10 tensor(0.6773)
1 11 tensor(0.5963)
1 12 tensor(0.6441)
1 13 tensor(0.5918)
1 14 tensor(0.5882)
1 15 tensor(0.6813)
1 16 tensor(0.6434)
1 17 tensor(0.6808)
1 18 tensor(0.6617)
1 19 tensor(0.6438)
1 20 tensor(0.6063)
1 21 tensor(0.6434)
1 22 tensor(0.7195)
1 23 tensor(0.6210)

전체 소스 공유합니다.


import torch
import numpy as np
from torch.autograd import Variable
from torch.utils.data import Dataset,DataLoader
class DiabetesDataset(Dataset):
    
    def __init__(self):
        xy=np.loadtxt('./data/diabetes.csv.gz',delimiter=',',dtype=np.float32)
        self.len=xy.shape[0]
        self.x_data=torch.from_numpy(xy[:,0:-1])
        self.y_data=torch.from_numpy(xy[:,[-1]])
        
    def __getitem__(self,index):
        return self.x_data[index], self.y_data[index]
    
    def __len__(self):
        return self.len
    
dataset = DiabetesDataset()
train_loader = DataLoader(dataset = dataset,
                         batch_size=32,
                         shuffle=True,
                         num_workers=0)
class MyModel(torch.nn.Module):
    
    def __init__(self):
        super(MyModel,self).__init__()
        self.l1=torch.nn.Linear(8,4)
        self.l2=torch.nn.Linear(4,6)
        self.l3=torch.nn.Linear(6,1)
        
        self.sigmoid=torch.nn.Sigmoid()
        
    def forward(self,x):
        out1 = self.sigmoid(self.l1(x))
        out2 = self.sigmoid(self.l2(out1))
        y_pred = self.sigmoid(self.l3(out2))
        return y_pred
model = MyModel()

criterion = torch.nn.BCELoss(size_average = True)
optimizer = torch.optim.SGD(model.parameters(),lr=0.1)
# Training loop
for epoch in range(2):
    for i, data in enumerate(train_loader):
        # get the inputs
        inputs, labels = data
        # wrap them in Variable
        inputs, labels = Variable(inputs), Variable(labels)

        # Forward pass: Compute predicted y by passing x to the model
        y_pred = model(inputs)

        # Compute and print loss
        loss = criterion(y_pred, labels)
        print(epoch, i, loss.data[0])

        # Zero gradients, perform a backward pass, and update the weights.
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()








파이토치는 이미 여러 데이터set을 파이토치에 구비해 놓았습니다.







요로코롬 구현하시면 됩니다.




숙제




이미 있는 데이터set을 한번 찾아보시구요.


케글에 있는 데이터셋을 가져와서 데이터로더로 불러와보세요!