Autograd 기초
자동 미분이란?
섹션 제목: “자동 미분이란?”딥러닝 학습의 핵심은 손실 함수를 최소화하기 위해 파라미터를 업데이트하는 것입니다. 이를 위해 각 파라미터에 대한 손실의 미분값(그래디언트)이 필요합니다.
수식으로 표현하면:
여기서 은 손실(loss), 는 모델 파라미터입니다. 경사하강법(Gradient Descent)으로 파라미터를 업데이트합니다:
여기서 는 학습률(learning rate)입니다.
파라미터가 수백만 개인 신경망에서 이 미분을 손으로 계산하는 것은 불가능합니다. PyTorch의 Autograd 는 연산 과정을 자동으로 기록하고, 역전파(backpropagation) 알고리즘을 통해 모든 그래디언트를 자동으로 계산합니다.
requires_grad=True 설정
섹션 제목: “requires_grad=True 설정”텐서를 생성할 때 requires_grad=True 를 지정하면, 해당 텐서를 사용하는 모든 연산이 추적됩니다.
import torch
# 추적 활성화x = torch.tensor(3.0, requires_grad=True)print(x) # tensor(3., requires_grad=True)print(x.requires_grad) # True
# 추적 비활성화 (기본값)y = torch.tensor(3.0)print(y.requires_grad) # False
# 기존 텐서에 추적 활성화z = torch.tensor([1.0, 2.0, 3.0])z.requires_grad_(True) # in-place 설정print(z.requires_grad) # Truerequires_grad=True 인 텐서와의 연산 결과는 자동으로 추적됩니다.
a = torch.tensor(2.0, requires_grad=True)b = torch.tensor(3.0, requires_grad=True)
c = a * b + a # c = a*b + aprint(c.requires_grad) # True — 자동으로 상속됨print(c.grad_fn) # <AddBackward0 object> — 역전파 함수 참조계산 그래프 (Computation Graph)
섹션 제목: “계산 그래프 (Computation Graph)”PyTorch는 연산할 때마다 동적 계산 그래프 를 구성합니다. 이 그래프는 순전파(forward pass) 중에 만들어지고, backward() 호출 시 역으로 순회하며 그래디언트를 계산합니다.
x = 3.0 (requires_grad=True) │ ▼ y = x² (PowBackward) │ ▼ z = y + 2x (AddBackward) │ ▼ loss = z (스칼라 출력)x = torch.tensor(3.0, requires_grad=True)
y = x ** 2 # y = x², dy/dx = 2xz = y + 2 * x # z = x² + 2x, dz/dx = 2x + 2
print(f"y = {y.item()}") # 9.0print(f"z = {z.item()}") # 15.0
# 각 노드의 grad_fn 확인print(f"y.grad_fn: {y.grad_fn}") # <PowBackward0>print(f"z.grad_fn: {z.grad_fn}") # <AddBackward0>.backward()로 그래디언트 계산
섹션 제목: “.backward()로 그래디언트 계산”스칼라 텐서에 .backward() 를 호출하면 계산 그래프를 역으로 순회하며 모든 requires_grad=True 텐서의 .grad 를 채웁니다.
x = torch.tensor(3.0, requires_grad=True)loss = x ** 2 + 2 * x + 1 # loss = x² + 2x + 1
# 역전파 실행loss.backward()
# 그래디언트 확인: d(loss)/dx = 2x + 2 = 2(3) + 2 = 8print(x.grad) # tensor(8.)여러 변수가 있는 경우:
a = torch.tensor(2.0, requires_grad=True)b = torch.tensor(3.0, requires_grad=True)
# loss = a²b + b³loss = a ** 2 * b + b ** 3
loss.backward()
# d(loss)/da = 2ab = 2(2)(3) = 12print(f"a.grad = {a.grad}") # tensor(12.)
# d(loss)/db = a² + 3b² = 4 + 27 = 31print(f"b.grad = {b.grad}") # tensor(31.).grad 속성 접근
섹션 제목: “.grad 속성 접근”.backward() 호출 후, requires_grad=True 인 리프 노드(leaf node) 텐서에만 .grad 가 채워집니다.
x = torch.tensor(2.0, requires_grad=True)y = x * 3 # 중간 노드 (non-leaf)z = y ** 2 # 스칼라
z.backward()
print(f"x.grad = {x.grad}") # tensor(24.) — x=2: dz/dx = 6x·3 = 12·2 = 24print(f"y.grad = {y.grad}") # None — 중간 노드는 기본적으로 저장 안 됨
# 중간 노드 그래디언트가 필요하면 retain_grad() 사용x2 = torch.tensor(2.0, requires_grad=True)y2 = x2 * 3y2.retain_grad() # 중간 노드 그래디언트 보존 요청z2 = y2 ** 2
z2.backward()print(f"y2.grad = {y2.grad}") # tensor(12.) — dz/dy = 2y = 2(6) = 12핵심 요약
섹션 제목: “핵심 요약”| 개념 | 설명 |
|---|---|
requires_grad=True | 텐서의 연산을 추적하도록 설정 |
grad_fn | 텐서를 만든 연산(역전파 함수) 참조 |
.backward() | 계산 그래프를 역순으로 순회하며 그래디언트 계산 |
.grad | 리프 노드에 축적되는 그래디언트 값 |
retain_grad() | 중간 노드의 그래디언트도 보존 |
다음 장에서는 그래디언트 누적 문제, zero_grad(), torch.no_grad() 등 실전에서 필수인 개념들을 다룹니다.