본문 바로가기
Theory/Pytorch

[파이토치 트랜스포머 #8] 4장 파이토치 심화 - 2) 가중치 초기화

by 남디윤 2024. 4. 12.

 

가중치 초기화

  • 가중치 초기화 Weight Initialization: 모델의 초기 가중치 값을 설정하는 것
  • 적절한 초깃값 설정
    • 기울기 폭주나 기울기 소실 문제 완화 가능
    • 모델의 수렴 속도 향상, 전반적인 학습 프로세스 개선

 

상수 초기화

  • 가중치를 초기화 하는 간단한 방법, 비용 거의 x
  • 초기 가중치 값을 모두 같은 값으로 초기화
  • 대표적으로, 0, 1, 특정 값 (Constant), 단위 행렬(Unit Matrix), 디랙 델타 함수(Dirac Delta Function) 값 등이 있음
  • 일반적으로 사용되지 않는 초기화 방법. 모든 가중치 초깃값을 같은 값으로 초기화하면, 배열 구조의 가중치에서 문제 발생하기 때문
    • 대칭 파괴 Breaking Symmetry 현상 발생. 모든 노드가 동일한 출력 생성. 모델 학습 불가
    • 스칼라값을 입력으로 받는 매우 작은 모델, 퍼셉트론 등에 적용.
    • 편향을 초기화하는 경우 0이나 0.01 등의 형태로 초기화하는데 사용됨

 

무작위 초기화

  • 초기 가중치 값을 무작위 값이나 특정 분포 형태로 초기화하는 것
  • 대표적으로, 무작위(Random), 균등 분포(Uniform Distribution), 정규 분포(Normal Distribution), 잘린 정규 분포(Truncated Normal Distribution), 희소 정규 분포 초기화(Sparse Normal Distribution Initialization) 등
  • 대칭 파괴 문제 방지, 간단, 많이 사용되는 초기화 방법
  • 기울기 소실 현상 발생 가능성 존재
    • 계층이 적거나 하나만 있는 경우 보편적으로 적용 가능
    • 계층이 많아질수록 활성화 값이 양 끝단에 치우치게 되어 기울기 소실 현상 발생

 

제이비어 & 글로럿 초기화

  • 제이비어 초기화 Xavier Initialization: 글로럿 초기화 Glorot Initialization라고도 불림
  • 균등 분포나 정규 분포를 사용한 가중치 초기화 방법
  • 가중치의 초기 분산을 이전 레이어의 노드수(fan-in)와 다음 레이어의 노드 수(fan-out)의 평균에 반비례하게 설정
  • $$
    W \sim U\left(-\sqrt{\frac{6}{\text{fan_in} + \text{fan_out}}}, \sqrt{\frac{6}{\text{fan_in} + \text{fan_out}}}\right) $$
  • (확률 분포 초기화 방법과의 차이) 동일한 표준 편차를 사용하지 않고 은닉층의 노드 수에 따라 다른 표준 편차를 할당
    • 이전 계층의 노드 수와 다음 계층의 노드 수에 따라 표준 편차가 계산됨
  • 시그모이드나 하이퍼볼릭 탄젠트(S자형)를 활성화 함수로 하는 네트워크에서 효과적 (입력 데이터의 분산이 출력 데이터에서도 유지되도록 가중치 초기화를 하기 때문에)

 

카이밍 & 허 초기화

  • 카이밍 초기화 Kaiming Initialization: 허 초기화 He Initialization라고도 불림
  • 레이어의 입력 노드 수(fan_in)에 따라 가중치 스케일을 조정하는 방법
  • $$
    W \sim N\left(0, \sqrt{\frac{2}{\text{fan_in}}}\right)
    $$

$$
W \sim U\left(-\sqrt{\frac{6}{\text{fan_in}}}, \sqrt{\frac{6}{\text{fan_in}}}\right)
$$

  • (제이비어 초기화와 비교)
    • (공통) 균등 분포나 정규 분포를 사용해 가중치를 초기화하는 방법
    • (공통) 각 노드의 출력 분산이 입력 분산과 동일하도록 가중치 초기화
    • (차이) 현재 계층의 입력 뉴런 수를 기반으로만 가중치를 초기화
  • ReLU를 활성화 함수로 사용하는 네트워크에서 효과적
    • 제이비어 초기화와 비교했을 때, 카이밍 초기화는 더 큰 분산을 사용하여 ReLU의 특성을 보완

 

직교 초기화

  • 직교 초기화 Orthogonal Initialization: 특잇값 분해를 활용해 자기 제신을 제외한 나머지 모든 열, 행 벡터들과 직교이면서 동시에 단위 벡터인 행렬을 만드는 방법
  • 가중치 행렬의 특이값을 보존
  • → 장기간 메모리 Long Short Term Memory, LSTM 및 게이트 순환 유닛 Gated Recurrent Unit GRU과 같은 순환 신경망 RNN에서 주로 사용됨
  • 직교 행렬의 고윳값의 절대값은 1이기 때문에 행렬 곱을 여러번 수행하더라도, 기울기 폭주나 기울기 소실 발생x

 

가중치 초기화 실습

  • 예제1: 간단한 예시 (모델 단순)
    • [_init_weights] 메서드: 모델 매개변수 초기값 설정
      • 프로텍티드 메서드 Protected Method
        • 메서드 이름 앞에 하나의 밑줄(_)을 붙여 사용
        • 클래스 또는 하위 클래스 외부에서 사용돼서는 안된다는 것을 나타내는 컨벤션으로 강제성은 없음
      • 가중치 초기화가 필요한 모듈, 클래스, 함수 등을 초기화
    • 가중치는 제이비어 초기화 적용, 편향은 상수 초기화 적용
      • 제이비어 초기화: nn.init.xavier_uniform_
      • 편향: fill_
    • 가중치 적용 변수: self.layer, self.fc
      • self.layer
        • init 메서드에서 Sequential 클래스를 활용해 선형 변환 함수(nn.Linear(1, 2))와 시그모이드 함수(nn.Sigmoid())를 하나로 묶음
        • 선형 변환 함수의 가중치와 편향 초기화 → 첫번째 index=0값을 불러옴
      • self.fc
        • init 메서드에서 단순히 선형 변환 함수 설정이기에 단순히 불러오면 됨
    • 초기화 메서드 호출은 모델의 계층이 정의된 직후 호출됨
from torch import nn

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

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

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

model = Net()
  • 예제2: 가중치 초기화 함수를 모듈화해 적용
    • 모델이 커지고 복잡해지면 코드도 복잡해짐 → 모듈화해 적용
    • torch.apply
      • 가중치 초기화 메서드를 범용적으로 적용
      • 적용 함수: 텐서의 각 요소에 임의의 함수를 적용, 결과와 함께 새 텐서 반환
      • 예제에서는 적용 함수에 가중치 초기화 메서드 전달해 초깃값 설정
    • 가중치 초기화 메서드에 module 매개변수 추가
      • nn.Module 클래스의 인스턴스들(여기서는 선형 레이어, 활성화 함수 등) 모두 모듈로 간주
      • 각 모듈이 nn.Linear 타입의 인스턴스인지 확인 (isinstance)
        • nn.Linear 타입의 인스턴스라면, 해당 모듈의 가중치를 제이비어 초기화하고, 편향을 상수 0.01로 초기화
    • 하위 모듈에 재귀적으로 적용
      • self.layer 변수의 선형 변환 함수, 시그모이드 함수가 적용된 후에 시퀀셜 호출
      • 모든 계층 호출했다면 최종으로 네트워크까지 호출하고 종료
from torch import nn

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

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

model = Net()
  • 이외에도 self.modules 메서드로도 모듈을 호출해 코드 구성 방법
  • 가중치 초기화 메서드는 파이토치의 내장 함수가 아니므로, 필수적인 요소는 아니지만, 코드의 가독성과 생산성을 위해 사용하기를 권장