What Do We Need Autograd for?
Tensor가 거쳐가는 모든 계산 과정을 기록해 chain rule과 backpropagation에 사용
Simple Example
requires_grad=True로 설정한 tensor는 autograd를 사용
a = torch.linspace(0., 2. * math.pi, steps=25, requires_grad=True)
b = torch.sin(a)
print(b)
- Output
tensor([ 0.0000e+00, 2.5882e-01, 5.0000e-01, 7.0711e-01, 8.6603e-01, 9.6593e-01, 1.0000e+00, 9.6593e-01, 8.6603e-01, 7.0711e-01, 5.0000e-01, 2.5882e-01, -8.7423e-08, -2.5882e-01, -5.0000e-01, -7.0711e-01, -8.6603e-01, -9.6593e-01, -1.0000e+00, -9.6593e-01, -8.6603e-01, -7.0711e-01, -5.0000e-01, -2.5882e-01, 1.7485e-07], grad_fn=<SinBackward0>)
- 이전에 사용된 함수가 sin이라는 것을 기록
c = 2 * b
d = c + 1
grad_fn 속성을 통해 계산 기록을 모두 확인할 수 있음
print('d:')
print(d.grad_fn)
print(d.grad_fn.next_functions)
print(d.grad_fn.next_functions[0][0].next_functions)
print(d.grad_fn.next_functions[0][0].next_functions[0][0].next_functions)
print(d.grad_fn.next_functions[0][0].next_functions[0][0].next_functions[0][0].next_functions)
print('\\nc:')
print(c.grad_fn)
print('\\nb:')
print(b.grad_fn)
print('\\na:')
print(a.grad_fn)
- Output
d:
<AddBackward0 object at 0x7f784ef4ded0>
((<MulBackward0 object at 0x7f784ef4d4e0>, 0), (None, 0))
((<SinBackward0 object at 0x7f784ef4d4e0>, 0), (None, 0))
((<AccumulateGrad object at 0x7f784ef4ded0>, 0),)
()
c:
<MulBackward0 object at 0x7f784ef4d4e0>
b:
<SinBackward0 object at 0x7f784ef4d4e0>
a:
None
실제 계산은 tensor에 backward() 메소드를 부르고 input의 grad 속성으로 기울기를 확인
out.backward()
plt.plot(a.detach(), a.grad.detach())
# 2 cos(a) 함수
Autograd in Training
먼저 간단한 모델을 정의
BATCH_SIZE = 16
DIM_IN = 1000
HIDDEN_SIZE = 100
DIM_OUT = 10
class TinyModel(torch.nn.Module):
def __init__(self):
super(TinyModel, self).__init__()
self.layer1 = torch.nn.Linear(DIM_IN, HIDDEN_SIZE)
self.relu = torch.nn.ReLU()
self.layer2 = torch.nn.Linear(HIDDEN_SIZE, DIM_OUT)
def forward(self, x):
x = self.layer1(x)
x = self.relu(x)
x = self.layer2(x)
return x
some_input = torch.randn(BATCH_SIZE, DIM_IN, requires_grad=False)
ideal_output = torch.randn(BATCH_SIZE, DIM_OUT, requires_grad=False)
model = TinyModel()
- requires_grad=True를 명시하지 않아도 torch.nn.Module의 subclass에서는 모두 gradient를 기록한다 가정
backward() 전에는 grad 값이 None
print(model.layer2.weight[0][0:10]) # just a small slice
# tensor([ 0.0920, 0.0916, 0.0121, 0.0083, -0.0055, 0.0367, 0.0221, -0.0276,
# -0.0086, 0.0157], grad_fn=<SliceBackward0>)
print(model.layer2.weight.grad)
# None
backward() 후에는 grad 값이 기울기 값으로 변경됨. (단 weight 값은 변화 x)
optimizer = torch.optim.SGD(model.parameters(), lr=0.001)
prediction = model(some_input)
loss = (ideal_output - prediction).pow(2).sum()
loss.backward()
print(model.layer2.weight[0][0:10])
# tensor([ 0.0920, 0.0916, 0.0121, 0.0083, -0.0055, 0.0367, 0.0221, -0.0276,
# -0.0086, 0.0157], grad_fn=<SliceBackward0>)
print(model.layer2.weight.grad[0][0:10])
# tensor([12.8997, 2.9572, 2.3021, 1.8887, 5.0710, 7.3192, 3.5169, 2.4319,
# 0.1732, -5.3835])
실제 grad를 weight에 적용하려면 optimizer.step()을 호출
optimizer.step()
print(model.layer2.weight[0][0:10])
# tensor([ 0.0791, 0.0886, 0.0098, 0.0064, -0.0106, 0.0293, 0.0186, -0.0300,
# -0.0088, 0.0211], grad_fn=<SliceBackward0>)
print(model.layer2.weight.grad[0][0:10])
# tensor([12.8997, 2.9572, 2.3021, 1.8887, 5.0710, 7.3192, 3.5169, 2.4319,
# 0.1732, -5.3835])
optimizer.step() 후 optimizer.zero_grad()를 호출해 grad를 0으로 초기화
- 그러지 않으면 grad 값이 계속 축적됨
Turning AutoGrad Off and On
간단히 requires_grad 속성을 직접 변경 가능
a = torch.ones(2, 3, requires_grad=True)
b1 = 2 * a
a.requires_grad = False
b2 = 2 * a
일시적으로 grad 기록을 끄려면 torch.no_grad() 사용
a = torch.ones(2, 3, requires_grad=True) * 2
b = torch.ones(2, 3, requires_grad=True) * 3
c1 = a + b
print(c1)
with torch.no_grad():
c2 = a + b
print(c2)
c3 = a * b
print(c3)
- Output
tensor([[5., 5., 5.],
[5., 5., 5.]], grad_fn=<AddBackward0>)
tensor([[5., 5., 5.],
[5., 5., 5.]])
tensor([[6., 6., 6.],
[6., 6., 6.]], grad_fn=<MulBackward0>)
함수나 메소드 decorator로도 사용 가능
def add_tensors1(x, y):
return x + y
@torch.no_grad()
def add_tensors2(x, y):
return x + y
만약 복사본을 만들 때 기록도 복사하고 싶지 않다면 detach() 메소드를 사용
x = torch.rand(5, requires_grad=True)
y = x.detach()
Autograd and In-place Operations
주의 점으로 in-place 작업을 requires_grad=True인 tensor에 적용하면 잘못된 결과가 나올 수 있다
a = torch.linspace(0., 2. * math.pi, steps=25, requires_grad=True)
torch.sin_(a)
# RUNTIME ERROR!
Autograd Profiler
Autograd가 사용되는 모든 계산 기록을 가지고 있기 때문에 각 계산이 얼마나 걸리고 얼마나 많은지 profiling 할 때 유용
# 예시
device = torch.device('cpu')
run_on_gpu = False
if torch.cuda.is_available():
device = torch.device('cuda')
run_on_gpu = True
x = torch.randn(2, 3, requires_grad=True)
y = torch.rand(2, 3, requires_grad=True)
z = torch.ones(2, 3, requires_grad=True)
with torch.autograd.profiler.profile(use_cuda=run_on_gpu) as prf:
for _ in range(1000):
z = (z / x) * y
print(prf.key_averages().table(sort_by='self_cpu_time_total'))
'코딩 > PyTorch' 카테고리의 다른 글
[Introduction to PyTorch] Building Models (0) | 2025.01.11 |
---|---|
[Introduction to PyTorch] Tensors (0) | 2025.01.11 |
[PyTorch] Save and Load the Model (1) | 2025.01.11 |
[PyTroch] Optimizing Model Parameters (2) | 2025.01.11 |
[PyTorch] Automatic Differentiation with torch.autogrid (0) | 2025.01.10 |