Linear regression: Pytorch, nn.module로 구현
<목차>
1. Data에 대한 이해
2. 가설(Hypothesis) 수립
3. 손실 계산(Compute Loss)
4. Optimizer : 경사하강법(Gradient Descent)
- 풀고자하는 문제에 따라 Hypothesis, Cost func, optimizer는 다를 수 있고,
선형 회귀에 가장 적절한 cost func는 MSE, optimizer는 gradient descent임
1. Data에 대한 이해
훈련 데이터셋(training dataset) : 예측을 위해 사용하는 데이터셋
- 모델을 학습시키기 위한 데이터는 파이토치의 텐서의 형태(torch.tensor)를 가지고 있어야함
- 그리고 입력과 출력을 각기 다른 텐서에 저장할 필요가 있음(보편적으로 입력은 x, 출력은 y)
테스트 데이터셋(test dataset) : 학습이 끝난 후, 이 모델이 얼마나 잘 작동하는지 판별하는 데이터셋
2. 가설(Hypothesis) 수립
선형 회귀란 학습 데이터와 가장 잘 맞는 하나의 직선을 찾는 일
x와 곱해지는 W를 가중치(Weight)라고 하며, b를 편향(bias)이라고 함
3. 손실 계산(Compute Loss)
비용 함수(cost function) = 손실 함수(loss function) = 오차 함수(error function) = 목적 함수(objective function)
오차: 각 실제값과 각 예측값과의 차이
평균 제곱 오차(Mean Squared Error): 오차들을 제곱해준 뒤에 전부 더한 뒤 데이터의 개수인 n으로 나눔
cost(W,b)를 최소가 되게 만드는 W와 b를 구하면 훈련 데이터를 가장 잘 나타내는 직선을 구할 수 있음
최적화 알고리즘 (Optimizer) - 경사하강법(Gradient Descent)
- 학습(Training) : Optimizer 알고리즘을 통해 적절한 W와 b를 찾아내는 과정
편향을 고려하지 않는, b=0인 y= Wx 식에서 W와 cost의 관계는 다음과 같음
cost가 최소화가 되는 지점은 접선의 기울기가 0이 되는 지점이며, 또한 미분값이 0이 되는 지점임
비용 함수(Cost function)를 미분하여 현재 W에서의 접선의 기울기를 구하고, 접선의 기울기가 낮은 방향으로 W의 값을 변경하는 작업을 반복하는 것이 경사하강법의 아이디어
- 기울기가 음수일 때 : W의 값이 증가
- 기울기가 양수일 때 : W의 값이 감소
- 알파는 학습률(learning rate)를 뜻함. 학습률은 W의 값을 변경할 때 얼마나 크게 변경할 지를 결정
Pytorch로 선형 회귀 구현
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
# 현재 실습하고 있는 파이썬 코드를 재실행해도 다음에도 같은 결과가 나오도록 랜덤 시드(random seed)를 줍니다.
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)
#가중치 W를 0으로 초기화하고 학습을 통해 값이 변경되는 변수임을 명시함.
b = torch.zeros(1, requires_grad=True)
#현 직선의 방정식은 y = 0 *x + 0
# optimizer 설정
optimizer = optim.SGD([W, b], lr=0.01)
nb_epochs = 1999 # 원하는만큼 경사 하강법을 반복
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() # gradient를 0으로 초기화
cost.backward() # 비용 함수를 미분하여 gradient 계산
optimizer.step() # W와 b를 업데이트
# 100번마다 로그 출력
if epoch % 100 == 0:
print('Epoch {:4d}/{} W: {:.3f}, b: {:.3f} Cost: {:.6f}'.format(
epoch, nb_epochs, W.item(), b.item(), cost.item()
))
Epoch : 전체 훈련 데이터가 학습에 한 번 사용된 주기 (위 실습의 경우 1999번 수행)
- optimizer.zero_grad()가 필요한 이유: pytorch는 미분을 통해 얻은 기울기를 이전에 계산된 기울기 값에 누적시킴
- torch.manual_seed() : 다른 컴퓨터에서 실행시켜도 동일한 결과를 얻을 수 있음
- .backward() : 자동미분(Autograd)
nn.module로 구현
파이토치에서는 선형 회귀 모델이 nn.Linear()라는 함수로, 또 평균 제곱오차가 nn.functional.mse_loss()라는 함수로 구현되어져 있음
import torch.nn as nn
model = nn.Linear(input_dim, output_dim)
import torch.nn.functional as F
cost = F.mse_loss(prediction, y_train)
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]])
# 모델을 선언 및 초기화. 단순 선형 회귀이므로 input_dim=1, output_dim=1.
model = nn.Linear(1,1) #(입력 차원, 출력 차원)
# optimizer 설정. 경사 하강법 SGD를 사용하고 learning rate를 의미하는 lr은 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# 전체 훈련 데이터에 대해 경사 하강법을 2,000회 반복
nb_epochs = 2000
for epoch in range(nb_epochs+1):
# H(x) 계산
prediction = model(x_train)
# cost 계산
cost = F.mse_loss(prediction, y_train) # <== 파이토치에서 제공하는 평균 제곱 오차 함수
# cost로 H(x) 개선하는 부분
# gradient를 0으로 초기화
optimizer.zero_grad()
# 비용 함수를 미분하여 gradient 계산
cost.backward() # backward 연산
# W와 b를 업데이트
optimizer.step()
if epoch % 100 == 0:
# 100번마다 로그 출력
print('Epoch {:4d}/{} Cost: {:.6f}'.format(
epoch, nb_epochs, cost.item()
))
new_var = torch.FloatTensor([[4.0]]) # 임의의 입력 4를 선언
pred_y = model(new_var) # 입력한 값 4에 대해서 예측값 y를 리턴받아서 pred_y에 저장
print(pred_y) # y = 2x 이므로 입력이 4라면 y가 8에 가까운 값이 나와야 제대로 학습이 된 것
- H(x) 식에 입력 x로부터 예측된 y를 얻는 것을 forward 연산이라고 함
- 학습 전, prediction = model(x_train) 은 x_train으로부터 예측값을 return 하므로 forward 연산임
- 학습 후, pred_y = model(new_var) 는 임의의 값 new_var로부터 예측값을 return하므로 forward 연산임
- 학습 과정에서 비용 함수를 미분해 기울기를 얻는 것 -> backward
- cost.backward()는 비용함수로부터 기울기를 구하라는 의미 -> backward