04. Back-propagation and Autograd

Park Jong Hun·2021년 1월 25일
0

PytorchZeroToAll

목록 보기
4/5

Sung Kim님의 유투브 강의 자료인 PytorchZeroToAll를 바탕으로 공부한 내용에 대한 글입니다.

Back propagation


1. Complicated network


03. Gradient Descent에서 매우 간단한 선형 모델로 loss를 weight에 대해 미분하여 gradient를 구한 후 weight를 update하며 training하는 과정에 대해 다뤘다. 하지만 모델이 매우 복잡한 형태라면 하나씩 gradient를 계산하는 과정은 너무 오래 걸릴 것이다.

2. Computational graph + Chain rule


Chain rule

g=gˉ(x)g = \bar g(x)
f=fˉ(g)=fˉ(gˉ(x))f = \bar f(g) = \bar f(\bar g(x))

dfdx{df \over dx} = dfˉ(gˉ(x))dx=dfˉ(g)dgdgˉ(x)dx{d\bar f(\bar g(x)) \over dx} = {d\bar f(g) \over dg}{d\bar g(x) \over dx}

gˉ(x)\bar g(x)함수는 xx를 입력으로 받아서 gg를 출력하고, fˉ(g)\bar f(g)함수는 gg를 입력받아 ff를 출력한다. 마지막 결과인 ffxx로 미분한 값을 얻기위해선 먼저 ffgg로 미분한 값에 ggxx로 미분한 값을 곱하면 얻을 수 있다. 따라서 Chain rule이란 각각의 gradient를 구한 후 전부 곱하는 모습이 사슬로 엮여 있는 모습과 같아서 붙여진 이름이다.

3. Back-propagation


f라는 노드가 존재하며 연결된 다른 노드들로 부터 x, y를 입력받아서 z를 출력하여 다음 노드에 입력시킨다. 마지막 노드까지 값이 전해지고 loss를 계산한다. 이렇게 입력값을 받아 연결된 노드들을 거쳐 마지막 출력값을 구하는 연산 과정을 forward propagation이라고 부른다. 그리고 forward propagation을 거친 후 backward propagation을 진행한다. 이때는 마지막 노드부터 gradient 값을 구하며 그 전 노드들의 gradient 값에 곱해주면서 update하려는 weight와 bias까지 전파된다. Back-propagation은 이 두 과정을 거쳐 진행된다.

Code


import torch

# Training Data
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
w = torch.tensor([1.0], requires_grad=True)

# our model forward pass
def forward(x):
    return x * w

# Loss function
def loss(y_pred, y_val):
    return (y_pred - y_val) ** 2
    
# Before training
print("Prediction (before training)",  4, forward(4).item())

# Training loop
for epoch in range(10):
    for x_val, y_val in zip(x_data, y_data):
        y_pred = forward(x_val) # 1) Forward pass
        l = loss(y_pred, y_val) # 2) Compute loss
        l.backward() # 3) Back propagation to update weights -> gradient 값만 계산됨
        print("\tgrad: ", x_val, y_val, w.grad.item())
        # w.grad : gradient 값을 나타내는 tensor가 반환되고, item() 함수로 값만 반환됨
        w.data = w.data - 0.01 * w.grad.item()

        # Manually zero the gradients after updating weights
        w.grad.data.zero_()  #  -> optimization 후엔 항상 gradient 값을 0으로 초기화 해야함

    print(f"Epoch: {epoch} | Loss: {l.item()}")

# After training
print("Prediction (after training)",  4, forward(4).item())

실행 결과

Prediction (before training) 4 4.0
grad: 1.0 2.0 -2.0
grad: 2.0 4.0 -7.840000152587891
grad: 3.0 6.0 -16.228801727294922
Epoch: 0 | Loss: 7.315943717956543
grad: 1.0 2.0 -1.478623867034912
grad: 2.0 4.0 -5.796205520629883
grad: 3.0 6.0 -11.998146057128906
Epoch: 1 | Loss: 3.9987640380859375
grad: 1.0 2.0 -1.0931644439697266
grad: 2.0 4.0 -4.285204887390137
grad: 3.0 6.0 -8.870372772216797
Epoch: 2 | Loss: 2.1856532096862793
grad: 1.0 2.0 -0.8081896305084229
grad: 2.0 4.0 -3.1681032180786133
grad: 3.0 6.0 -6.557973861694336
Epoch: 3 | Loss: 1.1946394443511963
grad: 1.0 2.0 -0.5975041389465332
grad: 2.0 4.0 -2.3422164916992188
grad: 3.0 6.0 -4.848389625549316
Epoch: 4 | Loss: 0.6529689431190491
grad: 1.0 2.0 -0.4417421817779541
grad: 2.0 4.0 -1.7316293716430664
grad: 3.0 6.0 -3.58447265625
Epoch: 5 | Loss: 0.35690122842788696
grad: 1.0 2.0 -0.3265852928161621
grad: 2.0 4.0 -1.2802143096923828
grad: 3.0 6.0 -2.650045394897461
Epoch: 6 | Loss: 0.195076122879982
grad: 1.0 2.0 -0.24144840240478516
grad: 2.0 4.0 -0.9464778900146484
grad: 3.0 6.0 -1.9592113494873047
Epoch: 7 | Loss: 0.10662525147199631
grad: 1.0 2.0 -0.17850565910339355
grad: 2.0 4.0 -0.699742317199707
grad: 3.0 6.0 -1.4484672546386719
Epoch: 8 | Loss: 0.0582793727517128
grad: 1.0 2.0 -0.1319713592529297
grad: 2.0 4.0 -0.5173273086547852
grad: 3.0 6.0 -1.070866584777832
Epoch: 9 | Loss: 0.03185431286692619
Prediction (after training) 4 7.804864406585693

Excercise 4-3: implement computational graph and backprop using Numpy


import torch
from torch.autograd import Variable

x_data = 2
y_data = 4
w = Variable(torch.tensor([1.0]), requires_grad=True)

y_pred = x_data * w
s = y_pred - y_data
loss = s**2
loss.backward()
print('grad : ', w.grad.item())

실행 결과

grad : -8.0

Excercise 4-4: Compute gradients using computational graph (manually) / Compute gradients using PyTorch


x_data = [1, 2, 3]
y_data = [2, 4, 6]

w1 = Variable(torch.tensor([0.]), requires_grad=True)
w2 = Variable(torch.tensor([0.]), requires_grad=True)
b = Variable(torch.tensor([0.]), requires_grad=True)

for x, y in zip(x_data, y_data):
    y_pred = x**2 * w1 + x * w2 + b
    loss = (y_pred - y) ** 2
    loss.backward()
    print('grad(w1) : ', w1.grad.item())
    print('grad(w2) : ', w2.grad.item())
    print('grad(b) : ', b.grad.item())
    w1.grad.data.zero_()
    w2.grad.data.zero_()
    b.grad.data.zero_()

실행 결과

grad(w1) : -4.0
grad(w2) : -4.0
grad(b) : -4.0
grad(w1) : -32.0
grad(w2) : -16.0
grad(b) : -8.0
grad(w1) : -108.0
grad(w2) : -36.0
grad(b) : -12.0

profile
NLP, AI, LLM, MLops

0개의 댓글