본문 바로가기
Deep Learning

[Pytorch]Autograd

by Yonghip 2023. 3. 19.

 

 

 

 

 

Module

torch.nn.Module, forward

Input, Output, Forward, Backward을 정의하는 Pytorch에서 모델을 만드는데 필요한 base class

class MyLiner(nn.Module):
    def __init__(self, in_features, out_features, bias=True):
        super().__init__()
        self.in_features = in_features
        self.out_features = out_features
        
        self.weights = nn.Parameter(
                torch.randn(in_features, out_features))
        
        self.bias = nn.Parameter(torch.randn(out_features))

    def forward(self, x : Tensor):
        return x @ self.weights + self.bias

기본적으로 이와 같은 형태로 정의한다.

 

 

 

x = torch.randn(5, 7)
layer = MyLiner(7, 12)
print(layer(x))
print(layer(x).shape)
tensor([[-1.0976, -3.7846, -0.3487,  0.6130, -1.4488, -2.3584,  1.9038, -1.8659,
         -2.0500,  3.6289, -3.1050,  1.0894],
        [-1.6706,  0.3755, -1.5286,  2.5324, -1.1470, -2.9243,  0.6232, -1.3477,
         -2.9759, -0.7234, -2.1702,  1.6472],
        [-0.6848, -3.1662,  1.6751,  2.8280,  0.0547, -7.0330,  4.6043,  0.4106,
          2.5847,  2.0780, -2.8696, -2.6945],
        [-2.6873,  5.5187,  0.4022,  0.9746, -2.5212, -1.7941, -5.1957,  1.9673,
          5.3406,  0.0358, -0.6157, -1.8670],
        [-0.6576, -4.4255, -3.1510, -3.5183, -0.8047,  3.2060,  0.0453, -1.6277,
          0.4563, -0.6117, -0.9793, -0.9038]], grad_fn=<AddBackward0>)
          
torch.Size([5, 12])

보시다시피 instance호출시 결과로 x에 forward연산을 진행한 값을 리턴해준다.

Module class를 직접 뜯어보지는 않았지만 아마도

객체 호출시 연산이 자동적으로 실행되는 forward를 미리 정의해두어서 이를 overriding하여 연산을 넣어주는것 같다.

 

 

x = torch.randn(3, 5, 7)
layer = MyLiner(7, 12)
print(layer(x).shape)
torch.Size([3, 5, 12])

batch_size를 앞에 붙여줘도 이에 맞춰서 동작한다.

 

 

Backward

Backward연산은 특히 중요하므로 따로 떼서 설명해보겠다.

 

backward

매개변수의 기울기를 반환한다

x = torch.ones(5)  # input tensor
y = torch.zeros(3)  # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w)+b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)

loss.backward()
print(w.grad)
print(b.grad)
tensor([[0.1868, 0.2503, 0.2583],
        [0.1868, 0.2503, 0.2583],
        [0.1868, 0.2503, 0.2583],
        [0.1868, 0.2503, 0.2583],
        [0.1868, 0.2503, 0.2583]])
tensor([0.1868, 0.2503, 0.2583])

loss가 목적함수이고 w와b를 requires_grad=True로 설정했으니

loss.backwart( )로 loss에 대한 w와b의 도함수를 구할 수 있다(도함수 구하는 공식을 몰라 어째서 값이 저렇게 똑같이 나왔는지는 모르겠다...)

 

 

 

이 backward를 Pytorch의 모듈 클래스에 적용하려면 하는 방식은 아래와 같다

class LinearRegression(torch.nn.Module):
    def __init__(self):
        super(LinearRegression, self).__init__()
        self.w = torch.nn.Parameter(torch.rand(1, requires_grad=True))
        self.b = torch.nn.Parameter(torch.rand(1, requires_grad=True))        
    

    def forward(self, x):
        out = x * self.w
        out = x + self.b
        return out
              
criterion = torch.nn.MSELoss() 
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

print(list(model.parameters()))
print(print(model.w, model.b))
[Parameter containing:
 tensor([0.7073], requires_grad=True),
 Parameter containing:
 tensor([0.0711], requires_grad=True)]
 
 Parameter containing:
tensor([0.7073], requires_grad=True) Parameter containing:
tensor([0.0711], requires_grad=True)

먼저 Parameter에 w와b를 넣어주고 forward연산을 정의한다.(실제로 torch.nn.Parameter로 파라미터를 추가하는 경우는 거의 없다 그저 예시를 위해 가장 근본적인 attribute를 사용했을뿐이다)

model이 parameter로 저 둘을 추적하고 있는걸 확인할 수 있다.

 

 

 

for epoch in range(epochs):
    inputs = torch.from_numpy(x_train)
    labels = torch.from_numpy(y_train)

    # Clear gradient buffers because we don't want any gradient from previous epoch to carry forward, dont want to cummulate gradients
    optimizer.zero_grad()

    # get output from the model, given the inputs
    outputs = model(inputs)
`
    # get loss for the predicted output
    loss = criterion(outputs, labels)
    print(loss)
    # get gradients w,b to parameters
    loss.backward()

    # update parameters
    optimizer.step()

    print('epoch {}, loss {}'.format(epoch, loss.item()))
tensor(40.4280, grad_fn=<MseLossBackward0>)
epoch 0, loss 40.427982330322266
tensor(39.2230, grad_fn=<MseLossBackward0>)
epoch 1, loss 39.2230339050293
tensor(38.0658, grad_fn=<MseLossBackward0>)
epoch 2, loss 38.065799713134766
tensor(36.9544, grad_fn=<MseLossBackward0>)
epoch 3, loss 36.95439147949219
tensor(35.8870, grad_fn=<MseLossBackward0>)
epoch 4, loss 35.88699722290039
tensor(34.8619, grad_fn=<MseLossBackward0>)
......

실제로 사용시에는  torch내부에 구현되어 있는 loss와 optimizer를 사용하게 된다.

loss에 모델의 출력과 라벨값을 넣어주면 모델의 손실이 나오고 backward( )를 통해 w와b의 gradient를 계산한다

optimizer.step( )은 미리 넣어준 model.parameters( )들을 gradient만큼 optimizer공식대로 이동시켜준다.

실제로 이 과정을 통해 epoch별로 loss가 줄어드는 것을 확인할 수 있다.

 

 

 


 

설명이 길어졌지만 막상 모델을 만들때는 마지막 부분만 응용해서 만들 느낌이다

 

 

 

 

 

 

 

출처: https://tutorials.pytorch.kr/beginner/basics/autogradqs_tutorial.html

https://towardsdatascience.com/how-to-build-your-own-pytorch-neural-network-layer-from-scratch-842144d623f6