02. Linear Regression 완전정복
OLS, Gradient Descent, L1/L2 정규화, VIF, 잔차 분석
학습 목표
이 튜토리얼을 완료하면 다음을 할 수 있습니다:
- OLS (Ordinary Least Squares) 수식을 이해하고 정규 방정식으로 직접 구현
- Gradient Descent 알고리즘을 구현하고 학습률에 따른 수렴 과정 이해
- L1 (Lasso) / L2 (Ridge) 정규화의 원리 이해 및 효과 비교
- 다중공선성(Multicollinearity) 문제와 VIF (Variance Inflation Factor) 계산
- **잔차 분석(Residual Analysis)**을 통한 선형회귀 가정 검증
핵심 개념
1. 선형회귀 모델
선형회귀는 입력 변수 X와 출력 변수 y 사이의 선형 관계를 모델링합니다:
ŷ = w₀ + w₁x₁ + w₂x₂ + ... + wₚxₚw₀: 절편 (intercept, bias)w₁...wₚ: 각 피처의 가중치 (coefficients)
선형회귀에서 "선형"은 파라미터(가중치)에 대해 선형이라는 의미입니다. 입력 피처를 변환(예: x²)해도 가중치가 선형 결합되면 선형회귀입니다.
2. 손실 함수 (MSE)
**Mean Squared Error (MSE)**를 최소화합니다:
L(w) = (1/n) Σ(yᵢ - ŷᵢ)² = (1/n)||y - Xw||²MSE를 사용하는 이유:
- 미분 가능하여 최적화에 용이
- 큰 오차에 더 큰 페널티 부여
- 닫힌 형태의 해(closed-form solution) 존재
3. 정규 방정식 (Normal Equation)
손실 함수를 가중치에 대해 미분하고 0으로 설정하면 닫힌 형태의 해를 얻습니다:
w = (XᵀX)⁻¹XᵀyFrom Scratch 구현
class LinearRegressionScratch:
def fit(self, X, y):
# 편향 항 추가 (X에 1로 이루어진 열 추가)
X_b = np.c_[np.ones((len(X), 1)), X]
# 정규 방정식
w = np.linalg.inv(X_b.T @ X_b) @ X_b.T @ y
self.bias = w[0]
self.weights = w[1:]
return self
def predict(self, X):
return X @ self.weights + self.biasIntercept(절편) 누락 주의! 1로 이루어진 열을 추가하지 않으면 회귀선이 원점을 지나야 합니다. 실험 결과, Intercept 없이 계산하면 R² 점수가 0.58에서 -2.71로 급락할 수 있습니다.
정규 방정식의 장단점
| 장점 | 단점 |
|---|---|
| 닫힌 형태의 해 (한 번에 계산) | O(n³) 복잡도 (역행렬 계산) |
| 학습률 튜닝 불필요 | 피처가 많으면 느림 |
| 수렴 보장 | XᵀX가 비가역적일 수 있음 |
4. Gradient Descent
피처가 많거나 데이터가 클 때는 반복적으로 기울기 반대 방향으로 이동하는 Gradient Descent를 사용합니다:
w_{t+1} = w_t - α * ∇L(w_t)여기서 그래디언트는:
∇L(w) = -(2/n) Xᵀ(y - Xw)From Scratch 구현
class LinearRegressionGD:
def fit(self, X, y, lr=0.01, n_iter=1000):
self.weights = np.zeros(X.shape[1])
self.bias = 0
self.loss_history = []
for _ in range(n_iter):
y_pred = X @ self.weights + self.bias
error = y_pred - y
# 그래디언트 계산
dw = (1/len(y)) * (X.T @ error)
db = (1/len(y)) * np.sum(error)
# 가중치 업데이트
self.weights -= lr * dw
self.bias -= lr * db
# 손실 기록
loss = (1/(2*len(y))) * np.sum((y - y_pred)**2)
self.loss_history.append(loss)
return selfLearning Rate 선택의 중요성
| Learning Rate | 결과 | 예시 수렴 속도 |
|---|---|---|
| 너무 작음 (0.001) | 수렴이 느림 | 1000 iteration 후에도 미수렴 |
| 적절함 (0.1) | 빠르고 안정적 수렴 | 100-200 iteration에 수렴 |
| 너무 큼 (1.0+) | 발산 또는 진동 | Loss가 무한대로 증가 |
Gradient Descent를 사용할 때는 반드시 Feature Scaling을 해야 합니다. 스케일이 다른 피처들은 손실 함수의 등고선을 찌그러뜨려 수렴을 방해합니다.
OLS vs Gradient Descent 비교
| 측면 | 정규 방정식 (OLS) | Gradient Descent |
|---|---|---|
| 계산 복잡도 | O(n³) | O(n·k·iter) |
| 피처 수 | 10,000개 이하 권장 | 대규모 가능 |
| 메모리 | XᵀX 저장 필요 | 배치 크기만큼 |
| 하이퍼파라미터 | 없음 | lr, n_iter 튜닝 필요 |
| 수렴 보장 | 항상 최적해 | lr에 따라 다름 |
5. 정규화 (Regularization)
과적합을 방지하고 가중치를 제어하기 위해 손실 함수에 페널티 항을 추가합니다:
| 방법 | 손실 함수 | 효과 | 사용 시점 |
|---|---|---|---|
| Ridge (L2) | L(w) + λ‖w‖₂² | 가중치를 작게 유지 | 다중공선성이 있을 때 |
| Lasso (L1) | L(w) + λ‖w‖₁ | 일부 가중치를 0으로 | Feature Selection이 필요할 때 |
| Elastic Net | L(w) + λ₁‖w‖₁ + λ₂‖w‖₂² | L1 + L2 결합 | 상관된 피처가 많을 때 |
Ridge vs Lasso 기하학적 해석
L1 (Lasso): 다이아몬드 형태의 제약 조건
→ 손실 함수 등고선과 꼭짓점에서 접촉 → 일부 계수가 정확히 0
L2 (Ridge): 원형 제약 조건
→ 손실 함수 등고선과 곡면에서 접촉 → 모든 계수가 축소되나 0은 아님최적 alpha 찾기 (Cross-Validation)
from sklearn.linear_model import RidgeCV, LassoCV
# Cross-Validation으로 최적 alpha 자동 탐색
ridge_cv = RidgeCV(alphas=np.logspace(-4, 4, 50), cv=5)
ridge_cv.fit(X_train_scaled, y_train)
print(f"Best alpha: {ridge_cv.alpha_}")
lasso_cv = LassoCV(alphas=np.logspace(-4, 1, 50), cv=5, max_iter=10000)
lasso_cv.fit(X_train_scaled, y_train)
print(f"Best alpha: {lasso_cv.alpha_}")California Housing 데이터셋에서 실험 결과, 세 모델(OLS, Ridge, Lasso)의 R² 점수가 거의 동일했습니다 (~0.576). 이는 기본 선형회귀로도 충분한 경우입니다. 정규화는 과적합이 의심될 때나 다중공선성이 있을 때 더 유용합니다.
6. 다중공선성 (VIF)
**다중공선성(Multicollinearity)**은 독립 변수들 간에 높은 상관관계가 있을 때 발생합니다.
VIF (Variance Inflation Factor) 계산
각 피처를 다른 피처들로 회귀하여 R²를 계산합니다:
VIF = 1 / (1 - R²)| VIF 값 | 해석 |
|---|---|
| 1 | 다중공선성 없음 |
| 1-5 | 보통 허용 범위 |
| 5-10 | 주의 필요 |
| > 10 | 심각한 다중공선성 → 해당 변수 제거 고려 |
VIF 계산 구현
def calculate_vif(X):
vif_data = []
for col in X.columns:
X_temp = X.drop(columns=[col])
r2 = LinearRegression().fit(X_temp, X[col]).score(X_temp, X[col])
vif = 1 / (1 - r2) if r2 < 1 else float('inf')
vif_data.append({'Feature': col, 'VIF': vif})
return pd.DataFrame(vif_data)다중공선성의 문제점
- 계수 추정의 불안정성: 작은 데이터 변화에도 계수가 크게 바뀜
- 계수 해석의 어려움: 개별 변수의 영향을 분리하기 어려움
- 표준 오차 증가: 통계적 유의성 검정이 불확실해짐
California Housing 데이터셋에서 Latitude(9.2)와 Longitude(8.9)의 VIF가 높게 나왔습니다. 이는 두 변수가 지리적으로 강하게 연관되어 있기 때문입니다. 해결책: 변수 제거 또는 Ridge 정규화 사용
잔차 분석
선형회귀의 가정을 검증하기 위해 잔차(residual = 실제값 - 예측값)를 분석합니다:
4가지 주요 가정
| 가정 | 검증 방법 | 위반 시 조치 |
|---|---|---|
| 선형성 | 잔차 vs 예측값 플롯에서 패턴 없음 | 비선형 변환, 다항 회귀 |
| 정규성 | Q-Q Plot, Shapiro-Wilk 검정 | 타겟 변환 (로그 등) |
| 등분산성 | 잔차 vs 예측값에서 분산 일정 | 가중 최소제곱법 |
| 독립성 | Durbin-Watson 검정 | 시계열 모델 고려 |
from scipy import stats
residuals = y_test - y_pred
# 기본 통계
print(f"평균: {residuals.mean():.6f}") # 0에 가까워야 함
print(f"왜도: {stats.skew(residuals):.4f}") # 0에 가까워야 함
print(f"첨도: {stats.kurtosis(residuals):.4f}") # 0에 가까워야 함
# 정규성 검정
_, p_value = stats.shapiro(residuals[:5000])
print(f"Shapiro-Wilk p-value: {p_value:.6f}")코드 요약
from sklearn.linear_model import LinearRegression, Ridge, Lasso, RidgeCV
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score, mean_squared_error
# 스케일링 (GD와 정규화에 필수!)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 모델 학습 및 비교
models = {
'OLS': LinearRegression(),
'Ridge': Ridge(alpha=1.0),
'Lasso': Lasso(alpha=0.1)
}
for name, model in models.items():
model.fit(X_train_scaled, y_train)
y_pred = model.predict(X_test_scaled)
print(f"{name}: R²={r2_score(y_test, y_pred):.4f}, "
f"RMSE={np.sqrt(mean_squared_error(y_test, y_pred)):.4f}")체크리스트
| 단계 | 체크 항목 |
|---|---|
| 데이터 전처리 | ☐ 결측치 처리 |
| ☐ Feature Scaling (StandardScaler 권장) | |
| ☐ 이상치 확인 및 처리 | |
| 모델 선택 | ☐ 다중공선성 확인 (VIF 계산) |
| ☐ VIF > 10인 변수 제거 또는 Ridge 사용 | |
| ☐ Feature Selection 필요시 Lasso 사용 | |
| 검증 | ☐ 잔차 분석 (선형성, 정규성, 등분산성) |
| ☐ Cross-Validation으로 alpha 튜닝 | |
| ☐ Test Set으로 최종 성능 평가 |
면접 질문 맛보기
- OLS와 Gradient Descent의 차이점은?
- Ridge와 Lasso의 차이점은? 언제 각각을 사용하나요?
- 다중공선성이 왜 문제인가요? 어떻게 해결하나요?
- 선형회귀의 가정은 무엇이고, 어떻게 검증하나요?
- 정규화에서 alpha(lambda)가 너무 크면 어떤 일이 발생하나요?
더 많은 면접 질문은 Premium Interviews (opens in a new tab)에서 확인하세요.
실습 노트북
California Housing 데이터셋으로 위 개념들을 직접 실습해보세요:
노트북에서는 추가로 다음 내용을 다룹니다:
- 상세한 EDA (탐색적 데이터 분석) 및 시각화
- Gradient Descent 수렴 과정 애니메이션
- Learning Rate별 수렴 속도 비교 실험
- Bootstrap을 통한 다중공선성의 계수 불안정성 시각화
- 잔차 분석 4-panel 플롯
- 연습문제 (Mini-batch GD, Elastic Net, Polynomial Regression)
이전: 01. ML Pipeline | 다음: 03. Logistic Regression