LLM 엔지니어링2장 · 신경망과 딥러닝의 기본 원리11 / 23← 이전목차다음 →MetaContents

2장. 신경망과 딥러닝의 기본 원리

역전파와 자동 미분

일이 잘못되었을 때 회사에서 원인을 따지는 모습을 떠올려 보세요. 부장은 결과가 틀린 만큼 각 팀장의 의견이 얼마나 영향을 주었는지 따지고, 팀장은 다시 각 담당자에게 받은 책임을 나눕니다. 결재가 올라간 길을 거꾸로 내려오며 책임을 나누는 셈입니다. 신경망에서는 이것을 역전파라고 합니다.

오차에서 출발해 출력층, 은닉층 2, 은닉층 1 의 순서로 거꾸로 책임을 나누는 흐름도

순전파 그림과 방향이 반대입니다. 왼쪽 오차에서 출발해 출력층, 은닉층 2, 은닉층 1 로 거꾸로 가며, 가중치마다 "이 가중치를 조금 바꾸면 손실이 얼마나 바뀌나", 곧 기울기를 구합니다. 여기서 결과의 잘못이 손실, 사람마다 나눠 받은 책임이 기울기입니다.

이름의 유래와 뜻

역전파는 영어로 backpropagation 이고, backward propagation of errors(오차를 뒤로 퍼뜨리기)를 줄인 말입니다. 줄여서 backprop 이라고도 부릅니다. 우리말 이름도 낱말을 그대로 옮겼습니다.

우리말영어뜻
역back(ward)거꾸로, 뒤로
전파propagation퍼뜨리기, 전하기
순전파forward propagation입력에서 출력 쪽으로 앞으로 전하기
역전파backpropagation오차를 출력에서 입력 쪽으로 거꾸로 전하기

순전파가 "입력 → 예측"을 앞으로 계산한다면, 역전파는 "오차 → 가중치마다의 기울기"를 거꾸로 계산합니다. 두 이름이 짝을 이룹니다.

계산 방법의 뿌리는 1970년 무렵으로 거슬러 올라갑니다. 핀란드의 린나인마(Seppo Linnainmaa)가 복잡한 계산의 미분을 뒤에서부터 구하는 방법을 내놓았고, 미국의 워보스(Paul Werbos)가 1974년 박사 논문에서 이를 신경망 학습에 쓸 수 있다고 제안했습니다. 이 방법이 널리 퍼진 것은 1986년 럼멜하트(David Rumelhart), 힌턴(Geoffrey Hinton), 윌리엄스(Ronald Williams)가 은닉층이 있는 신경망을 역전파로 학습시킨 결과를 학술지 Nature 에 발표하면서입니다. 오늘날의 딥러닝과 LLM 도 모두 이 방법으로 학습합니다.

자동 미분이 해 주는 일

가중치 수백 개의 기울기를 사람이 식으로 구할 수는 없습니다. PyTorch 는 순전파를 하는 동안 어떤 계산을 했는지 기록해 두었다가, backward() 한 줄로 모든 기울기를 거꾸로 구해 줍니다. 이것이 자동 미분입니다. 영어로 automatic differentiation 이고 줄여서 autodiff 라고 부릅니다. PyTorch 에서 이 일을 맡는 부분의 이름 autograd(automatic gradient, 자동 기울기)도 여기서 왔습니다.

코드하는 일
torch.tensor(..., requires_grad=True)"이 값의 기울기가 필요하다"고 표시한다. 학습할 가중치에 붙인다
loss.backward()손실에서 거꾸로 따라가며 기울기를 구한다
w.grad구한 기울기가 담기는 곳
torch.no_grad()이 안의 계산은 기록하지 않는다. 가중치를 고칠 때와 평가할 때 쓴다
Python
import torch

w = torch.tensor(2.0, requires_grad=True)   # 기울기가 필요한 값
y = w ** 2 + 3 * w                          # 계산을 하면 PyTorch 가 과정을 기록한다

y.backward()       # y 를 w 로 미분한 값(기울기)을 구한다: 2w + 3
print(w.grad)      # w = 2 일 때 7

w.grad 에 tensor(7.) 이 담깁니다. 미분 식(2w + 3)을 우리가 적지 않았는데도 PyTorch 가 계산 기록을 거꾸로 따라가 구했습니다.

이 코드가 뜻하는 것

이 코드는 "w 를 아주 조금 바꾸면 y 가 얼마나 바뀌나"를 PyTorch 에 물어본 것입니다. 답 7 은 "w 를 0.001 늘리면 y 가 약 0.007 늘어난다"는 뜻입니다. w 를 2 에서 2.001 로 바꿔 y 를 직접 계산하면 y 의 변화 ÷ 0.001 이 7.001 로, 7 과 거의 같습니다.

줄하는 일회사로 치면
requires_grad=Truew 를 "책임을 따질 대상"으로 정한다결재 라인에 담당자 이름을 올린다
y = w ** 2 + 3 * w계산하면서 무엇을 했는지 기록한다서류가 지나간 길을 결재 기록에 남긴다
y.backward()기록을 거꾸로 따라가 기울기를 구한다결과를 보고 길을 거꾸로 짚으며 책임을 나눈다
w.grad구한 기울기를 읽는다담당자가 받은 책임의 크기를 본다

계산이 기록되었다는 표시는 y 를 찍어 보면 보입니다. print(y) 는 tensor(10., grad_fn=<AddBackward0>) 를 냅니다. grad_fn 은 "이 값이 덧셈(Add)으로 만들어졌고, 거꾸로 갈 때 쓸 계산(Backward)이 붙어 있다"는 꼬리표입니다. backward() 는 이 꼬리표를 따라 거꾸로 내려갑니다.

책임이 거꾸로 전해지는 모습

가중치가 둘인 아주 작은 신경망으로 역전파가 책임을 나누는 순서를 따라가 봅니다. 입력 x 는 3, 정답은 4, 가중치는 w1 = 0.5, w2 = 2 입니다.

Python
import torch

x = torch.tensor(3.0)          # 입력(학습하지 않는 값이라 표시하지 않는다)
true = torch.tensor(4.0)       # 정답
w1 = torch.tensor(0.5, requires_grad=True)   # 첫 층의 가중치
w2 = torch.tensor(2.0, requires_grad=True)   # 둘째 층의 가중치

h = w1 * x                     # 순전파 1: 0.5 × 3 = 1.5
out = w2 * h                   # 순전파 2: 2 × 1.5 = 3.0
loss = (out - true) ** 2       # 손실: (3 - 4)² = 1
loss.backward()                # 역전파: 손실에서 거꾸로 기울기를 구한다
print(w1.grad, w2.grad)        # 결과: w1 은 -12, w2 는 -3

실행하면 tensor(-12.) tensor(-3.) 이 나옵니다. backward() 가 거꾸로 간 길을 손으로 따라가면 이렇습니다.

순서따지는 것계산값
1손실이 출력 out 에 얼마나 민감한가2 × (out − 정답) = 2 × (3 − 4)-2
2w2 의 책임: out = w2 × h 이므로 h 만큼 곱한다-2 × h = -2 × 1.5-3
3h 가 받은 책임: out 에서 w2 만큼 곱해 넘어온다-2 × w2 = -2 × 2-4
4w1 의 책임: h = w1 × x 이므로 x 만큼 곱한다-4 × x = -4 × 3-12

책임은 출력에서 출발해 한 층씩 거꾸로 내려가며, 지나는 곳마다 그 자리의 값이 곱해집니다. 그래서 앞에서 구한 값(1번의 -2, 3번의 -4)을 다시 쓰며 아래 층으로 내려갑니다. 가중치가 수십억 개여도 계산을 거꾸로 한 번만 훑으면 모든 기울기가 나오는 까닭이 이것입니다. 두 기울기가 모두 음수이므로, 경사하강법은 w1 과 w2 를 키웁니다. 둘을 키우면 출력 3 이 정답 4 쪽으로 커집니다.

주의할 점

  • w.grad 는 backward() 를 부를 때마다 앞의 값에 더해집니다. 그래서 학습 루프에서는 매번 zero_grad() 로 지우고 시작합니다. 빠뜨리면 기울기가 쌓여 학습이 엉뚱하게 흘러갑니다.
  • 회사의 책임 따지기와 다른 점이 있습니다. 역전파는 누구를 탓하려는 것이 아니라, 고칠 방향과 크기를 정하려는 것입니다.
← 이전 절2장 · 학습률다음 절 →2장 · 학습 루프: 에포크와 배치