6  학습의 실전: 학습률, 배치, 역전파, 일반화, 정형 데이터

학습 목표
  • 학습률(Learning Rate)이 학습에 미치는 영향을 실험적으로 확인할 수 있다
  • 배치 크기(Batch Size)에 따른 Full-batch, Mini-batch, SGD의 차이를 이해한다
  • 연쇄법칙(Chain Rule)으로 2층 신경망의 그래디언트를 직접 계산할 수 있다
  • 과적합(Overfitting)을 정의하고, 훈련/검증 데이터를 나누는 이유를 설명할 수 있다
  • 러닝커브(Learning Curve)를 읽고 과소적합/과적합을 진단할 수 있다
  • 연쇄법칙이 왜 “곱”인지 설명하고, 작은 합성 함수의 미분을 직접 구할 수 있다
  • 정형 데이터 전처리(결측치 처리, 원-핫 인코딩, 표준화)를 직접 계산할 수 있다
  • 손실과 성능 지표의 차이를 설명하고, 회귀·분류 지표를 직접 계산할 수 있다
  • 정형 데이터에서 딥러닝과 부스팅 계열 모형의 관계를 설명할 수 있다

6.1 들어가며: 학습 루프의 나머지 절반

Ch03에서 우리는 학습 루프의 뼈대를 완성했다: 순전파 → 손실 계산 → 역전파 → 파라미터 갱신.

그런데 세 가지 질문이 미해결로 남아 있다.

  1. 갱신 폭을 정하는 학습률과 한 번에 사용할 데이터 양인 배치 크기는 어떻게 정하는가?
  2. 역전파의 \(\partial\mathcal{L}/\partial w\) 수식은 어디서 오는가?
  3. 훈련 데이터의 손실이 충분히 작아졌다면, 그 모형은 정말 잘 배운 것인가?

이 장에서 이 세 질문에 차례로 답한다. 셋 모두 “이론”이 아니라, 딥러닝을 실제로 돌릴 때 매번 부딪히는 실전의 문제들이다. 그리고 장의 마지막에서는 여기까지 모은 도구 전부 — MLP, 손실, 경사하강, 과적합 관리 — 를 산업 현장에서 가장 흔한 정형 데이터에 처음으로 적용할 준비를 한다.


6.2 학습률 (Learning Rate)

6.2.1 학습률의 역할

학습률 \(\eta\)는 경사하강법에서 가장 중요한 하이퍼파라미터이다.

학습률 효과
너무 작으면 수렴이 매우 느림, 학습 시간 낭비
적절하면 안정적으로 최솟값에 도달
너무 크면 최솟값 주변을 진동하거나 발산

6.2.2 수치 비교

\(\mathcal{L}(\theta) = (\theta - 3)^2\)에서 다양한 학습률로 10스텝 실행:

코드
import pandas as pd

etas = [0.01, 0.05, 0.1, 0.3, 0.5, 0.9]
rows = []

for eta in etas:
    theta = 0.0
    for _ in range(10):
        grad = 2 * (theta - 3)
        theta = theta - eta * grad
    loss = (theta - 3) ** 2

    if loss < 0.01:
        status = '수렴'
    elif loss < 1.0:
        status = '수렴 중'
    elif loss > 100:
        status = '발산!'
    else:
        status = '느림'

    rows.append({
        '학습률 $\\eta$': eta,
        '10스텝 후 $\\theta$': f'{theta:.4f}',
        '10스텝 후 손실': f'{loss:.4f}' if loss < 1e6 else f'{loss:.2e}',
        '상태': status,
    })

pd.DataFrame(rows).to_html(index=False, border=0, escape=False)
표 6.1: 학습률에 따른 수렴 비교 (10 스텝 후)
학습률 \(\\eta\) 10스텝 후 \(\\theta\) 10스텝 후 손실 상태
0.01 0.5488 6.0085 느림
0.05 1.9540 1.0942 느림
0.10 2.6779 0.1038 수렴 중
0.30 2.9997 0.0000 수렴
0.50 3.0000 0.0000 수렴
0.90 2.6779 0.1038 수렴 중

Ch03의 1차원 경사하강법 인터랙티브에서 학습률 슬라이더를 0.9까지 올렸을 때 본 것과 같은 현상이다. 실무에서는 보통 0.1~0.0001 범위에서 시작해, 러닝커브(이 장 후반)를 보며 조정한다.


6.3 배치 크기 (Batch Size)

6.3.1 전체 배치 vs 확률적 경사하강법

실제 딥러닝에서는 수천~수백만 개의 데이터가 있다. 매번 전체 데이터로 그래디언트를 계산하면 비용이 매우 크다.

배치 크기에 따른 경사하강법 분류
방법 배치 크기 특징
Full-batch GD 전체 데이터 (\(n\)) 정확한 그래디언트, 느린 갱신
SGD 1개 매우 빠른 갱신, 높은 노이즈
Mini-batch SGD \(B\)개 (보통 32~256) 속도와 안정성의 균형
전체 데이터 (10개) — 1 에폭(Epoch) 동안의 분할 1 2 3 4 5 6 7 8 9 10 Full-batch 1~10 한꺼번에 → 갱신 1회/에폭 Mini-batch (B=5) 배치 1: 1~5 → 갱신 배치 2: 6~10 → 갱신 2회/에폭 SGD (B=1) 10회/에폭 1 2 3 10

6.3.2 수치 예시

10개 데이터에 대해 배치 크기별 1 에폭(epoch) 동안의 파라미터 갱신 횟수:

코드
import pandas as pd

n = 10
batches = [10, 5, 2, 1]

rows = []
for b in batches:
    updates = n // b
    method = "Full-batch GD" if b == n else "SGD" if b == 1 else f"Mini-batch (B={b})"
    rows.append({
        '방법': method,
        '배치 크기': b,
        '갱신 횟수': f'{n} / {b} = {updates}',
        '그래디언트 정확도': '높음' if b == n else '낮음' if b == 1 else '중간',
        '갱신 속도': '느림' if b == n else '빠름' if b == 1 else '중간',
    })

pd.DataFrame(rows).to_html(index=False, border=0, escape=False)
표 6.2: 배치 크기에 따른 1 에폭 갱신 횟수 (데이터 10개)
방법 배치 크기 갱신 횟수 그래디언트 정확도 갱신 속도
Full-batch GD 10 10 / 10 = 1 높음 느림
Mini-batch (B=5) 5 10 / 5 = 2 중간 중간
Mini-batch (B=2) 2 10 / 2 = 5 중간 중간
SGD 1 10 / 1 = 10 낮음 빠름

핵심: 배치 크기가 작을수록 갱신이 자주 일어나지만 그래디언트에 노이즈가 많고, 클수록 안정적이지만 느리다. 실무에서는 보통 32~256 크기의 미니배치를 사용한다.


인터랙티브 — 배치 크기에 따른 경사하강법 비교

같은 목표 함수에서 배치 크기에 따라 그래디언트 경로가 어떻게 달라지는지 확인하세요. 작은 배치는 노이즈가 많지만 빠르게 탐색하고, 큰 배치는 안정적이지만 느립니다. (배치 크기에 따른 그래디언트 노이즈는 이 시뮬레이션에서 인공적으로 재현한 것입니다 — 실제로는 배치에 어떤 데이터가 뽑히느냐에 따라 생깁니다.)

시도해 보세요: SGD(B=1)를 선택하고 시드를 바꿔보면, 매번 다른 경로로 수렴하는 것을 확인할 수 있습니다.


6.4 역전파: 그래디언트는 어디서 오는가

6.4.1 미뤄뒀던 질문

Ch03의 학습 루프 인터랙티브에서 우리는 이런 수식을 그냥 사용했다:

\[\frac{\partial \mathcal{L}}{\partial w} = -2(y - \hat{y}) \cdot x\]

이 수식은 어디서 왔는가? 답은 미분의 연쇄법칙(Chain Rule)이다.

연쇄법칙 (Chain Rule)

합성 함수의 미분은 중간 변수를 거치는 미분들의 곱이다.

\[\frac{\partial \mathcal{L}}{\partial w} = \frac{\partial \mathcal{L}}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial w}\]

신경망은 “입력 → 가중합 → 활성화 → … → 예측 → 손실”의 합성 함수이므로, 손실에서 출발하여 뒤에서 앞으로 미분을 곱해 나가면 모든 파라미터의 그래디언트를 얻는다. 이것이 역전파(Backpropagation)이다.

6.4.2 왜 하필 “곱”인가 — 톱니바퀴로 보기

연쇄법칙에서 가장 이해가 안 되는 부분은 왜 더하지 않고 곱하는가이다. 맞물린 톱니바퀴 두 개를 떠올리면 명확해진다.

x 입력 u 중간 L 손실 ×2 du/dx = 2 ×6 dL/du = 6 x가 1 움직이면 L은 2 × 6 = 12 움직인다
  • \(x\)1 만큼 움직이면 \(u\)2 만큼 움직인다
  • \(u\)1 만큼 움직이면 \(\mathcal{L}\)6 만큼 움직인다
  • 그렇다면 \(x\)가 1 움직일 때 \(\mathcal{L}\)은? → \(2 \times 6 = \mathbf{12}\)

변화가 단계를 거치며 전달될 때는 배율이 곱해진다. 이것이 연쇄법칙의 전부다.

6.4.3 ✍️ 손으로 따라 계산하기 ① — 가장 작은 연쇄

\[u = 2x + 1, \qquad \mathcal{L} = u^2\]

\(x = 1\) 인 지점에서 \(\dfrac{d\mathcal{L}}{dx}\) 를 구해 보자.

단계 1 — 순전파로 중간값을 구한다.

\[u = 2(1) + 1 = 3, \qquad \mathcal{L} = 3^2 = 9\]

단계 2 — 화살표마다 미분을 하나씩 구한다. (Ch03의 규칙 ②만 쓰면 된다)

\[\frac{d\mathcal{L}}{du} = 2u = 2(3) = 6, \qquad \frac{du}{dx} = 2\]

단계 3 — 곱한다.

\[\frac{d\mathcal{L}}{dx} = \frac{d\mathcal{L}}{du} \cdot \frac{du}{dx} = 6 \times 2 = \mathbf{12}\]

단계 4 — 숫자로 검산. \(x\)\(0.001\) 늘려 본다.

\[u = 2(1.001)+1 = 3.002, \quad \mathcal{L} = 3.002^2 = 9.012004\]

\[\frac{9.012004 - 9}{0.001} = 12.004 \;\approx\; 12 \quad ✅\]

주목: 단계 2에서 \(\dfrac{d\mathcal{L}}{du} = 2u\) 를 계산할 때 순전파에서 구해 둔 \(u = 3\) 을 그대로 썼다. 역전파가 순전파의 중간값을 필요로 하는 이유가 이것이다.

6.4.4 ✍️ 손으로 따라 계산하기 ② — Ch03에서 미뤄둔 공식의 정체

Ch03에서 우리는 이 공식을 유도 없이 받아서 썼다.

\[\frac{\partial \mathcal{L}}{\partial w} = -2(y - \hat{y})\,x\]

이제 이것을 직접 만들어 보자. 모형과 손실은 다음과 같았다.

\[\hat{y} = wx + b, \qquad \mathcal{L} = (y - \hat{y})^2\]

단계 1 — 경로를 그린다. \(w\)\(\mathcal{L}\)에 도달하려면 \(\hat{y}\)를 거쳐야 한다.

\[w \;\longrightarrow\; \hat{y} \;\longrightarrow\; \mathcal{L}\]

화살표가 2개 → 미분도 2개를 구해서 곱한다.

단계 2 — 첫 화살표: \(\hat{y} \to \mathcal{L}\).

\(\mathcal{L} = (y - \hat{y})^2\)\(\hat{y}\) 로 미분한다. 괄호 안이 \(\hat{y}\)의 함수이므로 여기도 작은 연쇄다.

\[\frac{\partial \mathcal{L}}{\partial \hat{y}} = \underbrace{2(y - \hat{y})}_{\text{겉: } (\ )^2 \text{ 미분}} \times \underbrace{(-1)}_{\text{속: } (y-\hat{y}) \text{를 } \hat{y}\text{로 미분}} = -2(y - \hat{y})\]

\((y - \hat{y})\)\(\hat{y}\) 로 미분하면 왜 \(-1\) 인가? \(y\)데이터가 준 상수라 미분하면 0이고, \(-\hat{y}\) 를 미분하면 \(-1\) 이다. → \(0 - 1 = -1\). 부호가 붙는 이유가 여기다.

단계 3 — 둘째 화살표: \(w \to \hat{y}\).

\(\hat{y} = wx + b\)\(w\) 로 편미분한다. \(x\)\(b\)는 상수로 붙잡는다.

\[\frac{\partial \hat{y}}{\partial w} = x\]

단계 4 — 곱한다.

\[\frac{\partial \mathcal{L}}{\partial w} = \frac{\partial \mathcal{L}}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial w} = \big[-2(y - \hat{y})\big] \times x = \mathbf{-2(y - \hat{y})\,x} \quad ✅\]

Ch03에서 그냥 받아 썼던 공식이 나왔다. 편향도 같은 방법이다 — \(\dfrac{\partial \hat{y}}{\partial b} = 1\) 이므로 \(\dfrac{\partial \mathcal{L}}{\partial b} = -2(y-\hat{y})\).

\(u = 3x - 2\), \(\mathcal{L} = u^2\) 일 때, \(x = 2\) 에서 \(\dfrac{d\mathcal{L}}{dx}\) 를 구하라.

정답 펼치기

① 순전파: \(u = 3(2) - 2 = 4\),   \(\mathcal{L} = 16\)

② 화살표별 미분: \(\dfrac{d\mathcal{L}}{du} = 2u = 8\),   \(\dfrac{du}{dx} = 3\)

③ 곱: \(8 \times 3 = \mathbf{24}\)

④ 검산: \(x = 2.001 \to u = 4.003 \to \mathcal{L} = 16.024009\),   \((16.024009 - 16)/0.001 = 24.009 \approx 24\)

이제 화살표가 더 많은 진짜 신경망으로 간다. 새로운 아이디어는 없다 — 곱하는 항이 늘어날 뿐이다.

6.4.5 2층 네트워크 손계산

은닉 노드 1개짜리 가장 작은 2층 네트워크로 역전파 전체를 손으로 따라가 보자.

\[x \;\xrightarrow{w_1,\, b_1}\; z_1 = w_1 x + b_1 \;\xrightarrow{\sigma}\; h = \sigma(z_1) \;\xrightarrow{w_2,\, b_2}\; \hat{y} = w_2 h + b_2 \;\longrightarrow\; \mathcal{L} = (y - \hat{y})^2\]

설정: \(x = 1.0\), 정답 \(y = 1.0\), 파라미터 \(w_1 = 0.5\), \(b_1 = 0\), \(w_2 = 1.0\), \(b_2 = 0\). 은닉층 활성화는 Sigmoid.

계산의 지도를 먼저 그려두자. 위쪽 화살표가 순전파(값의 흐름), 아래쪽 점선 화살표가 역전파(미분의 흐름)이다.

순전파 → (값을 계산) x z₁ h ŷ L × w₁ + b₁ σ( · ) × w₂ + b₂ (y − ·)² ∂L/∂ŷ = −2(y−ŷ) ∂ŷ/∂h = w₂ ∂h/∂z₁ = σ′(z₁) ∂z₁/∂w₁ = x ← 역전파 (화살표의 미분을 곱하며 되돌아감) w₁, b₁이 들어오는 곳 w₂, b₂가 들어오는 곳

1단계 — 순전파 (왼쪽에서 오른쪽으로):

코드
import pandas as pd
import numpy as np

x, y = 1.0, 1.0
w1, b1, w2, b2 = 0.5, 0.0, 1.0, 0.0

z1 = w1 * x + b1
h = 1 / (1 + np.exp(-z1))
y_hat = w2 * h + b2
L = (y - y_hat) ** 2

pd.DataFrame([
    {'단계': '가중합', '수식': '$z_1 = w_1 x + b_1 = 0.5 \\times 1 + 0$', '값': f'{z1:.4f}'},
    {'단계': '활성화', '수식': '$h = \\sigma(z_1) = \\sigma(0.5)$', '값': f'{h:.4f}'},
    {'단계': '출력', '수식': '$\\hat{y} = w_2 h + b_2 = 1 \\times 0.6225 + 0$', '값': f'{y_hat:.4f}'},
    {'단계': '손실', '수식': '$\\mathcal{L} = (y - \\hat{y})^2 = (1 - 0.6225)^2$', '값': f'{L:.4f}'},
]).to_html(index=False, border=0, escape=False)
표 6.3: 순전파 계산
단계 수식
가중합 \(z_1 = w_1 x + b_1 = 0.5 \\times 1 + 0\) 0.5000
활성화 \(h = \\sigma(z_1) = \\sigma(0.5)\) 0.6225
출력 \(\\hat{y} = w_2 h + b_2 = 1 \\times 0.6225 + 0\) 0.6225
손실 \(\\mathcal{L} = (y - \\hat{y})^2 = (1 - 0.6225)^2\) 0.1425

2단계 — 각 화살표의 미분(국소 미분) 준비. 역전파를 시작하기 전에, 그래프의 화살표 하나하나가 갖는 미분을 먼저 구해 둔다. 각각은 한 줄짜리 미분이다:

화살표 함수 미분 왜 이렇게 되는가
\(\hat{y} \to \mathcal{L}\) \(\mathcal{L}=(y-\hat{y})^2\) \(\dfrac{\partial\mathcal{L}}{\partial\hat{y}} = -2(y-\hat{y})\) 바깥 미분 \(2(y-\hat{y})\) × 안쪽 미분 \((-1)\)\(\hat{y}\) 앞에 음수 부호가 있으므로
\(w_2 \to \hat{y}\) \(\hat{y}=w_2 h + b_2\) \(\dfrac{\partial\hat{y}}{\partial w_2} = h\) \(w_2\)에 곱해져 있는 것이 \(h\) (\(b_2\)는 상수라 사라짐)
\(b_2 \to \hat{y}\) \(1\) \(b_2\)의 계수는 1
\(h \to \hat{y}\) \(w_2\) \(h\)에 곱해져 있는 것이 \(w_2\)
\(z_1 \to h\) \(h=\sigma(z_1)\) \(\sigma'(z_1) = h(1-h)\) 아래 공식 참고
\(w_1 \to z_1\) \(z_1=w_1 x + b_1\) \(x\) \(w_1\)의 계수는 \(x\)
\(b_1 \to z_1\) \(1\) \(b_1\)의 계수는 1
Sigmoid의 도함수 — 공식으로 기억하자

\[\sigma'(z) = \sigma(z)\,(1 - \sigma(z))\]

유도는 이 수업 범위 밖이다(합성함수 미분 연습문제일 뿐이다). 중요한 것은 실용적 성질이다: 순전파에서 이미 계산해 둔 \(h = \sigma(z_1)\)만 있으면 도함수가 \(h(1-h)\)바로 나온다. Ch02의 활성화 함수 그래프에서 봤던 점선(도함수)이 바로 이 값이다.

3단계 — 역전파 (오른쪽에서 왼쪽으로). 이제 남은 일은 곱하기뿐이다. 손실에서 출발해, 목적지까지 지나가는 화살표들의 국소 미분을 차례로 곱한다:

코드
import pandas as pd

dL_dyhat = -2 * (y - y_hat)          # ∂L/∂ŷ
dL_dw2 = dL_dyhat * h                # ∂L/∂w₂ = ∂L/∂ŷ · ∂ŷ/∂w₂,  ∂ŷ/∂w₂ = h
dL_db2 = dL_dyhat * 1                # ∂ŷ/∂b₂ = 1
dL_dh = dL_dyhat * w2                # ∂ŷ/∂h = w₂
sigma_prime = h * (1 - h)            # σ'(z₁) = σ(z₁)(1−σ(z₁))
dL_dz1 = dL_dh * sigma_prime
dL_dw1 = dL_dz1 * x                  # ∂z₁/∂w₁ = x
dL_db1 = dL_dz1 * 1

pd.DataFrame([
    {'대상': '$\\partial\\mathcal{L}/\\partial\\hat{y}$', '연쇄법칙 전개': '$-2(y - \\hat{y}) = -2(1 - 0.6225)$', '값': f'{dL_dyhat:.4f}'},
    {'대상': '$\\partial\\mathcal{L}/\\partial w_2$', '연쇄법칙 전개': '$\\partial\\mathcal{L}/\\partial\\hat{y} \\cdot h = -0.7551 \\times 0.6225$', '값': f'{dL_dw2:.4f}'},
    {'대상': '$\\partial\\mathcal{L}/\\partial b_2$', '연쇄법칙 전개': '$\\partial\\mathcal{L}/\\partial\\hat{y} \\cdot 1$', '값': f'{dL_db2:.4f}'},
    {'대상': '$\\partial\\mathcal{L}/\\partial h$', '연쇄법칙 전개': '$\\partial\\mathcal{L}/\\partial\\hat{y} \\cdot w_2 = -0.7551 \\times 1$', '값': f'{dL_dh:.4f}'},
    {'대상': "$\\sigma'(z_1)$", '연쇄법칙 전개': '$h(1-h) = 0.6225 \\times 0.3775$', '값': f'{sigma_prime:.4f}'},
    {'대상': '$\\partial\\mathcal{L}/\\partial w_1$', '연쇄법칙 전개': "$\\partial\\mathcal{L}/\\partial h \\cdot \\sigma'(z_1) \\cdot x = -0.7551 \\times 0.2350 \\times 1$", '값': f'{dL_dw1:.4f}'},
    {'대상': '$\\partial\\mathcal{L}/\\partial b_1$', '연쇄법칙 전개': "$\\partial\\mathcal{L}/\\partial h \\cdot \\sigma'(z_1) \\cdot 1$", '값': f'{dL_db1:.4f}'},
]).to_html(index=False, border=0, escape=False)
표 6.4: 역전파 계산 — 2단계의 국소 미분들을 뒤에서 앞으로 곱하기
대상 연쇄법칙 전개
\(\\partial\\mathcal{L}/\\partial\\hat{y}\) \(-2(y - \\hat{y}) = -2(1 - 0.6225)\) -0.7551
\(\\partial\\mathcal{L}/\\partial w_2\) \(\\partial\\mathcal{L}/\\partial\\hat{y} \\cdot h = -0.7551 \\times 0.6225\) -0.4700
\(\\partial\\mathcal{L}/\\partial b_2\) \(\\partial\\mathcal{L}/\\partial\\hat{y} \\cdot 1\) -0.7551
\(\\partial\\mathcal{L}/\\partial h\) \(\\partial\\mathcal{L}/\\partial\\hat{y} \\cdot w_2 = -0.7551 \\times 1\) -0.7551
\(\\sigma\'(z_1)\) \(h(1-h) = 0.6225 \\times 0.3775\) 0.2350
\(\\partial\\mathcal{L}/\\partial w_1\) \(\\partial\\mathcal{L}/\\partial h \\cdot \\sigma\'(z_1) \\cdot x = -0.7551 \\times 0.2350 \\times 1\) -0.1774
\(\\partial\\mathcal{L}/\\partial b_1\) \(\\partial\\mathcal{L}/\\partial h \\cdot \\sigma\'(z_1) \\cdot 1\) -0.1774

주목할 점: \(w_1\)의 그래디언트는 손실 → 출력 → 은닉 → 가중합을 거쳐온 미분들의 곱이다. 깊은 층의 파라미터일수록 곱해지는 항이 늘어난다.

4단계 — 갱신과 검증. 학습률 \(\eta = 0.5\)로 네 파라미터를 갱신하고, 손실이 실제로 줄었는지 확인한다:

코드
import pandas as pd
import numpy as np

eta = 0.5
w1_new = w1 - eta * dL_dw1
b1_new = b1 - eta * dL_db1
w2_new = w2 - eta * dL_dw2
b2_new = b2 - eta * dL_db2

# 갱신된 파라미터로 다시 순전파
z1_new = w1_new * x + b1_new
h_new = 1 / (1 + np.exp(-z1_new))
y_hat_new = w2_new * h_new + b2_new
L_new = (y - y_hat_new) ** 2

pd.DataFrame([
    {'항목': '$w_1$', '갱신 전': f'{w1:.4f}', '갱신식': f'$0.5 - 0.5 \\times ({dL_dw1:.4f})$', '갱신 후': f'{w1_new:.4f}'},
    {'항목': '$b_1$', '갱신 전': f'{b1:.4f}', '갱신식': f'$0 - 0.5 \\times ({dL_db1:.4f})$', '갱신 후': f'{b1_new:.4f}'},
    {'항목': '$w_2$', '갱신 전': f'{w2:.4f}', '갱신식': f'$1 - 0.5 \\times ({dL_dw2:.4f})$', '갱신 후': f'{w2_new:.4f}'},
    {'항목': '$b_2$', '갱신 전': f'{b2:.4f}', '갱신식': f'$0 - 0.5 \\times ({dL_db2:.4f})$', '갱신 후': f'{b2_new:.4f}'},
    {'항목': '**손실 $\\mathcal{L}$**', '갱신 전': f'**{L:.4f}**', '갱신식': '(갱신된 파라미터로 재계산)', '갱신 후': f'**{L_new:.4f}**'},
]).to_html(index=False, border=0, escape=False)
표 6.5: 파라미터 갱신 후 손실 변화
항목 갱신 전 갱신식 갱신 후
\(w_1\) 0.5000 \(0.5 - 0.5 \\times (-0.1774)\) 0.5887
\(b_1\) 0.0000 \(0 - 0.5 \\times (-0.1774)\) 0.0887
\(w_2\) 1.0000 \(1 - 0.5 \\times (-0.4700)\) 1.2350
\(b_2\) 0.0000 \(0 - 0.5 \\times (-0.7551)\) 0.3775
손실 \(\\mathcal{L}\) 0.1425 (갱신된 파라미터로 재계산) 0.0386

역전파 한 번으로 손실이 감소했다. 이것이 딥러닝 학습의 한 스텝 전부이며, 실제 프레임워크(PyTorch 등)는 이 계산을 자동미분으로 대신해줄 뿐 원리는 동일하다. 이 수업에서 손으로 하는 역전파는 여기까지다 — 층이 많아지면 곱하는 항이 늘어날 뿐, 새로운 아이디어는 없다.

6.4.6 기울기 소실 — Ch02의 복선 회수

위 계산에서 \(\sigma'(z_1) = 0.2350\)이었다. Sigmoid의 도함수는 최대값이 0.25이다.

층이 10개라면? 그래디언트에 \(\sigma'\)이 10번 곱해지고, 최선의 경우에도 \(0.25^{10} \approx 0.0000010\). 앞쪽 층의 그래디언트가 사실상 0이 되어 학습이 멈춘다. 이것이 기울기 소실(Vanishing Gradient) 문제다.

Ch02에서 “ReLU가 현재 가장 많이 쓰인다”고 했던 이유가 바로 이것이다 — ReLU는 \(z > 0\)인 구간에서 도함수가 항상 1이라, 곱해도 그래디언트가 줄어들지 않는다.


6.5 일반화와 과적합

6.5.1 훈련 손실 0 = 좋은 모형?

여기 함정이 있다. 학습의 목표가 “훈련 데이터의 손실 최소화”라면, 훈련 데이터를 통째로 암기하는 모형이 최고가 된다. 손실이 0이니까.

하지만 우리가 모형을 만드는 이유는 본 적 없는 새 데이터를 예측하기 위해서다. 시험 족보를 암기한 학생과 원리를 이해한 학생의 차이 — 새로운 문제가 나왔을 때 드러난다.

일반화 (Generalization)

학습에 사용하지 않은 새로운 데이터에 대해서도 좋은 예측을 하는 능력. 딥러닝의 진짜 목표는 훈련 손실 최소화가 아니라 일반화다.

과적합과 과소적합
  • 과적합 (Overfitting): 모형이 훈련 데이터의 노이즈까지 암기하여, 훈련 성능은 좋지만 새 데이터에서 나쁜 상태
  • 과소적합 (Underfitting): 모형이 너무 단순하여 훈련 데이터의 패턴조차 담지 못한 상태

6.5.2 훈련/검증 데이터 분할

일반화 능력은 어떻게 측정하는가? 학습에 쓰지 않은 데이터를 남겨두고 그것으로 평가한다.

데이터 용도 비유
훈련 (Train) 파라미터 학습 교과서 연습문제
검증 (Validation) 하이퍼파라미터 선택, 과적합 감시 모의고사
테스트 (Test) 최종 성능 보고 (한 번만) 수능

핵심 규칙: 검증·테스트 데이터는 학습에 절대 사용하지 않는다. 모의고사 문제로 공부하면 모의고사 점수는 의미를 잃는다.

6.5.2.1 얼마나 나누는가

데이터 크기 관례
작음 (수백 ~ 수천 행) 훈련 60% / 검증 20% / 테스트 20%
보통 (수만 행) 훈련 80% / 검증 10% / 테스트 10%
매우 큼 (수십만 행 이상) 훈련 98% / 검증 1% / 테스트 1%

데이터가 많을수록 검증·테스트의 비율은 줄여도 된다. 중요한 것은 비율이 아니라 개수이기 때문이다 — 10,000건이면 성능을 충분히 안정적으로 잴 수 있다.

분할은 가장 먼저 한다

전처리(표준화, 결측치 대치)보다 분할이 먼저다. 순서가 바뀌면 검증 데이터의 정보가 훈련 과정에 새어 들어간다.

데이터 누수 (Data Leakage)

훈련 시점에 알 수 없어야 할 정보가 모형에 흘러 들어가는 것. 검증 성능은 좋아 보이지만 실제 배포에서 무너진다.

전체 데이터로 평균을 구해 표준화한 뒤 나누면, 그 평균에는 이미 검증 데이터의 정보가 섞여 있다.

6.5.2.2 나눌 때의 두 가지 주의

  • 층화 추출 (Stratified Split) — 분류 문제에서는 각 분할이 원본과 같은 클래스 비율을 갖도록 나눈다. 불량률 3%인 데이터를 무작정 나누면, 검증 세트에 불량이 하나도 안 들어갈 수 있다.
  • 시계열 데이터는 무작위로 나누지 않는다 — 반드시 과거로 학습하고 미래로 검증한다. 시간을 섞어 나누는 것은 내일 신문을 보고 오늘을 맞히는 것과 같다.

인터랙티브 — 모형 복잡도와 과적합

다항식의 차수를 조절해 보세요. 차수가 낮으면 훈련 데이터조차 못 맞추고(과소적합), 차수가 높으면 훈련 점들은 완벽히 통과하지만 검증 오차가 폭발합니다(과적합).

검증 오차 곡선이 U자 모양인 것에 주목하자. 복잡도를 올리면 처음엔 둘 다 좋아지다가, 어느 지점부터 훈련 오차만 내려가고 검증 오차는 되올라간다. 그 갈림길이 과적합의 시작점이다.

6.5.3 러닝커브 읽기

모형 복잡도뿐 아니라 학습 시간(에폭)에 대해서도 같은 현상이 나타난다. 에폭에 따른 훈련/검증 손실 그래프를 러닝커브(Learning Curve)라 하며, 딥러닝 실무에서 가장 자주 보는 그림이다.

러닝커브 진단표:

훈련 손실 검증 손실 진단 처방
높음 높음 과소적합 모형 키우기, 더 오래 학습
낮음 높음 (벌어짐) 과적합 아래 대응책
낮음 낮음 잘 학습됨 그대로 종료

6.5.4 과적합 대응책

대응책 원리
데이터 늘리기 가장 확실한 방법 — 암기할 수 없을 만큼 다양한 예시 제공
조기 종료 (Early Stopping) 검증 손실이 되올라가기 시작하면 학습 중단
모형 줄이기 층·노드 수를 줄여 암기할 용량 자체를 제한
드롭아웃 (Dropout) 학습 중 무작위로 일부 노드를 꺼서, 특정 노드에 대한 의존(암기)을 방해
가중치 감쇠 (L2 정규화) 손실에 가중치 제곱합을 더해, 가중치가 커지는 것 자체에 벌칙 — 모형을 “부드럽게” 만든다

데이터를 늘리는 것이 왜 1순위인지는 팀 프로젝트에서 직접 체험하게 된다 — 모형 코드를 바꾸는 것보다 데이터를 다양하게 수집하는 것이 성능에 더 크게 기여하는 경우가 많다.


6.6 정형 데이터 실전

지금까지 배운 도구(MLP + 손실 + 경사하강 + 과적합 관리)로 실제 문제를 풀 준비가 되었다. 첫 상대는 산업 현장에서 가장 흔한 정형 데이터(Tabular Data) — 행과 열로 된 테이블이다.

6.6.1 신경망에 넣기 전: 전처리

신경망의 입력은 적절한 스케일의 숫자여야 한다. 정형 데이터는 그렇지 않은 경우가 많다.

문제 1 — 범주형 변수: “브랜드 = 삼성”을 어떻게 숫자로 넣는가? 삼성=1, 애플=2, LG=3으로 넣으면 “LG는 삼성의 3배”라는 거짓 정보가 생긴다 (Ch01의 범주형 변수 성질).

원-핫 인코딩 (One-Hot Encoding)

범주형 변수를 범주 수만큼의 0/1 열로 변환한다. 각 데이터는 자기 범주 열만 1이다.

코드
import pandas as pd

pd.DataFrame({
    '원래 값': ['삼성', '애플', 'LG', '애플'],
    '브랜드_삼성': [1, 0, 0, 0],
    '브랜드_애플': [0, 1, 0, 1],
    '브랜드_LG': [0, 0, 1, 0],
}).to_html(index=False, border=0)
표 6.6: 원-핫 인코딩 예시 — 브랜드
원래 값 브랜드_삼성 브랜드_애플 브랜드_LG
삼성 1 0 0
애플 0 1 0
LG 0 0 1
애플 0 1 0

원-핫 인코딩은 입력층의 크기를 바꾼다: “브랜드” 변수 하나가 입력 노드 3개가 된다. Ch02의 파라미터 개수 공식(\(n_{l-1} \times n_l + n_l\))에 그대로 반영되므로, 범주가 수백 개인 변수는 파라미터를 수백 배로 늘린다는 점도 기억해 두자.

문제 2 — 스케일 차이: 나이(20~60)와 연봉(30,000~130,000)이 같은 네트워크에 들어가면, 연봉 쪽 가중치의 그래디언트가 압도적으로 커져 학습이 불안정해진다.

표준화 (Standardization)

각 변수를 평균 0, 표준편차 1로 변환한다.

\[z = \frac{x - \mu}{s} \qquad (\mu: \text{평균},\ s: \text{표준편차})\]

표준편차는 편차 제곱합을 \(n\) 으로 나눈 뒤 제곱근을 취한다 (\(n-1\) 이 아니다).

\[s = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(x_i - \mu)^2}\]

\(n\) 인가 \(n-1\) 인가 — 시험에서 갈리는 지점

통계 과목에서는 표본표준편차(\(n-1\)로 나눔)를 주로 배운다. 하지만 머신러닝의 표준화는 \(n\) 을 쓴다 (numpy.std, scikit-learn의 StandardScaler 모두 \(n\)).

예시 데이터 \([24, 30, 36, 42, 48]\) 에서 \(n\)이면 \(s = 8.485\), \(n-1\)이면 \(s = 9.487\) 로 답이 달라진다. 이 수업의 교재·도장·시험은 모두 \(n\) 으로 통일한다.

코드
import pandas as pd
import numpy as np

ages = [24, 30, 36, 42, 48]
mu = np.mean(ages)          # 36
s = np.std(ages)            # √72 ≈ 8.485

rows = []
for a in ages:
    rows.append({
        '나이 $x$': a,
        '$x - \\mu$': f'{a - mu:.0f}',
        '$z = (x-\\mu)/s$': f'{(a - mu) / s:.3f}',
    })
rows.append({'나이 $x$': f'$\\mu = {mu:.0f}$, $s = {s:.3f}$', '$x - \\mu$': '', '$z = (x-\\mu)/s$': '평균 0, 표준편차 1'})

pd.DataFrame(rows).to_html(index=False, border=0, escape=False)
표 6.7: 표준화 손계산 — 나이 [24, 30, 36, 42, 48]
나이 \(x\) \(x - \\mu\) \(z = (x-\\mu)/s\)
24 -12 -1.414
30 -6 -0.707
36 0 0.000
42 6 0.707
48 12 1.414
\(\\mu = 36\), \(s = 8.485\) 평균 0, 표준편차 1

주의: 평균과 표준편차는 훈련 데이터에서만 계산하고, 검증·테스트에는 그 값을 그대로 적용한다. 검증 데이터를 섞어 계산하면 “모의고사 문제로 공부”하는 것과 같다 (데이터 누수).

Min-Max 정규화 (Normalization) — 표준화의 사촌

값을 \([0, 1]\) 범위로 눌러 담는 방법.

\[x' = \frac{x - x_{\min}}{x_{\max} - x_{\min}}\]

표준화 \((x-\mu)/s\) Min-Max 정규화
결과 평균 0, 표준편차 1 (범위 제한 없음) 항상 \([0, 1]\)
이상치에 비교적 강함 매우 약함 (최댓값 하나가 전체를 눌러버림)
쓰는 곳 정형 데이터 대부분 값의 범위가 이미 정해진 경우

Ch05에서 이미지 픽셀값 \(0 \sim 255\)\(255\)로 나누어 \(0 \sim 1\) 로 만드는 것이 바로 이 Min-Max 정규화다 (최솟값 0, 최댓값 255가 이미 정해져 있으므로 나누기만 하면 된다).

문제 3 — 결측치: 실제로 수집한 데이터에는 빈칸이 있다. 설문에 답을 안 했을 수도, 센서가 잠깐 꺼졌을 수도 있다. 신경망은 빈칸을 계산할 수 없다 — NaN이 한 번 들어가면 손실 전체가 NaN이 되어 학습이 통째로 죽는다.

결측치 (Missing Value)

기록되지 않았거나 누락된 값. 판다스에서는 NaN으로 표시된다. 학습 전에 반드시 처리해야 한다.

방법 언제 쓰는가 주의할 점
행 삭제 결측이 아주 적을 때 (5% 미만) 데이터가 줄어든다. 결측이 무작위가 아니면 편향이 생긴다
평균/중앙값 대치 숫자형, 결측이 흩어져 있을 때 이상치가 있으면 중앙값이 안전
최빈값 대치 범주형
‘결측’ 범주 추가 범주형이고, 빈칸 자체가 정보일 때 원-핫 열이 하나 늘어난다
결측 표시 열 추가 빈칸 여부가 신호일 때 소득_결측 같은 0/1 열을 하나 더 만든다

대치에 쓰는 평균·중앙값·최빈값도 훈련 데이터에서만 계산한다. 표준화와 완전히 같은 이유다 — 그러지 않으면 데이터 누수다.

이상치(Outlier)도 함께 본다. 나이가 999인 행, 가동시간이 음수인 행은 결측보다 위험하다 — 오류인 줄 모르고 학습되어 평균과 표준편차를 통째로 망가뜨린다. 직접 수집한 데이터라면 학습 전에 각 열의 최솟값·최댓값을 눈으로 확인하는 습관을 들이자.


6.7 손실과 성능 지표는 다르다

학습 중에는 손실을 본다. 하지만 “이 모형 어때요?”라는 질문에 “손실이 0.34입니다” 라고 답하면 아무도 이해하지 못한다. 보고할 때는 사람이 알아들을 수 있는 숫자가 필요하다.

성능 지표 (Metric)

모형의 성능을 사람이 해석할 수 있는 단위로 보고하는 값.

손실 (Loss) 성능 지표 (Metric)
쓰는 곳 학습 — 그래디언트를 계산해 파라미터를 갱신 보고·비교 — 모형을 고르고 설명
조건 미분 가능해야 한다 미분 가능할 필요 없다
MSE, 교차 엔트로피 정확도, MAE, \(R^2\)

정확도는 계단처럼 뚝뚝 끊겨서 미분이 안 되므로 손실로는 쓸 수 없다. 그래서 학습은 교차 엔트로피로 하고, 보고는 정확도로 한다 — 둘은 다른 일을 한다.

6.7.1 회귀의 지표

지표 어떻게 읽는가
MSE \(\frac{1}{n}\sum (y_i - \hat{y}_i)^2\) 단위가 제곱이라 해석이 어렵다 (손실로는 최적)
RMSE \(\sqrt{\text{MSE}}\) 원래 단위로 돌아온다 — “평균 3.2만원 틀린다”
MAE \(\frac{1}{n}\sum \lvert y_i - \hat{y}_i \rvert\) 원래 단위. 이상치에 덜 민감
\(R^2\) \(1 - \dfrac{\sum (y_i-\hat{y}_i)^2}{\sum (y_i-\bar{y})^2}\) 평균으로 찍는 것보다 얼마나 나은가. 1이면 완벽, 0이면 평균과 동급, 음수면 평균만도 못함

6.7.2 ✍️ 손으로 따라 계산하기 — 회귀 지표 세 개

실제값 \(y = [10, 12, 14, 16]\), 예측값 \(\hat{y} = [11, 12, 12, 19]\) 이다.

단계 1 — 오차를 먼저 한 줄로 쓴다.

\[y - \hat{y} = [-1,\; 0,\; 2,\; -3]\]

단계 2 — MSE와 RMSE.

\[\text{MSE} = \frac{1 + 0 + 4 + 9}{4} = \frac{14}{4} = 3.5, \qquad \text{RMSE} = \sqrt{3.5} = 1.871\]

단계 3 — MAE (절댓값이므로 부호를 떼고 더한다).

\[\text{MAE} = \frac{1 + 0 + 2 + 3}{4} = 1.5\]

단계 4 — \(R^2\). 먼저 \(y\)의 평균 \(\bar{y} = \dfrac{10+12+14+16}{4} = 13\).

\[\sum (y_i - \bar{y})^2 = 9 + 1 + 1 + 9 = 20\]

\[R^2 = 1 - \frac{14}{20} = 1 - 0.7 = 0.3\]

해석: 평균으로만 찍는 것보다 30% 나은 정도다. 좋은 모형이 아니다. MAE 1.5는 “평균 1.5쯤 틀린다”로 바로 읽히지만, MSE 3.5는 단위가 제곱이라 그렇게 읽히지 않는다 — 보고에 RMSE와 MAE를 쓰는 이유다.

6.7.3 분류의 지표

정확도 (Accuracy)

\[\text{정확도} = \frac{\text{맞힌 개수}}{\text{전체 개수}}\]

가장 직관적이지만, 한 가지 경우에 완전히 무너진다.

정확도의 함정 — 클래스 불균형

불량률이 3%인 공정에서 검사 모형을 만들었다. 이 모형이 모든 제품을 “정상”이라고만 답한다면 정확도는?

\[\frac{97}{100} = 97\%\]

불량을 하나도 못 잡았는데 정확도 97%다. 클래스 비율이 치우친 데이터에서 정확도만 보고하는 것은 아무 말도 하지 않는 것과 같다.

이런 경우에는 클래스별로 나누어 봐야 한다 — 불량인 것 중 몇 개를 잡았는지, 잡았다고 한 것 중 몇 개가 진짜였는지. 자체 수집 데이터는 대개 불균형하므로, 항상 클래스별 개수를 먼저 세어 보라.

팀 프로젝트의 객체탐지에서도 같은 문제가 반복된다. Ch09에서 탐지 전용 지표(IoU, mAP)를 다룰 때 이 이야기를 다시 만난다.


6.7.4 정직한 이야기: 정형에서 딥러닝은 최강이 아니다

여기서 이 수업이 정직해야 할 지점이 있다. 중소규모 정형 데이터에서는 그래디언트 부스팅 계열(XGBoost, LightGBM 등 트리 앙상블)이 딥러닝을 이기는 경우가 많다는 것이 실무와 벤치마크의 일관된 결과다.

MLP (딥러닝) 그래디언트 부스팅
중소규모 정형 (수천~수십만 행) 보통 강함
초대규모 데이터 강함 보통
이미지·텍스트·음성 압도적 부적합
전처리 민감도 높음 (표준화 필수) 낮음
다른 모달과의 결합 (예: 테이블+이미지) 유연 어려움

그럼에도 정형 데이터로 MLP를 직접 다뤄보는 것에는 분명한 가치가 있다: 데이터가 작고 학습이 빨라서, 학습률·배치·과적합 같은 이 장의 개념들을 몇 분 안에 실험해볼 수 있는 최고의 연습장이기 때문이다.

6.7.5 전체 작업 흐름

이 장에서 배운 도구들은 실전에서 하나의 흐름으로 모인다. HW1에서 이 흐름을 그대로 따라가게 된다.

① 데이터 분할 훈련 / 검증 / 테스트 ② 전처리 원-핫 · 표준화 (훈련 통계만) ③ MLP 학습 학습률 · 배치 설정 ④ 러닝커브 진단 과적합? 과소적합? 대응책 적용 후 재학습 (조기 종료, 모형 조정, 데이터 추가) 진단 완료 후 테스트로 최종 보고 (한 번만)
HW1 예고 — 정형 데이터 대결

과제에서 같은 정형 데이터로 MLP와 부스팅 모형을 직접 비교한다. 목표는 승패가 아니라: 러닝커브를 읽고, 과적합을 진단하고, “이 문제에 이 모형이 왜 맞는가”를 근거를 들어 설명하는 것이다.


6.8 핵심 요약

개념 핵심 내용
학습률 보폭 — 너무 작으면 느리고, 너무 크면 발산
배치 크기 Full-batch(정확) / Mini-batch(균형) / SGD(빠르지만 노이즈)
연쇄법칙 합성 함수의 미분 = 중간 미분들의 곱 (변화의 배율이 단계마다 곱해진다)
역전파 손실에서 뒤로 미분을 곱해 모든 파라미터의 그래디언트 계산
기울기 소실 Sigmoid 도함수(≤0.25)가 층마다 곱해져 그래디언트 소멸 → ReLU
일반화 새 데이터에 대한 성능 — 딥러닝의 진짜 목표
과적합/과소적합 암기(훈련↓ 검증↑) / 미달(둘 다 ↑)
훈련/검증/테스트 연습문제 / 모의고사 / 수능 — 검증·테스트는 학습에 사용 금지
러닝커브 훈련·검증 손실의 간격이 벌어지면 과적합 → 조기 종료
결측치 학습 전 반드시 처리 — 삭제 / 대치 / ‘결측’ 범주. 통계는 훈련 데이터에서만
원-핫 인코딩 범주형 → 0/1 열들 (거짓 크기 정보 방지)
표준화 \((x-\mu)/s\) — 분모는 \(n\). 훈련 데이터의 통계량만 사용
손실 vs 지표 손실은 학습용(미분 가능해야 함), 지표는 보고용(RMSE·MAE·\(R^2\)·정확도)
정확도의 함정 클래스가 불균형하면 정확도만으로는 아무것도 말하지 못한다
데이터 누수 분할을 전처리보다 먼저. 통계량은 훈련 데이터에서만

다음 장부터는 무대를 바꾼다 — 테이블이 아니라 이미지를 신경망에 넣으면 무슨 일이 생기는가.