Skip to main content
Summary

autograd는 무엇을 자동으로 해 줄까

모델 학습은 결국 "손실을 줄이도록 파라미터를 조금씩 바꾸는 일"입니다. 이때 필요한 것이 기울기입니다. 기울기는 어떤 값이 조금 변했을 때 결과가 얼마나 변하는지를 알려 줍니다.

예를 들어 다음 수식을 생각해 봅시다.

import torch

x = torch.tensor(2.0, requires_grad=True)
y = x * x + 3 * x

수식으로 쓰면 y = x² + 3x입니다. x가 바뀌면 y도 바뀝니다. PyTorch는 requires_grad=True가 붙은 텐서가 연산에 참여하면, 그 연산 과정을 내부적으로 기록합니다. 이 기록을 계산 그래프라고 부릅니다.

순전파는 입력에서 결과를 계산하는 과정입니다. 위 코드에서는 x로 y를 계산하는 과정이 순전파입니다. 이때 PyTorch는 "제곱을 했고, 3을 곱했고, 더했다"는 흐름을 기억합니다.

자동 미분과 autograd 처음 배우기

backward로 기울기 계산하기

기울기를 실제로 계산하려면 backward()를 호출합니다.

y.backward()

print(x.grad)

x.grad에는 y를 x에 대해 미분한 값이 저장됩니다. y = x² + 3x의 미분은 2x + 3이고, x가 2이므로 결과는 7입니다.

tensor(7.)

이 흐름이 신경망 학습의 핵심입니다. 실제 모델에서는 x 대신 가중치와 편향 같은 파라미터가 있고, y 대신 손실값 loss가 있습니다. 그래서 학습 코드에서 자주 다음과 같은 형태를 봅니다.

loss.backward()

이 한 줄은 "손실에 영향을 준 모든 학습 대상 파라미터의 기울기를 계산하라"는 뜻입니다. 단, backward()는 보통 스칼라 값, 즉 숫자 하나짜리 손실에 대해 호출합니다. 대부분의 손실 함수는 배치의 손실을 평균이나 합으로 줄여 스칼라를 만들어 주기 때문에 초보 단계에서는 자연스럽게 이 형태를 사용하게 됩니다.

.grad에는 무엇이 저장될까

간단한 파라미터 예제로 살펴보겠습니다.

w = torch.tensor(1.0, requires_grad=True)
b = torch.tensor(0.0, requires_grad=True)

x = torch.tensor(2.0)
target = torch.tensor(5.0)

pred = w * x + b
loss = (pred - target) ** 2

loss.backward()

print(w.grad)
print(b.grad)

여기서 w와 b는 학습해야 할 값입니다. pred는 예측값이고, loss는 예측값이 정답과 얼마나 다른지 나타냅니다.

loss.backward()를 실행하면 PyTorch는 loss에서 출발해 거꾸로 계산 그래프를 따라갑니다. 그리고 w와 b가 손실에 얼마나 영향을 주었는지 계산해 각각 w.grad, b.grad에 저장합니다.

옵티마이저는 이 .grad 값을 보고 파라미터를 어느 방향으로 얼마나 바꿀지 결정합니다. 즉, .grad는 모델이 배우기 위해 참고하는 방향표입니다.

torch.no_grad()가 필요한 순간

항상 모든 연산을 추적할 필요는 없습니다. 예를 들어 학습이 끝난 모델로 예측만 할 때는 기울기가 필요하지 않습니다. 이때는 torch.no_grad()를 사용합니다.

with torch.no_grad():
    pred = w * x + b

이 블록 안의 연산은 계산 그래프에 기록되지 않습니다. 그래서 메모리를 덜 쓰고, 실행도 더 가벼워집니다.

수동으로 파라미터를 갱신할 때도 no_grad()가 필요합니다.

lr = 0.01

with torch.no_grad():
    w -= lr * w.grad
    b -= lr * b.grad

파라미터 갱신은 학습 과정의 일부이지만, 그 갱신 연산 자체를 다시 미분 대상으로 기록하면 계산이 꼬일 수 있습니다. 그래서 "이 부분은 기록하지 말라"고 명확히 알려 주는 것입니다.

기울기는 자동으로 사라지지 않는다

초보자가 자주 놓치는 점이 있습니다. PyTorch의 기울기는 기본적으로 누적됩니다. backward()를 호출하면 기존 .grad 값을 지우고 새로 쓰는 것이 아니라, 기존 값 위에 새 기울기를 더합니다.

다음 예시는 같은 파라미터 w에 대해 두 번의 순전파와 역전파를 수행합니다.

w = torch.tensor(1.0, requires_grad=True)

loss1 = (w * 2 - 4) ** 2
loss1.backward()
print(w.grad)

loss2 = (w * 3 - 6) ** 2
loss2.backward()
print(w.grad)  # 첫 번째 기울기에 두 번째 기울기가 더해짐

여기서 중요한 점은 각 손실을 새로 계산했다는 것입니다. 같은 loss에 대해 그대로 backward()를 두 번 호출하면, 일반적으로 이미 사용한 계산 그래프가 해제되어 오류가 날 수 있습니다. 일반적인 학습 루프에서는 매 배치마다 새로 순전파를 하고 새 손실을 만든 뒤 역전파를 합니다.

기울기 누적은 여러 배치의 기울기를 모아 한 번에 갱신하는 방식에도 활용할 수 있습니다. 하지만 초보자가 작성하는 보통의 학습 루프에서는 매번 이전 기울기를 지우는 것이 기본입니다.

그래서 학습 코드에는 보통 다음 줄이 들어갑니다.

optimizer.zero_grad()

전체 흐름은 다음처럼 이어집니다.

optimizer.zero_grad()  # 이전 기울기 제거
pred = model(inputs)   # 순전파
loss = loss_fn(pred, labels)
loss.backward()        # 역전파로 기울기 계산
optimizer.step()       # 파라미터 갱신

이제 학습 루프의 핵심 코드는 암기할 문장이 아니라 이해할 수 있는 절차가 됩니다. 순전파에서 손실을 계산하고, backward()로 기울기를 구하고, 옵티마이저가 그 기울기를 이용해 파라미터를 갱신합니다. PyTorch의 autograd는 그중 가장 번거로운 미분 계산을 대신 맡아 주는 기능입니다.

책 목록

초보자를 위한 PyTorch 사용법: 텐서부터 이미지 분류 프로젝트까지

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기