[PyTorch] Autograd-03 : Practice01

olxtar·2022년 3월 31일
0

[PyTorch] Autograd

목록 보기
3/5
post-thumbnail

[PyTorch] Autograd-01 : 기초사용법
[PyTorch] Autograd-02 : With Jacobian




PyTorch.Autograd

이놈의 Autograd...
DeepLearning에서 중요한 부분인 Backpropagation을 컴퓨터(PyTorch)가 어떻게 연산하는지 정확히 이해하기 위해서는 필수로 알아야되는 부분인 것 같다.
다소 어려운 부분이 많아 끙끙거리던 중 오태호님의 블로그 [PyTorch Autograd - Dable Tech Blog] 를 찾았고 해당 블로그의 글을 참고하여 해당 글을 작성하면서 PyTorch.Autograd를 보다 명확하게 이해하려고 한다.

앞서 작성한 [PyTorch] Autograd 글에서

  • Autograd의 기초적인 사용법(.requries_grad(True/False), .backward() .grad 등)
  • Loss, Loss Function, Jacobian Matrix, Jacobian Vector Product, DCG(Dynamic Computation Graph), leaf & root 개념

을 학습하였다. 밍밍한 설명들이지만 조금씩 참고하자!







01. 속성값 호출

x = torch.tensor(5.0)
y = x ** 3
z = torch.log(y)

먼저 아래와 같은 세 개의 Tensor를 생성
x=5x=5
y=f(x)=x3y=f(x)=x^3
z=g(y)=ln  y=ln  x3z=g(y)=ln\;y=ln\;x^3





print(x.requires_grad)
print(x.is_leaf)
print(x.grad_fn)
print(x.grad)

>>>>
False
True
None
None

각 Tensor에 대한 Autograd 기본정보를 출력 (귀찮으니 Tensor 1개만...)





02. 함수로 속성값 호출

[!] 이 함수 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_gradretains_grad는 모두 False이고
is_leaf는 모두 True이다.
grad_fngrad에는 None값만이 할당되어있다.




03. requires_grad

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>))

xx
y=f(x)y=f(x)
z=g(y)z=g(y)

z=g(y)=g(f(x))\therefore z=g(y)=g(f(x))
z=ln  y=ln  x3\therefore z=ln\;y=ln\;x^3

지금 x,y,zx,y,z 세개의 Tensor는 서로 연결되어 있다. (xx에 operation한것이 yy, 그 yy에 operation한것이 zz이므로...)
앞서 requires_grad를 활성화시키지 않은 x,y,zx,y,z와 활성화 후의 x,y,zx,y,z를 비교해보자.

# 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
before requires_grad=False / after requires_grad=True
[!] 연결된 Tensor들, 맨 처음 Tensor에 requires_grad=True
1. 맨처음 Tensor에 requires_grad 켜주면 연결된 Tensor도 모두 켜짐!
2. is_leaf는 기본 True로 설정되나, 맨처음 Tensor에 requires_grad가 켜지면 이후 연산되는 Tensor들은 is_leafFalse로 변함.
3. 연결된 Tensor들의 grad_fn에 Backward function이 할당됨

어느 Tensor AA의 requires_grad가 True인것의 의미는...
해당 Tensor에 대한 어떤 변수의 Gradient (?A\frac{\partial ?}{\partial A})를 언젠가는(?) 계산할 것 이라는 의미이다.
(그래서 requries_grad 즉, gradient를 필요로하느냐?)

위 Code에서처럼 Tensor x로부터 파생된 Tensor y, Tensor z에서
x에 대한 z gradient를 즉, zx\frac{\partial z}{\partial x}를 구하려면
체인룰을 통하여 Gradient를 구해야하므로 자연스럽게 y에 대한 z gradient, x에 대한 y gradient를 구해야한다. zx=zyyx\because\frac{\partial z}{\partial x}=\frac{\partial z}{\partial y}\cdot\frac{\partial y}{\partial x}

따라서 Tensor AA로부터 파생된 즉, 연산된 Tensor들도 requires_grad가 True가 된다.




04. backward()

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 xrequires_grad를 활성화시켜줬음. 이말인즉슨,

해당 Tensor에 대한 어떤 변수의 Gradient (?A\frac{\partial ?}{\partial A})를 언젠가는(?) 계산할 것 이라는 의미이다.

여기서 어떤 변수를 바로
backward() 함수!
를 통해서 지정해줄 수 있다.

Tensor z에 대하여 backward()를 호출하면 x로 부터 z가 계산되었던 흐름

z=g(y)=g(f(x))z=g(y)=g(f(x))
z=ln  y=ln  x3z=ln\;y=ln\;x^3

x=5\because x=5
x3=53=125x^3=5^3=125

y=x3=125\because y=x^3=125
ln  y=ln  1254.83ln\;y=ln\;125\simeq4.83

z=4.83\therefore z=4.83

~의 역순으로 즉, 거꾸로 거슬러 올라가며 (==backward)
z를 편미분한다!

zz=1{\color{green}\frac{\partial z}{\partial z}}=1

zy=yln  y=1y=1x3=1125=0.008{\color{green}\frac{\partial z}{\partial y}}=\frac{\partial}{\partial y}ln\;y=\frac{1}{y}=\frac{1}{x^3}=\frac{1}{125}=0.008

yx=xx3=3x2=75{\color{green}\frac{\partial y}{\partial x}}=\frac{\partial}{\partial x}x^3=3x^2=75


And then... Chain Rule!!
zz×zy×yx=zx=1×0.008×75=0.6{\color{green}\frac{\partial z}{\partial z}\times\frac{\partial z}{\partial y}\times \frac{\partial y}{\partial x}}=\frac{\partial z}{\partial x}=1\times0.008\times75=0.6


z{\color{purple}z}에는 .backward()
x{\color{skyblue}x}에는 .requires_grad(True)
이렇게 함수를 써주면

zx\frac{\partial{\color{purple}z}}{\partial{\color{skyblue}x}}

이 값을 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로 계산함

앞서 말했듯이 xx에 대한 zz의 Gradient를 구하려면 파생된 yyzz에 대한 zz의 Gradient를 구해야한다.
하지만 우리는 에 대한 어떤 애의 Gradient를 구할거야 즉, requires_gradxx에만 켜줬웠으므로 컴퓨터는 yyzz에 대한 zz의 Gradient값은 계산만 할뿐 따로 저장해두지 않는다.


zx\frac{\partial z}{\partial x} 즉, x.grad의 값은 0.6

zz,zy\frac{\partial z}{\partial z},\frac{\partial z}{\partial y} 즉,z.grad,y.grad의 값은 None





05. retain_grad()

[+] 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_grad04.backward()를 통해서
A에 대한 Z의 Gradient를 구하는 걸 배웠으나

AZ사이의 파생된 Variable에 대해서도 Gradient를 구하고 싶을때는
위 코드와 같이 retain_gradbackward()전에 사용하여 gradient를 "유지하라!"라고 말해주면 된다.

실행결과를 보면
x뿐만 아니라 yz에게도 grad값이 저장되어 있다.
y.grad 에는 zy=yln  y=1y=1x3=1125=0.008\frac{\partial z}{\partial y}=\frac{\partial}{\partial y}ln\;y=\frac{1}{y}=\frac{1}{x^3}=\frac{1}{125}=0.008
z.grad에는 zz=1\frac{\partial z}{\partial z}=1이 할당되어 있다.

# retain_grad()
# retain_grad

profile
예술과 기술

1개의 댓글

comment-user-thumbnail
2024년 10월 6일

감사합니다!!

답글 달기