[기계학습] 선형회귀(Linear Regression) (Part 2/2)
이전 포스팅의 개념적인 부분을 python의 pytorch 패키지를 통해 구현하는 포스팅입니다.
이전 포스팅을 보고오세요~ :D (https://jsy-coding-blog.tistory.com/45)
포스팅에서 사용하는 PPT 자료는 제 Github에서 pdf 형식으로 받을 수 있습니다.
(https://github.com/JoSangYeon/Machine_Learning_Project/blob/master/PPT/01.%20Machine%20Learning.pdf)
선형회귀 구현(with Pytorch)
이전 포스팅에서 다뤘던 선형회귀의 개념을 Pytorch를 통해 구현해보자!
사용할 라이브러리를 import 한다.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
머신러닝 연산 과정
데이터들을 가장 잘 대변하는 직선 방정식을 찾는 것
순서는 아래와 같이 정의할 수 있겠다.
- 가설 설정
- H(X) = W * X + b
- 가설 직선은 W(기울기)와 b(절편)으로 선의 모양이 결정된다.
- Loss 값 계산
- Loss = (Label - H(x)) ** 2
- 실제값(Label)과 예측값(H(x))간의 차이를 계산
- MSE(평균오차제곱) : Loss(W,b) = Mean((Label - H(x))^2)
- 실제값과 예측값의 대한 차이 Loss는 W,b에 대한 함수로 표현 할 수 있음
- 즉 축이 W축, b축, Loss축인 3차원 그래프가 형성 됨.
- Loss = (Label - H(x)) ** 2
- 비용 최소화
- Loss(W,b)가 최소가 되는 W와 b를 찾는 작업
- 경사하강법을 통해 W,b 값을 갱신 해나간다.
- H(X) = W * X + b
- 가설 직선은 W(기울기)와 b(절편)으로 선의 모양이 결정된다.
- Loss(W,b)가 최소가 되는 W와 b를 찾는 작업
# 예제
x_data = torch.tensor([[1],[2],[3],[4],[5]]).float()
y_data = torch.tensor([[3],[5],[2],[4],[1]]).float()
plt.plot(x_data, y_data, 'ro--')
plt.show()
# 가설 설정
def H(x):
return x*W.detach().numpy() + b.detach().numpy()
W = torch.tensor([3.2], requires_grad=True)
b = torch.tensor([-1.5], requires_grad=True)
plt.plot(x_data, y_data, 'ro--')
plt.plot(x_data, H(x_data), 'b-')
plt.show()
학습을 통해 오차가 최소가 되는 가설 직선 H(x)를 구해보자
hypothesis = lambda x : x * W + b
loss_fn = lambda x : torch.mean((hypothesis(x) - y_data) ** 2)
opt = optim.SGD([W, b], lr=0.01)
print("학습 전 >>> W : {:.3f}\tb : {:.3f}\n".format(W.item(), b.item()))
for epoch in range(1, 1001):
output = hypothesis(x_data)
loss = loss_fn(x_data)
opt.zero_grad()
loss.backward()
opt.step()
if epoch % 25 ==0:
print("epoch {} >>> loss : {:.4f}".format(epoch,loss.item()))
print("\n학습 후 >>> W : {:.3f}\tb : {:.3f}".format(W.item(), b.item()))
학습 전 >>> W : 0.154 b : 2.138
epoch 25 >>> loss : 2.3632
epoch 50 >>> loss : 2.2287
epoch 75 >>> loss : 2.1152
epoch 100 >>> loss : 2.0194
...
중간생략
...
epoch 800 >>> loss : 1.5045
epoch 825 >>> loss : 1.5038
epoch 850 >>> loss : 1.5032
epoch 875 >>> loss : 1.5027
epoch 900 >>> loss : 1.5023
epoch 925 >>> loss : 1.5019
epoch 950 >>> loss : 1.5016
epoch 975 >>> loss : 1.5014
epoch 1000 >>> loss : 1.5012
학습 후 >>> W : -0.478 b : 4.420
print("\n학습 후 >>> W : {:.3f}\tb : {:.3f}".format(W.item(), b.item()))
plt.plot(x_data, y_data, 'ro--')
plt.plot(x_data, H(x_data), 'b-')
plt.show()
학습 후 >>> W : -0.478 b : 4.420
nn.Module
- 1Layer 딥러닝 관점
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.fc = nn.Linear(1,1)
def forward(self, x):
x = self.fc(x)
return x
model = MyModel()
loss_fn = nn.MSELoss()
opt = optim.SGD(model.parameters(), lr=0.01)
print(model)
print(list(model.parameters()))
MyModel(
(fc): Linear(in_features=1, out_features=1, bias=True)
)
[Parameter containing:
tensor([[0.4373]], requires_grad=True), Parameter containing:
tensor([-0.8967], requires_grad=True)]
W, b = get_weights(model)
print("학습 전 >>> W : {:.3f}\tb : {:.3f}\n".format(W, b))
plt.plot(x_data, y_data, 'ro--')
plt.plot(x_data, x_data*W + b, 'b-')
plt.show()
학습 전 >>> W : 0.437 b : -0.897
def get_weights(model):
weights = list(model.parameters())
W = weights[0].item()
b = weights[1].item()
return W,b
W, b = get_weights(model)
print("학습 전 >>> W : {:.3f}\tb : {:.3f}\n".format(W, b))
for epoch in range(1, 1001):
output = model(x_data)
loss = loss_fn(output, y_data)
opt.zero_grad()
loss.backward()
opt.step()
if epoch % 25 ==0:
print("epoch {} >>> loss : {:.4f}".format(epoch,loss.item()))
W, b = get_weights(model)
print("\n학습 후 >>> W : {:.3f}\tb : {:.3f}".format(W, b))
학습 전 >>> W : 0.437 b : -0.897
epoch 25 >>> loss : 5.7695
epoch 50 >>> loss : 5.1044
epoch 75 >>> loss : 4.5430
epoch 100 >>> loss : 4.0690
...
중간 생략
...
epoch 800 >>> loss : 1.5224
epoch 825 >>> loss : 1.5189
epoch 850 >>> loss : 1.5160
epoch 875 >>> loss : 1.5135
epoch 900 >>> loss : 1.5114
epoch 925 >>> loss : 1.5096
epoch 950 >>> loss : 1.5081
epoch 975 >>> loss : 1.5069
epoch 1000 >>> loss : 1.5058
학습 후 >>> W : -0.451 b : 4.322
print("\n학습 후 >>> W : {:.3f}\tb : {:.3f}".format(W, b))
plt.plot(x_data, y_data, 'ro--')
plt.plot(x_data, x_data*W + b, 'b-')
plt.show()
학습 후 >>> W : -0.451 b : 4.322
다변수 회귀
- Input_Feature의 개수가 2개 이상
- 따라서 가설직선 함수의 모양은
H(X1,X2,X3... Xn) = W1*X1 + W2*X2 + ... + Wn * Xn + b
- 학습시켜야할 가중치들은 W1 ~ Wn와 b이다.
# Data & Label
x1 = torch.tensor([[73.],[93.],[89.],[96.],[73.]])
x2 = torch.tensor([[80.],[88.],[91.],[98.],[66.]])
x3 = torch.tensor([[75.],[93.],[90.],[100.],[70.]])
Y = torch.tensor([[152.],[185.],[180.],[196.],[142.]])
# 가설 설정
def H(x1, x2, x3):
return x1*W1.detach().numpy() + x2*W2.detach().numpy() + x3*W3.detach().numpy() + b.detach().numpy()
W1 = torch.tensor([-1.5], requires_grad=True)
W2 = torch.tensor([1.5], requires_grad=True)
W3 = torch.tensor([-3.], requires_grad=True)
b = torch.tensor([3.], requires_grad=True)
즉, 초기 H(x) = -1.5*x1 + 1.5*x2 -3*x3 + 3
print("실제값 Y :", Y[0].item())
print("예측값 Y :", H(x1[0], x2[0], x3[0]).item())
실제값 Y : 152.0
예측값 Y : -211.5
아직 학습 전이라 완전히 다른 값이 나오게 된다.
# 학습 설계
opt = optim.SGD([W1, W2, W3, b], lr=0.0000005)
print("\n학습 전 >>> W1 : {:.3f}\tW2 : {:.3f}\n\t W3 : {:.3f}\tb : {:.3f}\n".format(W1.item(), W2.item(), W3.item(), b.item()))
for epoch in range(1, 251):
output = x1*W1 + x2*W2 + x3*W3 + b
loss = torch.mean(output - Y) ** 2
opt.zero_grad()
loss.backward()
opt.step()
if epoch % 25 ==0:
print("epoch {} >>> loss : {:.4f}".format(epoch,loss.item()))
print("\n학습 후 >>> W1 : {:.3f}\tW2 : {:.3f}\n\t W3 : {:.3f}\tb : {:.3f}".format(W1.item(), W2.item(), W3.item(), b.item()))
학습 전 >>> W1 : -1.500 W2 : 1.500
W3 : -3.000 b : 3.000
epoch 25 >>> loss : 63116.0977
epoch 50 >>> loss : 21099.0605
epoch 75 >>> loss : 7053.1982
epoch 100 >>> loss : 2357.8110
epoch 125 >>> loss : 788.1897
epoch 150 >>> loss : 263.4839
epoch 175 >>> loss : 88.0808
epoch 200 >>> loss : 29.4445
epoch 225 >>> loss : 9.8428
epoch 250 >>> loss : 3.2905
학습 후 >>> W1 : 0.156 W2 : 3.152
W3 : -1.328 b : 3.020
print("실제값 Y :", Y)
print("예측값 Y :", H(x1, x2, x3))
실제값 Y : tensor([[152.],
[185.],
[180.],
[196.],
[142.]])
예측값 Y : tensor([[166.9637],
[171.3931],
[184.2102],
[194.0849],
[129.4749]])
Matrix(행렬)
- 행렬의 곱셈 공식을 이용(내적 = Dot Product)
- $$ H(x) = w x + b $$
$$
H(x_1, x_2, x_3) = w_1 x_1 + w_2 x_2 + w_3 x_3 + b
$$
여기서 H(x1,x2,x3)는...
- $$ H(x_1, x_2, x_3) = \underline{w_1 x_1 + w_2 x_2 + w_3 x_3} + b $$
$$ w_1 x_1 + w_2 x_2 + w_3 x_3 $$
$$
\begin{pmatrix} w_{ 1 } & w_{ 2 } & w_{ 3 } \end{pmatrix}\cdot \begin{pmatrix} x_{ 1 } \ x_{ 2 } \ x_{ 3 }
\end{pmatrix}
$$
$$
WX
$$
- (W, X 는 matrix)
$$
H(x_1, x_2, x_3) = w_1 x_1 + w_2 x_2 + w_3 x_3 + b
$$
$$
\ = b + w_1 x_1 + w_2 x_2 + w_3 x_3
$$
$$
\ = \begin{pmatrix} 1 & x_{ 1 } & x_{ 2 } & x_{ 3 } \end{pmatrix}\cdot \begin{pmatrix} b \ w_{ 1 } \ w_{ 2 } \ w_{ 3 } \end{pmatrix}
$$
$$
\ = XW
$$
- 즉, 복잡한 모양의 데이터(데이터의 개수 모양에 상관 X)에도 간략하게 표현이 가능
- 따라서, H(x1, x2, x3) => H(X) = Xn * Wn + b
X도 여러개, Y도 여러개일 경우
### 연습 ###
x_data = torch.tensor([
[13, 24, 52],
[42, 53, 42],
[33, 29, 57],
[25, 26, 81],
[17, 28, 39]
]).float()
y_data = torch.tensor([
[21,45],
[67,42],
[82,91],
[57,52],
[17,49]
]).float()
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.fc = nn.Linear(3,2)
def forward(self, x):
x = self.fc(x)
return x
model = MyModel()
loss_fn = nn.MSELoss()
opt = optim.SGD(model.parameters(), lr=0.0001)
print(model)
print(list(model.parameters()))
MyModel(
(fc): Linear(in_features=3, out_features=2, bias=True)
)
[Parameter containing:
tensor([[-0.2880, -0.3012, -0.3388],
[-0.0368, 0.3511, -0.2049]], requires_grad=True), Parameter containing:
tensor([ 0.0319, -0.2258], requires_grad=True)]
print("학습 전 파라미터 : \n\t\t\t {}\n".format(list(model.parameters())))
for epoch in range(1, 1001):
output = model(x_data)
loss = loss_fn(output, y_data)
opt.zero_grad()
loss.backward()
opt.step()
if epoch % 25 ==0:
print("epoch {} >>> loss : {:.4f}".format(epoch,loss.item()))
print("학습 후 파라미터 : \n\t\t\t {}\n".format(list(model.parameters())))
학습 전 파라미터 :
[Parameter containing:
tensor([[-0.2880, -0.3012, -0.3388],
[-0.0368, 0.3511, -0.2049]], requires_grad=True), Parameter containing:
tensor([ 0.0319, -0.2258], requires_grad=True)]
epoch 25 >>> loss : 353.1830
epoch 50 >>> loss : 320.6848
epoch 75 >>> loss : 300.9650
epoch 100 >>> loss : 285.6898
epoch 125 >>> loss : 272.5851
epoch 150 >>> loss : 260.9588
epoch 175 >>> loss : 250.5402
epoch 200 >>> loss : 241.1762
...
중간생략
...
epoch 800 >>> loss : 163.6429
epoch 825 >>> loss : 162.9643
epoch 850 >>> loss : 162.3523
epoch 875 >>> loss : 161.8005
epoch 900 >>> loss : 161.3027
epoch 925 >>> loss : 160.8535
epoch 950 >>> loss : 160.4481
epoch 975 >>> loss : 160.0821
epoch 1000 >>> loss : 159.7514
학습 후 파라미터 :
[Parameter containing:
tensor([[ 3.1737, -1.4059, 0.2107],
[ 1.3804, -0.5531, 0.6554]], requires_grad=True), Parameter containing:
tensor([-0.0266, -0.0338], requires_grad=True)]
model.eval()
output = model(x_data)
print("실제 값 : {}".format(y_data))
print("예측 값 : {}".format(output))
실제 값 : tensor([[21., 45.],
[67., 42.],
[82., 91.],
[57., 52.],
[17., 49.]])
예측 값 : tensor([[18.4441, 38.7139],
[67.6032, 56.1495],
[75.9423, 66.8319],
[59.8264, 73.1771],
[22.7765, 33.5031]], grad_fn=<AddmmBackward0>)
전체코드는 아래 링크에서 확인할 수 있다.
https://github.com/JoSangYeon/Machine_Learning_Project/blob/master/01.%20Linear_regression.ipynb