Autograd¶
- Automatic differentiation이라고도 불리고 이건 모든 DL library에 내장 되어 있다.
- forward와 backward pass가 가능하다.
라이브러리 로드¶
In [1]:
import numpy as np
import torch
x에 대한 y gradient 구하기¶
In [2]:
x=torch.randn(2,requires_grad=True)
print(x.grad)
In [3]:
x=torch.randn(2,requires_grad=True)
y=x*3
gradients=torch.tensor([100,0.1],dtype=torch.float)
y.backward(gradients)
print(x.grad)
print(y.grad)
y.backward()
함수는 y와 관련된 연산들에 대해서 모든 편미분 값을 계산하게 된다.- 이때 들어오는 인자는 각 계산된 편미분 값에 곱해져서 들어가게 된다.
.grad
는backward()
로 계산된 값이 저장되게 된다.
requires_grad¶
In [4]:
x=torch.randn(2,requires_grad=True)
y=x*3
gradients=torch.tensor([100,0.1],dtype=torch.float)
y.backward(gradients)
print(x.grad)
- 여기서
requires_grad
인자는 gradient를 저장하고 계산할 수 있는 변수로 선언하는 것이다. - 이
requires_grad
인자가 True여야backward()
계산과.grad
속성에 값이 저장된다.
In [5]:
x=torch.randn(2,requires_grad=False)
y=x*3
gradients=torch.tensor([100,0.1],dtype=torch.float)
y.backward(gradients)
print(x.grad)
requires_grad
가 False값을 가지면backward()
함수를 실행하게 되면 에러가 발생하게 된다.- 에러가 나는 원인은 y가
backward()
함수를 실행해서 뒤로 가보니 gradient를 담을 그릇이 없기 때문에 에러가 발생한다.
backward¶
In [6]:
x=torch.randn(2,requires_grad=True)
y=x*3
gradients=torch.tensor([100,0.1],dtype=torch.float)
y.backward(gradients)
print(x.grad)
y.backward(gradients)
print(x.grad)
backward()
를 두번 호출하게 되면 에러가 발생하게 된다.backward()
를 한번 실행하게 되면 중간 리소스를 해제하는게 기본 설정이다.
In [7]:
x=torch.randn(2,requires_grad=True)
y=x*3
gradients=torch.tensor([100,0.1],dtype=torch.float)
y.backward(gradients,retain_graph=True)
print(x.grad)
y.backward(gradients)
print(x.grad)
backward()
를 2번 실행하고 싶으면 중간 리소스를 해제 하지 않는다는 정보를retain_graph
인자로 주면 된다.- 2번 실행하면 결과 값이 축적된다.
grad_fn¶
In [8]:
x=torch.randn(2,requires_grad=True)
y=x*3
z=x/2
w=x+y
w,y,z
Out[8]:
- 각 출력한 텐서는 계산된 결과의 값이 출력이 된다.
- 이때 어떤 것으로 계산이 되었는지
grad_fn
속성에 저장이 되게 된다. - 나중에
backward()
함수를 위해 참조하기 위해서
register_forwrd_hook¶
- gradient를 구할 때 중간까지만 구하고 싶을 때 사용한다.(CAM 계산할 때 사용해야 한다.)
- hook은 중간에 있는 값을 빼오게 된다.
- register_hook
In [9]:
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
먼저 간단한 3 layer를 구성해 보자¶
In [10]:
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet,self).__init__()
self.conv1 = nn.Conv2d(1,10,5)
self.pool1 = nn.MaxPool2d(2,2)
self.conv2 = nn.Conv2d(10,20,5)
self.pool2 = nn.MaxPool2d(2,2)
self.fc = nn.Linear(320,50)
self.out = nn.Linear(50,10)
def forward(self, input):
x=self.pool1(F.relu(self.conv1(input)))
x=self.pool2(F.relu(self.conv2(x)))
x=x.view(x.size(0),-1)
x=F.relu(self.fc(x))
x=F.relu(self.out(x))
return x
hook이 될때 함수를 선언해 줘야 한다.¶
In [11]:
def hook_func(self,input,output):
print('Inside '+self.__class__.__name__+' forwrd')
print('')
print('input:',type(input))
print('input[0] shape:',input[0].shape)
print('output shape:',output.shape)
print('')
- type(self)는 무조건 Tensor가 되어야 한다.
이제 hook함수 등록(register_forward_hook)¶
In [12]:
net=SimpleNet()
In [13]:
# conv1에 등록
net.conv1.register_forward_hook(hook_func)
Out[13]:
In [14]:
# conv2에 등록
net.conv2.register_forward_hook(hook_func)
Out[14]:
register_forward_hook
은 foward계산이 진행 될때 걸리게 된다.
forward pass가 진행되는 동안 hook function이 자동으로 호출이 된다.¶
In [15]:
input=torch.randn(1,1,28,28)
out=net(input)
register_forward_pre_hook¶
In [16]:
def hook_pre(self,input):
print('Inside '+self.__class__.__name__+' forward')
print()
print('input: ',type(input))
print('input[0] shape: ',input[0].shape)
In [17]:
net=SimpleNet()
net.conv1.register_forward_pre_hook(hook_pre)
input=torch.randn(1,1,28,28)
out=net(input)
register_forward_pre_hook
은 해당 layer에 forward가 진행되기 진적에 hook이 된다.- 따라서 인자로 input값만 존재하게 된다.
register_backward_hook¶
In [18]:
def hook_grad(self,grad_input,grad_output):
print('Inside '+self.__class__.__name__+' backward')
print()
print('grad_input len: ',len(grad_input))
print('grad_input[1] shape: ',grad_input[1].shape)
print('grad_input[2] shape: ',grad_input[2].shape)
print('grad_output len: ',len(grad_output))
print('grad_output[0] shape: ',grad_output[0].shape)
- 이 hook에서 input, output의 인자를 바꿔주면 안된다.
- 그래도 gradient를 바꾸고 싶으면 선택적으로
grad_input
대신에 새로운 gradient를 return을 할 수 있다.
In [19]:
net=SimpleNet()
net.conv1.register_backward_hook(hook_grad)
input=torch.randn(1,1,28,28)
out=net(input)
target=torch.tensor([3],dtype=torch.long)
loss_fn=nn.CrossEntropyLoss()
err=loss_fn(out,target)
err.backward()
hook 지우기¶
In [20]:
net=SimpleNet()
h=net.conv1.register_forward_hook(hook_func)
input=torch.randn(1,1,28,28)
out=net(input)
In [22]:
h.remove()
out=net(input)
- 먼저 register의 return값을 저장한다. Handle객체가 return 된다.
remove()
함수를 사용하여 지울 수 있다.
✅ Autograd 예시¶
- 우리는
register_hook
을 통해서 중간 층의 activation을 저장해야 한다.
hook 함수를 정의 한다.¶
In [ ]:
save_feat=[]
def hook_feat(module,input,output):
save_feat.append(output)
return output
hook함수 등록¶
In [ ]:
for name, module in model.get_model_shortcuts():
if(name=='target_layer_name'):
module.register_forward_hook(hook_feat)
forward pass¶
In [ ]:
img=img.unsqueeze(0)
s=model(img)[0]
'AI > 이론' 카테고리의 다른 글
Multi-modal tasks I (Visual data & Text) (0) | 2021.03.12 |
---|---|
Panoptic segmentation (0) | 2021.03.11 |
Instance segmentation (0) | 2021.03.11 |
Conditional generative model (0) | 2021.03.11 |
Object Detection (0) | 2021.03.10 |
Semantic segmentation (0) | 2021.03.09 |
Image classification II (0) | 2021.03.09 |
Annotation data efficient learning (2) | 2021.03.08 |