머신러닝, 인공지능 소식을 접하면 심심치 않게 회귀라는 말을 접한다. 회귀란 뭘까?
회귀:한 바퀴 돌아서 본디의 자리로 돌아옴. [출처: Oxford Languages]
데이터를 다루는 머신러닝 입장에서 보면, 흩어져 있는 데이터들이 자신의 자리로 돌아간다고 할 때, 이를 가장 잘 설명하는 선형의 그래프를 찾는 게 선형 회귀의 목표이다. 쉬운 말로 하면, 데이터의 흩어져 있는 모습과 유사한 형태의 선형 그래프를 찾으면 된다.
일변수 함수의 선형 회귀 분석
데이터 생성
본격적으로 회귀 분석을 하기 위해 데이터를 생성해 보자! 선형 회귀 분석을 하기 위해 데이터의 전체적인 경향을 나타내는 선형 함수를 하나 만들자. y = ax+b 의 형태로 중학교 수학을 배웠다면 누구나 아는 함수이다.
데이터가 이 선형 함수의 경향을 따라가기 위해서는 선형 함수에서 10개의 점을 추출하여 약간의 noise를 섞어서 만들고 그래프에 표시해 보자!
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
def function(x, a, b):
return a*x + b
a = 10
b = 20
x_real = np.linspace(0, 1, 100)
y_real = function(x_real, a, b)
# 데이터 생성
n = 10
x_fake = np.linspace(0, 1, n)
y_fake = function(x_fake, a, b) + np.random.randn(n)
plt.plot(x_real, y_real, 'b', label="Ground Truth")
plt.plot(x_fake, y_fake, 'r+', label="Data")
plt.legend()
plt.xlim([0, 1])
plt.ylim([20, 30])
plt.show()
Pytorch를 사용하기 위한 전처리
import torch
# 현재 데이터의 data type은 numpy이다!
print(type(x_fake), type(y_fake))
print('-------------------------')
# 데이터 변환: numpy -> pytorch tensors
x_fake_tensor = torch.from_numpy(x_fake.astype(np.float32))
y_fake_tensor = torch.from_numpy(y_fake.astype(np.float32))
print(type(x_fake_tensor), type(y_fake_tensor))
print('-------------------------')
print(x_fake_tensor.shape, y_fake_tensor.shape)
output:
<class 'numpy.ndarray'> <class 'numpy.ndarray'>
-------------------------
<class 'torch.Tensor'> <class 'torch.Tensor'>
-------------------------
torch.Size([10]) torch.Size([10])
# pytorch 모델을 사용하기 위해서 tensor를 array로 변환
x_fake_tensor = x_fake_tensor.view(-1, 1)
y_fake_tensor = y_fake_tensor.view(-1, 1)
print(x_fake_tensor.shape, y_fake_tensor.shape)
output:
torch.Size([10, 1]) torch.Size([10, 1])
torch.Size([10])를 torch.Size([10,1])로 전환한다.
Pytorch: torch.nn.Linear 을 이용한 선형 회귀
torch.nn.Linear은 2개의 필수 parameter를 받는다. y = ax+b의 x의 차원을 첫번째 parameter, y의 차원을 두번째 parameter로 받는다. torch.nn.Linear를 정의하는 것만으로도 weight과 bias를 정해주는 간편한 함수이다. weight이 선형 함수의 기울기(a)를 의미하고, bias가 선형 함수의 y 절편 (b)를 의미한다.
# torch.nn.Linear 사용해보기
import torch.nn as nn
input_dimension = 1
output_dimension = 1
one_function = nn.Linear(input_dimension, output_dimension)
# 선형 모델의 weight, bias 출력해보기
print(one_function.weight)
print(one_function.bias)
print('-----------------------')
output:
Parameter containing: tensor([[-0.1378]], requires_grad=True)
-----------------------
Parameter containing: tensor([-0.2638], requires_grad=True)
모델 생성과 동시에 weight과 bias가 초기화된다!
선형 회귀를 위한 최적화 함수와 손실 함수 정의
최적화 함수는 SGD(Stochastic Gradient Descent)를 사용하고 손실 함수로는 Mean Squared Error를 사용한다. (L2 Distance).
최적화 함수에 선형 함수 모델의 parameters()와 learning_rate을 넘겨준다. 이를 통해 선형 함수 모델과 연결되고 학습률이 결정된다!
# 최적화 함수와 손실 함수 설정
# 손실 함수: Mean Squared Error
loss_func = nn.MSELoss()
# 최적화 함수: Stochastic Gradient Method
learning_rate = 0.01
opti_func = torch.optim.SGD(one_function.parameters(), lr=learning_rate)
모델의 weight, bias 학습 (순전파(Forward Propagation)와 역전파(Backward Propagation)을 통한)
코드에 충분한 설명이 있으니 확인 바란다!
epochs = 1000
for epoch in range(epochs):
# 순전파
y_pred_tensor = one_function(x_fake_tensor)
# 손실 함수
loss = loss_func(y_pred_tensor, y_fake_tensor)
# 손실 함수를 이용한 역전파
loss.backward()
# Weight, Bias 업데이트
opti_func.step()
opti_func.zero_grad() # For next, operation
# 과정 출력
if (epoch+1) % 100 == 0:
print(f"Epoch: {epoch+1}\tLoss = {loss.item():.5f}")
output:
Epoch: 100 Loss = 4.64059
Epoch: 200 Loss = 0.75198
Epoch: 300 Loss = 0.71111
Epoch: 400 Loss = 0.69753
Epoch: 500 Loss = 0.68777
Epoch: 600 Loss = 0.68068
Epoch: 700 Loss = 0.67554
Epoch: 800 Loss = 0.67181
Epoch: 900 Loss = 0.66910
Epoch: 1000 Loss = 0.66714
그래프로 표시하여 비교하기
위에서 학습된 모델을 통해 실제 예측된 선형 함수를 그려보자! x의 범위는 동일해야 하므로 x의 범위는 동일하게 설정하고 모델에 순전파를 시켜서 나온 prediction을 확인한다!
# 그래프 그리기
# 첫번째: 실제 선형 함수. 두번째: 가짜 데이터. 세번째: 학습된 선형 함수
# numpy 배열에서 pytorch tensor로 변환
x_real_tensor = torch.from_numpy(x_real.astype(np.float32)).view(-1, 1)
# 예측된 결과값 얻기
y_pred_tensor = one_function(x_real_tensor).detach() # 실제 예측은 반영되면 안되므로 .detach()
y_pred = y_pred_tensor.numpy()
# 학습된 모델의 weight, bias 확인
print(f"Original parameters: w = {a}, b = {b}")
weight = one_function.weight[0,0].item()
bias = one_function.bias[0].item()
print(f"Trained parameters: w = {weight:.5f}, b = {bias:.5f}")
# Plot the results
plt.plot(x_real, y_real, "k-", label="Ground Truth")
plt.plot(x_real, y_pred, "b", label="Linear Regression")
plt.plot(x_fake, y_fake, "r.", label="Data")
plt.legend()
plt.xlim([0,1])
plt.ylim([20, 30])
plt.show()