PyTorch 선형회귀

IngCoding·2022년 6월 15일
1

머신러닝

목록 보기
8/34

1.이론 정리

가설

  • 선형회귀 가설은 직선방정식으로 보통 y=Wx+b 의 형태를 지닌다
  • W는 가중치, b는 편향이라고 한다.

비용함수

  • 비용함수 = 손실함수 = 오차함수 = 목적함수
  • 비용, 손실, 오차 : 선형회귀 가설(직선)과 실제 데이터(점)의 차이
  • 어떤 가설(직선)이 가장 적절한 직선인지 표현하기 위해 오차 표시

image.png

  • 단순히'오차 = 실제값 - 예측값'으로 정의하면 오차값이 음수가 나옴
  • 따라서 평균 제곱 오차를 활용 (오차의 제곱을 더하고, 데이터의 수로 나눔)
  • 평균 제곱 오차를 W와 b에 의한 비용 함수를 정의하면 아래와 같음

image.png

옵티마이저 - 경사하강법(Gradient Descent)

  • 위에 정의한 비용 함수를 최소로 하는 W와 b를 찾을 때 사용되는 것이 옵티마이저(Optimizer) 알고리즘
  • 가장 기본적인 옵티마이저 알고리즘은 '경사하강법'이다.
  • 비용 함수에서 W의 값이 크거나 작으면 cost가 무한대로 늘어난다.
  • cost와 W의 관계를 그래프로 그렸을 때 접선의 기울기가 최소인 점이 최적의 W, b 임을 알 수 있다.

image.png

2. 파이토치로 선형회귀 구현

# 기본세팅
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
# 랜덤 시드(random seed) 할당 (재실행해도 같은 결과가 나올 수 있도록)
torch.manual_seed(1)
<torch._C.Generator at 0x21cb51c5d30>
# 학습 데이터 선언
x_train = torch.FloatTensor([[1], [2], [3]])
y_train = torch.FloatTensor([[2], [4], [6]])
print(x_train)
print(x_train.shape)
tensor([[1.],
        [2.],
        [3.]])
torch.Size([3, 1])
# 가중치(W)를 0으로 초기화, 학습으로 값이 변경되는 변수임을 명시
W = torch.zeros(1, requires_grad=True) 
print(W)  # 가중치 W를 출력
tensor([0.], requires_grad=True)
b = torch.zeros(1, requires_grad=True)
print(b)
tensor([0.], requires_grad=True)
# 가설세우기 
hypothesis = x_train * W + b
print(hypothesis)
tensor([[0.],
        [0.],
        [0.]], grad_fn=<AddBackward0>)

비용함수 선언하기

# torch.mean으로 평균을 구한다.
cost = torch.mean((hypothesis - y_train) ** 2) 
print(cost)
tensor(18.6667, grad_fn=<MeanBackward0>)

경사하강법 구현하기

optimizer = optim.SGD([W, b], lr=0.01)
# gradient를 0으로 초기화
optimizer.zero_grad() 
# 비용 함수를 미분하여 gradient 계산
cost.backward() 
# W와 b를 업데이트
optimizer.step() 

3. 전체코드

# 데이터
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 설정
optimizer = optim.SGD([W, b], lr=0.01)

nb_epochs = 2000 # 원하는만큼 경사 하강법을 반복
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()

    # 500번마다 로그 출력
    if epoch % 500 == 0:
        print('Epoch {:4d}/{} W: {:.3f}, b: {:.3f} Cost: {:.6f}'.format(
            epoch, nb_epochs, W.item(), b.item(), cost.item()
        ))
Epoch    0/2000 W: 0.187, b: 0.080 Cost: 18.666666
Epoch  500/2000 W: 1.903, b: 0.221 Cost: 0.007024
Epoch 1000/2000 W: 1.971, b: 0.066 Cost: 0.000633
Epoch 1500/2000 W: 1.991, b: 0.020 Cost: 0.000057
Epoch 2000/2000 W: 1.997, b: 0.006 Cost: 0.000005

최종 훈련 결과는 W: 1.997, b : 0.006 이므로, 정답인 W는 2, b가 0과 거의 유사

4. 기타

optimizer.zero_grad()가 필요한 이유

import torch
w = torch.tensor(2.0, requires_grad=True)

nb_epochs = 20
for epoch in range(nb_epochs + 1):

  z = 2*w

  z.backward()
  print('수식을 w로 미분한 값 : {}'.format(w.grad))
수식을 w로 미분한 값 : 2.0
수식을 w로 미분한 값 : 4.0
수식을 w로 미분한 값 : 6.0
수식을 w로 미분한 값 : 8.0
수식을 w로 미분한 값 : 10.0
수식을 w로 미분한 값 : 12.0
수식을 w로 미분한 값 : 14.0
수식을 w로 미분한 값 : 16.0
수식을 w로 미분한 값 : 18.0
수식을 w로 미분한 값 : 20.0
수식을 w로 미분한 값 : 22.0
수식을 w로 미분한 값 : 24.0
수식을 w로 미분한 값 : 26.0
수식을 w로 미분한 값 : 28.0
수식을 w로 미분한 값 : 30.0
수식을 w로 미분한 값 : 32.0
수식을 w로 미분한 값 : 34.0
수식을 w로 미분한 값 : 36.0
수식을 w로 미분한 값 : 38.0
수식을 w로 미분한 값 : 40.0
수식을 w로 미분한 값 : 42.0
  • 미분값을 0으로 초기화하지 않으면 계속해서 미분값인 2가 누적된다.

torch.manual_seed()를 하는 이유

import torch
torch.manual_seed(3)
print('랜덤 시드가 3일 때')
for i in range(1,3):
  print(torch.rand(1))
랜덤 시드가 3일 때
tensor([0.0043])
tensor([0.1056])
torch.manual_seed(5)
print('랜덤 시드가 5일 때')
for i in range(1,3):
  print(torch.rand(1))
랜덤 시드가 5일 때
tensor([0.8303])
tensor([0.1261])
torch.manual_seed(3)
print('랜덤 시드가 다시 3일 때')
for i in range(1,3):
  print(torch.rand(1))
랜덤 시드가 다시 3일 때
tensor([0.0043])
tensor([0.1056])
  • 위에서 보는 것과 같이 랜덤시드를 정해주지 않으면 결과값이 매번 달라지게 된다.
profile
Data & PM

0개의 댓글