위와 같은 두 함수가 있음, 그리고 를 다음과 같이 정의한다면
를 에 대하여 미분하면 Chain Rule에 의하여 아래와 같이 된다.
이를 PyTorch로 구현해보면 아래와 같다. (function들의 Operation은 다름)
# Settings
import torch
# Make input x
x = torch.randn(3,3, requires_grad=True)
print(x)
tensor([[ 0.4490, -0.7287, -1.2367],
[ 1.0262, 0.3886, -1.2934],
[ 0.2356, -1.5048, 0.1285]], requires_grad=True)
먼저 에 임의의 값을 가진 Tensor를 할당한다. requires_grad=True
를 통해 해당 Tensor의 Gradient를 구할 준비를 한다.
[!] requires_grad=True
를 통해 생성한 Tensor에 다른 Operation을 한 뒤 변수에 할당하고 추후에 x.grad
를 통해 Gradient를 불러올라고하면 오류난다.
[+] Tensor를 출력해보면 마지막에 requires_grad=True
를 확인할 수 있다.
# Make function g
g = 2*(x**3)
print(g)
print(g.grad_fn)
tensor([[ 1.8108e-01, -7.7389e-01, -3.7829e+00],
[ 2.1612e+00, 1.1733e-01, -4.3271e+00],
[ 2.6150e-02, -6.8144e+00, 4.2403e-03]], grad_fn=<MulBackward0>)
<MulBackward0 object at 0x000001C028EE13D0>
function을 생성한다.
[+] function의 operation이 진행된 Tensor에 grad_fn
을 붙이면 Multiply 연산이 진행되었다고 알려준다. MulBackward0 object
z = g/12
z.backward()
RuntimeError: grad can be implicitly created only for scalar outputs
[!] 최종으로 Gradient를 구할 값은 Scalar output이어야 한다. 값!
z = g.sum()/12
print(z)
tensor(-1.1007, grad_fn=<DivBackward0>)
z.backward()
print(x.grad)
print( (x**2)/2 )
tensor([[0.1008, 0.2655, 0.7647],
[0.5265, 0.0755, 0.8364],
[0.0278, 1.1321, 0.0083]])
tensor([[0.1008, 0.2655, 0.7647],
[0.5265, 0.0755, 0.8364],
[0.0278, 1.1321, 0.0083]], grad_fn=<DivBackward0>)
위의 코드와 수식처럼 z.backward()
를 실행시키면 x.grad
를 통해서 쉽게 gradient를 구할 수 있다. (를 에 대해 편미분한거)
Gradient는 기울기 값!
Gradient는 기울기 값!
Gradient는 기울기 값!
2022.03.25(Fri)
왜
.backward()
에 scalar 값 or 자기자신과 같은 shape의 gradient를 넘겨줘야하는가?
[+] arbitrary : adj. 임의의
[+] w.r.t
: with respect to
torch.autograd
provides classes and functions implementing automatic differentiation of arbitrary scalar valued functions. It requires minimal changes to the existing code - you only need to declare Tensor
for which gradients should be computed with the requires_grad=True
keyword.
.backward()
: Computes the sum of gradients of given tensors with respect to graph leaves.
.grad()
: Computes and returns the sum of gradients of outputs with respect to the inputs.
Keypoint : "Weight에 대한 Loss의 Gradient를 구해준다."
import torch
x = torch.tensor( [[1,2,3,4]], dtype=torch.float32, requires_grad=True)
print(x)
>>>
tensor([[1., 2., 3., 4.]], requires_grad=True)
xx = x**2
print(xx)
>>>
tensor([[ 1., 4., 9., 16.]], grad_fn=<PowBackward0>)
xx2 = xx*2
print(xx2)
>>>
tensor([[ 2., 8., 18., 32.]], grad_fn=<MulBackward0>)
xx2.backward( gradient = torch.tensor( [[1,0,0,0]] )
x.grad
>>>
tensor([[4., 0., 0., 0.]])
x.grad.data.zero_() # gradient가 누적되므로 초기화! (안되는데?)
xx2.backward( gradient = torch.tensor( [[0,1,0,0]] )
x.grad
>>>
tensor([[0., 8., 0., 0.]])
...
...
...
[?] 왜 loss.backward()
에서 loss
는 Scalar 값이어야 할까?
[?] 또는 .backward( gradient = torch.tensor)
와 같이 이러한 Tensor(vector)가 들어가면 왜 되는걸까?