2주차 Model 과제정리

kimkihoon·2022년 2월 1일
0

부스트캠프 AI

목록 보기
4/49

Pytorch Model 정리

Tensor vs tensor

Tensor는 클래스, int 입력시 float로 자동변환, 데이터 입력시 새롭게 torch.Tensor를 만든 후 사용하고 입력받은 데이터의 메모리 공간 사용
tensor는 함수, int 입력시에 int 그대로 출력, 입력 받은 데이터를 새로운 메모리 공간으로 복사 후 사용

인덱싱

torch.Tensor.index_select

torch.Tensor.index_select(A,1,torch.tensor([0]))
A는 인덱싱을 할 행렬, 1 axis를 의미하고, torch.tensor([0])는 어떤 부분을 자를 것인지 의미한다.
이와 같은 방법으로는 A[:,0] 를 사용할 수 있다.

torch.gather

torch.Tensor.gather(A,1,torch.tensor([[0],[1]]))
tensor 좌표들 중 내가 모으고 싶은 것들만 모으는 함수로 index_select와 비슷하게 생각하면 된다.

torch.nn.linear

torch.nn.linear(입력값,출력값)와 같은 형태로 사용하며 tensor의 크기나 모양을 변환하고 싶을 때에 사용한다. torch.nn.parameter를 사용하며 layer 생성 시에 parameter(W,b)가 초기화 된다.

Module

torch.nn.Module

여러 기능들을 모아두는 상자같은 역할로 neural network 모듈로써 parameter를 GPU로 옮기거나 내보내기 불러오기 등의 보조 작업을 이용하여 parameter를 캡슐화하는 방법이다. 기본적인 구조는 다음과 같다.

import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.conv1 = nn.Conv2d(1, 20, 5)
        self.conv2 = nn.Conv2d(20, 20, 5)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        return F.relu(self.conv2(x))

init 을 통해 forward에서 사용할 변수들을 초기화해놓는다. 그리고 forward에서 이를 불러와서 사용한다. Module은 또한 Module을 포함할 수 있다.
function : 최소 기능 단위
layer : function들로 이루어짐
model : layer로 이루어짐
실행되는 순서는 function의 init -> layer의 init -> model의 init -> fucntion의 forward -> layer의 forward -> model의 forward

torch.nn.Sequential

위에서 만든 모듈같이 여러 개의 모듈들을 하나로 묶어서 사용하고자 할 때에 사용한다.

calculator = nn.Sequential(Add(3),
                           Add(2),
                           Add(5))

torch.nn.ModuleList

정의한 모듈들을 list로 만들어서 필요할 때마다 특정 모듈을 불러와서 호출하고자 할 때에 사용한다.

class Calculator(nn.Module):
    def __init__(self):
        super().__init__()
        self.add_list = nn.ModuleList([Add(2), Add(3), Add(5)])
    def forward()
    ~~~

굳이 파이썬이 지원하는 내장 list가 아닌 nn.ModuleList를 사용하는 이유는 파이썬의 list로 저장을 하면 submodule로 등록이 안되기 때문이다.

Parameter

Y = wX + b에서 w와 b에 해당하는 값이다. nn.Module 안에 미리 만들어둔 tensor들을 보관하여 사용한다. 이것들을 tensor가 아닌 parameter로 선언하는 이유는 parameter로 선언해주어야 back propagation에서 gradient값을 계산하여 update해주기 때문이다.
Model().named_parameters() 를 통해 parameter의 이름과 어떤 parameter를 가지고 있는지 확인 가능하다. 그리고 그것을 통해 찾은 parameter는 Model().get_parameter(str)를 통해 불러올 수 있다.

buffer

Model을 저장할 때에 저장할 Tensor를 buffer로 등록한다.
register_buffer('name',torch.tensor([1]),persistent= True)로 등록할 수있다.
forward에서 self.'name' 을 통해 사용이 가능하다.
parameter와 마찬가지로 Model().named_buffers()를 통해 이름과 어떤 값을 가지고 있는지 확인할 수 있고 이를 통해 확인한 buffer들을 Model().get_buffer(str)를 통해 불러올 수 있다.

hook

패키지화된 코드에서 다른 프로그래머가 custom 코드를 중간에 실행시킬 수 있도록 만들어놓은 인터페이스 프로그램 실행 전에 hook을 사용하고 싶을 때에는 pre_hook을 사용한다.
tensor.register_hook(hook이름) 을 통해 hook을 등록할 수 있다.
크게 Tensor에 적용하는 hook과 Module에 적용하는 hook으로 나눠볼 수 있다. Tensor에 적용하고 싶을 때에는 _backward_hooks 에서 확인할 수 있다.
forward에는 pre_hook과 hook이 있고 backward에는 hook만 존재한다.
hook을 통해 값을 저장하는 것 뿐만아니라 수정도 가능하다.

def hook(module, input, output):
    return output + 5

add.register_forward_hook(hook)

forward hook이 module에만 적용이 가능한 반면 backward hook은 tensor와 module 2가지에 적용이 가능하다.

def module_hook(module, grad_input, grad_output):
    answer.extend(grad_input)
    answer.extend(grad_output)

model.register_full_backward_hook(module_hook)

hook을 통해 x1,x2,output의 미분값을 구해서 저장한다.
이렇게 module 단위로 backward hook을 적용할 수 있지만 module 내부의 tensor의 미분값을 알고 싶을 때에는 다음과 같이 Tensor 단위로 접근할 수 있다.

def tensor_hook(grad):
    answer.extend(grad)

model.W.register_hook(tensor_hook) # tensor 단위

이런것들을 통해 gradient 값의 변화를 시각화하거나, 그 값이 특정 임계점을 넘으면 경고를 하거나 특정 tensor의 gradient 값이 너무 커지거나 작아지면 해당 tensor 한정으로 gradient clipping을 해볼 수 있다.

apply

nn.Module에 이미 구현되어 있는 method가 아닌 custom 함수를 모델에 적용하고 싶을 때에 사용한다. apply를 통해 적용받는 함수는 module을 입력으로 받고 model의 모든 module들을 순차적으로 입력받아서 처리한다. apply함수는 일반적으로 가중치 초기화에 많이 사용된다. 일반적으로 다음과 같이 가볍게 사용해볼 수 있다.

def print_module(module):
    print(module)
    print("-" * 30)

returned_module = model.apply(print_module)

가중치 초기화 예제

model = Model()
def weight_initialization(module):
    module_name = module.__class__.__name__

    if module_name.split('_')[0] == "Function":
        module.W.data.fill_(1.) # W의 모든 원소를 1로 초기화

returned_module = model.apply(weight_initialization)

0개의 댓글