4. AutoGrad & Optimizer

유승우·2022년 5월 11일
0

Layer


  • Layer는 하나의 레고블럭을 정의하고, 레고블럭들을 연결해 다음단계로 넘긴 다음 역전파를 통해 학습되어야 하는 가중치들을 업데이트 하는 형태의 구조로 이해하면 된다.
  • 따라서, 딥러닝의 큰 아키텍쳐는 레고블럭처럼 하나하나 쌓아서 다음으로 넘긴다 라고 생각하면 된다.

torch.nn.Module


  • 딥러닝을 구성하는 Layer의 base class
  • Input, Output, Forward, Backward 정의

torch.nn.Parameter


  • nn.Module을 구성하기 위한 학습의 대상이 되는 weight값들을 정의해야 하는데 이 값들을 parameter에 저장
  • nn.Module 내에 attribute가 될 때는 required_grad = True(Autograd)로 지정되어 학습 대상이 되는 Tensor
  • 대부분의 layer에는 weight 값들이 지정되어 있기 때문에, 우리가 직접 지정할 일은 없다
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

Backward


  • Layer에 있는 Parameter들의 미분을 수행하며, Forward의 결과값(output)과 실제값간의 차이(loss)에 대해 미분을 수행
  • 실제 backward는 Module 단계에서 직접 지정가능하지만, autograd가 알아서 해주기 때문에 필요가 없다.
  • Module에서 직접 지정하려면 backward와 optimizer을 오버라이딩해서 쓰는데 사용자가 직접 미분 수식을 써야하는 부담이 있다. → 쓸일은 없지만 순서는 이해할 필요가 있다.
for epoch in range(epochs):
    # Clear gradient buffers because we don't want any gradient from previous epoch to carry forward, dont want to cummulate gradients
    optimizer.zero_grad() # 이전의 gradient값이 지금의 영향을 주지 않게 하기 위해서 초기화

    # 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.r.t to parameters
    loss.backward()

    # update parameters
    optimizer.step()

    print('epoch {}, loss {}'.format(epoch, loss.item()))
# Module에서 parameter 지정
class LR(nn.Module):
    def __init__(self, dim, lr=torch.scalar_tensor(0.01)):
        super(LR, self).__init__()
        # intialize parameters
        self.w = torch.zeros(dim, 1, dtype=torch.float).to(device)
        self.b = torch.scalar_tensor(0).to(device)
        self.grads = {"dw": torch.zeros(dim, 1, dtype=torch.float).to(device),
                      "db": torch.scalar_tensor(0).to(device)}
        self.lr = lr.to(device)

    def forward(self, x):
        ## compute forward
        z = torch.mm(self.w.T, x) + self.b
        a = self.sigmoid(z)
        return a

    def sigmoid(self, z):
        return 1/(1 + torch.exp(-z))

    def backward(self, x, yhat, y):
        ## compute backward
        self.grads["dw"] = (1/x.shape[1]) * torch.mm(x, (yhat - y).T)
        self.grads["db"] = (1/x.shape[1]) * torch.sum(yhat - y)
    
    def optimize(self):
        ## optimization step
        self.w = self.w - self.lr * self.grads["dw"]
        self.b = self.b - self.lr * self.grads["db"]

## utility functions
def loss(yhat, y):
    m = y.size()[1]
    return -(1/m)* torch.sum(y*torch.log(yhat) + (1 - y)* torch.log(1-yhat))

def predict(yhat, y):
    y_prediction = torch.zeros(1, y.size()[1])
    for i in range(yhat.size()[1]):
        if yhat[0, i] <= 0.5:
            y_prediction[0, i] = 0
        else:
            y_prediction[0, i] = 1
    return 100 - torch.mean(torch.abs(y_prediction - y)) * 100

0개의 댓글