본문 바로가기

[Pytorch] Single Linear regression(단일 선형 회귀)

Derrick 발행일 : 2022-04-10
728x90
반응형

< Simple Linear Regression >

 - 선형회귀(Linear regression)을 이해하고, Pytorch로 모델을 만들어보자
  → 하나의 정보로부터 하나의 결과값을 추측!
 - 데이터 이해, 가설(Hypothesis)수립, 손실(Loss)계산, Gradient descent(Loss funcion) 최소화
  ex) 쪽지 시험 점수 예측

1) Data - Train, Test

 어떤 학생이 1,2,3시간을 공부했을 때 각각 2,4,6점을 받았다면, 4시간을 공부한다면 몇 점을 받을까?
  이 상황에서 예측하기 위해 학습하는 데이터를 ' Training Dataset '라고 하며,
 해당 모델이 얼마나 잘 작동하는지 판별하는 데이터를 ' Test Dataset '이라고 한다.

 

 

1-1) Training Dataset 구성

 학습할 데이터는 ' torch.Tensor '로 텐서의 형태로 존재해야 하고,
 입력과 출력은 서로 다른 텐서에 저장한다. ( x , y )

 

x_train = torch.FloatTensor([[1], [2], [3]])            # 공부한 시간
y_train = torch.FloatTensor([[2], [4], [6]])            # 각 공부한 시간에 대한 점수

 

2) 가설(Hypothesis) 수립

 선형 회귀(Linear regression)이란 학습 데이터와 가장 잘 매칭이 되는 하나의 직선을 찾는 과정이다.
 이 때의 가설은, " y = H(x) = Wx + b " 으로 표현되며 W (= Weight, 가중치), b (= Bias)라고 한다.

 

 가설(Hypothesis)를 정의하려면, 먼저 W와 b를 정의해야 한다.
 → 처음엔 각각 0 으로 초기화한 후, 학습을 하면서 변경되도록 설정!

 

hypothesis = x_train * W + b                        # 가설(hypothesis) 수립

 

3) Loss funcion 정의 (Cost = Loss = Error)

 평균 제곱 오차(MSE : Mean Squared Error)의 값(= Loss)을 최소값으로 만드는 parameter인
 Weight(W)와 Bias(b)를 찾아내기 위해서 Loss function을 정의한다.

 

 
 우리의 목표!
 : Cost(W, b)의 값을 최소로 하는 W 와 b를 학습을 통해 구함으로써, Training dataset을 가장 잘
 나타내는 직선을 구해서 하나의 결론(예측값)을 추측하는 것.

 

# torch.mean으로 평균을 구함
cost = torch.mean((hypothesis - y_train) ** 2)                    # cost function을 통해 값 구하기

 

4) Optimizer - Gradient Descent (경사 하강법)

 Loss function의 값을 최소로 만드는 W와 b 값을 찾을 때 사용되는 것이 " Optimizer (최적화 알고
 리즘)
"이고, 이 알고리즘 중 가장 기본 개념이 " Gradient Descent (경사 하강법) " 이다.

 

 
 Gradient Descent(경사 하강법) 은 미분에서 '한 점에서의 순간 변화율 혹은 기울기'의 개념을 사용.
 위의 그래프 상, 가장 아랫부분의 접선 기울기가 0인 지점에서 cost(=loss)의 최솟값을 가지게 된다.

 

 
 수식으로는 Cost(=Loss) function을 미분하여 현재 W 에서의 기울기를 구하고, 기울기가 낮은 방향
 으로 W의 값을 업데이트하는 작업을 반복하게 된다.
 → 위의 수식은 접선의 기울기가 양수/음수일 때 모두 기울기가 0인 방향으로 W의 값을 조정한다.

 
 학습률 (= Learning rate(α)) : W 의 값을 변경할 때, 얼마나 큰 폭으로 변경할지를 결정해주는 요소.
 → 지나치게 높을 경우 W의 값이 발산(diverge)할 수 있고, 너무 작다면 학습속도가 느려지므로 적당
  한 값을 찾아내서 넣어주는 것이 중요하다.

 

optimizer = optim.SGD([W, b], lr = 0.01)        # SGD : Stochastic Gradient Descent

optimizer.zero_grad()                           # gradient 0 으로 초기화
cost.backward()                                 # cost function을 미분하여 gradient 계산
optimizer.step()                                # W 와 b를 업데이트

 
 → 학습 과정에서 Loss 함수를 미분하여 Gradient를 구하는 것을 " backward 연산 "이라고 한다.
  cost.backwward()는 loss 함수로부터 Gradient를 구하라는 의미이므로 'backward' 연산

 

5-1) Full training code with Pytorch

 → 지금까지 학습한 것을 Pytorch로 직접 구현

 

# Import
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

# For reproducibility
torch.manual_seed(1)

# 데이터 정의
x_train = torch.FloatTensor([[1], [2], [3]])
y_train = torch.FloatTensor([[2], [4], [6]])

# 모델 초기화
W = torch.zeros(1, requires_grad=True)
b = torch.zeros(1, requires_grad=True)

# optimizer 설정 - Gradient Descent 
optimizer = optim.SGD([W, b], lr = 0.01)

nb_epochs = 1000    # Gradient 반복 횟수(1000번 반복)
for epoch in range(nb_epochs + 1):

  # H(x) 계산
  hypothesis = x_train * W + b

  # cost 계산
  cost = torch.mean((hypothesis - y_train) ** 2)

  # cost로 H(x) 개선
  optimizer.zero_grad()
  cost.backward()
  optimizer.step()

  # 100번마다 결과 출력
  if epoch % 100 == 0:
    print('Epoch {:4d}/{}  W: {:.3f}, b: {:.3f} Cost: {:.6f}'.format(
        epoch, nb_epochs, W.item(), b.item(), cost.item()
    ))

 
 - optimizer.zero_grad() : 미분으로 얻은 기울기를 초기화. 새로운 W에 대해 gradient 구하기
 - cost.backward() : Weight와 Bias에 대한 기울기(gradient)가 계산된다.
 - optimizer.step() : W, b를 업데이트 (return되는 변수들에 α 곱해서..)
 - torch.manual_seed() : Random variable 발생 순서와 값을 동일하게 보장해주는 특성 부여
  → 사용한 프로그램의 결과는 다른 컴퓨터나 환경에서 실행해도 동일한 결과를 얻을 수 있다.

 

  • output

    >>
    Epoch    0/1000  W: 0.187, b: 0.080 Cost: 18.666666
    Epoch  100/1000  W: 1.746, b: 0.578 Cost: 0.048171
    Epoch  200/1000  W: 1.800, b: 0.454 Cost: 0.029767
    Epoch  300/1000  W: 1.843, b: 0.357 Cost: 0.018394
    Epoch  400/1000  W: 1.876, b: 0.281 Cost: 0.011366
    Epoch  500/1000  W: 1.903, b: 0.221 Cost: 0.007024
    Epoch  600/1000  W: 1.924, b: 0.174 Cost: 0.004340
    Epoch  700/1000  W: 1.940, b: 0.136 Cost: 0.002682
    Epoch  800/1000  W: 1.953, b: 0.107 Cost: 0.001657
    Epoch  900/1000  W: 1.963, b: 0.084 Cost: 0.001024
    Epoch 1000/1000  W: 1.971, b: 0.066 Cost: 0.000633

 - 1000번의 학습을 수행한 결과(Epoch = 2000), W는 2에 가까워지고, Loss값은 점점 줄어듦.
 - 실제 정답은 W = 2, 즉 H(x) = 2x 인 점으로 보아 학습이 잘 된 것을 확인
  → x_train : 1,2,3 y_train : 2, 4, 6

 

5-2) 클래스로 Single Linear Rigression 구현하기 >

 → 위의 실습예제와 동일하고, 달라진 것은 모델을 클래스(class)로 구현했다는 점이다.

 

import torch 
import torch.nn as nn
import torch.nn.functional as F 

torch.manual_seed(1)

# 데이터
x_train = torch.FloatTensor([[1], [2], [3]])
y_train = torch.FloatTensor([[2], [4], [6]])

# 클래스로 선형 회귀 구현 (nn.Module)
class LinearRegressionModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(1, 1)

    def forward(self, x):
        return self.linear(x)

# 모델 선언
model = LinearRegressionModel()

# optimizer 설정. 경사 하강법 SGD를 사용하고 learning rate를 의미하는 lr은 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=0.01) 

# 전체 훈련 데이터에 대해 Gradient Descent를 1,000회 반복
nb_epochs = 1000
for epoch in range(nb_epochs+1):

    # H(x) 계산
    prediction = model(x_train)

    # cost 계산
    cost = F.mse_loss(prediction, y_train)  #  파이토치에서 제공하는 평균 제곱 오차 함수

    # cost로 H(x) 개선하는 부분
    optimizer.zero_grad()                    # gradient를 0으로 초기화
    cost.backward()                          # 비용 함수를 미분하여 gradient 계산(backward 연산)
    optimizer.step()                         # W와 b를 업데이트

    if epoch % 100 == 0:                        # 100번마다 결과출력
      print('Epoch {:4d}/{} Cost: {:.6f}'.format(
          epoch, nb_epochs, cost.item()
      ))

 

>>
Epoch    0/1000 Cost: 13.103541
Epoch  100/1000 Cost: 0.002791
Epoch  200/1000 Cost: 0.001724
Epoch  300/1000 Cost: 0.001066
Epoch  400/1000 Cost: 0.000658
Epoch  500/1000 Cost: 0.000407
Epoch  600/1000 Cost: 0.000251
Epoch  700/1000 Cost: 0.000155
Epoch  800/1000 Cost: 0.000096
Epoch  900/1000 Cost: 0.000059
Epoch 1000/1000 Cost: 0.000037

 

new_variable = torch.FloatTensor([[4.0]])         # input x에 임의의 값인 4를 넣어서 예측하는 y값을 확인
pred_y = model(new_variable)                      # pred_y : 예측되는 y값
print("학습 후 input이 4일 때의 예측값 : ", pred_y)

 

>>
학습 후 input이 4일 때의 예측값 :  tensor([[7.9879]], grad_fn=<AddmmBackward0>)

 
→ 문제의 정답 label은 ' y=2x '이므로 input이 4일 경우, y값은 8에 매우 가까운 값이 나오게 된다.




< 참고 : 파이토치로 시작하는 딥러닝 기초(boostcourse) >

댓글