ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [기계학습] 선형회귀(Linear Regression) (Part 2/2)
    코딩(Coding)/기계학습 2022. 1. 18. 12:49
    728x90

    이전 포스팅의 개념적인 부분을 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

    머신러닝 연산 과정

    데이터들을 가장 잘 대변하는 직선 방정식을 찾는 것

    순서는 아래와 같이 정의할 수 있겠다.

    1. 가설 설정
      • H(X) = W * X + b
      • 가설 직선은 W(기울기)와 b(절편)으로 선의 모양이 결정된다.
    2. 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차원 그래프가 형성 됨.
    3. 비용 최소화
      • Loss(W,b)가 최소가 되는 W와 b를 찾는 작업
        • 경사하강법을 통해 W,b 값을 갱신 해나간다.
      • H(X) = W * X + 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()

    output_8_0

    # 가설 설정
    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()

    output_9_0

    학습을 통해 오차가 최소가 되는 가설 직선 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

    output_13_1

    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

    output_16_1

    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

    output_18_1

    다변수 회귀

    • Input_Feature의 개수가 2개 이상
    • 따라서 가설직선 함수의 모양은 H(X1,X2,X3... Xn) = W1*X1 + W2*X2 + ... + Wn * Xn + b
    • 학습시켜야할 가중치들은 W1 ~ Wn와 b이다.
      image
    # 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도 여러개일 경우

    • image
    ### 연습 ###
    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

    728x90

    댓글

Designed by black7375.