딥러닝 모델을 설계할 때 우리가 가장 먼저 결정해야 하는 것은 무엇일까요? 바로 가중치(W)를 어떤 값으로 시작할지입니다. 단순히 숫자를 채워 넣는 작업처럼 보이지만, 초기값 설정에 따라 모델이 광속으로 수렴할 수도, 혹은 학습 자체가 불가능할 수도 있습니다.
1. 가중치 초기화(Weight Initialization) 기초
왜 하는가? (The Why)
가장 큰 이유는 대칭성 파괴(Breaking the Symmetry)입니다. 모든 뉴런이 같은 초기값을 가지면 역전파 시 모든 뉴런이 동일한 그래디언트를 업데이트받아, 결국 수백 개의 뉴런이 하나의 뉴런처럼 작동하게 됩니다. 이를 방지하고 각 뉴런이 서로 다른 특징을 배우도록 유도하는 것이 초기화의 첫 번째 목적입니다.
가중치는 입력 신호에 곱해지는 값입니다. 모든 가중치가 0이면 다음과 같은 파급 효과가 나타납니다.
동일한 출력: 입력값이 무엇이든 모든 뉴런의 출력(W⋅x)이 0이 됩니다.
동일한 그래디언트: 역전파 시 모든 뉴런이 똑같은 오차 신호를 받게 되어, 가중치가 모두 똑같은 값으로 업데이트됩니다.결과: 100개의 뉴런이 있어도 사실상 1개의 뉴런만 있는 것과 똑같은 결과를 초래합니다. 이를 '대칭적 구조'라고 하며, 학습이 전혀 진전되지 않습니다.
무엇을, 얼마나 자주 하는가? (The What & When)
대상: 뉴런 간의 연결 강도인 가중치(W)를 초기화합니다. (편향 b는 보통 0으로 둡니다.)
빈도: 모델을 생성하고 학습을 시작하기 직전 단 한 번 수행합니다. 학습이 시작되면 역전파(Backpropagation)를 통해 이 값들이 계속 업데이트됩니다.
편향(b)은 왜 0이어도 괜찮은가?
편향은 가중치 합에 더해지는 '상수'입니다 (y=W⋅x+b).
대칭성 파괴는 가중치가 담당: 이미 가중치(W)를 서로 다른 무작위 값으로 초기화했다면, 각 뉴런은 서로 다른 출력을 내놓습니다.
신호의 이동: 편향은 활성화 함수를 왼쪽이나 오른쪽으로 적절히 이동시키는 역할을 합니다. 학습 과정에서 가중치가 먼저 자리를 잡으면, 편향은 오차를 줄이는 방향으로 자연스럽게 0이 아닌 적절한 값으로 찾아 들어갑니다.
계산의 편의성: 굳이 편향까지 무작위로 초기화해서 불필요한 노이즈를 추가할 필요가 없기 때문에 보통 0으로 시작하는 것이 관례입니다.
2. 초기화 기법의 종류와 수식
1) 상수 초기화 (Constant Initialization)
모든 가중치를 0 또는 1 같은 특정 상수로 설정합니다.
수식:W=0 or W=1
특징: 계산이 가장 간단하지만, 앞서 말한 대칭성 문제 때문에 실제 학습에는 사용하지 않습니다.
2) 단순 무작위 초기화 (Random Initialization)
대칭성을 깨기 위해 작은 난수를 사용합니다.
정규 분포 초기화:W∼N(0,0.012)
희소 정규 분포 (Truncated Normal): 표준편차의 2배수 밖의 값을 제외하고 추출하여 극단적인 값을 방지합니다.
주의점: 층이 깊어질수록 신호가 사라지는 그래디언트 소실(Vanishing) 혹은 너무 커지는 폭주(Exploding) 현상이 발생하기 쉽습니다.
3) Xavier (Glorot) 초기화
Sigmoid나 Tanh처럼 중앙이 선형적인 활성화 함수를 위해 설계되었습니다. nin, nout를 각각 레이어로 들어오는 입력 노드 수 (Fan-in), 레이어로 들어오는 입력 노드 수 (Fan-in)라고 하면,
Xavier Uniform:W∼U(−a,a)
U(a,b): a와 b 사이의 균등 분포 (Uniform Distribution)
a=gain×nin+nout6
Sigmoid 사용 시: 보통 gain=1을 사용합니다.
Xavier Normal:W∼N(0,std2)
std=gain×nin+nout2
특징: 입력과 출력 노드 수를 모두 고려하여 층간 분산을 일정하게 유지합니다.
4) He (Kaiming) 초기화
ReLU 계열(f(x)=max(0,x))은 입력의 절반이 0이 되므로, Xavier보다 분산을 2배 더 키워야 신호가 보존됩니다.
He Uniform:W∼U(−a,a)
a=gain×nin6
He Normal:W∼N(0,std2)
std=ningain
특징: 현대 딥러닝(CNN, MLP 등)의 표준입니다.
5) 직교 초기화 (Orthogonal Initialization)
가중치 행렬 W를 서로 직교하는 행렬(WTW=I)로 만듭니다. 이 행렬의 가장 큰 특징은 어떤 벡터에 곱해지더라도 그 벡터의 길이를 변화시키지 않고 회전만 시킨다는 점입니다.
수학적 의미: 행렬 W의 모든 행 벡터(또는 열 벡터)들은 서로 직교하며, 크기가 1인 단위 벡터입니다.
고유값(Eigenvalue): 직교 행렬의 모든 고유값의 절대값은 정확히 1입니다.
원리: 행렬 곱셈 시 고유값의 절대값을 1로 유지하여 신호 크기를 보존합니다.
어떻게 만드는가? (SVD의 활용)
먼저 무작위 정규분포 행렬을 생성합니다.
이 행렬을 특이값 분해(SVD, Singular Value Decomposition) 합니다.
분해된 결과물 중 직교성이 보장된 행렬(U 또는 V)만을 취하여 가중치로 사용합니다.
언제 쓸까?
강력 추천: RNN, LSTM, GRU의 은닉 상태(Hidden State) 간 연결 가중치. 동일 가중치를 반복 곱하는 RNN(LSTM/GRU)에서 필수적입니다.
사용 가능: 매우 깊은 층을 가진 CNN이나 MLP (층이 너무 깊어 He 초기화로도 그래디언트 소실이 해결되지 않을 때).
비추천: 층이 얕은 네트워크 (계산 복잡도만 늘어나고 이득이 적음).
왜 RNN에서 필수적인가? (The RNN Problem)
RNN은 동일한 가중치 행렬(W)을 타임 스텝(t)마다 반복해서 곱하는 구조입니다.일반 초기화의 문제: 만약 W의 고유값이 1.1이라면, 100번의 타임 스텝을 거칠 때 신호의 크기는 1.1100이 되어 폭주(Exploding)합니다.
반대로 0.9라면 0.9100이 되어 소실(Vanishing)됩니다.
직교 초기화의 해법: 고유값이 1인 직교 행렬을 사용하면, 이론적으로 아무리 많은 타임 스텝을 거쳐도 신호의 강도가 처음과 동일하게 유지됩니다.
결과: 정보가 손실되지 않고 아주 먼 과거의 상태(Long-term dependency)까지 전달될 수 있습니다.
6) 사전 학습 기반 초기화 (Pre-trained)
이미 ImageNet(100만 장 이상의 이미지 데이터셋)이나 위키피디아 같은 대규모 데이터로 학습된 가중치를 가져오는 방식입니다. 전이 학습(Transfer Learning)에서 핵심이며, 적은 데이터로도 빠르게 수렴하게 돕습니다.
동작 원리: 데이터가 충분한 환경에서 학습된 모델은 이미지의 선, 면, 색상 혹은 언어의 문법적 구조를 파악하는 법을 이미 알고 있습니다. 이 지식(가중치)을 초기값으로 설정하면, 내 모델은 '가나다'부터 배우는 게 아니라 '문장 쓰기'부터 바로 시작할 수 있습니다.
Winitial=Wpretrained (무작위 분포에서 샘플링하지 않고, 기존 학습 결과인 고정된 행렬 값을 그대로 가져옴)
왜 사용하는가?(장점)
학습 속도(Convergence Speed): 0에서 시작하는 것보다 정답에 훨씬 가까운 지점에서 출발하므로 수렴 속도가 압도적으로 빠릅니다.
데이터 부족 해결: 내가 가진 데이터가 100장뿐이라도, 100만 장을 본 모델의 가중치를 가져오면 고성능 모델을 만들 수 있습니다.
일반화 성능(Generalization): 방대한 데이터를 경험한 가중치이므로, 새로운 데이터에 대해서도 더 유연하게 대처합니다.
주의사항
도메인 불일치: 의료용 X-ray 사진을 분석해야 하는데, 강아지/고양이 사진으로 학습된 모델을 가져오면 초기값이 오히려 독이 될 수 있습니다. (물론 무작위 초기화보다는 나은 경우가 많습니다.)
학습률(Learning Rate) 조절: 이미 잘 만들어진 가중치이므로, 너무 큰 학습률을 쓰면 공들여 쌓은 지식이 한순간에 파괴될 수 있습니다. 보통 일반적인 학습보다 10배~100배 작은 학습률을 설정합니다.
파라미터 a의 도출 과정
균등 분포 U(−a,a)의 분산은 Var(W)=3a2입니다. 우리가 원하는 목표 분산(σ2)을 맞추기 위해 a를 역산하면 다음과 같습니다.
1. Uniform Distribution
1-a) Xavier (Glorot) Uniform 초기화
입력과 출력 노드 수를 모두 고려하여 층간 분산을 일정하게 맞춥니다.
Xavier 목표:Var(W)=nin+nout2
3a2=nin+nout2⟹a=nin+nout6
1-b) He (Kaiming) Uniform 초기화
ReLU, Leaky ReLU 활성화 함수를 위해 설계되었습니다. ReLU는 입력의 절반을 0으로 날려버리기 때문에, Xavier보다 분산을 더 크게 잡아야 신호가 보존됩니다.
He 목표:Var(W)=nin2
3a2=nin2⟹a=nin6
ReLU 사용 시:gain=2를 사용합니다. 이를 수식에 대입하면 우리가 흔히 아는 a=nin12 형태가 유도됩니다.
2. Normal Distribution
Xavier 초기화를 만들 당시 연구자들은 두 가지를 모두 지키고 싶어 했습니다.
Forward (순전파): 입력의 분산과 출력의 분산이 같아야 한다.
유도 결과: Var(W)=nin1
Backward (역전파): 뒤에서 오는 그래디언트의 분산과 앞 레이어로 보낼 그래디언트의 분산이 같아야 한다.
유도 결과: Var(W)=nout1
2-a) Xavier (Glorot) Normal 초기화
Forward 분산과 Backward 분산이 다르기 때문에 어쩔 수 없이 평균을 내서 nin+nout2이라는 타협점을 찾은 것이죠.
추출 공식:W∼N(0,nin+nout22).
2-b) He (Kaiming) Normal 초기화
Kaiming He 박사는 ReLU(max(0,x))를 사용할 때의 분산을 다시 계산했습니다. 앞서 우리가 살펴봤듯이, ReLU는 신호를 반토막 내기 때문에 분산을 2배 키워야 합니다.
He의 순전파 조건: Var(W)=nin2
He의 역전파 조건: Var(W)=nout2여기까지는 Xavier와 비슷해 보입니다.
하지만 He 박사는 논문에서 수학적으로 증명하며 다음과 같은 결론을 내립니다.
"층이 충분히 깊다면, nin을 쓰든 nout을 쓰든 결과적으로 신호의 보존 능력은 동일하다. 그러므로 굳이 복잡하게 두 값을 섞지 않고 nin 하나만 기준으로 삼아도 충분하다."
추출 공식:W∼N(0,nin22)
요약 및 비교 (정규분포 vs 균등분포)
초기화 기법
분포 종류
수식 (파라미터)
목표 분산 (σ2)
Xavier
균등(Uniform)
a=nin+nout6
nin+nout2
Xavier
정규(Normal)
std=nin+nout2
nin+nout2
He (Kaiming)
균등(Uniform)
a=nin6
nin2
He (Kaiming)
정규(Normal)
std=nin2
nin2
왜 균등분포는 6이고 정규분포는 2인가요?
균등분포: 범위가 [−a,a]일 때 분산은 3a2입니다. 이 3a2을 목표 분산 nin2과 같게 만들려다 보니 a2=nin6이 되어 분자에 6이 나타나는 것입니다.
정규분포: 표준편차(std)를 제곱하면 바로 분산이 됩니다. 따라서 목표 분산이 nin2이면 표준편차는 nin2이 됩니다.
PyTorch 실전 코드 리뷰
import torch.nn as nn
import torch.nn.init as init
import torchvision.models as models
class ComprehensiveWeightsModel(nn.Module):
def __init__(self):
super(ComprehensiveWeightsModel, self).__init__()
# 테스트를 위한 레이어 정의
self.fc1 = nn.Linear(512, 256)
self.fc2 = nn.Linear(256, 128)
self.conv1 = nn.Conv2d(3, 16, kernel_size=3)
self.rnn = nn.RNN(128, 64)
# --- 1. 상수 초기화 (Constant) ---
# 가중치에는 지양하지만, 편향(Bias) 초기화에는 표준적으로 사용됩니다.
init.constant_(self.fc1.bias, 0)
# 모든 가중치를 1로 (학습 안 됨 - 대칭성 문제 예시용)
# init.ones_(self.fc1.weight)
# --- 2. 단순 무작위 정규 분포 (Random Normal) ---
# 평균 0, 표준편차 0.01로 설정하여 대칭성을 깨뜨립니다.
init.normal_(self.fc1.weight, mean=0, std=0.01)
# --- 3. Xavier (Glorot) 초기화 (Sigmoid/Tanh용) ---
# 3-1. Xavier Uniform: a = sqrt(6 / (in + out))
init.xavier_uniform_(self.fc2.weight, gain=init.calculate_gain('tanh'))
# 3-2. Xavier Normal: std = gain * sqrt(2 / (in + out))
# init.xavier_normal_(self.fc2.weight, gain=1.0)
# --- 4. He (Kaiming) 초기화 (ReLU용) ---
# 4-1. He Normal: std = gain * sqrt(1 / in)
# nonlinearity='relu' 설정 시 gain(sqrt(2))이 자동 적용되어 std = sqrt(2/in)이 됩니다.
init.kaiming_normal_(self.conv1.weight, mode='fan_in', nonlinearity='relu')
# 4-2. He Uniform: a = sqrt(6 / in)
# init.kaiming_uniform_(self.conv1.weight, mode='fan_in', nonlinearity='leaky_relu')
# --- 5. 직교 초기화 (Orthogonal) ---
# RNN의 은닉 상태 가중치(weight_hh)에 필수적입니다.
for name, param in self.rnn.named_parameters():
if 'weight_hh' in name:
init.orthogonal_(param)
elif 'weight_ih' in name:
init.kaiming_normal_(param)
# --- 6. 사전 학습 기반 초기화 (Pre-trained) ---
# 모델 구조 자체를 가져와서 특정 레이어의 가중치를 복사합니다.
pretrained_res = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
# 내 모델의 conv1에 사전 학습된 가중치 이식 (채널 수 등이 맞아야 함)
with torch.no_grad():
self.conv1.weight.copy_(pretrained_res.conv1.weight)
def forward(self, x):
# 학습 로직 생략
pass
# 모델 인스턴스화
model = ComprehensiveWeightsModel()
PyTorch에서의 gain 값
PyTorch의 torch.nn.init.calculate_gain(nonlinearity) 함수를 쓰면 각 활성화 함수에 맞는 최적의 gain 값을 돌려줍니다.
활성화 함수
Gain 값
Sigmoid
1
Tanh
5/3
ReLU
2≈1.414
Leaky ReLU
2/(1+negative_slope2)
주의사항
가중치 vs 편향: 가중치는 개성을 위해 무작위로, 편향은 보통 0으로 초기화합니다.
Batch Normalization: 배치 정규화를 쓰면 초기화의 영향이 줄어들지만, 여전히 좋은 초기화는 초기 학습 안정성에 큰 도움을 줍니다.
In-place 연산:init 함수들은 반환값이 아니라 원본을 직접 수정하므로 변수에 다시 할당하지 마세요.