research

회귀 모델 평가 지표 — MAE, RMSE, R²

회귀 모델 평가 지표 — MAE, RMSE, R²


## 분류 vs 회귀의 지표 차이

"정확도(Accuracy) 0.8"은 **분류(Classification)** 문제의 개념이다. 100개 중 80개를 맞췄다는 뜻으로, 맞다/틀리다가 명확한 문제에서만 쓴다.

판매량 예측은 **회귀(Regression)** 문제다. 연속적인 숫자를 예측하므로 맞다/틀리다로 판단할 수 없다. 다른 지표가 필요하다.

## 회귀 평가 지표

### MAE (Mean Absolute Error)
예측값과 실제값의 차이 절댓값 평균. "평균적으로 얼마나 틀리는가".
- 해석이 직관적 — 단위가 원본 데이터와 같음
- 이상값에 덜 민감

### RMSE (Root Mean Squared Error)
오차를 제곱해서 평균 낸 뒤 루트. 큰 오차에 더 민감하다.
- MAE보다 spike에 크게 반응
- MAE와 RMSE 차이가 크면 큰 오차(spike)가 많다는 신호

### R² (결정계수) — "회귀의 정확도"
모델이 판매량 변동의 몇 %를 설명하는지 0~1 사이로 나타낸다.

| R² | 의미 |
|----|------|
| 1.0 | 완벽한 예측 |
| 0.8 | 변동의 80% 설명 |
| 0.0 | 평균 예측과 동일 |
| < 0 | 평균 예측보다 못함 |

"정확도 0.8 이상"을 목표로 한다면 R² ≥ 0.8이 기준이 된다.

## 주의사항

R² 0.8이 항상 좋은 모델을 의미하지는 않는다. 판매량처럼 변동성이 크고 외부 요인(대량 주문, distribution shift)이 많은 데이터에서는 R² 0.6~0.7도 현실적으로 좋은 성능일 수 있다. 절대적인 기준보다 **baseline 대비 개선 여부**가 더 의미있다.

## 구현

```python
from sklearn.metrics import r2_score

fold_r2s = []
for tr_idx, val_idx in tscv.split(X_tv):
    model.fit(X_tv.iloc[tr_idx], y_tv.iloc[tr_idx])
    pred = model.predict(X_tv.iloc[val_idx])
    fold_r2s.append(r2_score(y_tv.iloc[val_idx], pred))

print(f'평균 R²: {np.mean(fold_r2s):.3f}')
```



## R² (결정계수) 결과 — CV 기준

| 모델 | 평균 R² |
|------|--------|
| HuberRegressor | -0.020 |
| SVR | -0.066 |
| ElasticNet | -0.084 |
| Ridge | -0.084 |
| Lasso | -0.085 |
| LinearRegression | -0.085 |
| ExtraTrees | -0.161 |
| RandomForest | -0.174 |
| KNN | -0.290 |
| GradientBoosting | -0.363 |
| DecisionTree | -1.707 |

## 핵심 해석 — 모든 모델 R² 음수

R² < 0은 모델이 "평균을 예측하는 것"보다도 못하다는 의미다. 이는 모델의 한계가 아니라 **데이터 구조의 한계**다.

**근본 원인**: lag/시간 feature만으로는 대량 주문 spike의 발생 시점을 예측할 수 없다. 대량 주문은 거래처의 발주 의사결정에서 오는 것이므로, 과거 판매 패턴이 아닌 외부 요인에 의해 결정된다.

**R² 0.8 목표**: 이 데이터 구조에서는 현재 feature set으로 달성하기 어렵다. R² 0.8은 데이터가 안정적이고 외부 요인이 적은 경우에 가능한 수준이다.

## 현실적인 모델 활용 방향

대량 주문(spike)을 제외한 일반 주문 baseline 예측에는 유효하다. 실제 비즈니스에서 대량 주문은 영업팀이 직접 관리하고, 모델은 일반 수요 수준을 잡는 데 활용하는 방식이 현실적이다.

## 다음 주 예측 결과

2026-03-23 예측 판매량: **2179** (HuberRegressor 기준)