Pytorch - 2. 모델 만들기

Ui Jin·2022년 9월 27일
1

Pytorch

목록 보기
2/4

Pytorch Model

1. 모델 제작

1) 개요

딥러닝 모델을 제작할 때, 우리는 여러 Parameter를 가지고 있는 하나의 층을 겹겹이 쌓아 하나의 모델을 만들게 된다.

이때, 쌓는 방법과 층의 종류 등에 따라서 모델의 성능이 결정된다.

Pytorch는 우리가 좀 더 모델을 편하게 만들 수 있도록 도와주기 위해 torch.nn.Module이라는 라이브러리를 제공한다.

2) nn.Module

위와 같이 torch.nn.Module에는 다양한 함수들이 존재한다.

모델을 만드는 과정을, 정말 간단하게 요약하면 다음과 같다.
1. torch.nn.Module상속
2. __init__()으로 클래스 초기화
3. forward()함수 재정의

즉, 위의 3과정은 반드시 해주어야 한다.


forward 작동원리

Python에는 Magic Method라고 불리는 Python 내부적으로 이미 구현된 함수가 존재한다.
(이 함수들은 언더바로 감싸져 있고 __init__()이 대표적인 예이다.)

__call__()은 호출자 함수로, 이미 생성된 객체를 호출할 때 실행되는 함수이다.

이때, nn.Module에서는 이 __call__()함수에 forward를 호출하도록 재정의 해 놓았기 때문에 객체를 호출할 때, foward가 실행되게 된다.

3) Parameter

층을 쌓기 전, 주의해야 할 부분이 있다.
다음 두 코드를 비교해 보자.


- Parameter

class Linear(nn.Module):
  def __init__(self, in_feat, out_feat):
      super().__init__()
      self.W = Parameter(torch.ones(out_feat, in_feat))
      self.b = Parameter(torch.ones(1, out_feat))

- Tensor

class Linear(nn.Module):
  def __init__(self, in_feat, out_feat):
      super().__init__()
      self.W = torch.ones(out_feat, in_feat)
      self.b = torch.ones(1, out_feat)

위의 두 코드는 Parameter인지 Tensor인지의 차이가 있다.

먼저 Tensor로 생성하면, 손실함수를 구해 값을 Update하고자 할 때, gradient를 계산하는 함수가 존재하지 않아 이 과정이 불가능하다.

반면에 Parameter로 생성할 경우에는 gradient를 계산하는 함수가 존재하여 이 과정이 가능하다.


앞으로는 대부분 linearConv2D와 같은 내부 모델을 사용하기 때문에 이 Parameter를 직접 설정하는 경우는 거의 없다.


- nn.Module.parameters()

모듈내의 auto_grad기능을 가진 변수들의 위치를 반환하는 nn.Module의 parameters와는 역할이 다르다는 것을 유의하자.

4) Container

  1. torch.nn.Sequential
    • 여러 Module들을 하나로 묶어 순차적으로 실행하고자 할 때 사용
class Test(nn.Module):
    def	__init__(self):
    	super().__init__()
        self.fc = nn.Sequential(
        	nn.Linear(~),
            nn.MaxPool1d(~)
        )
    def forward(self, x):
    	x = self.fc(x)
        return x

  1. torch.nn.ModuleList
    • 여러 Module들을 하나의 변수에 묶어 보관하고자 할 때 사용
    • 일반 list에 보관하는 방법과는 다르게 보관하는 Module을 확인 할 수 있다.
class Test(nn.Module):
    def	__init__(self):
    	super().__init__()
        self.fc = nn.ModuleList([
        	nn.Linear(~),
            nn.MaxPool1d(~)
        ])
    def forward(self, x):
    	x = self.fc[0](x)
    	x = self.fc[1](x)
        return x

  1. torch.nn.ModuleDict
    • 여러 Module들을 하나의 변수에 묶어 보관하고자 할 때 사용
    • MouleList와 비슷하나, dict형태라는 차이점이 있다.
class Test(nn.Module):
    def	__init__(self):
    	super().__init__()
        self.fc = nn.ModuleDict({
        	'one' : nn.Linear(~),
            'two' : nn.MaxPool1d(~)
        })
    def forward(self, x):
    	x = self.fc['one'](x)
    	x = self.fc['two'](x)
        return x

5) 제작

2번에 따라 딥러닝 모델을 만들고 nn.Module의 여러 기능들, Container등을 활용해 우리가 원하는 Model을 구성하자.


2. 학습

위의 코드처럼 학습은 다음의 과정을 Epoch만큼 반복하며 진행된다.

1) Forward

먼저 위에서 제작한 딥러닝 모델(Class)를 사용해 객체를 생성한다.

그 후에 해당 객체를 호출할 때마다 정해진 Layer를 지나 우리가 원하는 예측값(Output)을 얻는다.

2) Loss

위의 Forward과정에서 얻은 예측값과 실제 정답값을 비교하여 손실함수를 구한다.

3) Backward

위에서 구한 손실함수를 이용해 각 Parameter들의 gradient를 얻는다.

그리고 이 gradient를 활용해 parameter들을 update한다.


3. nn.Module 기타기능 설정

1) 모델 확인

아직까지는 우리가 모델을 직접 만들어 사용했기 때문에 이 부분이 잘 이해가 되지 않을 순 있다.

하지만 앞으로 우리는 대부분 다른 사람들의 모델을 참조하여 우리의 모델을 학습시킬일이 훨씬 많다.

이 경우 어떻게 모델을 분석해 볼 수 있을지 알아보자


모델의 Layer의 구성요소 출력

  1. (nn.Module객체).named_modules()
    • 모델에 속한 모든 Submodule 출력
    for name, module in model.named_modules():
     print(f"[ Name ] : {name}\n[ Module ]\n{module}")
     print("-" * 30)
  1. (nn.Module객체).named_children()
    • 모델의 바로 아래 단계의 Submodule만 출력
    for name, child in model.named_children():
     print(f"[ Name ] : {name}\n[ Children ]\n{child}")
     print("-" * 30)
  1. (nn.Module객체).named_parameters()
    • 모델에 존재하는 parameters들을 출력
    for name, parameter in model.named_parameters():
     print(f"[ Name ] : {name}\n[ Parameter ]\n{parameter}")
     print("-" * 30)

모델의 구성요소 가져오기

  1. (nn.Module객체).get_submodule("~")
    • parameter로 주어진 이름의 Submodule을 가져온다.
    submodule = model.get_submodule("ab.a")
  1. (nn.Module객체).get_parameter("~")
    • parameter로 주어진 이름의 parameter를 가져온다.
    parameter = model.get_parameter("ab.b.W1")

모듈의 extra출력 설정하기

  1. 다음과 같이 해당 모듈 안에서 extra_repr(self)함수를 재정의 해줄 경우 extra출력을 설정할 수 있다.

2) hook

객체의 call(), 즉 객체를 호출하기 전과, 객체의 호출이 끝난 후 항상 실행되어야 할 함수를 설정하는 방법이다.

nn.Module을 상속받았을 경우 이 hook함수가 주어지는데 이는 다음과 같다.

  1. register_forward_pre_hook(hook)
    • 객체 호출 전 실행되는 함수.
    • hook(module, input)의 prototype을 가지는 함수를 인자로 가진다.
  1. register_forward_hook(hook)
    • 객체 호출 후 실행되는 함수
    • hook(module, input, output)의 prototype을 가지는 함수를 인자로 가진다.
  1. register_full_backward_hook(hook)
    • 모델의 backward작업 후 실행되는 함수
    • hook(module, grad_input, grad_output)의 prototype을 가지는 함수를 인자로 가진다.

3) apply

  1. 모델의 Module들에 Postoreder순서로 임의의 함수를 적용하는 함수로, 주로 가중치 초기화에 사용한다.
def weight_initialization(module):
    module_name = module.__class__.__name__
    if module_name[:8] == "Function":
      module.W.data.fill_(1.0)

returned_module = model.apply(weight_initialization)

자주 사용하는 함수

  • torch.nn.Linear
    : linear parameter를 가지는 layer를 만들고 싶을 때 사용
  • torch.nn.lazyLinear
    : input의 크기를 정하지 않고, output의 크기만 정하고 싶을 때 사용
    : 즉, 첫번째 forward에서 임의로 초기화 하고 그 후에 nn.Linear와 같이 동작한다.

주의사항

pytorch에서는 다음과 같은 특징을 갖는다.

  1. BCELOSS = NLLLoss + Softmax
  2. BCEloss는 원핫인코딩이 아닌 index번호를 받는다
profile
github로 이전 중... (https://uijinee.github.io/)

0개의 댓글