튜토리얼
02. Linear Regression

02. Linear Regression 완전정복

OLS, Gradient Descent, L1/L2 정규화, VIF, 잔차 분석


학습 목표

이 튜토리얼을 완료하면 다음을 할 수 있습니다:

  1. OLS (Ordinary Least Squares) 수식을 이해하고 정규 방정식으로 직접 구현
  2. Gradient Descent 알고리즘을 구현하고 학습률에 따른 수렴 과정 이해
  3. L1 (Lasso) / L2 (Ridge) 정규화의 원리 이해 및 효과 비교
  4. 다중공선성(Multicollinearity) 문제와 VIF (Variance Inflation Factor) 계산
  5. **잔차 분석(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ᵀy

From 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.bias
🚫

Intercept(절편) 누락 주의! 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 self

Learning 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 NetL(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)

다중공선성의 문제점

  1. 계수 추정의 불안정성: 작은 데이터 변화에도 계수가 크게 바뀜
  2. 계수 해석의 어려움: 개별 변수의 영향을 분리하기 어려움
  3. 표준 오차 증가: 통계적 유의성 검정이 불확실해짐
⚠️

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으로 최종 성능 평가

면접 질문 맛보기

  1. OLS와 Gradient Descent의 차이점은?
  2. Ridge와 Lasso의 차이점은? 언제 각각을 사용하나요?
  3. 다중공선성이 왜 문제인가요? 어떻게 해결하나요?
  4. 선형회귀의 가정은 무엇이고, 어떻게 검증하나요?
  5. 정규화에서 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