가중치 초기값은 신경망의 학습에 큰 영향을 미칩니다.
초기값을 잘 설정하지 않으면, 학습 속도가 느려질 수 있거나, 국지적 최솟값에 갇히거나, 신경망이 제대로 학습되지 않는 등의 문제가 발생할 수 있습니다.
이 글 에서는 가중치 초기화 방법에 대한 주요 이론들 한번 다뤄보겠습니다.
목차
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
참고 문헌 및 참고자료: 밑바닥부터 시작하는 딥러닝
가중치 초기화란

가중치 초기값(weight initialization)은 신경망 또는 머신러닝 모델에서 가중치 파라미터를 처음에 어떻게 설정할 것인지를 나타냅니다. 초기값 설정은 모델의 성능과 학습 속도에 큰 영향을 미칠 수 있습니다.
예를 들어, 모든 가중치를 0으로 초기화하면, 그라디언트 디센트(Gradient Descent) 알고리즘이 제대로 작동하지 않을 수 있습니다. 이는 모든 뉴런이 동일한 출력을 생성하고, 따라서 동일한 그라디언트를 업데이트하기 때문에 모델이 복잡한 특성을 학습할 수 없게 됩니다.
가중치 초기화 방법
- 제로 초기화: 모든 가중치를 0으로 초기화합니다. 실제 문제에는 잘 사용되지 않습니다.
- 랜덤 초기화: 가중치를 작은 랜덤 값으로 초기화합니다. 이는 가중치 간의 대칭을 깨뜨려서 각 뉴런이 다르게 학습될 수 있게 합니다.
- 정규분포와 균등분포가 있습니다.
- Xavier/Glorot 초기화: 입력과 출력 뉴런의 수를 고려하여 가중치를 초기화합니다. 일반적으로 tanh 활성화 함수나 Sigmoid와 같은 S-형 함수에 사용됩니다.
- He 초기화: ReLU와 같은 활성화 함수를 사용할 때 이 방법이 흔히 사용됩니다.
- LeCun 초기화: Leaky ReLU나 Scaled Exponential Linear Units(SELU)과 같은 활성화 함수에 사용됩니다.
- 직교 초기화(Orthogonal Initialization): 직교 행렬은 그 자신과 그 자신의 전치(transpose) 행렬을 곱하면 단위 행렬(identity matrix)이 되는 특성을 가집니다. 수식으로는 A \times A^T = I 또는 A^T \times A= I 로 표현됩니다.
이러한 초기화 방법 외에도 다양한 방법이 연구되고 있습니다. 가중치 초기화는 모델의 학습 성능과 일반화 능력, 그리고 학습이 수렴하는 속도에 큰 영향을 미칠 수 있으므로 중요한 주제입니다.
1. 제로 초기화
제로 초기화(Zero Initialization)는 모든 가중치를 0으로 초기화하는 방법입니다. 이 방법은 몇 가지 문제점을 가지고 있습니다:
- 대칭성 문제: 모든 가중치가 0이면, 각 뉴런은 동일한 출력을 생성합니다. 이 경우 역전파(backpropagation) 과정에서 모든 뉴런에 동일한 그라디언트가 적용되므로, 가중치들은 학습 과정에서 동일한 값을 유지하게 됩니다. 이를 ‘대칭성 문제(symmetry problem)’라고 합니다.
- 그라디언트 소실 문제: 특히 깊은 네트워크에서는 그라디언트 소실(vanishing gradient) 문제가 심화될 수 있습니다. 가중치가 0이면, 그라디언트 역시 0이 될 가능성이 높으며, 이는 모델이 제대로 학습되지 않을 수 있습니다.
- 성능 문제: 제로 초기화는 모델의 성능을 저하시킬 수 있습니다. 초기 가중치가 0이면, 신경망이 복잡한 패턴을 학습하는 데 어려움을 겪을 것입니다.
그럼에도 불구하고, 편향(bias)을 0으로 초기화하는 것은 상대적으로 안전한 방법이며, 많이 사용됩니다. 이는 편향이 하나의 뉴런 당 하나만 존재하기 때문에 대칭성 문제가 발생하지 않기 때문입니다.
따라서 제로 초기화는 일반적으로 가중치 초기화 방법으로 권장되지 않습니다. 다만, 특별한 경우나 특정 목적을 위해 사용될 수는 있습니다. 모든 값이 0이기 때문에 그래프를 그릴 필요는 없을것 같습니다.
2. 랜덤 초기화
랜덤 초기화(Random Initialization)는 신경망의 가중치를 무작위의 작은 값으로 설정하는 방법입니다. 랜덤 초기화는 가중치가 너무 크거나 작지 않도록 적절한 분포에서 무작위 값을 선택하는 것이 일반적입니다. 이 방법은 대칭성을 깨뜨리고, 그라디언트 디센트가 제대로 작동하도록 도와줍니다.
- 정규 분포: 정규 분포를 따르는 가중치 초기화는 Xavier 초기화나 He 초기화와 같은 방법에서도 역시 자주 사용됩니다. 이는 보통 Sigmoid, Hyperbolic Tangent(tanh), ReLU(Rectified Linear Unit)와 같은 활성화 함수와 잘 어울립니다.
- 균등 분포: LeCun 초기화나 일부 자체 제작 초기화 방법에서 균등 분포를 사용하는 경우도 있습니다.
정규 분포 (Normal Distribution)
정규 분포는 가장 자주 사용되는 확률 분포 중 하나로, 종 모양의 그래프를 가집니다. 이 분포는 평균과 표준편차 두 매개변수로 정의됩니다. 평균은 그래프의 중심(정점)을 나타내고, 표준편차는 그래프가 얼마나 퍼져 있는지를 나타냅니다.
- 평균: 데이터의 중심값, 종 모양의 꼭지점이 어디인지 결정합니다.
- 표준편차: 데이터가 평균 주변에 얼마나 퍼져 있는지를 나타냅니다. 표준편차가 크면 그래프는 넓게, 작으면 좁게 퍼집니다.
import numpy as np
import matplotlib.pyplot as plt
# 정규 분포의 평균과 표준편차 설정
mean = 0 #평균
std_dev = 1 #표준편차
# 랜덤 데이터 생성
normal_data = np.random.normal(mean, std_dev, 10000)
# 히스토그램으로 그리기
plt.hist(normal_data, bins=30, density=True)
plt.title('Normal Distribution')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.show()

위그래프의 양상을 보면 0을중심으로 하여 0에서 멀어질수록 점점 빈도수가 적게 나오는 것을 알 수있습니다.
균등 분포 (Uniform Distribution)
균등 분포는 모든 이벤트가 동일한 확률로 발생하는 분포를 말합니다. 예를 들어, 주사위를 던지는 경우 1부터 6까지 나올 확률이 모두 1/6입니다. 균등 분포는 최소값(a)과 최대값(b) 두 매개변수로 정의됩니다.
- 최소값: 가능한 값의 하한입니다.
- 최대값: 가능한 값의 상한입니다.
# 균등 분포의 최소값과 최대값 설정
min_value = 0
max_value = 1
# 랜덤 데이터 생성
uniform_data = np.random.uniform(min_value, max_value, 10000)
# 히스토그램으로 그리기
plt.hist(uniform_data, bins=30, density=True)
plt.title('Uniform Distribution')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.show()

위 그래프에서 보면 알 수 있듯이 0부터 1의 모든 값들이 비슷한 빈도수로 분포되어 있다는 것을 알 수 있습니다.
이 두 분포는 다음과 같은 상황에서 자주 사용됩니다:
- 정규 분포 (Normal Distribution): 실생활의 많은 현상에서 관측되며, 자연스럽게 발생하는 데이터의 분포를 설명할 때 자주 사용됩니다.
- 균등 분포(Uniform Distribution): 모든 사건이 동일한 확률로 발생할 때 사용됩니다. 예를 들어, 랜덤한 수를 생성할 때 균등 분포를 사용할 수 있습니다.
가중치 초기화, 모델링, 시뮬레이션 등 다양한 분야에서 이 두 분포가 활용됩니다
이제 정규분포와 균등 분포를 시그모이드와 렐루에 한번 적용시켜 보겠습니다.
3. Xavier 초기화
Xavier 초기화 또는 Glorot 초기화는 신경망의 가중치를 초기화할 때 자주 사용되는 방법 중 하나입니다. 이 방법은 Xavier Glorot과 Yoshua Bengio가 2010년에 발표한 논문에서 처음 소개되었습니다. 이 초기화 방법은 특히 활성화 함수가 선형 함수이거나, 시그모이드(sigmoid), 하이퍼볼릭 탄젠트(tanh)와 같이 중심 부근이 선형적인 함수를 사용할 때 효과적입니다.
Xavier 초기화에서는 가중치를 균등 분포나 정규 분포에서 뽑습니다. 분포의 범위나 표준편차가 특정한 값을 갖도록 조절됩니다. 뉴런수에 따라 이값은 동적으로 변하기 때문에 이 초기화를 사용합니다.
이글에서는 정규분포를 이용한 초기화인 평균이 0이고 분산이 \sqrt{ \frac{2}{ n_{\text{in}} + n_{\text{out}} }} 정규 분포를 사용해 보겠습니다.
그라디언트 소실 (Vanishing Gradient) 가중치와 그라디언트의 절대값이 1보다 작을 경우, 역전파가 이루어질 때마다 그라디언트는 점점 작아지게 됩니다. 이는 실제로 제곱을 거듭할수록 0에 수렴하는 특성 때문에 발생합니다. 그 결과, 입력층에 가까운 가중치는 거의 업데이트되지 않게 됩니다. 그라디언트 폭발 (Exploding Gradient) 반대로, 가중치와 그라디언트의 절대값이 1보다 클 경우, 역전파 과정에서 그라디언트 값이 점점 커져 무한대로 발산하게 됩니다. 이 경우에는 신경망의 학습이 불안정해지며, 가중치 값들이 무한대로 발산하는 문제가 발생합니다.
4. He 초기화 (He Initialization)
He 초기화는 ReLU(Rectified Linear Unit) 활성화 함수와 그 변형을 사용할 때 효과적인 초기화 방법입니다. He 초기화는 입력 노드의 수로 정규화하여 초기 가중치를 설정합니다.
W = \text{정규 분포} \times \sqrt{\frac{2}{ n_{in}}} 이 초기화 방법은 ReLU 활성화 함수가 음수를 0으로 매핑하기 때문에 더 큰 가중치로 초기화하는 것이 유용하다고 판단됩니다.
5. LeCun 초기화
LeCun 초기화는 시그모이드 함수나 하이퍼볼릭 탄젠트(tanh)와 같은 S-형 함수 또는 선형함수나 LeakyReLu, SELU 등의 활성화 함수를 같이 사용할 때 효과적입니다. 또는
W = \text{정규 분포} \times \sqrt{\frac{1}{ n_{in}}} LeCun 초기화는 S-형 함수의 특성을 고려하여 가중치를 초기화하며, 이는 활성화 함수의 출력이 너무 크거나 작지 않게 도와줍니다.
6. 직교 초기화(Orthogonal Initialization)
직교 초기화는 임의의 직교 행렬로 가중치를 초기화하는 방법입니다. 이 방법은 특히 순환 신경망(Recurrent Neural Networks, RNNs)에서 유용하게 사용됩니다.
직교 행렬은 자신과 자신의 전치 행렬을 곱하면 단위 행렬이 되는 행렬입니다. 이러한 특성 덕분에, 직교 초기화는 기울기 소실(vanishing gradient)과 기울기 폭발(exploding gradient) 문제를 완화할 수 있습니다.
이러한 초기화 방법 각각은 특정 활성화 함수나 네트워크 구조에 더 적합할 수 있으므로, 실제로 어떤 초기화 방법이 더 효과적인지는 실험을 통해 알아내야 합니다.
가중치 초기화 비교
지금까지 여러 초기화에 대한 이론에 대해 알아보았는데요, 이제부터 파이썬으로 코드를 작성하여 그래프를 이용해 비교해 보겠습니다.
일단 넘파이 라이브러리를 불러오고 가중치 초기화와 메서드를 만들어 보겠습니다.
import numpy as np
import matplotlib.pyplot as plt
#가중치 초기화 선택하기
'''
shape - 입력값의 shape을 입력합니다.
init_name - 표준편차가 init_name 인 정규분포를 만들어줍니다. (float값만 받습니다.)
'''
def initialization_method(shape,init_name):
fan_in, fan_out = shape
if init_name == 'xavier':
stddev = np.sqrt(2 / (fan_in + fan_out))
elif init_name == 'he':
stddev = np.sqrt(2 / (fan_in))
elif init_name == 'lecun':
stddev = np.sqrt(1 / (fan_in))
else:
print ("float 타입의 숫자나 'xavier', 'he', 'lecun' 를 변수로 지정하세요")
return np.random.normal(0, stddev, shape)
def sigmoid(x):
return 1/(1+np.exp(-x))
def relu(x):
return np.maximum(0,x)
def affine(x,w):
return np.dot(w,x)
def weights_init_method(init_name,shape):
if type(init_name) == str:
return initialization_method(shape,init_name)
elif type(init_name) == float:
return init_name*np.random.randn(*shape)
def forward_layer(shape, weight_method,input,active_func='sigmoid'):
weight = weights_init_method(weight_method, shape)
affine_ = affine(input,weight)
if active_func == 'sigmoid':
return sigmoid(affine_)
elif active_func == 'relu':
return relu(affine_)
이 코드에서는 초기화값을 정규 분포, Xavier, He, Lecun으로 선택 가능하게 하였고, 활성화 함수로 ReLU, Sigmoid를 선택하여 비교하였습니다.
#아래의 메서드는 각 초기화와 활성화 함수에 대한 히스토그램을 보여주는 코드입니다.
def draw_histogram(x,inputsize,layersize, init_func, active_func):
fig, axes = plt.subplots(1, 6, figsize=(20, 5))
fig.suptitle(f'init={init_func}, active={active_func}')
shapes = [inputsize,layersize]
#입력층
y = x
y = forward_layer(shapes[0], init_func, y, active_func)
axes[0].hist(y.flatten(), bins=100, alpha=1)
axes[0].set_title("Input")
axes[0].set_xlim(0, 1)
axes[0].set_ylim(0, 8000)
#이후 레이어층
current_axis = 0
for i in range(100):
y = forward_layer(shapes[1], init_func, y, active_func)
if i % 20 == 0:
current_axis += 1
axes[current_axis].hist(y.flatten(), bins=100, alpha=1)
axes[current_axis].set_title(f"Hidden {i} Layer")
axes[current_axis].set_xlim(0, 1)
axes[current_axis].set_ylim(0, 8000)
plt.show()
레이어는 약 100층을 쌓았고 20층마다 이에 대한 히스토그램 (수량에 대한 분포그래프)를 자동으로 그려주게 됩니다.
x=np.random.randn(600,500)
inputsize = (500, 600)
layersize = (500, 500)
우선 x(인풋값), 인풋 사이즈, 각레이어 사이즈를 지정해줍니다.
이제부터 각 초기화에 대한 Relu와 Sigmoid 활성화 함수를 이용한 활성화 값에대한 히스토그램을 그려보겠습니다.
1. 가중치 초기화: 제로 초기화
draw_histogram(x,inputsize,layersize, 0. , 'sigmoid')
draw_histogram(x,inputsize,layersize, 0. , 'relu')

제로 초기화 시 시그모이드 활성화를 거치면 모든 가중치(w)에 대한 활성화 값이 0.5로 설정됩니다. 이는 모든 가중치의 초기값이 동일하게 설정되어 있음을 의미합니다. 이러한 초기화 방법은 신경망 학습에서 매우 중요한 역할을 합니다. 그러나 이러한 초기화 방법은 가중치들 사이의 차이를 무시하기 때문에 신경망의 표현력을 제한할 수 있습니다.
신경망은 가중치들을 통해 입력 데이터를 학습하고 패턴을 인식하는데, 초기값이 동일하다면 모든 가중치들이 동일한 역할을 수행하게 됩니다. 이는 신경망이 다양한 패턴을 학습하는 능력을 제한하게 되는 것입니다. 따라서 초기화 방법은 신경망 학습의 성능에 영향을 미칠 수 있는 중요한 요소입니다.

렐루 함수는 입력값이 음수나 0일 때 0으로 처리되기 때문에, 이경우 w의 입력값이 0이기때문에 모든 값이 0이 되었습니다.
2. 가중치 초기화: 표준 편차가 0.01인 정규분포
draw_histogram(x,inputsize,layersize, 0.01 , 'sigmoid')
draw_histogram(x,inputsize,layersize, 0.01 , 'relu')

초기 값을 표준편차가 0.01인 정규분포를 적용할 시에 시그모이드는 0부터 1 사이값을 보여주긴 하지만 0.5에 모이는 경향이 심해 학습이 잘 되지 않을 것으로 예상됩니다.

렐루로 활성화 실행시키면 윗단계로 층 이동을 할 때 그래프에서 보이지 않을 정도로 소실되어버립니다.
3. 가중치 초기화: 표준 편차가 0.3인 정규분포
draw_histogram(x,inputsize,layersize, 0.3 , 'sigmoid')
draw_histogram(x,inputsize,layersize, 0.3 , 'relu')

이 경우에는 0과 1에 가까운 활성화 값을 보여주고 있으며, 이는 Sigmoid 함수의 특성으로 인한 것입니다. Sigmoid 함수는 입력된 값을 0과 1 사이로 압축시키는 역할을 합니다. 역전파 과정에서는 이 Sigmoid 함수의 기울기를 구하기 위해 미분을 수행하는데, 이 때 입력값이 0 또는 1에 가까울 경우, 기울기가 0에 수렴하게 됩니다.

렐루 함수를 거친 w값들이 1보다 큰 경우가 많고 또한 어떤층에는 0으로 되어 학습을 할 수 없는 상태가 되고 있습니다.
4. 가중치 초기화: Xavier 초기화
draw_histogram(x,inputsize,layersize, 'xavier' , 'sigmoid')
draw_histogram(x,inputsize,layersize, 'xavier' , 'relu')

레이어의 갯수가 늘어나도 Xavier와 sigmoid의 조합은 0.2~ 0.8 사이로 잘 분포가 되어있음을 알 수 있습니다.

하지만 Xavier와 ReLU 조합은 깊은 층의 신경망에는 층이 높아질수록 옳지 않아 보입니다.
5. 가중치 초기화: He 초기화
draw_histogram(x,inputsize,layersize, 'he' , 'sigmoid')
draw_histogram(x,inputsize,layersize, 'he' , 'relu')


He 초기화같은 경우 Sigmoid와 ReLU층 둘 다 사용하기에 적합해 보일 수도 있지만 He 초기화 이용시 Sigmoid 함수는 그 출력이 0과 1 사이로 제한되므로, 역전파 과정에서 그래디언트가 급격하게 소실될 가능성이 높습니다.
이 문제를 완화하기 위해서는 Xavier 초기화나 LeCun 초기화와 같이 Sigmoid 활성화 함수와 더 잘 어울리는 초기화 방법을 사용하는 것이 일반적입니다.
또한 일반적으로 He 초기화는 ReLU층과 조화가 좋다고 알려져 있습니다.
6. 가중치 초기화: Lecun 초기화
draw_histogram(x,inputsize,layersize, 'lecun' , 'sigmoid')
draw_histogram(x,inputsize,layersize, 'lecun' , 'relu')

시그모이드 활성화 함수를 사용하면 위와같은 그래프가 나옵니다.

하지만 LeCun과 ReLU 조합은 깊은 층의 신경망에는 층이 높아질수록 옳지 않아 보입니다.
가중치 초기화: 결론
“이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.”
이 글에서는 신경망에서 사용되는 다양한 가중치 초기화 방법과 그 효과에 대해 다뤘습니다. 각 초기화 방법은 신경망의 학습 성능, 일반화 능력, 그리고 수렴 속도에 영향을 미칠 수 있습니다. 여러 가지 초기화 방법 (제로 초기화, 표준 편차가 0.01 이거나 0.3인 정규분포, Xavier 초기화, He 초기화, LeCun 초기화)을 활성화 함수 (ReLU, Sigmoid)와 결합하여 실험해 보았습니다.
- 제로 초기화는 신경망 학습에서 문제가 될 수 있으며, 일반적으로 권장되지 않습니다.
- 표준 편차가 0.01이나 0.3인 정규분포 초기화는 깊은 네트워크에서는 그라디언트 소실이나 폭발 문제를 야기할 수 있습니다.
- Xavier 초기화는 Sigmoid 활성화 함수와 잘 어울리지만, ReLU와는 그렇지 않았습니다.
- He 초기화는 ReLU 활성화 함수와 잘 어울립니다.
- LeCun 초기화는 Sigmoid 활성화 함수와 어느 정도 어울리지만, ReLU와는 그렇지 않았습니다.
따라서 초기화 방법과 활성화 함수의 조합은 신경망의 성능에 큰 영향을 미칩니다. 이를 실험적으로 확인하는 것이 중요하며, 특정 문제에 가장 적합한 초기화 방법을 찾는 것이 좋습니다.
함께 참고하면 좋은 글
4 thoughts on “가중치 초기화 이론: 딥러닝 기초 시리즈 6”