[PyTorch] Autograd-01 : 기초사용법
[PyTorch] Autograd-02 : With Jacobian
이놈의 Autograd...
DeepLearning에서 중요한 부분인 Backpropagation을 컴퓨터(PyTorch)가 어떻게 연산하는지 정확히 이해하기 위해서는 필수로 알아야되는 부분인 것 같다.
다소 어려운 부분이 많아 끙끙거리던 중 오태호님의 블로그 [PyTorch Autograd - Dable Tech Blog] 를 찾았고 해당 블로그의 글을 참고하여 해당 글을 작성하면서 PyTorch.Autograd를 보다 명확하게 이해하려고 한다.
앞서 작성한 [PyTorch] Autograd 글에서
.requries_grad(True/False)
, .backward()
.grad
등)을 학습하였다. 밍밍한 설명들이지만 조금씩 참고하자!
x = torch.tensor(5.0)
y = x ** 3
z = torch.log(y)
먼저 아래와 같은 세 개의 Tensor를 생성
print(x.requires_grad)
print(x.is_leaf)
print(x.grad_fn)
print(x.grad)
>>>>
False
True
None
None
각 Tensor에 대한 Autograd 기본정보를 출력 (귀찮으니 Tensor 1개만...)
[!] 이 함수 get_tensor_info
는 쭉 사용예정!
def get_tensor_info(tensor):
for name in ['requires_grad', 'is_leaf', 'retains_grad', 'grad_fn', 'grad']:
info.append(f'{name} ( {getattr(tensor, name)} )')
info.append(f'tensor({str(tensor)})')
return ' '.join(info)
print('x', get_tensor_info(x))
print('y', get_tensor_info(y))
print('z', get_tensor_info(z))
>>>
x requires_grad(False) is_leaf(True) retains_grad(False) grad_fn(None) grad(None) tensor(tensor(5.))
y requires_grad(False) is_leaf(True) retains_grad(False) grad_fn(None) grad(None) tensor(tensor(125.))
z requires_grad(False) is_leaf(True) retains_grad(False) grad_fn(None) grad(None) tensor(tensor(4.8283))
Tensor의 (Autograd 관련) 기본정보를 출력하는 함수를 만들어서 출력
requires_grad
와retains_grad
는 모두False
이고
is_leaf
는 모두True
이다.
grad_fn
과grad
에는None
값만이 할당되어있다.
x = torch.tensor(5.0, requires_grad=True) #requires_grad 활성화
y = x ** 3
z = torch.log(y)
print('x', get_tensor_info(x))
print('y', get_tensor_info(y))
print('z', get_tensor_info(z))
>>>
x requires_grad(True) is_leaf(True) retains_grad(False) grad_fn(None) grad(None) tensor(tensor(5., requires_grad=True))
y requires_grad(True) is_leaf(False) retains_grad(False) grad_fn(<PowBackward0 object at 0x000001D4832934C0>) grad(None) tensor(tensor(125., grad_fn=<PowBackward0>))
z requires_grad(True) is_leaf(False) retains_grad(False) grad_fn(<LogBackward0 object at 0x000001D483293940>) grad(None) tensor(tensor(4.8283, grad_fn=<LogBackward0>))
지금 세개의 Tensor는 서로 연결되어 있다. (에 operation한것이 , 그 에 operation한것이 이므로...)
앞서requires_grad
를 활성화시키지 않은 와 활성화 후의 를 비교해보자.
# x 하나만 requires_grad=True해줬는데 y,z까지 requires_grad=True됨!
x requires_grad(False) -> x requires_grad(True)
y requires_grad(False) -> y requires_grad(True)
z requires_grad(False) -> z requires_grad(True)
# is_leaf는 원래는 다 True였는데 x 제외 y,z는 False됐네. x만 '잎'이라는 거구나
x is_leaf(True) -> x is_leaf(True)
y is_leaf(True) -> y is_leaf(False)
y is_leaf(True) -> y is_leaf(False)
# 얘는 변화없음
x retains_grad(False) -> x retains_grad(False)
y retains_grad(False) -> y retains_grad(False)
z retains_grad(False) -> z retains_grad(False)
# y,z에 ~Backward라는게 생겼네
x grad_fn(None) -> x grad_fn(None)
y grad_fn(None) -> y grad_fn(<PowBackward0 object at 0x000001D4832934C0>)
z grad_fn(None) -> z grad_fn(<LogBackward0 object at 0x000001D483293940>)
# 얘도 변화없음
x grad(None) -> x grad(None)
y grad(None) -> y grad(None)
z grad(None) -> z grad(None)
Keypoint
beforerequires_grad=False
/ afterrequires_grad=True
[!] 연결된 Tensor들, 맨 처음 Tensor에requires_grad=True
1. 맨처음 Tensor에requires_grad
켜주면 연결된 Tensor도 모두 켜짐!
2.is_leaf
는 기본True
로 설정되나, 맨처음 Tensor에requires_grad
가 켜지면 이후 연산되는 Tensor들은is_leaf
가False
로 변함.
3. 연결된 Tensor들의grad_fn
에 Backward function이 할당됨어느 Tensor 의 requires_grad가 True인것의 의미는...
해당 Tensor에 대한어떤 변수의Gradient ()를언젠가는(?)계산할 것 이라는 의미이다.
(그래서requries_grad
즉, gradient를 필요로하느냐?)위 Code에서처럼 Tensor
x
로부터 파생된 Tensory
, Tensorz
에서
x
에 대한z
gradient를 즉, 를 구하려면
체인룰을 통하여 Gradient를 구해야하므로자연스럽게y
에 대한z
gradient,x
에 대한y
gradient를 구해야한다.따라서 Tensor 로부터 파생된 즉, 연산된 Tensor들도 requires_grad가 True가 된다.
x = torch.tensor(5.0, requires_grad=True) # backward() 호출을 위해 requries_grad를 켜준다.
y = x ** 3
z = torch.log(y)
print('x', get_tensor_info(x))
print('y', get_tensor_info(y))
print('z', get_tensor_info(z))
z.backward() # z에 대해서 backward() 호출!
print('x_after_backward', get_tensor_info(x))
print('y_after_backward', get_tensor_info(y))
print('z_after_backward', get_tensor_info(z))
>>>
x requires_grad(True) is_leaf(True) retains_grad(None) grad_fn(None) grad(None) tensor(tensor(5., requires_grad=True))
y requires_grad(True) is_leaf(False) retains_grad(None) grad_fn(<PowBackward0 object at 0x7f7822fe19e8>) grad(None) tensor(tensor(125., grad_fn=<PowBackward0>))
z requires_grad(True) is_leaf(False) retains_grad(None) grad_fn(<LogBackward object at 0x7f7822fe19e8>) grad(None) tensor(tensor(4.8283, grad_fn=<LogBackward>))
x_after_backward requires_grad(True) is_leaf(True) retains_grad(None) grad_fn(None) grad(0.6000000238418579) tensor(tensor(5., requires_grad=True))
y_after_backward requires_grad(True) is_leaf(False) retains_grad(None) grad_fn(<PowBackward0 object at 0x7f7822fe19e8>) grad(None) tensor(tensor(125., grad_fn=<PowBackward0>))
z_after_backward requires_grad(True) is_leaf(False) retains_grad(None) grad_fn(<LogBackward object at 0x7f7822fe19e8>) grad(None) tensor(tensor(4.8283, grad_fn=<LogBackward>))
자 03. requires_grad에서처럼 Tensor
x
의requires_grad
를 활성화시켜줬음. 이말인즉슨,해당 Tensor에 대한
어떤 변수의Gradient ()를언젠가는(?)계산할 것 이라는 의미이다.여기서 어떤 변수를 바로
backward()
함수!
를 통해서 지정해줄 수 있다.Tensor
z
에 대하여backward()
를 호출하면x
로 부터z
가 계산되었던 흐름
~의 역순으로 즉, 거꾸로 거슬러 올라가며 (backward)
z
를 편미분한다!
And then... Chain Rule!!
에는
.backward()
에는.requires_grad(True)
이렇게 함수를 써주면이 값을
x.grad
에 계산해서 저장해놓음!
즉 어떤 Tensor A에 requires_grad를 켜놓고
=이 Tensor에 대한 Gradient를 계산할것이니 준비해놔라
Tensor A로 부터 파생되어 계산된 Tensor Z에 backward 함수를 호출하면
=이 Tensor의 Gradient를 계산해라
Tensor A에 대한 Tensor Z의 Gradient값을 계산한다.
Tensor Z가 Tensor A로부터 파생되어 계산된 역순으로 Gradeint값을 하나하나 계산하면서 마지막에 Chain Rule로 계산함
앞서 말했듯이 에 대한 의 Gradient를 구하려면 파생된 와 에 대한 의 Gradient를 구해야한다.
하지만 우리는 얘에 대한 어떤 애의 Gradient를 구할거야 즉,requires_grad
를 에만 켜줬웠으므로 컴퓨터는 와 에 대한 의 Gradient값은계산만 할뿐따로 저장해두지 않는다.
즉,
x.grad
의 값은0.6
즉,
z.grad
,y.grad
의 값은None
[+] retain : v. (~을) 유지하다, 보유하다, 간직하다
# 02. 함수로 속성값 호출 에서의 get_tensor_info 함수 사용
x = torch.tensor(5.0, requires_grad=True)
y = x ** 3
z = torch.log(y)
y.retain_grad() # make y's retain gradient True
z.retain_grad() # make z's retain gradient True
z.backward()
print('x_after_backward', get_tensor_info(x))
print('y_after_backward', get_tensor_info(y))
print('z_after_backward', get_tensor_info(z))
>>>
x after backward requires_grad(True) is_leaf(True) retains_grad(False) grad_fn(None) grad(0.6000000238418579) tensor(tensor(5., requires_grad=True))
y after backward requires_grad(True) is_leaf(False) retains_grad(True) grad_fn(<PowBackward0 object at 0x7f86f96555e0>) grad(0.00800000037997961) tensor(tensor(125., grad_fn=<PowBackward0>))
z after backward requires_grad(True) is_leaf(False) retains_grad(True) grad_fn(<LogBackward0 object at 0x7f86f9655e20>) grad(1.0) tensor(tensor(4.8283, grad_fn=<LogBackward0>))
03.requires_grad와 04.backward()를 통해서
A에 대한 Z의 Gradient를 구하는 걸 배웠으나A와 Z사이의 파생된 Variable에 대해서도 Gradient를 구하고 싶을때는
위 코드와 같이retain_grad
를backward()
전에 사용하여 gradient를 "유지하라!"라고 말해주면 된다.실행결과를 보면
x
뿐만 아니라y
와z
에게도grad
값이 저장되어 있다.
y.grad
에는
z.grad
에는 이 할당되어 있다.# retain_grad()
# retain_grad
감사합니다!!