다중 선형 회귀에서 모델 확장과 부분 F-검정, VIF 변수 선택
다중 선형 회귀 모델에 상호작용과 다항항을 확장하고, 부분 F-검정·VIF·변수 선택으로 안정성을 관리하는 방법
2026-08-14 · 최초 발행 2024-04-29
기본 OLS를 확장할 때 확인할 것
다중 선형 회귀는 종속변수 y와 독립변수 X의 선형 관계를 추정하는 방법이다. 모형은 y = Xβ + ε이며, ε ~ N(0, σ²)를 가정한다. 계수 β는 최소제곱법(OLS)으로 추정하며, 해석하기 쉽고 다른 예측 모형을 판단하는 기준선으로도 쓸 수 있다.
하지만 주효과만 넣은 모형이 항상 관계를 충분히 설명하는 것은 아니다. 변수 간 효과가 함께 달라지거나 곡선 형태의 관계가 나타나면 다항항(예: x²)과 상호작용항(예: x1·x2)을 더할 수 있다. 상호작용을 포함했다면 관련 주효과도 함께 유지하는 계층적 원칙을 따르는 편이 해석의 일관성을 지키는 데 도움이 된다.
확장 자체가 목적이 되어서는 안 된다. 추가한 변수 블록이 실제로 모형에 기여하는지, 새 항이 계수를 불안정하게 만들지는 않는지 함께 확인해야 한다.
확장 항의 기여는 중첩 모형으로 비교한다
부분 F-검정은 간단한 reduced 모형과 확장한 full 모형처럼 서로 중첩되는 두 모형을 비교하는 방법이다. 추가 변수 블록이 설명력을 제공하는지 잔차제곱합(RSS)의 차이로 판단한다.
검정 통계량은 다음과 같다.
F = [(RSS_R − RSS_F)/(df_R − df_F)] / (RSS_F/df_F)
다항항과 상호작용항을 더하기 전후를 비교할 때 특히 유용하다. 다만 다중 비교가 발생한다면 Bonferroni 같은 유의수준 보정이나 홀드아웃 검증을 함께 고려할 수 있다. 과적합을 피하려면 교차검증과 정보준거도 병행한다.
VIF와 변수 선택을 함께 다루는 이유
다중공선성은 설명변수 사이의 선형 상관이 강할 때 계수의 분산이 커지고 추정이 불안정해지는 현상이다. 변수별 VIF는 다음 식으로 구한다.
VIF_j = 1/(1 − R_j²)
일반적으로 VIF > 5는 경고, > 10은 심각한 상태로 본다. 높은 VIF가 확인된 변수는 제거하거나 결합할 수 있고, 주성분 변환이나 정규화 회귀를 적용하는 방법도 있다. 상호작용항과 다항항을 다룰 때는 센터링이 VIF 완화에 도움이 될 수 있으며, 데이터 수집 단계에서는 설계 요인의 직교화도 고려 대상이다.
변수 선택은 하나의 기준으로 끝나지 않는다. 예측 성능은 RMSE와 R²_adj로, 모형의 복잡도는 AIC/BIC로, 운영 관점에서는 해석 가능성과 안정성으로 판단할 수 있다. 상관관계나 도메인 지식으로 후보를 먼저 걸러낸 뒤 전진·후진·단계적 선택을 적용하고, 라쏘 같은 내장형 방법을 결합하는 흐름이 가능하다. 안정성을 확인할 때는 K-겹 교차검증과 부트스트랩 빈도도 활용한다.
잔차 진단이 확장 모형의 경계를 정한다
모형을 확장한 뒤에는 잔차의 독립성, 등분산성, 정규성, 영향점을 확인해야 한다. Cook’s D와 레버리지는 영향점 진단에 사용한다.
가정 위반이 확인되면 Box-Cox 변환, 가중최소제곱(WLS), 로버스트 표준오차(HC3)를 적용할 수 있다. 이 과정은 더 많은 변수를 넣어 얻은 설명력이 실제 운용에서도 유지되는지 판단하는 기준이 된다.
적용 맥락
마케팅 믹스 모델링에서는 채널 지출, 가격, 프로모션의 상호작용을 반영할 수 있다. 부분 F-검정으로 상호작용을 유지할지 판단하고, VIF로 채널 간 공선성을 관리한다.
제조 공정 품질 분석에서는 온도·압력·습도의 다항항을 포함할 수 있다. VIF가 높은 센서 변수는 라쏘로 축소한 뒤 해석 모형을 다시 학습한다.
리스크 스코어링에서는 신용 변수군을 블록으로 추가한 뒤 부분 F-검정으로 기여를 확인할 수 있다. 규제 준수가 필요한 경우에는 단순 모형을 유지하는 선택도 가능하다.
모델 확장과 VIF 제약을 적용하는 코드
전제조건:
- Python 3.11+, pandas>=2.0, numpy>=1.24, statsmodels>=0.14
- 설치: pip install pandas numpy statsmodels
# 환경: Python 3.11, pandas 2.2, numpy 1.26, statsmodels 0.14
import numpy as np
import pandas as pd
import statsmodels.api as sm
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
from statsmodels.stats.outliers_influence import variance_inflation_factor
# 1) 데이터 생성: 공선성 있는 변수, 상호작용·다항항 효과 포함
rng = np.random.default_rng(42)
n = 600
x1 = rng.normal(0, 1, n)
x2 = 0.85 * x1 + rng.normal(0, 0.5, n) # x1과 강한 상관
x3 = rng.normal(0, 1, n)
# 진짜 데이터 생성식: 상호작용 및 다항항 포함
y = 2.0 + 1.5*x1 - 0.8*x2 + 0.6*x3 + 1.2*(x1*x3) + 0.9*(x1**2) + rng.normal(0, 1.0, n)
df = pd.DataFrame({'y': y, 'x1': x1, 'x2': x2, 'x3': x3})
# 2) 기본 모형: 주효과만
m_reduced = ols('y ~ x1 + x2 + x3', data=df).fit()
# 3) 확장 모형: 상호작용과 다항항 추가 (계층 원칙 준수)
df['x1_c'] = df['x1'] - df['x1'].mean() # 센터링으로 공선성 완화
m_full = ols('y ~ x1 + x2 + x3 + x1:x3 + I(x1_c**2)', data=df).fit()
print('--- 부분 F-검정 (Reduced vs Full) ---')
anova_res = anova_lm(m_reduced, m_full)
print(anova_res) # p-value로 확장 변수 블록의 유의성 판단
# 4) VIF 계산 함수
def compute_vif(X: pd.DataFrame):
X_ = sm.add_constant(X, has_constant='add')
cols = X_.columns
vifs = [variance_inflation_factor(X_.values, i) for i in range(X_.shape[1])]
return pd.Series(vifs, index=cols)
print('\n--- VIF (확장 모형 설명변수) ---')
X_full = df[['x1', 'x2', 'x3', 'x1_c']]
print(compute_vif(X_full))
# 5) 간단 전진 선택(AIC) + VIF 제약
candidates = ['x1', 'x2', 'x3', 'x1:x3', 'I(x1_c**2)']
selected = []
best_aic = np.inf
while True:
improved = False
best_var, best_model = None, None
for var in candidates:
if var in selected:
continue
formula = 'y ~ ' + ' + '.join(selected + [var]) if selected else 'y ~ ' + var
model = ols(formula, data=df).fit()
# VIF 제약: 10 초과 변수 포함 금지
# 주의: 상호작용/다항항의 VIF는 주효과 변수 위주로 확인
design_cols = [c for c in model.model.exog_names if c not in ['Intercept']]
X_design = pd.DataFrame(model.model.exog, columns=model.model.exog_names).drop(columns=['Intercept'])
vif_series = pd.Series([variance_inflation_factor(X_design.values, i) for i in range(X_design.shape[1])],
index=design_cols)
if (vif_series > 10).any():
continue
if model.aic + 1e-6 < best_aic:
best_aic = model.aic
best_var = var
best_model = model
improved = True
if improved and best_var is not None:
selected.append(best_var)
candidates.remove(best_var)
else:
break
print('\n--- 전진 선택 결과 (AIC+VIF<=10) ---')
print('선택 변수:', selected)
if best_model is not None:
print(best_model.summary().tables[1])
anova_lm 결과의 Pr(>F)가 유의하면 확장 블록을 유지한다. VIF가 10을 넘는 변수를 차단해 과도한 공선성을 억제하고, 전진 선택에서는 AIC 최소화와 공선성 제약을 동시에 만족하는 변수 집합을 찾는다.
성능과 해석의 균형
상호작용·다항항을 추가하고 검정 결과에 따라 유지하면 RMSE가 5~15% 개선될 수 있다. VIF 제어와 교차검증을 함께 적용하면 계수 분산과 성능 분산을 줄이는 데 도움이 된다. 계층적 원칙과 부분 F-검정은 비즈니스 규칙에 맞는 설명력을 확보하는 기반이 된다.
자동 선택 루틴과 진단 체계를 갖추면 피처 튜닝 시간을 30~50% 절감할 수 있다. 다만 확장 항의 유의성, 공선성, 잔차 가정, 운영 환경에서의 안정성을 함께 확인해야 한다.
| 방법 | 성능 | 확장성 | 해석 일관성 | 안정성(공선성 민감도) | 운영 편의 |
|---|---|---|---|---|---|
| OLS | 데이터가 충분·가정 충족 시 우수 | 변수 급증 시 과적합 위험 | 계수 해석 용이 | 공선성에 매우 민감 | 하이퍼파라미터 불필요 |
| Ridge | 약간의 편향 대가로 RMSE 안정 | 고차원에서도 견고 | 계수 축소로 해석 가능성 양호 | 공선성 완화 우수 | α 튜닝 필요 |
| Lasso | 희소 해로 변수 선택 가능 | 고차원 변수 여럿에 유리 | 0 계수로 단순화 | 공선성 시 임의 선택 가능성 | α 튜닝·경로 분석 유용 |