데이터사이언스/PyTorch

PyTorch 2 - 로지스틱 회귀

_금융덕후_ 2019. 7. 11. 00:27
728x90
반응형

 

PyTorch를 활용한 로지스틱회귀

로지스틱회귀는 주어진 데이터를 0이나 1값으로 분류하는 선형 분류 모델이다.

In [176]:
import torch
from torch import autograd, nn, optim
import torch.nn.functional as F
import numpy as np
In [177]:
lin = lambda a, b, x : a*x + b

def gen_fake_data(n, a, b):
    x = np.random.uniform(-20, 20, (n, 2))
    x2_hat = lin(a,b, x[:,0])
    y = x[:,1] > x2_hat
    return x, y.astype(int)

# 분류문제에 맞는 가짜 데이터를 생성한다
x, y = gen_fake_data(100, 1., 0.5)
x[:5], y[:5]
Out[177]:
(array([[  4.99107176,  18.69478946],
        [  6.52351754,  13.2610419 ],
        [  7.51675585,  -7.88694411],
        [-19.15147914,  11.85283448],
        [ -7.03463095,  -2.16444838]]), array([1, 1, 0, 1, 1]))
In [178]:
import matplotlib.pyplot as plt
%matplotlib inline
t = np.arange(-20, 20, 0.2)

plt.scatter(x[:,0],x[:,1],c=y, s=8);
plt.xlabel("x1"); plt.ylabel("x2");
plt.plot(t, t + 0.5, 'r--')
Out[178]:
[<matplotlib.lines.Line2D at 0x7f4a5ea3f278>]
 
 

선형분류 문제에서는 데이터를 두개의 그룹으로 잘 나눌 수 있는 a * x + b를 데이터 기반으로 구하는 문제이다. (이때 데이터의 레이블 즉 y값은 0이나 1값이 될것이다.)

이제 새로운 데이터셋을 사용해 PyTorch 텐서로 만들어본다.

In [179]:
from sklearn.model_selection import train_test_split

x, y = gen_fake_data(10000, 1., 0.5)
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[179]:
(tensor([[  6.1737, -17.2928],
         [  3.8013,   3.9352],
         [  9.7561, -15.3053],
         ...,
         [-15.0632,   5.2753],
         [-17.6491,  13.4936],
         [  1.5890,  11.5680]]),
 tensor([0., 0., 0.,  ..., 1., 1., 1.]),
 tensor([[ 11.8085,  17.7216],
         [  5.4052, -19.7498],
         [  3.7033,   3.2523],
         ...,
         [-11.3972, -18.9951],
         [-16.3172,  12.3746],
         [-14.4151,  12.2233]]),
 tensor([1., 0., 0.,  ..., 0., 1., 1.]))
 

Model 클래스

모델 클래스를 만들어보도록 하겠다. 선형회귀 클래스와는 다르게 이번에는 sigmoid함수가 함께 들어가있다. sigmoid함수란 실수의 값을 0~1사이의 값으로 바꿔주는 함수이다. 이 함수의 출력값이 0또는 1값에 가깝게 수렴하도록 학습하는 것이다. 아래 경사하강법 Loop의 y_hat값을 보기 좋게 출력해놓았는데, 처음엔 0과1 사이의 소수점의 값으로 시작했다가, 학습을 반복할 수록 0또는 1로 수렴하는것을 볼 수 있다. 분류문제에서는 보통 loss함수를 MSE가 아닌 Cross Entropy로 사용한다. (MSE를 사용하면 결과가 제대로 나오지 않으니 주의)

In [190]:
class LogisticRegression(nn.Module):
    def __init__(self):
        super().__init__()
        self.lin = nn.Linear(2, 1)
        self.sigmoid = F.sigmoid
        
    def forward(self, x):
        x = self.lin(x)
        x = self.sigmoid(x)
        return x
    
model = LogisticRegression()
model
Out[190]:
LogisticRegression(
  (lin): Linear(in_features=2, out_features=1, bias=True)
)
In [191]:
learning_rate = 0.1
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

model.train()
for t in range(len(x_train)):
    y_hat = model(x_train)
    loss = F.binary_cross_entropy(y_hat, y_train.unsqueeze(1))
    if t %1000 == 0:
        y_hat_formatted ="[{}]".format(", ".join([str(round(y[0], 4)) for y in y_hat[:5].data.numpy()]))
        print(y_hat_formatted, "loss: {}".format(loss.item()))
    optimizer.zero_grad()
    loss.backward()
    
    optimizer.step()
 
[0.0293, 0.6583, 0.0669, 0.1955, 0.9728] loss: 1.0972055196762085
[0.0, 0.2096, 0.0, 1.0, 1.0] loss: 0.012745809741318226
[0.0, 0.1218, 0.0, 1.0, 1.0] loss: 0.008666223846375942
[0.0, 0.0695, 0.0, 1.0, 1.0] loss: 0.00663815438747406
[0.0, 0.0375, 0.0, 1.0, 1.0] loss: 0.005315354093909264
[0.0, 0.0186, 0.0, 1.0, 1.0] loss: 0.004346473608165979
[0.0, 0.0083, 0.0, 1.0, 1.0] loss: 0.0035925796255469322
In [192]:
print([p for p in model.parameters()])
 
[Parameter containing:
tensor([[-15.2184,  15.2057]], requires_grad=True), Parameter containing:
tensor([-7.7133], requires_grad=True)]
 

Scikit Learn에서 제공하는 accuracy_score를 사용해 모델을 평가해 보겠다. round함수는 0.9997과 같은 값이 1로 0.01과 같은 값이 0으로 잘 매핑되기 위해서 이다. 비슷한 정규분포를 사용했기 때문에 결과는 잘 나올 수 밖에 없다.

In [193]:
from sklearn.metrics import accuracy_score
with torch.no_grad():
    model.eval()
    x_test_unsqueezed = x_test.unsqueeze(1)
    outputs = model(x_test_unsqueezed)
    print(accuracy_score(y_test, outputs.squeeze().round()))
 
1.0
 

참고자료

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

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

728x90
반응형

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

PyTorch 5 - 딥러닝 기반 협업필터링  (0) 2019.07.22
PyTorch 4 - 협업필터링  (0) 2019.07.14
PyTorch 3 - 데이터 로더, 신경망  (0) 2019.07.13
PyTorch 1 - 텐서  (0) 2019.07.09