데이터사이언스/PyTorch

PyTorch 3 - 데이터 로더, 신경망

Johnny Yoon 2019. 7. 13. 21:57
728x90
반응형

DataLoader와 스토캐스틱 경사하강법

딥러닝은 하나의 중요한 알고리즘으로 많이 발전하게 되었다. 바로 스토캐스틱 경사하강법이다. 기존의 경사하강법은 한 epoch에 모든 데이터셋을 한번에 학습시킨다. 스토캐스틱 경사하강법은 이 큰 데이터셋을 여러개로 나누고, 나눠진 데이터의 서브셋 마다 학습을 시킨다. 이것을 기존 경사하강법의 근사치로 보면 될것이다. 이 하나의 서브셋을 배치 또는 미니배치라고 부른다.

In [101]:
import torch
from torch import autograd, nn, optim
import torch.nn.functional as F
import numpy as np

def lin(a,b,x): return a*x+b

# 가짜 데이터 생성
def gen_fake_data(n, a, b):
    x = np.random.uniform(0,1,n) 
    y = lin(a,b,x) + 0.1 * np.random.normal(0,3,n)
    return x.astype(np.float32), y.astype(np.float32)
 

Dataset클래스는 X와 Y 데이터를 엮어주는 역할을 한다. 입력과 출력을 각각 관리하면, 실수하기 쉽기 때문에, Dataset클래스를 사용하면 그런 위험성을 줄일 수 있게 된다.

In [102]:
from torch.utils.data import Dataset, DataLoader

# 데이터셋 클래스를 만들어준다
class RegressionDataset(Dataset):
    def __init__(self, a=3, b=8, n=10000):
        x, y = gen_fake_data(n, a, b)
        x = torch.from_numpy(x).unsqueeze(1)
        y = torch.from_numpy(y)
        self.x, self.y = x, y
    
    def __len__(self):
        return len(self.y)
    
    def __getitem__(self, idx):
        return self.x[idx], self.y[idx]
    
dataset_train = RegressionDataset()
dataset_test = RegressionDataset(n=3000)
 

DataLoader는 다음과같은 세가지 기능을 제공한다:

  • 데이터를 배치로 나눠준다
  • 데이터를 셔플링 해준다
  • 데이터를 병렬처리를 사용해 패러럴하게 로딩해준다
In [103]:
# 데이터로더에 데이터셋을 로딩하고,
# 배치사이즈는 100개로 정하고,
# 셔플링을 사용한다.
dataloader_train = DataLoader(dataset_train, batch_size=1000, shuffle=True)
x, y = next(iter(dataloader_train))
x[:5], y[:5]
Out[103]:
(tensor([[0.1126],
         [0.5620],
         [0.1210],
         [0.0220],
         [0.3510]]), tensor([8.3490, 9.6740, 8.4069, 8.3962, 9.3226]))
 

데이터로더로 배치와 셔플링을 사용해 선형회귀를 하려면 다음과같이 코드를 작성하면 된다:
(편의상 선형모델은 클래스를 만들지 않았다.)

In [104]:
linearModel = torch.nn.Sequential(
    torch.nn.Linear(1, 1),
)

learning_rate = 0.1
optimizer = torch.optim.Adam(linearModel.parameters(), lr=learning_rate)
In [105]:
model.train()
for t in range(1000):
    for i, (x, y) in enumerate(dataloader_train): 
        
        y_hat = linearModel(x)
        loss = F.mse_loss(y_hat, y.unsqueeze(1))
    
        optimizer.zero_grad()
        loss.backward()
    
        optimizer.step()
    if t % 100 == 0: print(loss.item())
 
84.38333892822266
0.09375748783349991
0.08921493589878082
0.08943391591310501
0.08563878387212753
0.09529353678226471
0.09006059914827347
0.07974283397197723
0.09142304956912994
0.09136796742677689
In [106]:
print([p for p in linearModel.parameters()])
 
[Parameter containing:
tensor([[2.9832]], requires_grad=True), Parameter containing:
tensor([7.9775], requires_grad=True)]
 

이제 모델을 평가해보겠다. 회귀문제이기 때문에 loss의 결과를 출력한다.

In [107]:
from sklearn.metrics import accuracy_score

dataloader_test = DataLoader(dataset_test, batch_size=300, shuffle=True)

with torch.no_grad():
    model.eval()
    losses = []
    for i, (x, y) in enumerate(dataloader_test):
        outputs = linearModel(x)
        loss = F.mse_loss(outputs, y.unsqueeze(1))
        losses.append(loss.item())
    print(sum(losses) / len(losses))
 
0.09088157042860985
 

레이어 두개의 신경망

레이어 두개짜리의 신경망을 만들어보겠다.

In [94]:
def sigmoid(x):
    return 1/(1 + np.exp(-x))

# 딥러닝용 가짜 데이터를 만든다
def gen_nn_fake_data(n):
    x = np.random.uniform(0,10, (n, 2))
    x1 = x[:,0]
    x2 = x[:,1]
    score1 = sigmoid(-x1 - 8* x2 + 50)
    score2 = sigmoid(-7*x1 - 2* x2 + 50)
    score3 = 2* score1 + 3*score2 - 0.1
    y = score3 < 0
    return x, y.astype(int)

x, y = gen_nn_fake_data(500)
In [95]:
import matplotlib.pyplot as plt
%matplotlib inline

plt.scatter(x[:,0],x[:,1],c=y, s=8);
plt.xlabel("x1"); plt.ylabel("x2");
 
 

학습데이터는 위와 같다. 보다시피 선형회귀로는 곡선적으로 생긴 두 데이터의 경계를 분류할 수 없다. 따라서 레이어 두개짜리의 신경망을 사용해 분류하는 모델을 만든다.

In [108]:
class TwoLayerNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.lin1 = nn.Linear(2, 2)
        self.sig1 = nn.Sigmoid()
        self.lin2 = nn.Linear(2, 1)
        self.sig2 = nn.Sigmoid()
    def forward(self, x):
        x = self.lin1(x)
        x = self.sig1(x)
        x = self.lin2(x)
        x = self.sig2(x)
        return x

model = TwoLayerNN()
model
Out[108]:
TwoLayerNN(
  (lin1): Linear(in_features=2, out_features=2, bias=True)
  (sig1): Sigmoid()
  (lin2): Linear(in_features=2, out_features=1, bias=True)
  (sig2): Sigmoid()
)
 

그리고 새로운 학습 데이터를 텐서에 로드한 뒤 모델을 학습시킨다.

In [109]:
from sklearn.model_selection import train_test_split

x, y = gen_nn_fake_data(10000)
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=42)

x_train = torch.tensor(X_train).float()
y_train = torch.tensor(y_train).float()
x_test = torch.tensor(X_test).float()
y_test = torch.tensor(y_test).float()
x_train, y_train, x_test, y_test
Out[109]:
(tensor([[6.8906, 8.1972],
         [0.2232, 9.6617],
         [7.5854, 9.9508],
         ...,
         [4.1615, 1.0413],
         [4.5910, 3.5149],
         [4.5001, 8.0641]]),
 tensor([1., 0., 1.,  ..., 0., 0., 0.]),
 tensor([[0.6413, 5.5821],
         [6.6125, 5.7313],
         [0.4252, 0.6637],
         ...,
         [2.9550, 4.8091],
         [8.9407, 4.1750],
         [8.5988, 7.9609]]),
 tensor([0., 0., 0.,  ..., 0., 0., 1.]))
 

이제 모델을 학습시킨다. loss함수는 분류모델이기 때문에 cross entropy를 사용하고, 최적화함수는 Adam을 사용한다.

In [110]:
learning_rate = 0.01
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
In [111]:
model.train()
for t in range(10000):
    y_hat = model(x_train)
    loss = F.binary_cross_entropy(y_hat, y_train.unsqueeze(1))
    if t % 1000 == 0: 
        print(loss.item())
       
    optimizer.zero_grad()
    loss.backward()
    
    optimizer.step()
 
0.5809152722358704
0.13357213139533997
0.09293916821479797
0.04865079000592232
0.02369396761059761
0.016989512369036674
0.012985560111701488
0.010147960856556892
0.008065451867878437
0.006532975472509861
In [112]:
print([p for p in model.parameters()])
 
[Parameter containing:
tensor([[ 6.0497,  1.7051],
        [-0.5421, -4.6742]], requires_grad=True), Parameter containing:
tensor([-45.7028,  30.2844], requires_grad=True), Parameter containing:
tensor([[ 25.7514, -27.9142]], requires_grad=True), Parameter containing:
tensor([-13.6894], requires_grad=True)]
 

모델을 평가해 보면 역시 높은 점수가 나오는 것을 볼 수 있다.

In [113]:
with torch.no_grad():
    model.eval()
    output = model(x_test)
    x_test_unsqueezed = x_test.unsqueeze(1)
    outputs = model(x_test_unsqueezed)
    print(accuracy_score(y_test, outputs.squeeze().round()))
 
0.9986666666666667

 

참고자료

아래의 원본 소스를 기반으로 번역 및 개인 의견을 덧붙인 코드입니다.

https://github.com/yanneta/pytorch-tutorials

728x90
반응형

'데이터사이언스 > PyTorch' 카테고리의 다른 글

PyTorch 5 - 딥러닝 기반 협업필터링  (0) 2019.07.22
PyTorch 4 - 협업필터링  (0) 2019.07.14
PyTorch 2 - 로지스틱 회귀  (0) 2019.07.11
PyTorch 1 - 텐서  (0) 2019.07.09