[CS300 #264] 선형 회귀 — 직선 하나로 배우는 학습의 전부
컴퓨터공학 300 주제 시리즈의 264번째 글이다. 전체 지도는 여기.
한 줄 요약
선형 회귀는 입력의 가중합으로 실수 값을 예측하고, 예측 오차 제곱의 평균(MSE)을 최소화하는 가중치를 찾는다. 모델·손실·최적화라는 머신러닝의 세 요소가 가장 단순한 모습으로 다 들어 있다.
왜 필요한가
선형 회귀는 오래되고 단순하다. 그래서 무시하기 쉽지만, 실무와 학습 양쪽에서 출발점이다.
- 실무에서는 기준선(baseline)이다. 복잡한 모델을 만들기 전에 선형 회귀가 얼마나 하는지 보면, 복잡함이 값을 하는지 판단할 수 있다.
- 해석이 쉽다. 가중치 하나가 “이 특징이 1 늘면 예측이 얼마나 변하는가”를 뜻한다.
- 신경망의 마지막 층은 결국 선형 변환이다. 경사 하강법, 손실 함수, 정규화 같은 개념이 여기서 처음 등장하고 신경망까지 그대로 이어진다.
핵심 개념
모델
특징이 d 개인 입력 x 에 대해
ŷ = w0 + w1·x1 + w2·x2 + ... + wd·xd = Xw (X 에 1 로 채운 열을 붙여 w0 를 흡수)
“선형”은 가중치에 대해 선형이라는 뜻이다. x² 이나 log x 같은 변환한 특징을 넣어도 가중치에 대해서는 여전히 선형이므로 같은 방법으로 푼다. 다항 회귀가 그렇다.
손실 — 최소제곱
MSE(w) = (1/n) · Σ (ŷi − yi)²
왜 제곱인가. 미분이 쉽고, 큰 오차를 더 세게 벌주며, 오차가 정규분포를 따른다고 가정하면 최소제곱 해가 최대우도 추정과 같아진다. 대신 이상치 하나에 크게 끌려간다. 이상치가 많으면 절댓값 오차나 Huber 손실을 쓴다.
풀이 1 — 정규방정식
MSE 는 w 에 대한 볼록한 2차 함수라 기울기가 0 인 점이 최솟값이다.
∇MSE = (2/n) · Xᵀ(Xw − y) = 0 ⟹ XᵀX w = Xᵀy ⟹ w = (XᵀX)⁻¹ Xᵀy
특징 수 d 가 작으면 한 번에 정확한 답이 나온다. 다만 역행렬을 직접 구하는 것은 수치적으로 불안정할 수 있어서 실제로는 QR 분해나 SVD 기반의 최소제곱 해법(numpy.linalg.lstsq)을 쓴다. 특징끼리 강하게 상관되어 XᵀX 가 특이 행렬에 가까우면 해가 크게 흔들리는데, 이것이 다중공선성 문제이고 릿지 정규화로 완화한다.
풀이 2 — 경사 하강법
w ← w − η · ∇MSE(w)
η 는 학습률이다. 기울기의 반대 방향으로 조금씩 내려간다. 데이터가 크거나 특징이 아주 많으면 정규방정식보다 낫고, 무엇보다 닫힌 해가 없는 모델(신경망)에도 그대로 쓸 수 있다는 점이 중요하다.
| 정규방정식 | 경사 하강법 | |
|---|---|---|
| 반복 | 없음 | 필요 |
| 학습률 | 필요 없음 | 골라야 함 |
| 비용 | 특징 수 d 에 대해 대략 d³ | 반복당 n·d |
| 일반화 | 선형 모델 전용 | 미분 가능한 모든 모델 |
학습률이 너무 크면 발산하고, 너무 작으면 한없이 느리다. 특징의 스케일이 크게 다르면 손실 곡면이 길쭉해져 수렴이 느려지므로 표준화(평균 0, 분산 1)를 먼저 한다.
평가 — R²
R² = 1 − Σ(ŷ − y)² / Σ(y − ȳ)²
“평균값 하나로 찍는 것보다 분산을 얼마나 더 설명하는가”다. 1 이면 완벽, 0 이면 평균과 같은 수준, 음수면 평균보다 못하다.
직접 해 보기
기울기 3, 절편 2 인 직선에 잡음을 섞은 데이터 50개를 만들고 두 방법으로 푼다.
import numpy as np
rng = np.random.default_rng(0)
x = rng.uniform(0, 10, 50)
y = 3.0 * x + 2.0 + rng.normal(0, 1.0, 50) # 참값: 기울기 3, 절편 2
X = np.column_stack([np.ones_like(x), x]) # 절편용 1 열
# 1) 정규방정식(수치적으로는 lstsq 가 안전)
w_closed, *_ = np.linalg.lstsq(X, y, rcond=None)
# 2) 경사 하강법
w = np.zeros(2); lr = 0.01
for step in range(5000):
grad = 2 / len(y) * X.T @ (X @ w - y)
w -= lr * grad
mse = np.mean((X @ w - y) ** 2)
r2 = 1 - np.sum((X @ w - y)**2) / np.sum((y - y.mean())**2)
print("정규방정식 [b, w]:", w_closed.round(3))
print("경사하강 [b, w]:", w.round(3))
print(f"MSE={mse:.3f} R^2={r2:.4f}")
실행 결과:
정규방정식 [b, w]: [1.757 3.052]
경사하강 [b, w]: [1.757 3.052]
MSE=0.993 R^2=0.9876
두 방법의 답이 소수 셋째 자리까지 같다. 참값(2, 3)과 약간 다른 것은 잡음 때문이다. 데이터 50개로 추정한 값에는 원래 오차가 있다. MSE 가 1 근처인 것도 우리가 넣은 잡음의 분산(1.0²)과 맞는다. 모델이 잡음 자체는 설명할 수 없다는 뜻이다.
학습률 lr 을 0.05 로 바꿔 보라. 이 데이터에서는 x 값이 0~10 이라 기울기가 커서 손실이 발산한다. 특징을 표준화한 뒤에 다시 해 보면 큰 학습률에서도 수렴한다.
현업에서는
- 용량 계획: “동시 접속자 수와 CPU 사용률” 같은 관계는 선형 근사로 충분한 경우가 많다. 홈랩 클러스터에서도 요청 수 대비 메모리 사용량에 직선을 맞춰 보면 언제 노드를 늘려야 할지 대략 감이 온다. 단, 범위 밖으로 외삽하면 틀리기 쉽다.
- A/B 테스트·계량 분석: 다른 변수의 영향을 통제한 효과 추정에 회귀를 쓴다. 이때는 예측보다 계수의 해석과 신뢰구간이 중요하다.
- 기준선: Kaggle 이든 사내 과제든, 선형 모델을 먼저 돌려 두면 이후 모델이 얼마나 나은지 숫자로 말할 수 있다.
확인 문제
- ŷ = w0 + w1·x + w2·x² 는 선형 회귀인가?
- 정규방정식 w = (XᵀX)⁻¹Xᵀy 를 유도하는 출발점은 무엇인가?
- 경사 하강법이 발산할 때 가장 먼저 의심할 두 가지는?
- R² 이 음수라는 것은 무엇을 뜻하는가?
풀이
- 그렇다. 가중치에 대해 선형이므로 x 와 x² 을 특징으로 넣은 선형 회귀다.
- MSE 의 w 에 대한 기울기를 0 으로 놓는 것. MSE 가 볼록 함수라 그 점이 최솟값이다.
- 학습률이 너무 큰 것, 특징의 스케일이 표준화되지 않은 것.
- 모델의 예측이 단순히 평균값을 내는 것보다 오차가 크다는 뜻이다. 보통 평가 데이터에서 과적합된 모델이나 잘못된 모델에서 나온다.
더 읽을거리 (References)
- scikit-learn User Guide, Linear Models — Ordinary Least Squares
- NumPy 공식 문서, numpy.linalg.lstsq 등 선형대수 API
- T. Hastie, R. Tibshirani, J. Friedman, The Elements of Statistical Learning, 2nd ed., 3장. 저자 사이트