torch.autograd는 파이토치의 자동 미분 엔진으로, 신경망 훈련을 촉진합니다. 본 절에서는 autograd가 신경망 훈련에 도움을 주는 개념을 이해할 수 있습니다.
배경
신경망(NNs)은 입력 데이터를 통해 순차적으로 실행되는 함수의 집합입니다. 이 함수들은 파라미터(가중치, 절편)로 정의되며, 파이토치에서는 텐서로 저장됩니다.
신경망을 훈련시키는 데에는 두 가지 단계가 있습니다:
- 순전파 (Forward Propagation): 순전파 과정에서 신경망은 가장 나은 예측을 내놓습니다. 이는 입력 데이터를 각 함수를 통해 전달하여 예측을 수행합니다.
- 역전파 (Backward Propagation): 역전파 과정에서 신경망은 예측과의 오차를 바탕으로 파라미터를 조정합니다. 이는 출력에서 시작해 각 함수 파라미터에 대한 오차의 도함수(경도)를 수집하고, 경사 하강법을 통해 파라미터를 최적화합니다. 역전파에 대해 더 많은 정보는 3Blue1Brown의 영상을 참조하세요.
파이토치에서의 사용
다음은 단일 훈련 스텝을 보여주는 예입니다. 이 예제에서는 torchvision에서 사전훈련된 resnet18 모델을 로드했습니다. 랜덤 데이터 텐서를 통해 3채널 이미지를 나타내며, 해당 label은 랜덤으로 초기화되었습니다.
import torch, torchvision
model = torchvision.models.resnet18(pretrained=True)
image = torch.rand(1, 3, 64, 64)
label = torch.rand(1, 1000)
다음, 데이터를 모델에 입력하여 각 층을 통해 예측을 수행합니다. 이 과정을 순전파 과정이라고 합니다.
output = model(image) # 순전파 과정
예측과 대조 labels를 통해 오차를 계산합니다. 다음 단계는 이 오차 텐서上调用 .backward()를 통해 역전파를 시작합니다. 그런 다음, autograd는 모델 파라미터에 대한 경도를 계산하고, 해당 파라미터의 .grad 속성에 저장합니다.
loss = (output - label).sum()
loss.backward() # 역전파 과정
다음, 하이퍼파라미터를 설정한 최적화기를 로드합니다. 이 예제에서는 학습률 0.01, 운동량 0.9인 SGD를 사용합니다. 최적화기는 모델의 모든 파라미터를 등록합니다.
optim = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=0.9)
마지막 단계로, .step()를 호출하여 경사 하강을 수행합니다. 최적화기는 .grad 속성에 저장된 파라미터 경도를 바탕으로 모든 파라미터를 조정합니다.
optim.step() # 경사 하강
이제 신경망 훈련에 필요한 모든 내용을 갖추었습니다. 이하 부분은 autograd가 어떻게 작동하는지 자세히 설명합니다.
autograd의 미분
a와 b라는 두 텐서를 생성합니다. 이들은 requires_grad=True로 설정되어 autograd가 그 위에서 수행되는 모든 작업을 추적하도록 합니다.
import torch
a = torch.tensor([2., 3.], requires_grad=True)
b = torch.tensor([6., 4.], requires_grad=True)
a와 b로 텐서 Q를 생성합니다.
[Q = 3a^2 - b^2 ]
Q = 3*a**2 - b**2
a와 b가 신경망의 파라미터일 때, Q는 오차입니다. 신경망 훈련 중에 파라미터에 대한 도함수를 구하는 것은 아래와 같습니다:
[\frac{\partial Q}{\partial a} = 6a ][\frac{\partial Q}{\partial b} = -2b ]
Q上调用 .backward()를 통해 autograd가 위의 도함수를 계산하고, 각 텐서의 .grad 속성에 저장합니다. backward()는 텐서이므로, Q.backward()에 gradient 매개변수를 명시적으로 전달해야 합니다. gradient는 Q와 같은 형상의 텐서이며, Q에 대한 도함수를 나타내며:
[\frac{\partial Q}{\partial Q} = 1 ]
또는 Q를 스칼라로 변환한 후 .backward()를 호출하는 방법도 있습니다.
external_grad = torch.tensor([1., 1.])
Q.backward(gradient=external_grad)
경도는 a.grad와 b.grad에 저장됩니다.
## 수집된 경도가 정확한지 확인
print(9*a**2 == a.grad)
print(-2*b == b.grad)
출력:
tensor([True, True])
tensor([True, True])
추가 읽기 - autograd를 통해 벡터 미분 수행
계산 그래프
개념적으로, autograd는 데이터(텐서)와 모든 수행된 작업(새로운 텐서를 생성하는 데 사용된 연산)로 구성된 함수 객체로 이루어진 유향무순도그램(DAG)을 기록합니다. 이 그래프에서 잎节点는 입력 텐서이며, 루트 node는 출력 텐서입니다. 루트 node에서 잎 node로 역순으로 추적할 때, autograd는 체인법칙을 통해 경도를 자동으로 계산합니다.
순전파 과정에서 autograd는 두 가지 작업을 수행합니다:
- 수행된 작업의 결과 텐서를 계산
- 해당 작업의 경도 함수를 그래프에 저장
backward()를 루트 node上调용하면 역전파 과정이 시작됩니다. 그런 다음, autograd는:
- 각
.grad_fn을 통해 경도를 계산 - 경도를 해당 텐서의
.grad속성에 누적 - 체인법칙을 통해 경도를 잎 node로 전파합니다.
위 예제의 DAG 시각화는 다음과 같습니다. 이 그림에서 화살표는 순전파 과정의 방향을 나타내며, node는 각 작업의 역전파 함수를 나타냅니다. 파란색 잎 node는 우리의 텐서 a와 b를 나타냅니다.
참고: 파이토치에서 DAG는 동적입니다. 주의해야 할 점은, 각 .backward() 호출 때마다 새로운 그래프가 만들어집니다. 이는 모델에서 제어문을 사용할 수 있는 이유입니다. 만약 그래프를 변경하려면, 각 반복에서 모양, 크기, 작업을 변경할 수 있습니다.
그래프에서 제외시키기
torch.autograd는 requires_grad=True로 설정된 텐서 위의 작업을 추적합니다. requires_grad=False로 설정된 텐서는 경도 계산에서 제외됩니다.
하나의 텐서가 requires_grad=True일 때, 그 결과 텐서는 여전히 경도를 계산합니다.
x = torch.rand(5, 5)
y = torch.rand(5, 5)
z = torch.rand((5, 5), requires_grad=True)
a = x + y
print(f".Does 'a' require gradients? : {a.requires_grad}")
b = x + z
print(f".Does 'b' require gradients? : {b.requires_grad}")
출력:
.Does `a` require gradients? : False
.Does `b` require gradients? : True
신경망에서, 경도를 계산하지 않는 파라미터는 얼凍結 파라미터라고 합니다. 만약 미리 알고서 특정 파라미터의 경도를 필요하지 않다면, 모델의 일부를 얼凍결하는 것이 유용합니다(이를 통해 autograd의 계산량을 줄일 수 있는 성능상의 이점도 있습니다).
얼凍결된 네트워크에서 미세조정하기
from torch import nn, optim
model = torchvision.models.resnet18(pretrained=True)
모든 파라미터를 얼凍결합니다.
for param in model.parameters():
param.requires_grad = False
예를 들어, 10개의 라벨 데이터셋에 대해 미세조정을 수행한다고 가정합니다. resnet의 분류 층은 마지막 선형 층 model.fc입니다. 우리는 분류기를 새로운 선형 층으로 간단히 교체할 수 있습니다.
model.fc = nn.Linear(512, 10)
모델의 모든 파라미터 중, 경도를 계산해야 하는 파라미터는 분류 층의 가중치와 절편뿐입니다.
# 분류 층만 최적화
optimizer = optim.SGD(model.parameters(), lr=1e-2, momentum=0.9)
각종 제외 기능은 torch.no_grad()라는 컨텍스트 관리자로도 사용할 수 있습니다.