본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] 가중치 초기화의 2가지 핵심 기법(He vs Xavier)과 활성화 함수 결합의 수학적 정당성 해결 방법

by Papa Martino V 2026. 4. 15.
728x90

He vs Xavier
He vs Xavier

 

딥러닝 모델을 설계할 때 우리가 가장 먼저 직면하는 기술적 난제는 "어떻게 네트워크의 파라미터를 초기화할 것인가?"입니다. 단순히 무작위 숫자를 채워 넣는 것만으로는 심층 신경망의 복잡한 기울기 흐름을 제어할 수 없습니다. 특히 활성화 함수의 선택에 따라 가중치 초기화 전략이 달라져야 한다는 사실은 모델의 수렴 속도와 성능을 결정짓는 결정적인 요소입니다. 본 글에서는 Xavier 초기화와 He 초기화가 각각 Tanh 및 ReLU와 결합될 때 가지는 수학적 배경과 파이썬 실무 적용 사례를 심층적으로 다룹니다.


1. 왜 가중치 초기화(Weight Initialization)인가?

신경망이 깊어질수록 Gradient Vanishing(기울기 소실)Gradient Exploding(기울기 폭주) 현상이 빈번하게 발생합니다. 이는 역전파(Backpropagation) 과정에서 층을 거칠수록 기울기가 0으로 수렴하거나 무한대로 발산하여 학습이 불가능해지는 현상을 말합니다. 이를 해결하기 위한 근본적인 해결책은 각 층의 출력 분산(Variance)을 일정하게 유지하여 정보 전달의 효율성을 극대화하는 것입니다.


2. Xavier Initialization (Glorot Initialization)

2010년 Xavier Glorot에 의해 제안된 이 방식은 선형적인 활성화 함수(또는 선형에 가까운 S자형 함수)를 사용할 때 최적의 성능을 발휘합니다.

수학적 정당성

Xavier 초기화의 핵심 가정은 각 층의 입력과 출력의 분산이 같아야 한다는 것입니다. 활성화 함수를 $f(z)$라고 할 때, 입력을 $x$, 가중치를 $w$라고 하면 출력 $y$는 다음과 같습니다.

$$Var(y) = Var(x) \cdot n \cdot Var(w)$$

여기서 $Var(y) = Var(x)$가 되기 위해서는 $Var(w) = 1/n$이 되어야 합니다. Xavier는 입력 노드 수($n_{in}$)와 출력 노드 수($n_{out}$)를 모두 고려하여 다음과 같은 분산을 제안했습니다.

$$Var(W) = \frac{2}{n_{in} + n_{out}}$$


3. He Initialization (Kaiming Initialization)

ReLU(Rectified Linear Unit) 함수가 등장하면서 Xavier 방식의 한계가 드러났습니다. ReLU는 출력의 절반을 0으로 만들기 때문에 출력 분산이 절반으로 줄어듭니다. 이를 보완하기 위해 2015년 Kaiming He는 새로운 초기화 공식을 제시했습니다.

수학적 정당성

ReLU를 통과한 값의 분산은 입력 분산의 절반이 됩니다. 따라서 이를 보상하기 위해 가중치의 분산을 2배로 늘려야 합니다. He 초기화의 가중치 분산 공식은 다음과 같습니다.

$$Var(W) = \frac{2}{n_{in}}$$

이 수학적 보정을 통해 ReLU 기반의 심층 신경망에서도 안정적인 학습이 가능해졌습니다.


4. He vs Xavier 비교 요약

비교 항목 Xavier Initialization He Initialization
제안 시점 2010년 (Glorot et al.) 2015년 (Kaiming He et al.)
최적 활성화 함수 Tanh, Sigmoid ReLU, Leaky ReLU, ELU
핵심 수학 공식 $Var(W) = 2 / (n_{in} + n_{out})$ $Var(W) = 2 / n_{in}$
주요 목적 입출력 분산의 보존 ReLU의 비활성화 특성 보정
기울기 소실 방지 S자형 함수에서 탁월 심층 ReLU 네트워크에서 탁월

5. 실무 적용을 위한 Python 코드 사례 (Example 7선)

개발자가 실무에서 즉시 활용할 수 있도록 PyTorch와 NumPy 기반의 다양한 초기화 구현 사례를 제공합니다.

Example 1: PyTorch에서 기본 Xavier 초기화 적용 (Tanh 조합)

import torch
import torch.nn as nn

class XavierNetwork(nn.Module):
    def __init__(self):
        super(XavierNetwork, self).__init__()
        self.layer = nn.Linear(784, 256)
        # Xavier Normal 초기화 적용
        nn.init.xavier_normal_(self.layer.weight)
        self.activation = nn.Tanh()

    def forward(self, x):
        return self.activation(self.layer(x))

model = XavierNetwork()
print(model.layer.weight.std())

Example 2: PyTorch에서 He 초기화 적용 (ReLU 조합)

import torch.nn as nn

class HeNetwork(nn.Module):
    def __init__(self):
        super(HeNetwork, self).__init__()
        self.layer = nn.Linear(784, 256)
        # He Normal 초기화 적용 (Kaiming)
        nn.init.kaiming_normal_(self.layer.weight, mode='fan_in', nonlinearity='relu')
        self.activation = nn.ReLU()

    def forward(self, x):
        return self.activation(self.layer(x))

model = HeNetwork()

Example 3: NumPy를 이용한 Xavier 초기화 직접 구현

import numpy as np

def xavier_init(n_in, n_out):
    # 표준편차 계산: sqrt(2 / (n_in + n_out))
    std = np.sqrt(2.0 / (n_in + n_out))
    return np.random.randn(n_in, n_out) * std

weights = xavier_init(784, 256)
print(f"Xavier Weights Shape: {weights.shape}")

Example 4: NumPy를 이용한 He 초기화 직접 구현

import numpy as np

def he_init(n_in):
    # 표준편차 계산: sqrt(2 / n_in)
    std = np.sqrt(2.0 / n_in)
    return np.random.randn(n_in, 256) * std

weights = he_init(784)
print(f"He Weights Shape: {weights.shape}")

Example 5: 가중치 초기화에 따른 레이어별 출력 분산 시각화 (실무 디버깅용)

import matplotlib.pyplot as plt
import torch

def check_variance(init_type='he'):
    x = torch.randn(1000, 512)
    layers = [nn.Linear(512, 512) for _ in range(10)]
    
    activations = []
    curr_x = x
    for layer in layers:
        if init_type == 'he':
            nn.init.kaiming_normal_(layer.weight)
            curr_x = torch.relu(layer(curr_x))
        else:
            nn.init.xavier_normal_(layer.weight)
            curr_x = torch.tanh(layer(curr_x))
        activations.append(curr_x.var().item())
    
    plt.plot(activations)
    plt.title(f"Variance Flow: {init_type}")
    plt.show()

# check_variance('he')

Example 6: Custom Layer에서 가중치 초기화 자동화

class CustomDense(nn.Module):
    def __init__(self, in_features, out_features, act_func='relu'):
        super().__init__()
        self.linear = nn.Linear(in_features, out_features)
        
        if act_func == 'relu':
            nn.init.kaiming_normal_(self.linear.weight)
        elif act_func == 'tanh':
            nn.init.xavier_normal_(self.linear.weight)
            
    def forward(self, x):
        return self.linear(x)

Example 7: 전이 학습(Transfer Learning) 시 파라미터 고정 및 초기화

import torchvision.models as models

def setup_model():
    model = models.resnet18(pretrained=True)
    # 기존 가중치 고정
    for param in model.parameters():
        param.requires_grad = False
    
    # 마지막 레이어 교체 및 He 초기화 적용
    num_ftrs = model.fc.in_features
    model.fc = nn.Linear(num_ftrs, 10)
    nn.init.kaiming_normal_(model.fc.weight)
    return model

6. 결론 및 고찰

가중치 초기화는 단순한 하이퍼파라미터 튜닝 이상의 의미를 갖습니다. 이는 신경망 내부의 통계적 신호 흐름을 설계하는 과정입니다. ReLU 계열을 사용할 때는 반드시 He Initialization을, Tanh나 Sigmoid를 사용할 때는 Xavier Initialization을 선택하는 것이 수학적으로나 실무적으로 타당합니다. 잘못된 초기화는 학습 초기 단계에서 모델을 '죽은 상태'로 만들 수 있음을 명심해야 합니다.


7. 내용 출처 및 참고 문헌

  • Glorot, X., & Bengio, Y. (2010). Understanding the difficulty of training deep feedforward neural networks. AISTATS.
  • He, K., Zhang, X., Ren, S., & Sun, J. (2015). Delving deep into rectifiers: Surpassing human-level performance on imagenet classification. ICCV.
  • PyTorch Documentation: torch.nn.init module.
  • CS231n: Convolutional Neural Networks for Visual Recognition, Stanford University.
728x90