본문 바로가기

책/파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습

04 파이토치 심화 (2) 가중치 초기화

가중치 초기화

모델의 초기 가중치 값을 설정하는 것을 말한다. 모델 매개변수에 적절한 초깃값을 설정한다면 기울기 폭주나 기울기 소실 문제를 완화할 수 있다. 또한 모델의 수렴 속도를 향상시켜 전반적인 학습 프로세스를 개선할 수 있다.

 

무작위 초기화

초기 가중치의 값을 무작위 값이나 특정 분포 형태로 초기화하는 것을 말한다.

무작위(Random), 균등 분포(Uniform Distribution), 정규 분포(Normal Distribution), 잘린 정규 분포(Truncated Normal Distribution), 희소 정규 분포 초기화(Sparse Normal Distribution Initialization) 등이 있다.

 

무작위 초기화는 계층이 많아지고 깊어질수록 활성화 값이 양 끝단에 치우치게 되어 기울기 소실 현상이 발생한다.

 

Xavier & Glorot

제이비어 초기화는 글로럿 초기화라고도 하며, 균등 분포나 정규 분포를 사용해 가중치를 초기화 하는 방법이다.

 

제이비어 초기화는 평균이 0이고 현재 계층의 입력 및 출력 노드 수를 기반으로 계산되는 표준 편차를 가지는 정규분포로 가중치를 초기화한다.

 

균등 분포

$W=U(-a, a)$

$a = gain*\sqrt{6 \over fan_{in}+fan_{out}}$

 

정규 분포

$W=N(0, std^2)$

$std = gain*\sqrt{2 \over fan_{in}+fan_{out}}$

 

kaiming & He

제이비어와 마찬가지로 균등 분포나 정규 분포를 사용해 가중치를 초기화하는 방법이다.

 

균등 분포

$W=U(-a, a)$

$a = gain*\sqrt{3 \over fan_{in}}$

 

정규 분포

$W=N(0, std^2)$

$std = {gain \over \sqrt{fan_{in}}}$

 

수식에서 알 수 있듯이 입력 뉴런 수를 기반으로만 가중치를 초기화한다. 각 노드의 출력 분산이 입력 분산과 동일하게 만들어 ReLU 함수의 죽은 뉴런 문제를 최소화 할 수 있어 ReLU 활성화 함수를 사용하는 네트워크에서 효과적이다.

 

가중치 초기화 실습

모델 클래스를 구축하고 모델 매개변수의 초깃값을 설정할 때 주로 사용된다. 다음 코드는 가중치 초기화 시 가장 많이 사용하는 방식이다.

 

class Net(torch.nn.Module):
  def __init__(self):
    super().__init__()
    self.layer = torch.nn.Sequential(
        torch.nn.Linear(1, 2),
        torch.nn.Sigmoid(),
    )
    self.fc = torch.nn.Linear(2, 1)
    self._init_weights()

  def _init_weights(self): # Protected Method
    torch.nn.init.xavier_uniform_(self.layer[0].weight)
    self.layer[0].bias.data.fill_(0.01)

    torch.nn.init.xavier_uniform_(self.fc.weight)
    self.fc.bias.data.fill_(0.01)

model = Net()
  • _init_weights 메서드에서 모델 매개변수의 초깃값을 설정한다. 메서드 이름 앞에 언더바를 하나 붙여 protected method로 사용한다. 이는 클래스 또는 해당 하위 클래스 외부에서 사용돼서는 안된다는 것을 나타내는 컨벤션으로 강제성은 없다.
  • 제이비어 초기화 함수는 nn.init.xavier_uniform_으로 값을 초기화 할수 있으며, 편향은 간단하게 fill_ 메서드로 값을 채울 수 있다.
  • 일반적으로 가중치 초기화 메서드는 모델의 계층이 정의된 직후 호출한다.

 

모델의 구조가 복잡해지면 가중치 초기화 메서드를 모듈화해 적용한다.

class Net(torch.nn.Module):
  def __init__(self):
    super().__init__()
    self.layer = torch.nn.Sequential(
        torch.nn.Linear(1, 2),
        torch.nn.Sigmoid(),
    )
    self.fc = torch.nn.Linear(2, 1)
    self.apply(self._init_weights)

  def _init_weights(self, module):
    if isinstance(module, torch.nn.Linear):
      torch.nn.init.xavier_uniform_(module.weight)
      torch.nn.init.constant_(module.bias, 0.01)
    print(f"Apply : {module}")

model = Net()

torch.apply는 텐서의 각 요소에 임의의 함수를 적용하고 결과와 함께 새 텐서를 반환한다.

가중치 초기화 메서드에 module 매개변수를 추가한다. module 매개변수는 초기화 메서드에서 선언한 모델의 매개변수를 의미한다.

모델의 매개변수를 객체 식별 함수(isinstance)로 선형 변환 함수인지 확인하여 가중치와 편향을 초기화한다. 이번 예제에서는 편향을 fill_ 메서드가 아닌 nn.init.constant_ 함수로 초기화하였다.