선형 회귀(Linear Regression)란?
선형 회귀는 머신러닝에서 가장 기본이 되는 지도 학습 알고리즘으로, 두 연속형 변수 사이의 관계를 직선 방정식으로 모델링하는 기법입니다. 이 글에서는 선형 회귀의 핵심 개념을 살펴보고, PyTorch를 활용해 실제로 선형 회귀 모델을 구축하고 학습시키는 전 과정을 코드와 함께 단계별로 설명합니다.
단순 선형 회귀의 기본 개념
두 개의 연속형 변수 간의 관계를 이해하고 예측할 수 있게 해줍니다.
변수의 구성은 다음과 같습니다.
x = 독립 변수(independent variable), 예: 몸무게
y = 종속 변수(dependent variable), 예: 키
관계식은 y = αx + β 형태로 표현됩니다.
먼저 간단한 시각화를 통해 단순 선형 회귀가 어떻게 작동하는지 확인해 보겠습니다.
# 단순 선형 회귀 시각화 import numpy as np import matplotlib.pyplot as plt np.random.seed(1) n = 70 x = np.random.randn(n) y = x * np.random.randn(n) colors = np.random.rand(n) plt.plot(np.unique(x), np.poly1d(np.polyfit(x, y, 1))(np.unique(x))) plt.scatter(x, y, c=colors, alpha=0.5) plt.show()
선형 회귀의 목적
데이터 포인트들과 회귀 직선(y = αx + β) 사이의 거리를 최소화하는 것
다음 두 파라미터를 조정하여 최적의 직선을 찾는 것
계수(Coefficient): α — 기울기
절편/편향(Intercept/Bias): β
PyTorch로 선형 회귀 모델 만들기
이제 계수(α)를 2, 절편(β)을 1로 가정한 선형 모델을 PyTorch로 구현해 보겠습니다. 목표는 모델이 데이터로부터 자동으로 y = 2x + 1이라는 관계를 학습하도록 만드는 것입니다.
y = 2x + 1 # 선형 모델
1단계: 데이터셋 구축
먼저 입력 값 x를 생성합니다.
x_values = [i for i in range(11)] x_values
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
리스트를 NumPy 배열로 변환합니다. 딥러닝 프레임워크에서 일반적으로 float32 타입을 사용합니다.
x_train = np.array(x_values, dtype=np.float32) x_train.shape
(11,)
중요: PyTorch의 nn.Linear 레이어는 2차원 입력을 요구하므로, 반드시 reshape로 차원을 맞춰줘야 합니다.
x_train = x_train.reshape(-1, 1) x_train.shape
(11, 1)
다음으로 y = 2x + 1 공식에 따라 정답(label) 값을 만듭니다.
y_values = [2*i + 1 for i in x_values] y_values
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]
동일한 결과를 for문으로도 작성할 수 있습니다.
y_values = []
for i in x_values:
result = 2*i + 1
y_values.append(result)
y_values[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]
y값 역시 NumPy 배열로 변환하고 2차원으로 reshape합니다.
y_train = np.array(y_values, dtype=np.float32) y_train.shape
(11,)
y_train = y_train.reshape(-1, 1) y_train.shape
(11, 1)
2단계: 모델 클래스 정의 및 학습
PyTorch의 nn.Module을 상속받아 선형 회귀 모델 클래스를 정의합니다. 손실 함수는 평균 제곱 오차(MSE)를, 옵티마이저는 확률적 경사 하강법(SGD)을 사용합니다.
# 라이브러리 임포트
import torch
import torch.nn as nn
from torch.autograd import Variable
# 모델 클래스 생성
class LinearRegModel(nn.Module):
def __init__(self, input_size, output_size):
super(LinearRegModel, self).__init__()
self.linear = nn.Linear(input_dim, output_dim)
def forward(self, x):
out = self.linear(x)
return out
input_dim = 1
output_dim = 1
model = LinearRegModel(input_dim, output_dim)
# 손실 함수: 평균 제곱 오차(MSE)
criterion = nn.MSELoss()
# 옵티마이저: SGD, 학습률 0.01
learning_rate = 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
epochs = 100
for epoch in range(epochs):
epoch += 1
# NumPy 배열을 Torch Variable로 변환
inputs = Variable(torch.from_numpy(x_train))
labels = Variable(torch.from_numpy(y_train))
# 기존 그래디언트 초기화
optimizer.zero_grad()
# 순전파(forward pass)로 출력 계산
outputs = model.forward(inputs)
# 손실 계산
loss = criterion(outputs, labels)
# 역전파로 그래디언트 계산
loss.backward()
# 파라미터 업데이트
optimizer.step()
print('epoch {}, loss {}'.format(epoch, loss.item()))학습 결과
epoch 1, loss 276.7417907714844 epoch 2, loss 22.601360321044922 epoch 3, loss 1.8716105222702026 epoch 4, loss 0.18043726682662964 epoch 5, loss 0.04218350350856781 epoch 6, loss 0.03060017339885235 epoch 7, loss 0.02935197949409485 epoch 8, loss 0.02895027957856655 epoch 9, loss 0.028620922937989235 epoch 10, loss 0.02830091118812561 ...... ...... epoch 94, loss 0.011018744669854641 epoch 95, loss 0.010895680636167526 epoch 96, loss 0.010774039663374424 epoch 97, loss 0.010653747245669365 epoch 98, loss 0.010534750297665596 epoch 99, loss 0.010417098179459572 epoch 100, loss 0.010300817899405956
학습 로그를 보면 첫 에폭(epoch)에서 약 276.7이었던 손실(loss)이 100 에폭 이후 약 0.01 수준까지 크게 감소한 것을 확인할 수 있습니다. 이는 모델이 y = 2x + 1 관계를 성공적으로 학습했다는 의미입니다.
3단계: 예측 결과 시각화
학습된 모델로 예측을 수행하고, 실제 데이터와 예측값을 그래프로 비교해 보겠습니다.
# 추론(inference) predicted = model(Variable(torch.from_numpy(x_train))).data.numpy() # 그래프 초기화 plt.clf() # 실제 데이터 플롯 plt.plot(x_train, y_train, 'go', label='True data', alpha=0.5) # 예측값 플롯 plt.plot(x_train, predicted, '--', label='Predictions', alpha=0.5) # 범례 및 출력 plt.legend(loc='best') plt.show()
결과
그래프를 보면 초록색 점으로 표시된 실제 데이터(True data)와 점선으로 표시된 모델의 예측값(Predictions)이 거의 일치하는 것을 확인할 수 있습니다. 이처럼 PyTorch를 사용하면 몇 줄의 코드만으로 선형 회귀 모델을 정의하고, 경사 하강법을 통해 손실을 최소화하며, 데이터의 패턴을 정확하게 학습할 수 있습니다.