MLP 50층에서 초기화 비교: 활성화와 기울기가 소실/발산하는 조건 측정 (NumPy 사용)
요약
본 글은 깊은 신경망에서 활성화와 기울기가 소실/발산하는 현상을 직접 측정하기 위해 자체 제작한 MLP를 사용한 실험 결과를 공유합니다. 50층의 ReLU MLP에 대해 naive-small, naive-large, He 초기화 방식을 적용하여 각 층을 통과할 때 신호 및 기울기의 표준편차 변화를 분석했습니다.
핵심 포인트
- 깊은 네트워크 학습 실패 원인: 활성화/기울기의 소실 또는 폭발
- 초기화 방식에 따른 스케일링 배율 직접 측정 가능
- 50층을 통과해도 오더가 유지되는 초기화가 필수적
깊은 신경망(Deep Neural Network)은 얼마 전까지만 해도 '거의 학습할 수 없다'는 것이 일반적이었습니다. 층을 쌓을수록 더 똑똑해져야 하는데, 실제로 10층, 20층 등으로 쌓아 올리면 어째서인지 전혀 학습이 진행되지 않습니다. 이 현상의 주범으로 자주 언급되는 것이 바로 **가중치 초기값(Weight Initialization)**입니다. 신호(활성화)와 기울기(Gradient)는 층을 한 번 통과할 때마다 몇 배씩 확대되거나 축소됩니다. 그 배율이 1보다 아주 조금만 작아도, 수십 개의 층을 쌓아가면서 신호는 지수적으로 0으로 붕괴합니다(기울기 소실, Vanishing Gradient). 반대로 아주 조금만 커도, 지수적으로 발산합니다(기울기 폭발, Exploding Gradient). 이 '사소한 차이가 층을 거듭할수록 곱셈으로 영향을 미친다'는 이야기는 머리로는 이해했다고 생각했지만, 직접 그 배율을 측정해 본 적은 단 한 번도 없었습니다.
그래서 자체 제작한 numpy MLP를 사용하여, 초기화 방식의 차이가 층을 지날 때마다 얼마나 큰 배율로 작용하는지 실제로 측정해 보았습니다.
실험에 사용된 코드 전체는 GitHub에 올려두었습니다.
실험 설계: 너비 64, 50층 ReLU MLP를 3가지 초기화 방식으로
활성화 함수로는 ReLU를 사용했습니다. 교과서적으로 볼 때 Xavier/Glorot 초기화는 tanh나 sigmoid를 대상으로 설계된 것이지만, tanh나 sigmoid의 출력은
너비 64, 50층의 완전 연결 MLP(바이어스는 0으로 고정하고, 가중치 행렬만의 효과를 관찰) - 입력은 64차원, 배치 크기 256의
고정된 입력 데이터(모든 초기화 방식에서 완전히 동일하게 사용) - 역전파용 손실은 고정된 랜덤 타겟에 대한 MSE(이 역시 모든 초기화 방식에서 공통)
- 초기화 방식은 3가지
naive-small: 층의 너비와 관계없이 표준편차 0.01로 너무 작은 초기화 -
naive-large: 층의 너비와 관계없이 표준편차 1.5로 너무 큰 초기화 -
He 초기화: 표준편차를 $\sqrt{2/\text{fan}_in}}$ 즉, 입력 측 층의 너비에 따라 스케일링하는 원리적인 초기화
def make_weights(scheme, depth, width, input_dim, seed):
rng = np.random.default_rng(seed)
Ws = []
...
여기서 '고정된 입력 데이터'라고 부르는 것의 정체를 먼저 명확히 하겠습니다. 이것은 MNIST와 같은 실제 데이터셋이 아니라, 표준 정규 분포입니다(역전파용 타겟 역시 마찬가지로 난수). 이번 실험에서는 학습을 전혀 진행하지 않습니다. 보고 싶은 것은 '초기화 직후 네트워크에 신호가 한 번 왕복했을 때, 층을 지날수록 스케일(표준편차)이 어떻게 변하는지'뿐이므로, 입력 내용에 의미가 있을 필요는 없고, 스케일의 자(ruler) 역할을 하는 '표준편차가 대략 1인 신호'만 있으면 충분합니다(실측 결과에서 층0=입력의 표준편차는 0.996이었고, 이것이 모든 그래프의 출발점이 됩니다). 그리고 이번 '성공'의 정의도 간단하여,
50층을 통과해도 활성화와 기울기의 표준편차가 자릿수 단위로 움직이지 않고, 입력과 비슷한 오더에 머무르는 것입니다. 반대로, 자릿수가 층과 함께 지수적으로 변해버린다면, 그 초기화는 깊은 네트워크에서는 사용할 수 없다는 판정이 됩니다.
측정 방법은 이렇습니다. 동일한 고정 입력 배치로 한 번만 순전파(Forward Propagation)하여 각 층의 활성화 표준편차를 기록합니다. 이어서 같은 순전파 결과로부터 한 번만 역전파(Backward Propagation)하여 각 층의 기울기(손실을 각 층의 활성화로 미분한 값)의 표준편차를 기록합니다.
결과 1: 활성화 크기는 50층에서 이렇게 변했다

숫자로 보면 다음과 같습니다.
layer 1 layer 50
naive-small(0.01) 0.0463 → 2.67e-63 (61자릿수 소실, 1/1.73e61배)
naive-large(1.5) 7.09 → 2.00e+46 (45자릿수 팽창, 2.83e45배)
...
naive-small은 층1에서 활성화 표준편차가 0.0463으로 아직 '꽤 괜찮아 보이는 값'인데도, 층50에서는 $2.67\times 10^{-63}$입니다. 61자릿수가 소실되었습니다. 이는 수치적으로 거의 완벽하게 0이며, 실제 학습에서는 이 층보다 앞의 정보는 네트워크에게 '아무것도 없는 것'과 같습니다.
naive-large는 반대로, 층1의 7.09에서 층50에서는 $2.00\times 10^{46}$까지 45자릿수가 팽창했습니다. float64가 아직 처리 가능한 범위(최대 약 $1.8\times 10^{308}$)에는 들어와 있지만, 이미 의미 있는 수치는 아닙니다.
He 초기화만은 레이어 1의 0.820에서 레이어 50의 0.260까지, 총 50개 레이어를 거치며 최소 0.235, 최대 1.019 범위에 머물렀다. 완전히 일정하지는 않지만(ReLU가 절반의 유닛을 0으로 만들기 때문에, 레이어마다 약간의 변동은 남아있다), 레이어 30 부근에서 한 번 0.396까지 떨어지는 장면도 있었으나, 그럼에도 불구하고 '61자리 소실'이나 '45자리 팽창'에 비하면 오차 범위 내의 흔들림이라고 할 수 있다.
결과 2: 기울기는 훨씬 더 극단적으로 작용한다
활성화(activation)는 순전파(forward propagation)에서 한 방향으로 곱해지기만 하지만, 기울기(gradient)는 역전파(backpropagation)에서도 한 단계의 곱셈이 추가된다. 그 결과를 살펴보자.

layer 50(출력측) layer 0(입력측)
naive-small(0.01) 3.90e-03 → 1.40e-65 (추가로 62자리 소실)
naive-large(1.5) 7.83e+43 → 1.34e+91 (추가로 47자리 팽창)
...
naive-small은 출력에 가까운 레이어 50에서 기울기의 표준 편차가 0.0039로 아직 정상적인 값인데도, 거기서 입력에 가까운 레이어 0을 향해 역전파함에 따라 추가로 62자리가 소실되어 1.40×10⁻⁶⁵이 된다. 즉, 입력에 가까운 레이어의 파라미터는 실질적으로 기울기를 전혀 받지 못한다. 학습을 진행해도 그 이후의 가중치는 거의 움직이지 않는다.
naive-large는 역전파를 시작하는 순간 출력 측에서 이미 7.83×10⁴³이라는 비현실적인 크기가 되어 있다. 레이어를 한 번도 거치지 않았는데 왜 그런가 궁금할 수 있지만, 그 비밀은 손실 함수(loss function)에 있다. MSE 손실의 미분은 (출력 − 타겟)/배치 사이즈이며, naive-large의 출력 활성화는 이미 2.00×10⁴⁶까지 팽창했기 때문에, 2.00×10⁴⁶ ÷ 256 ≈ 7.83×10⁴³이다. 즉, 순전파에서의 폭발이 손실의 미분을 통해 그대로 역전파의 '초기값'으로 가져와지고 있는 것이다. 반대로, naive-small의 출력 측 기울기 3.90×10⁻³가 정상적인 값인 이유는 출력이 거의 0에 소실되어 (출력 − 타겟)이 거의 타겟 그 자체가 되며, std(타겟)/256 ≈ 0.0039라는 무사한 값에서 역전파가 시작되기 때문이다. naive-large는 이 거대한 초기값으로부터 더욱 입력 측을 향해 47자리 팽창하여 1.34×10⁹¹에 도달한다.
He 초기화는 출력 측의 0.00401에서 입력 측의 0.00364까지, 거의 변하지 않았다. 전체 레이어를 거친 범위도 [0.00315, 0.00674]로 2배가 조금 넘게 수렴하여 활성화보다 더욱 안정적이었다.
naive-large는 깊이 165층에서 정말 오버플로우했다
50개 레이어 시점의 naive-large의 2.00×10⁴⁶은 '거대하지만 유한'한 수치였다. 그렇다면 실제로 float64가 오버플로우하는 지점까지 보고자, 같은 구성(폭 64, 표준 편차 1.5 고정)을 유지하며 레이어 수를 400까지 늘려 어디서 깨지는지 확인해 보았다.
naive_large를 depth=400까지 늘려서 순전파 → 레이어 165에서 overflow(inf)에 도달
레이어 165에서 실제로 float64의 표현 범위를 초과하여 inf가 되었다. 여기까지만 해도 '자릿수가 커져가는 그래프'로 보고 있었지만, 단순한 이론적 우려가 아니라, 순진한 초기화 상태 그대로 깊게 계속하면 정말 계산이 망가지는 지점까지 직접 확인할 수 있었다.
결과 3: 깊이를 늘리면서 무너져가는 분포 자체를 보다
표준 편차라는 요약 통계뿐만 아니라, 각 깊이에서의 활성화 값 그 자체(히스토그램)가 어떻게 변하는지도 애니메이션으로 만들어 보았다.

이 애니메이션에서 흥미로운 점은 히스토그램의 '모양' 자체보다, x축 눈금 숫자가 움직이는 것이다. naive-small은 깊이가 증가할수록 x축 표시가 1e-2, 1e-10, ...와 끊임없이 0에 가까워지고, naive-large는 반대로 x축 표시가 점점 더 큰 지수로 커져간다. 가장 아래의 He만은 깊이가 50까지 진행되어도 x축 눈금이 거의 움직이지 않는다. 분포 모양 자체는 ReLU답게 0 근처에 치우친 형태를 유지한 채, 스케일(scale)만 세 가지 방식이 완전히 다른 운명을 따른다.
의외의 점
솔직히 He 초기화가 이렇게 깔끔하게 안정될 줄은 예상하지 못했지만, 가장 '아하'했던 부분은 기울기(gradient) 쪽 수치 배열이었다. 층을 한 번 통과할 때의 붕괴 속도 자체는 순전파(61자리・45자리)와 역전파(62자리・47자리)에서 거의 동일하다. 역전파 역시 결국 같은 가중치 행렬을 (전치하여) 계속 곱하고 있는 것이므로, 이는 논리적으로 말이 된다. 그런데도 최종적으로 도달하는 값은 기울기 쪽이 훨씬 더 극단해질 수 있다. naive-large의 경우 활성화값의 최댓값이 2.00×10⁴⁶인 반면, 입력 측의 기울기는 1.34×10⁹¹로—무려 45자리나 더 극단하다. 핵심은 결과 2에서 본 바와 같이, 역전파는 '손상되지 않은 값'부터 시작할 수 없다는 것이다. 순전파에서 폭발한 출력(2.00×10⁴⁶)이 손실의 미분을 통해 그대로 역전파의 초기값(7.83×10⁴³)이 되고, 그 위에 추가로 50개 층 분량의 팽창(47자리)이 더해진다. 즉, 43자리 + 47자리가 합쳐져 약 90자리의 누적된 계산이 되는 것이다. 반면 naive-small의 경우, 출력값이 0으로 수축되었기 때문에 역전파의 초기값은 손상되지 않은 값(0.0039)을 유지하고, 입력 측 기울기(10⁻⁶⁵)는 활성화값(10⁻⁶³)과 큰 차이가 없다. '이중의 벌칙'을 받는 것은 폭발하는 쪽만이라는 비대칭성은 실제로 측정해보고서야 실감한 구조였다. 순전파에서의 붕괴와 역전파에서의 붕괴는 독립적으로 발생하는 것이 아니라, 1차 붕괴의 결과가 그대로 2차 붕괴의 출발점이 된다. 한 번의 붕괴가 다음 붕괴의 토대가 되는 것이다.
요약
- 너비 64・50층의 ReLU MLP(자체 numpy 구현)에서, 초기화 표준편차를 0.01(naive-small)・1.5(naive-large)・He 초기화( ) 세 가지로 변경하여 고정된 입력 배치로 활성화값과 기울기의 표준편차를 측정했다.
$ ext{std} = rac{ ext{sqrt}(2/ ext{fan} ext{_in})}{ ext{초기화 값}}$ - naive-small은 활성화값이 1층의 0.0463에서 50층에 2.67×10⁻⁶³까지 61자리 수축했고, 기울기는 입력 쪽으로 더 나아가 62자리가 수축하여 1.40×10⁻⁶⁵이 되었다.
- naive-large는 활성화값이 1층의 7.09에서 50층에 2.00×10⁴⁶까지 45자리 팽창했고, 기울기는 더 나아가 입력 쪽에서 47자리가 팽창하여 1.34×10⁹¹이 되었다. 같은 구조를 깊이를 400까지 늘리면, 165층에서 실제로 float64의 오버플로우(inf)에 도달했다.
- He 초기화는 활성화값이 [0.235, 1.019], 기울기가 [0.00315, 0.00674]로 50층을 거치면서도 둘 다 몇 배의 범위 안에 머물렀다.
- 각 층별 붕괴 속도 자체는 순전파와 역전파에서 거의 동일(61자리 vs 62자리, 45자리 vs 47자리)했지만, naive-large에서는 순전파로 폭발한 출력이 MSE 손실의 미분을 통해 그대로 역전파 초기값($2.00 imes 10^{46} ext{ ÷ } 256 ext{ ≈ } 7.83 imes 10^{43}$)으로 전달되었고, 입력 측 기울기는 활성화값($10^{46}$)보다 훨씬 극단한 $10^{91}$에 도달했다. 수축되는 쪽인 naive-small에서는 출력이 0으로 수축하는 만큼 역전파가 손상되지 않은 값에서 시작하기 때문에, 이 '이중의 벌칙'은 폭발하는 쪽에만 작용하는 비대칭성이었다.
본 실험의 범위와 재현 시 주의사항
너비 64・ReLU를 사용한 이번 네트워크로 신호와 기울기의 스케일을 조사한 결과입니다. He 초기화가 모든 활성화 함수・잔차 연결・정규화 구성에서 최적이라는 의미는 아니며, 초기화 시점의 안정성만으로 학습 후의 정확도까지 보장할 수는 없습니다.
본 기사는 Shake AI Lab의 동명 주제 글을 Zenn용으로 편집한 것입니다. 실험 다이어그램・주요 코드・결과는 본 기사에 게재했으며, 전체 코드는 상단 GitHub 링크에서 참조할 수 있습니다.
논의(Discussion)

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn ML의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기