모델 오차를 쪼개면 드러나는 편향-분산 트레이드오프
Machine Learning

모델 오차를 쪼개면 드러나는 편향-분산 트레이드오프

2026.01.13.
ML 기초39편
  1. 11거리만으로 분류하는 K-최근접 이웃(KNN)
  2. 12마진을 최대화하는 분류기 서포트 벡터 머신(SVM)
  3. 13모델 오차를 쪼개면 드러나는 편향-분산 트레이드오프읽는 중
  4. 14질문을 반복해 데이터를 쪼개는 결정 트리
  5. 15약한 모델 여러 개를 합쳐 분산을 줄이는 배깅
3 / 8

훈련 데이터에서는 오차가 거의 0인데 새 데이터만 만나면 형편없어지는 모델이 있다. 반대로 훈련 데이터조차 제대로 못 맞추는 모델도 있다. 앞이 과적합(Overfitting), 뒤가 과소적합(Underfitting)이다. 여기까지는 감으로도 안다.

정작 어려운 건 눈앞의 모델이 둘 중 어느 쪽이냐다. 검증 점수가 낮다는 사실 하나로는 데이터를 더 모아야 하는지, 모델을 키워야 하는지, 규제를 걸어야 하는지 알 수 없다. 세 처방은 서로 정반대라서 잘못 고르면 며칠을 그냥 버린다.

편향-분산 분해는 그 판단에 필요한 좌표를 준다. 모델의 오차를 세 조각으로 나누고, 각 조각이 무엇에 반응하는지 알려준다.

오차는 세 조각으로 나뉜다

데이터가 y=f(x)+εy = f(x) + \varepsilon 로 만들어졌다고 하자. ff 는 우리가 모르는 진짜 함수이고, ε\varepsilon 은 평균 0에 분산 σ2\sigma^2 인 노이즈다. 모델 f^\hat{f} 는 훈련 데이터 한 벌로 학습한 결과이므로, 훈련 데이터를 다시 뽑으면 다른 f^\hat{f} 가 나온다. 아래 기댓값은 전부 이 “훈련셋을 다시 뽑는” 가상의 반복에 대한 평균이다.

한 지점 xx 에서 제곱 오차의 기댓값을 전개하면 세 항으로 갈라진다.

E[(y−f^)2]=(f−E[f^])2⏟Bias2+E[(f^−E[f^])2]⏟Var+σ2⏟줄일 수 없는 노이즈E[(y - \hat{f})^2] = \underbrace{(f - E[\hat{f}])^2}_{\text{Bias}^2} + \underbrace{E[(\hat{f} - E[\hat{f}])^2]}_{\text{Var}} + \underbrace{\sigma^2}_{\text{줄일 수 없는 노이즈}}

전개는 “더하고 빼기” 두 번이면 끝난다. 먼저 yy 를 f+εf + \varepsilon 으로 바꾸면 교차항 2E[(f−f^)ε]2E[(f - \hat{f})\varepsilon] 이 생기는데, 새 데이터의 노이즈 ε\varepsilon 은 훈련으로 만든 f^\hat{f} 와 독립이고 E[ε]=0E[\varepsilon] = 0 이라 이 항은 사라지고 σ2\sigma^2 만 떨어져 나온다. 남은 E[(f−f^)2]E[(f - \hat{f})^2] 안에 E[f^]E[\hat{f}] 를 더하고 빼서 다시 전개하면, 이번에는 E[E[f^]−f^]=0E[E[\hat{f}] - \hat{f}] = 0 이라 교차항이 사라지고 편향 제곱과 분산이 남는다.

항 정의 무엇에 반응하나
Bias2\text{Bias}^2 (f−E[f^])2(f - E[\hat{f}])^2 모델 구조. 예측의 평균이 진짜 값에서 얼마나 빗나가 있는가
Var\text{Var} E[(f^−E[f^])2]E[(\hat{f} - E[\hat{f}])^2] 훈련 데이터. 훈련셋이 바뀔 때 예측이 얼마나 흔들리는가
σ2\sigma^2 노이즈의 분산 아무것에도. 어떤 모델을 써도 남는 하한선

σ2\sigma^2 는 손댈 수 없으니 우리가 통제할 수 있는 건 앞의 두 항뿐이다. 그리고 이 둘은 같은 방향으로 움직이지 않는다.

하나뿐인 훈련셋으로 어떻게 기댓값을 말하나

실제로 우리 손에는 훈련 데이터가 한 벌뿐이다. 편향과 분산은 “같은 분포에서 훈련셋을 다시 뽑아 학습을 반복하면 예측이 어떻게 흩어질까”라는 가상의 실험 위에 정의된 양이다. 측정하려고 만든 개념이 아니라 모델 구조의 성질을 드러내려고 만든 개념이다. 실전에서 재는 건 이 값들이 아니라 뒤에 나올 훈련 오차와 검증 오차다.

차수를 바꿔가며 세 조각을 재보면

사인 곡선에 표준편차 0.3짜리 노이즈를 얹은 점 18개(np.random.RandomState(21))에 다항 회귀를 맞춰 보자. 차수만 바꾸고 나머지는 전부 같다.

위 · 차수 1 훈련 MSE 0.35 가운데 · 차수 3 훈련 MSE 0.05 아래 · 차수 12 훈련 MSE 0.02 실제 함수 학습된 모델

차수 1은 직선이라 사인 곡선의 오르내림을 애초에 표현할 수 없다. 데이터를 아무리 더 넣어도 직선은 직선이다. 차수 12는 점 하나하나를 지나가려고 몸을 비틀어서 훈련 MSE가 0.02까지 떨어졌지만, 그 굴곡은 데이터의 패턴이 아니라 그 18개 점에 섞여 있던 노이즈의 모양이다. 훈련셋을 다시 뽑으면 굴곡이 완전히 다른 자리에 생긴다.

이 직관은 실제로 측정할 수 있다. 같은 분포에서 훈련셋 300벌을 뽑아 각각 학습시킨 뒤, 예측들의 평균이 진짜 함수에서 얼마나 벗어났는지(편향 제곱)와 예측들이 서로 얼마나 흩어졌는지(분산)를 따로 재면 된다.

import numpy as np
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline

grid = np.linspace(0.3, 2 * np.pi - 0.3, 60).reshape(-1, 1)
truth = np.sin(grid.ravel())

def decompose(degree, n_sets=300, n=40):
    preds = []
    for s in range(n_sets):
        r = np.random.RandomState(s)
        X = r.uniform(0, 2 * np.pi, n).reshape(-1, 1)
        y = np.sin(X.ravel()) + r.normal(0, 0.3, n)   # sigma^2 = 0.09
        model = Pipeline([('poly', PolynomialFeatures(degree)),
                          ('lr', LinearRegression())]).fit(X, y)
        preds.append(model.predict(grid))
    P = np.array(preds)
    return ((P.mean(axis=0) - truth) ** 2).mean(), P.var(axis=0).mean()
차수 Bias2\text{Bias}^2 Var\text{Var} 합 + σ2\sigma^2
1 0.1548 0.0161 0.2609
3 0.0031 0.0090 0.1021
5 0.0001 0.0144 0.1044
9 0.0003 0.1066 0.1969
11 0.0348 11.4075 11.5323

편향 제곱은 차수 3에서 이미 거의 0으로 떨어지고 그 뒤로는 더 내려갈 자리가 없다. 반면 분산은 차수 5부터 꾸준히 오르다가 9를 지나면서 폭주한다. 총합이 가장 작은 곳은 양쪽이 다 작은 중간 지점이다.

마지막 줄의 분산 11.4가 이상해 보이지만 계산 오류가 아니다. 표본 40개에 11차 다항식을 맞추면 설계 행렬이 거의 특이해져서 계수가 수백 배로 튀고, 훈련점 사이에서 예측이 크게 요동친다. 훈련셋이 조금만 달라져도 그 요동의 위치와 크기가 통째로 바뀐다.

편향과 분산은 반대로 움직인다

모델을 복잡하게 만들면 편향은 줄고 분산은 는다. 이 둘의 합이 그리는 모양이 편향-분산 트레이드오프의 전부다.

모델 복잡도에 따른 오차 오차 σ² 최적 복잡도 과소적합 구간 과적합 구간 모델 복잡도 편향² 분산 총 오차

가로축의 “복잡도”는 다항식의 차수만 뜻하지 않는다. 트리의 최대 깊이, 신경망의 층 수와 폭, 규제 강도의 역수, KNN에서 kk 의 역수가 전부 같은 손잡이다. 손잡이의 이름만 다를 뿐 곡선의 모양은 같다.

한 가지 더 읽어야 할 것은 점선이다. 총 오차 곡선은 최저점에서도 σ2\sigma^2 선에 닿지 않는다. 편향 제곱과 분산이 동시에 0이 되는 복잡도는 없기 때문이다. 검증 오차가 어느 선 아래로 내려가지 않는다고 해서 모델이 잘못됐다는 뜻은 아니다. 데이터 자체가 정한 바닥일 수 있다.

학습 곡선이 둘을 가른다

지금까지의 분해는 진짜 함수 ff 를 안다는 전제 위에 있다. 실전에서는 ff 를 모르니 편향과 분산을 직접 잴 수 없다. 대신 훈련 데이터의 양을 늘려가며 훈련 오차와 검증 오차를 함께 그린다. 두 곡선의 높이와 간격이 어느 쪽 문제인지 알려준다.

위 · 높은 편향 오차 목표 수준 갭 작음 훈련 데이터 크기 아래 · 높은 분산 오차 목표 수준 갭 큼 훈련 데이터 크기 훈련 오차 검증 오차 목표 수준

위 패널은 두 곡선이 목표 수준보다 한참 높은 자리에서 만난다. 데이터를 더 넣어도 만나는 높이가 내려가지 않는다. 모델이 표현할 수 있는 함수의 집합 안에 정답이 없기 때문이고, 데이터는 그 집합을 넓혀주지 못한다. 앞의 차수 1 실험이 정확히 이 모양이다. 같은 방식으로 표본만 50개에서 5만 개로 천 배 늘리면 훈련 MSE의 평균은 0.273에서 0.286으로 오히려 조금 올라간다. 직선으로는 좁힐 수 없는 편향 0.196에 노이즈 분산 0.09를 더한 0.286이 이 모델의 바닥이라, 표본이 늘수록 그 바닥에 붙는 것이다.

아래 패널은 훈련 오차가 바닥에 붙어 있는데 검증 오차만 위에 남아 있다. 모델이 훈련셋 하나를 외운 상태다. 이때는 데이터를 늘리는 게 실제로 듣는다. 외워야 할 점이 많아질수록 노이즈를 통째로 기억하기 어려워지고 갭이 좁혀진다.

진단 신호 듣는 처방 헛수고
높은 편향 훈련 오차 자체가 높고 검증 오차와 거의 붙어 있다 복잡도 올리기, 특성 추가, 규제 완화 데이터 더 모으기
높은 분산 훈련 오차는 낮은데 검증 오차와 크게 벌어진다 규제, 데이터 추가, 앙상블, 특성 줄이기 모델 더 키우기

분산 쪽 처방에서 규제가 앞자리에 있는 이유는 복잡도를 깎지 않고도 듣기 때문이다. 위 decompose 에서 LinearRegression() 만 Ridge(alpha=1) 로 바꿔 차수 11을 다시 재보면, 분산이 11.4075에서 0.1097로 백 분의 일이 되고 편향 제곱도 0.0348에서 0.0015로 함께 내려간다. 차수는 11 그대로인데 계수 크기를 눌러 훈련점 사이의 요동만 걷어낸 결과다. 다만 총 오차는 0.2011에 머물러 차수 3의 0.1021에는 끝내 못 미친다. 규제는 과한 복잡도를 사후에 다듬는 도구이지 맞는 복잡도를 고르는 일을 대신해주지는 않는다.

검증 오차 하나만 보면 진단이 안 된다

검증 MSE 하나만 들고서는 편향 문제인지 분산 문제인지 가릴 수 없고, 두 문제의 처방은 서로 반대다. 훈련 오차를 같이 찍어보는 데는 코드 한 줄이면 충분한데 이걸 건너뛰고 하이퍼파라미터 탐색부터 돌리는 경우가 많다. cross_validate(..., return_train_score=True) 로 훈련 점수를 같이 받아두면 된다.

분산을 줄이는 또 하나의 손잡이

복잡도를 낮추는 것 말고도 분산을 줄이는 방법이 있다. 여러 모델의 예측을 평균내는 것이다. 분산이 σm2\sigma_m^2 인 모델 MM 개가 서로 완전히 독립이라면, 평균의 분산은 σm2/M\sigma_m^2 / M 로 줄어든다. 편향은 그대로다. 복잡도를 낮추지 않고 분산만 깎을 수 있다는 뜻이다.

현실에서는 모델들이 같은 데이터에서 나오니 완전히 독립일 수 없다. 예측들 사이의 상관계수를 ρ\rho 라 하면 평균의 분산은 이렇게 된다.

Var(1M∑m=1Mf^m)=ρ σm2+1−ρM σm2\text{Var}\left(\frac{1}{M}\sum_{m=1}^{M} \hat{f}_m\right) = \rho\,\sigma_m^2 + \frac{1 - \rho}{M}\,\sigma_m^2

MM 을 아무리 키워도 둘째 항만 0으로 가고 첫째 항 ρσm2\rho\sigma_m^2 는 남는다. 모델 수를 늘리는 것보다 모델들 사이의 상관을 낮추는 게 더 중요하다는 결론이 여기서 나온다. 랜덤 포레스트가 각 분기마다 특성을 무작위로 골라 트리들을 일부러 다르게 만드는 이유다.

흔한 오해

데이터를 더 모으면 웬만한 건 해결된다

분산 문제에만 해당한다. 편향은 모델이 표현할 수 있는 함수의 범위에서 오는 것이고, 데이터의 양은 그 범위를 넓히지 못한다. 앞의 직선 실험이 그 증거다. 데이터 수집은 대개 가장 비싸고 오래 걸리는 작업이니, 착수하기 전에 학습 곡선을 그려 좁힐 갭이 실제로 있는지부터 확인하는 편이 낫다.

규제는 세게 걸수록 안전하다

규제는 복잡도 손잡이를 왼쪽으로 미는 일이다. 너무 밀면 반대편 낭떠러지로 떨어진다. 차수 4 다항 회귀에 Ridge 강도만 바꿔가며 같은 방식으로 총 오차를 재보면 이렇다.

alpha 없음 0.1 1 100 10000
총 오차 0.1047 0.1067 0.1173 0.1444 0.3084

차수 4는 이 데이터에 이미 알맞은 크기라 규제를 걸 여유분이 없다. alpha를 올리는 족족 편향만 늘고, 10000에서는 총 오차가 0.3084로 차수 1 직선의 0.2609보다도 나빠진다. 계수를 끝까지 누르면 4차 다항식이 직선을 지나 거의 평평한 상수 쪽으로 끌려가기 때문이다. 규제 강도도 다른 하이퍼파라미터와 똑같이 검증으로 골라야 한다.

마치며

편향-분산 분해가 실전에서 하는 일은 하나다. “성능이 안 나온다”는 막연한 상태를 “편향 문제다” 또는 “분산 문제다”라는 결정 가능한 상태로 바꾼다. 그 뒤로는 할 일이 정해져 있다.

이 분해는 특정 알고리즘의 성질이 아니라 제곱 오차의 대수적 항등식이다. 선형 회귀든 트리든 신경망이든 복잡도 손잡이가 있는 한 같은 U자 곡선 위에 있고, 최적점 위치만 다르다. 새로운 모델을 배울 때마다 “이 모델의 복잡도 손잡이는 무엇인가”를 먼저 찾으면 튜닝의 절반은 끝난 셈이다.

한편 총 오차를 σ2\sigma^2 아래로 내리는 방법은 없다. 검증 점수를 더 짜내려고 씨름하기 전에, 지금 바닥이 모델의 한계인지 데이터의 한계인지 구분하는 게 먼저다.

함께 보면 좋은 글

참고자료

Thank You for Visiting My Blog, Have a Good Day 😆