Skip to content

10장. 오차의 강을 거슬러 — 역전파

이 장의 질문: 파라미터 수백만 개의 기울기를 어떻게 순식간에 전부 계산하는가? loss.backward() 한 줄의 실체 — 역전파를 숫자 예제로 완전히 해부한다.

전제: 3장(연쇄법칙), 9장(신경망 구조).

문제의 크기를 실감하기

기울기란 "이 파라미터를 조금 건드리면 손실이 얼마나 변하나"였다(3장). 가장 우직한 계산법은 정의 그대로다 — 파라미터 하나를 0.0001만큼 키워 보고, 순전파를 다시 돌려 손실 변화를 잰다. 정확하다. 그런데 파라미터가 20만 개면 순전파 20만 번이 필요하다. GPT급의 수천억 개면… 한 걸음 내딛는 데 우주적 시간이 걸린다. 이 길은 막혔다.

역전파(backpropagation)의 약속은 이렇다. 순전파 한 번 + 역방향 계산 한 번 — 도합 순전파 두어 번의 비용으로, 모든 파라미터의 기울기를 정확하게 얻는다. 파라미터가 2개든 2천억 개든 똑같이. 이 알고리즘 없이 딥러닝은 없다.

비결은 새로운 수학이 아니다. 3장의 연쇄법칙("겹친 함수의 민감도는 단계별 민감도의 곱") 하나를, 중복 계산이 없도록 영리한 순서로 적용하는 것뿐이다.

계산 그래프: 수식을 조립 라인으로

먼저 수식을 보는 눈을 바꾼다. 아무리 복잡한 모델도 결국 단순 연산(덧셈, 곱셈, ReLU…)들의 조립 라인이다. 이 라인을 그림으로 그린 것이 계산 그래프다. 아주 작은 예로 전 과정을 굴려 보자. 뉴런 하나짜리 "신경망"이다:

z=wx+b,a=ReLU(z),L=(ay)2

입력 x=2, 정답 y=10, 현재 파라미터 w=3,b=1이라 하자.

1단계 — 순전파: 왼쪽에서 오른쪽으로, 값을 계산하며 기록한다.

z=3×2+1=7a=ReLU(7)=7L=(710)2=9

포인트: 중간값 z=7,a=7버리지 않고 저장한다. 곧 쓸 것이다.

2단계 — 역전파: 오른쪽에서 왼쪽으로, 민감도를 계산하며 거슬러 온다.

맨 끝에서 시작한다. LL 자신에 대한 민감도는 당연히 1. 이제 한 칸씩 왼쪽으로:

  • a에 대한 민감도: L=(ay)2이므로 La=2(ay)=2(710)=6. — "a를 1 키우면 손실이 약 6 줄어든다."
  • z에 대한 민감도: a=ReLU(z)이고 z=7>0이라 ReLU는 그대로 통과시키는 중이므로 국소 민감도는 1. 연쇄법칙으로 Lz=La×1=6. (만약 z가 음수였다면 ReLU가 0으로 눌러 버리므로 국소 민감도 0 — 뒤에서 온 민감도가 여기서 차단된다. 기억해 두라.)
  • w에 대한 민감도: z=wx+b에서 w의 국소 민감도는 x=2. 따라서 Lw=6×2=12.
  • b에 대한 민감도: 국소 민감도 1. Lb=6.

끝났다. w12, b6 — 둘 다 음수이니 키우면 손실이 준다. 실제로 그런지 검산해 보라. w3.001로 바꾸면 L=(7.00210)2=8.988... — 손실이 약 0.012=12×0.001 줄었다. 정확히 예측대로다.

이 작은 예에 역전파의 전부가 들어 있다. 일반화하면:

각 연산 노드는 두 가지만 할 줄 알면 된다. ① 순전파 때 자기 계산을 하고 입력값을 기억한다. ② 역전파 때, 뒤에서 흘러온 민감도에 자기 연산의 국소 민감도를 곱해 앞으로 넘긴다.

곱셈 노드의 국소 민감도는 "반대편 입력값"(z=wx에서 w 쪽 민감도는 x), 덧셈 노드는 1(민감도를 그대로 분배), ReLU는 통과 중이면 1, 차단 중이면 0. 이런 국소 규칙만 연산마다 정의해 두면, 아무리 복잡한 그래프도 기계적으로 미분된다. 갈림길(한 값이 여러 곳에 쓰인 경우)에서는 되돌아온 민감도들을 더하면 된다.

왜 빠른가? 핵심은 재사용이다. w의 기울기와 b의 기울기는 둘 다 Lz=6이라는 중간 결과를 공유한다. 뒤에서부터 계산하면 이 공유 값이 딱 한 번만 계산되어 앞의 모든 파라미터가 나눠 쓴다. 층이 100개, 파라미터가 수억 개여도, 각 중간 민감도는 정확히 한 번씩만 계산된다 — 그래서 전체 비용이 순전파와 같은 규모로 끝나는 것이다.

자동 미분: 프레임워크가 해 주는 일

PyTorch 같은 프레임워크는 위 과정을 자동화한다. 당신이 순전파 코드를 쓰면, 프레임워크는 실행하면서 몰래 계산 그래프를 기록하고(각 연산과 입력값 저장 — 위의 "기억한다"), loss.backward()가 호출되면 그 그래프를 거꾸로 걸으며 모든 파라미터의 .grad를 채운다.

python
import torch

w = torch.tensor(3.0, requires_grad=True)   # "이 값의 기울기를 추적하라"
b = torch.tensor(1.0, requires_grad=True)
x, y = torch.tensor(2.0), torch.tensor(10.0)

z = w * x + b                # 순전파 — 그래프가 몰래 기록된다
a = torch.relu(z)
loss = (a - y) ** 2

loss.backward()              # 역전파 — 그래프를 거꾸로 걷는다
print(w.grad, b.grad)        # tensor(-12.), tensor(-6.)  ← 손 계산과 일치!

손으로 구한 12,6이 그대로 나온다. 이제 몇 가지 실무 현상이 원리로 설명된다.

  • 왜 학습은 추론보다 메모리를 훨씬 먹는가 — 역전파가 쓸 중간값들(위의 z,a)을 순전파 내내 전부 들고 있어야 하기 때문이다. 추론만 할 때 torch.no_grad()로 감싸면 이 기록을 생략해 메모리가 뚝 떨어진다.
  • optimizer.zero_grad()를 매번 불러야 하는가 — PyTorch는 .grad를 덮어쓰지 않고 누적한다(갈림길에서 더하는 규칙의 구현 방식 때문). 안 지우면 이전 배치의 기울기가 계속 합산되어 학습이 미쳐 간다. 초심자 버그 1순위다.
  • 왜 모든 연산이 미분 가능해야 하는가 — 그래프의 한 노드라도 국소 민감도를 정의할 수 없으면(예: "반올림") 민감도의 강이 거기서 끊긴다. 신경망 설계란 미분 가능한 부품의 조립이다.

민감도의 곱이 예고하는 재앙

역전파를 이해했으니, 다음 장의 문제를 정확히 예고할 수 있다. 층이 100개면 맨 앞 파라미터의 기울기는 국소 민감도 100개의 곱이다. 곱해지는 수들이 평균적으로 1보다 조금만 작아도 — 예컨대 0.9씩 — 0.91000.00003. 기울기가 사실상 0이 되어 앞쪽 층들은 아무것도 배우지 못한다(기울기 소실). 반대로 1.1씩이면 1.110014000 — 폭발한다. 시그모이드 활성 함수가 몰락하고 ReLU가 왕좌에 오른 이유도 여기 있다. 시그모이드의 국소 민감도는 최대 0.25라서 곱할수록 필연적으로 소실되지만, ReLU는 통과 구간에서 정확히 1 — 곱해도 줄지 않는다. 깊은 신경망의 역사는 "이 100개의 곱을 어떻게 1 근처로 관리하는가"의 역사이며, 그것이 다음 장의 내용이다.

이 장을 읽었다면 지금이 코드랩 01 — NumPy로 신경망 밑바닥 구현의 최적기다. 오늘 손으로 굴린 계산을 784차원 실제 데이터에서 직접 코딩하고, 수치 미분으로 검산까지 해 본다. 한 번 해 본 사람과 안 해 본 사람의 이해는 영영 다르다.

핵심 요약

  • 수치 미분은 파라미터 수만큼 순전파가 필요해 불가능. 역전파는 순전파+역방향 한 번으로 전체 기울기를 정확히 얻는다.
  • 원리: 모델은 단순 연산의 계산 그래프다. 순전파 때 입력값을 기억하고, 역전파 때 "뒤에서 온 민감도 × 국소 민감도"를 앞으로 넘긴다. 갈림길에서는 더한다. 중간 민감도가 재사용되므로 빠르다.
  • 프레임워크의 자동 미분이 이를 대행한다. 학습이 메모리를 먹는 이유(중간값 저장), zero_grad가 필요한 이유(기울기 누적), 미분 가능성이 설계 제약인 이유가 모두 여기서 나온다.
  • 깊은 망의 기울기는 국소 민감도 수십 개의 — 소실과 폭발의 씨앗이다. ReLU의 승리(통과 시 민감도 1)도 이 구조에서 설명된다.

스스로 점검

  1. 본문 예제에서 x=2 대신 x=1이면(z=2) w의 기울기는 얼마인가? ReLU의 차단이 어떤 결과를 낳는지 말로 설명해 보라.
  2. L=(w1w2xy)2, x=1,y=0,w1=w2=0.1일 때 w1의 기울기를 역전파 순서로 구해 보라. 이 값의 크기에서 "곱의 재앙"의 냄새를 맡아 보라.
  3. zero_grad()를 빠뜨리면 두 번째 스텝의 실효 기울기는 무엇의 합이 되는가?
  4. "학습 서버는 메모리 부족인데 추론 서버는 여유롭다" — 이 흔한 현상을 역전파의 관점에서 설명해 보라.

다음 장에서

엔진은 완성됐다. 그러나 이 엔진으로 진짜 깊은 망을 돌리면 — 소실, 폭발, 발산, 정체 — 온갖 방식으로 학습이 무너진다. 지난 십여 년간 인류가 쌓아 온 처방전들 — 똑똑한 초기화, 정규화 층, 잔차 연결, Adam — 을 배우고 나면, 비로소 "100층짜리 망이 학습되는 시대"가 어떻게 열렸는지 알게 된다.

11장. 깊이의 대가 →