선형 회귀(Linear Regression)
선형 회귀는 주어진 데이터로부터 x와 y의 관계를 가장 잘 나타내는 직선을 그리는 것을 의미한다.
여기서 x는 독립적으로 변할 수 있는 독립 변수, y는 x 값에 의해 종속적으로 결정되므로 종속 변수에 해당한다.
1. 단순 선형 회귀(Simple Linear Regression)
독립 변수 x가 1개이면 단순 선형 회귀이다. 직선의 방정식에서 기울기를 머신러닝에서는 w 가중치(weight)라고 하며, 별도로 더해지는 값 b를 편향(bias)라고 한다.
2. 다중 선형 회귀 분석(Multiple Linear Regression Analysis)
독립 변수 x가 2개 이상이면 다중 선형 회귀이다. 다수의 요소 x1, x2, ..., xn을 가지고 y 값을 예측한다.
예를 들어 오존 농도를 예측하는 모델을 만든다고 가정하면, 일조량, 기온, 풍속 등의 요소를 고려하여 오존 농도를 예측할 수 있다.
3. 단순 선형 회귀 예시
우선 단순 선형 회귀 예제를 통해 선형 회귀를 정리해볼 것이다. 공부시간을 예시로 공부 시간에 따른 시험 점수 데이터가 다음과 같이 있다.
시간 (x) | 2 | 3 | 4 | 5 |
점수 | 25 | 50 | 42 | 61 |
데이터를 좌표 평면에 나타내면 아래와 같다.
선형 회귀의 목표는 주어진 데이터로부터 x와 y의 관계를 가장 잘 표현하는 직선을 찾는 것이다.
이때 선형회귀는 $ y=Wx + b $의 형식을 가진다.
가설의 H를 따서 y 대신 $ H(x) = Wx + b $로 표현하기도 한다. 이제 해야할 일은 문제에 대한 규칙을 가장 잘 표현하는 W와 b를 찾는 것이다. 머신러닝에서는 최적의 W와 b를 찾기 위해서 실제값과 가설로부터 얻은 예측값의 오차를 계산하는 식을 세우고, 이 식의 값을 최소화하는 최적의 W와 b를 찾아낸다.
1) 비용 함수(Cost function) - MSE, 평균 제곱 오차
이 오차에 대한 식을 목적 함수(Objective function) 또는 비용 함수(Cost function) 또는 손실 함수(Loss function)라고 한다. 회귀 문제의 경우 비용 함수로 주로 평균 제곱 오차(Mean Squared Error, MSE)가 사용된다.
위 그림의 직선 4개 중에서 녹색선이 가장 4개의 점과 가깝게 느껴진다. 따라서 우선 임의로 그려진 녹색 선 $ y=13x+1 $을 선택하여 예측값과 실제값 사이의 오차를 계한다.
게시글 수 (x) | 2 | 3 | 4 | 5 |
실제값 | 25 | 50 | 42 | 61 |
예측값 | 27 | 40 | 53 | 66 |
오차 | -2 | 10 | -9 | -5 |
단순히 오차=실제값 - 예측값으로 계산하면 음수 오차와 양수 오차가 함께 존재하므로 오차의 절대적인 크기를 구할 수 없다. 따라서 MSE에서는 모든 오차를 제곱하여 더하는 방법을 사용한다. 오차를 제곱하여 평균을 낸 평균 제곱 오차의 값을 최소값으로 만드는 최적의 w와 b를 찾는 것이 목적인 것이다. 평균 제곱 오차를 w와 b에 의한 비용 함수(Cost function)로 재정의해보면 다음과 같다.
$ y=13x+1 $에서 평균 제곱 오차(MSE)의 값은 $ {(-2)^2 + 10^2 + (-9)^2 + (-5)^2}/4 = 210/4 = 52.5 $이다.
모든 점들과의 오차가 클수록 MSE는 커지며, 오차가 작아질수록 MSE 값은 작아진다. 그러므로 $ Cost(w, b) $를 최소가 되도록 만드는 w와 b를 구하면 결과적으로 y와 x의 관계를 가장 잘 나타내는 직선을 그릴 수 있다.
2) 옵티마이저(Optimizer) - 경사하강법(Gradient Descent)
비용함수를 최소화하는 매개변수인 w와 b를 찾기 위해 사용되는 알고리즘을 옵티마이저(Optimizer) 또는 최적화 알고리즘이라고 한다. 이 옵티마이저를 통해 적절한 w와 b를 찾아내는 과정을 머신러닝에서 훈련(training) 또는 학습(learning)이라고 부른다.
경사하강법은 옵티마이저 일고리즘 중 가장 기본적인 알고리즘이다. 기울기 w가 무한대로 커질수록 cost의 값또한 무한대로 커지고, 기울기 w가 무한대로 작아져도 cost의 값은 무한대로 커진다. 경사하강법의 아이디어는 비용 함수(Cost function)을 미분하여 현재 W에서의 접선의 기울기, 즉 미분값을 구하고 접선의 기울기가 0이 되는 지점을 향해 w의 값을 변경하는 작업을 반복하는 작업을 반복하는 것에 있다.
이 반복작업에는 현재 W에서 비용함수의 미분값에 학습률 α를 곱한 값을 빼서 새로운 W를 사용하는 작업이 진행된다.
학습률 α가 지나치게 큰 값을 가지면 아래와 같이 접선의 기울기가 0이 되는 W를 찾지 못하고 W의 값이 발산하는 상황이 발생한다. 하지만 반대로 학습률 α가 지나치게 작은 값을 가지면 학습 속도가 느려지므로 적당한 α 값을 설정하는 것도 중요하다.
4. Pytorch로 단순 선형회귀 구현하기
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
# training 데이터 선언
x_train = torch.FloatTensor([[1], [2], [3]])
y_train = torch.FloatTensor([[2], [4], [6]])
# 모델 초기화 - 가중치 W와 편향 b를 0으로 초기화한다.
W = torch.zeros(1, requires_grad=True)
b = torch.zeros(1, requires_grad=True)
# optimizer 설정 - SGD는 경사 하강법의 일종이다. 학습 대상인 W와 b가 SGD의 입력이 된다.
optimizer = optim.SGD([W, b], lr=0.01)
epochs = 1999 # epochs 만큼 경사 하강법을 반복
for e in range(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 e % 100 == 0:
print('Epoch {:4d}/{} W: {:.3f}, b: {:.3f} Cost: {:.6f}'.format(
e, epochs, W.item(), b.item(), cost.item()
))
결과는 다음과 같다. 최종적으로 최적의 기울기 W는 2에 가깝고, 편향 b는 0에 가까운 것을 볼 수 있다.
현재 훈련 데이터가 x_train은 [[1], [2], [3]]이고, y_train은 [[2], [4], [6]]인 것을 감안하면 실제 정답은 W가 2, b는 0이므로 거의 정답과 가깝다.
Epoch 0/1999 W: 0.187, b: 0.080 Cost: 18.666666
Epoch 100/1999 W: 1.746, b: 0.578 Cost: 0.048171
Epoch 200/1999 W: 1.800, b: 0.454 Cost: 0.029767
Epoch 300/1999 W: 1.843, b: 0.357 Cost: 0.018394
Epoch 400/1999 W: 1.876, b: 0.281 Cost: 0.011366
Epoch 500/1999 W: 1.903, b: 0.221 Cost: 0.007024
Epoch 600/1999 W: 1.924, b: 0.174 Cost: 0.004340
Epoch 700/1999 W: 1.940, b: 0.136 Cost: 0.002682
Epoch 800/1999 W: 1.953, b: 0.107 Cost: 0.001657
Epoch 900/1999 W: 1.963, b: 0.084 Cost: 0.001024
Epoch 1000/1999 W: 1.971, b: 0.066 Cost: 0.000633
Epoch 1100/1999 W: 1.977, b: 0.052 Cost: 0.000391
Epoch 1200/1999 W: 1.982, b: 0.041 Cost: 0.000242
Epoch 1300/1999 W: 1.986, b: 0.032 Cost: 0.000149
Epoch 1400/1999 W: 1.989, b: 0.025 Cost: 0.000092
Epoch 1500/1999 W: 1.991, b: 0.020 Cost: 0.000057
Epoch 1600/1999 W: 1.993, b: 0.016 Cost: 0.000035
Epoch 1700/1999 W: 1.995, b: 0.012 Cost: 0.000022
Epoch 1800/1999 W: 1.996, b: 0.010 Cost: 0.000013
Epoch 1900/1999 W: 1.997, b: 0.008 Cost: 0.000008
(참고) optimizer.zero_grad()로 기울기를 0으로 초기화하는 이유
Pytorch는 미분을 통해 얻은 기울기를 이전에 계산된 기울기 값에 누적시킨다.
import torch
W = torch.tensor(0., requires_grad=True)
optimizer = optim.SGD([W], lr=0.01)
epochs = 20
for e in range(epochs + 1):
h = 3*W
# optimizer.zero_grad()
h.backward()
print('수식을 w로 미분한 값 : {}'.format(W.grad))
결과는 다음과 같이 계속해서 미분값인 3이 누적되는 것을 볼 수 있다. 따라서 optimizer.zero_grad()를 통해 미분값을 0으로 계속 초기화시켜주어야 한다. 위 주석을 풀면 미분값은 모두 3.0으로 잘 출력된다.
수식을 w로 미분한 값 : 3.0
수식을 w로 미분한 값 : 6.0
수식을 w로 미분한 값 : 9.0
수식을 w로 미분한 값 : 12.0
수식을 w로 미분한 값 : 15.0
수식을 w로 미분한 값 : 18.0
수식을 w로 미분한 값 : 21.0
수식을 w로 미분한 값 : 24.0
...
5. nn.Module로 단순 선형회귀 구현하기
위에서는 선형회귀를 이해하기 위해 가설, 비용함수를 직접 정의해서 선형회귀 모델을 구현했지만, Pytorch에서는 선형 회귀 모델이 nn.Linear()라는 함수로 구현되어있으며, 평균 제곱오차의 경우 nn.functional.mse_loss()라는 함수로 구현되어있다.단순 선형회귀이므로 하나의 입력 x에 대해서 하나의 출력 y를 가지므로, nn.Linear 인자인 입력 차원(input_dim)과 출력 차원(output_dim)을 모두 1로 설정한다.model에는 가중치 w와 편향 b가 순서대로 저장되어있다. 이 두 값은 랜덤으로 초기화된다.
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
# training 데이터 선언
x_train = torch.FloatTensor([[1], [2], [3]])
y_train = torch.FloatTensor([[2], [4], [6]])
# Pytorch에서 제공하는 선형회귀 모델과 평균제곱오차 사용.
# model에는 가중치 w와 편향 b가 저장되어있음. model.parameters()로 호출 가능
model = nn.Linear(1, 1) # 단순 선형 회귀이므로 input_dim=1, output_dim=1.
print(list(model.parameters())) # 가중치 W와 편향 b 값 확인
# optimizer 설정 - 경사 하강법 SGD를 사용하고 learning rate를 의미하는 lr은 0.01로 설정
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
epochs = 2000 # epochs 만큼 경사 하강법을 반복
for e in range(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를 계산한다.
optimizer.step() # W와 b를 업데이트
# 100번마다 로그 출력
if e % 100 == 0:
print('Epoch {:4d}/{} Cost: {:.6f}'.format(
e, epochs, cost.item()
))
print(list(model.parameters())) # 최적화된 가중치 W와 편향 b 값 확인
결과는 다음과 같다. 학습 과정은 위와 동일하고, Parameter containing 부분이 model.parameters()로 가중치 W와 편향 b를 출력한 부분이다. 학습 이전에는 각각 0.1876, 0.0488 이었지만 학습 후에는 정답 y=2x에 가까운 W는 1.9975, b는 0.0057이 나왔다.
[Parameter containing:
tensor([[0.1876]], requires_grad=True), Parameter containing:
tensor([0.0488], requires_grad=True)]
Epoch 0/2000 Cost: 14.977695
Epoch 100/2000 Cost: 0.044542
... 생략 ...
Epoch 1900/2000 Cost: 0.000008
Epoch 2000/2000 Cost: 0.000005
[Parameter containing:
tensor([[1.9975]], requires_grad=True), Parameter containing:
tensor([0.0057], requires_grad=True)]
6. 새로운 값 예측해보기
임의의 값을 선정하여 16이라는 새로운 데이터를 넣어 예측값을 확인해보았다.
new_var = torch.FloatTensor([[16.0]]) # 새로운 데이터 생성
pred_y = model(new_var) # forward 연산. # 입력값 16에 대해 예측값 y를 리턴받아서 pred_y에 저장
print("훈련 후 입력이 16일 때의 예측값 :", pred_y)
정답 y=2x와 가까운 16의 2배인 32에 가까운 값이 출력된 것을 확인할 수 있다.
훈련 후 입력이 16일 때의 예측값 : tensor([[31.9653]], grad_fn=<AddmmBackward0>)
다중 선형회귀에 대해서는 다음 게시물에서 참고하자!
https://daeunnniii.tistory.com/193
[ML] 다중 선형회귀(Multivariable Linear Regression) 정리 & Pytorch 구현
선형 회귀(Linear Regression) 선형 회귀는 주어진 데이터로부터 x와 y의 관계를 가장 잘 나타내는 직선을 그리는 것을 의미한다. 여기서 x는 독립적으로 변할 수 있는 독립 변수, y는 x 값에 의해 종속
daeunnniii.tistory.com
'AI > 머신러닝' 카테고리의 다른 글
[Pytorch] 소프트맥스 회귀(Softmax Regression) 구현 & MNIST 분류 적용 (0) | 2023.05.14 |
---|---|
[ML] 다중 클래스 분류(Multi-Class Classification) 정리 (0) | 2023.05.12 |
[ML] 로지스틱 회귀(Logistic Regression) 쉽게 이해하기 & Pytorch 구현 (0) | 2023.04.09 |
[ML] 다중 선형회귀(Multivariable Linear Regression) 정리 & Pytorch 구현 (0) | 2023.04.09 |
Microsoft Azure Machine Learning Studio(classic) 사용법과 자동차 가격 예측 (1) | 2020.08.27 |