0. 딥러닝 모델 학습 / 모델 훈련 프로세스
1. 모델 초기화(Initialization): 최초 가중치(weight) 값을 설정합니다.
2. 예측(Prediction): 설정된 가중치와 입력 feature(X) 값을 사용하여 예측 값을 계산합니다.
3. 손실(Loss) 계산: 예측 값과 실제 값의 차이를 계산하여 손실 값을 얻습니다.
4. 가중치 업데이트(Weight Update): 손실을 최소화하기 위해 가중치를 조정합니다. 이 과정은 경사 하강법 등의 최적화 알고리즘을 사용하여 수행됩니다.
1. 가중치 초기화의 중요성
가중치 초기화는 딥러닝 모델을 학습시키기 위해 가중치들을 어떤 값으로 초기화할지 결정하는 중요한 단계입니다. 초기 가중치는 모델이 학습을 시작할 때의 출발점을 의미하며, 이 값들이 모델의 학습 및 수렴 동작에 영향을 미칩니다.
만약 모든 가중치를 0으로 초기화하면 역전파 과정에서 모든 뉴런이 동일한 기울기(gradient)를 가지게 되어, 네트워크가 대칭성 문제에 빠지고 학습이 어려워집니다. 또한, 초기 가중치가 너무 크거나 작으면 기울기(gradient)가 너무 커지거나 작아져서 기울기(gradient) 폭주나 소실 문제가 발생할 수 있습니다.
2. 가중치 초기화를 하는 이유
2.1. 모델 학습의 출발점
모델은 초기 가중치로부터 시작하여 점차 실제 패턴과 가까운 결과를 만들도록 조정됩니다.
초기 가중치가 좋게 설정되면 모델은 더 빠르게 원하는 패턴을 학습하게 되고, 초기 가중치가 나쁘게 설정되면 학습이 더 느려지거나 불안정해질 수 있습니다.
예를 들어, 이미지 분류 모델을 생각해 봅시다. 초기 가중치가 잘 설정되면 모델은 이미지의 주요한 특징을 더 빠르게 파악하여 학습할 수 있습니다. 하지만 초기 가중치가 제대로 설정되지 않으면 모델은 처음부터 패턴을 찾아가는 과정이 어려워질 수 있습니다.
2.2.Gradient Vanishing과 Exploding 문제
Gradient Vanishing(기울기 소실)은 역전파 과정에서 기울기(gradient)가 너무 작아져서 가중치가 거의 갱신되지 않는 문제입니다. 이는 초기 가중치가 너무 작거나 활성화 함수의 기울기가 작을 때 발생할 수 있습니다. 결과적으로 모델은 학습을 거의 진행하지 못하고 수렴하지 않는 상태에 빠질 수 있습니다.
Gradient Exploding은 역전파 과정에서 기울기(gradient)가 너무 커져서 가중치가 급격하게 증가하는 문제입니다. 이는 초기 가중치가 너무 크거나 활성화 함수의 기울기가 큰 경우에 발생할 수 있습니다. 이 문제 역시 학습을 어렵게 만들고 수렴을 방해할 수 있습니다.
3. 가중치 초기화의 종류
3.1. 균등 분포(Uniform Distribution)
균등분포 초기화는 지정된 범위 내에서 모든 값이 발생할 확률이 동일하다는 특징을 가진 균등분포(uniform distribution)를 사용하여 파라미터를 초기화합니다. 균등분포 초기화는 신경망의 학습 초기 단계에서 가중치를 무작위로 설정하여 학습 과정에서의 대칭성 문제를 해결하는 데 도움을 줍니다.
균등분포 초기화를 실제로 적용할 때는 초기화할 가중치의 범위를 결정해야 합니다. 일반적으로는 다음과 같은 방법을 사용합니다.
1. 작은 범위를 사용하여 초기화합니다. 예를 들어, [−0.1,0.1] 범위 내에서 무작위로 선택된 값을 가중치로 할당할 수 있습니다.
2. 초기화 범위는 종종 레이어의 입력 또는 출력 유닛의 수에 따라 조정될 수 있습니다. 예를 들어, Xavier 초기화는 입력 및 출력 유닛의 수에 따라 초기화 범위를 자동으로 조정합니다.
3.1.1. 균등분포 초기화의 장점과 단점
장점
- 대칭성 깨기: 초기 가중치가 무작위로 분포됨으로써, 모든 뉴런이 동일한 출력을 생성하는 것을 방지합니다.
- 탐색 효율성 향상: 네트워크가 파라미터 공간에서 더 다양한 지역을 탐색할 수 있도록 도와줍니다.
단점
- 선택된 범위에 민감: 초기화 범위가 너무 크거나 작으면 학습 과정에 부정적인 영향을 미칠 수 있습니다. 너무 크면 그래디언트 폭주 문제가, 너무 작으면 그래디언트 소실 문제가 발생할 수 있습니다.
- 활성화 함수와의 조화 필요: ReLU와 같은 비선형 활성화 함수를 사용할 때는 He 초기화와 같은 다른 방식이 더 효과적일 수 있습니다.
균등분포 초기화는 특히 신경망의 깊이가 깊지 않고, 활성화 함수로 하이퍼볼릭 탄젠트나 시그모이드와 같은 전통적인 함수를 사용할 때 유용합니다.
3.1.2. 균등분포 초기화 코드 적용
torch.nn.init.uniform_(tensor, a=0.0, b=1.0, generator=None)
- tensor: 초기화할 텐서입니다.
- a (float): 균등 분포의 하한(lower bound)입니다.
- b (float): 균등 분포의 상한(upper bound)입니다.
- generator (torch.Generator, optional): 난수 생성기, 지정하지 않으면 기본 생성기를 사용합니다.
3.1.3. 균등 분포 초기화를 적용하는 방법
- 3행 5열의 빈 텐서를 생성합니다. "빈"이라 함은 텐서가 초기화되지 않고, 메모리에 남아 있는 임의의 값들로 채워져 있다는 의미입니다. 이 텐서는 초기화 과정을 거쳐야 실제 사용이 가능합니다.
- a=0.0와 b=1.0은 이 균등 분포의 하한과 상한을 지정합니다. 즉, 텐서의 각 요소는 0.0과 1.0 사이의 무작위 값으로 설정됩니다.
import torch
# 빈 텐서 생성
tensor = torch.empty(3, 5)
# 균등 분포로 초기화
torch.nn.init.uniform_(tensor, a=0.0, b=1.0)
print(tensor)
--------------------------------------------------
tensor([[0.9680, 0.7315, 0.5401, 0.9766, 0.3047],
[0.0765, 0.1740, 0.5465, 0.4614, 0.2670],
[0.5791, 0.9397, 0.1145, 0.8130, 0.3949]])
3.2. 정규 분포 (Normal Distribution)
정규분포 초기화는 신경망의 가중치를 평균이 0이고 표준편차가 설정된 값(예: 0.01)인 정규분포로 초기화합니다. 정규분포는 자연 및 사회 과학에서 널리 발견되는 분포로, 신경망의 가중치 초기화에서도 많이 사용됩니다. 정규분포 초기화는 깊은 네트워크에서의 그래디언트 소실 또는 폭주 문제를 완화하는 데 도움을 줄 수 있습니다.
3.2.1. 정규분포 초기화의 장점과 단점
장점
- 가중치 다양성 증가: 정규분포 초기화는 가중치에 다양성을 부여하여 네트워크가 다양한 특성을 학습할 수 있게 합니다.
- 효과적인 그래디언트 전파:적절한 표준편차 선택을 통해 그래디언트 소실과 폭주 문제를 줄일 수 있어, 깊은 네트워크의 학습이 원활해질 수 있습니다.
단점
- 하이퍼파라미터에 민감: 표준편차의 선택이 네트워크의 성능에 큰 영향을 미칠 수 있으며, 너무 크거나 작은 값은 학습에 부정적인 영향을 줄 수 있습니다.
- 초기값 선택의 어려움: 올바른 표준편차를 설정하는 것이 때로는 도전적일 수 있으며, 실험을 통해 최적의 값을 찾아야 합니다.
3.2.2. 정규분포 초기화 코드 적용
torch.nn.init.normal_(tensor, mean=0.0, std=1.0, generator=None)
- tensor: 초기화할 텐서입니다.
- mean (float): 정규 분포의 평균입니다.
- std (float): 정규 분포의 표준편차입니다.
3.2.3. 정규 분포 초기화를 적용하는 방법
3행 5열의 텐서를 생성하고, 각 요소를 평균이 0이고 표준편차가 0.01인 정규분포로 초기화합니다.
import torch
tensor = torch.empty(3, 5)
torch.nn.init.normal_(tensor, mean=0.0, std=0.01)
print(tensor)
--------------------------------------------------
tensor([[-0.0128, -0.0112, 0.0058, -0.0012, 0.0027],
[ 0.0113, -0.0014, 0.0112, -0.0118, 0.0088],
[-0.0076, -0.0005, 0.0350, -0.0215, 0.0073]])
3.3. 글로럿 초기화 (Glorot) = 자비에르 초기화(Xavier)
자비에르 초기화는 신경망에서 가중치를 초기화하는 방법으로, 각 층의 가중치를 그 층의 입력 노드 수와 출력 노드 수에 기반하여 조절된 분산을 가진 분포에서 무작위로 선택하여 초기화합니다. 이는 신경망의 깊이가 깊어져도 학습 초기 단계에서 안정적인 그래디언트를 유지하도록 돕습니다. 자비에르 초기화는 주로 하이퍼볼릭 탄젠트(tanh) 또는 시그모이드(sigmoid)와 같은 선형적인 활성화 함수에 적합합니다.
균등 분포를 사용할 경우, 텐서의 각 요소는 \([- \sqrt{\frac{6}{n_{in} + n_{out}}}, \sqrt{\frac{6}{n_{in} + n_{out}}}]\) 범위 내의 값으로 설정되며, 정규 분포를 사용할 경우, 텐서의 각 요소는 평균 0과 표준 편차 \(\sqrt{\frac{2}{n_{in} + n_{out}}}\)를 갖습니다.
3.3.1. 글로럿 초기화의 장점과 단점
장점
- 그래디언트 소실 및 폭주 문제 완화: 초기 가중치 범위가 레이어의 크기에 적절히 조정되어 레이어를 통과하는 신호 강도를 유지합니다.
- 네트워크의 효율적 학습: 더 깊은 네트워크에서도 초기화 덕분에 안정적인 학습이 가능합니다.
단점
- 활성화 함수 선택 제한: 주로 선형 활성화 함수(하이퍼볼릭 탄젠트나 시그모이드)에서 효과적이며, ReLU와 같은 비선형 활성화 함수에서는 헤비타이드 초기화(He initialization)가 더 적합할 수 있습니다.
3.3.2. 글로럿 초기화 코드 적용
# 균등 분포
torch.nn.init.xavier_uniform_(tensor, gain=1.0, generator=None)
# 정규분포
torch.nn.init.xavier_normal_(tensor, gain=1.0, generator=None)
- tensor: 초기화할 텐서입니다.
- gain (기본값: 1.0): 초기화의 스케일을 조절하는 이득(gain) 값입니다. 특정 활성화 함수를 사용할 때 이 값을 조절하여 초기화의 범위를 조정할 수 있습니다. 예를 들어, gain 값은 활성화 함수의 성질에 따라 조정될 수 있는데, ReLU 함수의 경우 gain = sqrt(2)가 일반적입니다
[참고]
`gain` 값이 변경되면 가중치 초기화에 사용되는 범위도 변경됩니다.
균등 분포의 경우 범위는 다음과 같이 변경됩니다.
\[
-\text{gain} \times \frac{\sqrt{6}}{\sqrt{n_{\text{in}} + n_{\text{out}}}} \quad \text{to} \quad \text{gain} \times \frac{\sqrt{6}}{\sqrt{n_{\text{in}} + n_{\text{out}}}}
\]
이때 `gain`이 2이면, 범위는 아래와 같습니다.
\[
-2 \times \frac{\sqrt{6}}{\sqrt{n_{\text{in}} + n_{\text{out}}}} \quad \text{to} \quad 2 \times \frac{\sqrt{6}}{\sqrt{n_{\text{in}} + n_{\text{out}}}}
\]
정규 분포의 경우 표준편차는 다음과 같이 변경됩니다.
\[
\text{std} = \text{gain} \times \sqrt{\frac{2}{n_{\text{in}} + n_{\text{out}}}}
\]
이때 `gain`이 2이면, 표준편차는 아래와 같습니다.
\[
\text{std} = 2 \times \sqrt{\frac{2}{n_{\text{in}} + n_{\text{out}}}}
\]
이런 방식으로 `gain` 값을 변경함으로써, 초기화하는 가중치의 범위를 조절할 수 있으며, 이는 네트워크의 특정 활성화 함수와 학습 조건에 맞춰 가중치 초기화의 효과를 최적화하는 데 도움을 줍니다.
3.3.3. 글로럿 초기화를 적용하는 방법
import torch
# 빈 텐서 생성
tensor_uniform = torch.empty(3, 5)
tensor_normal = torch.empty(3, 5)
# 균등 분포로 초기화
torch.nn.init.xavier_uniform_(tensor_uniform, gain=1.0)
print(f'균등 분포 ', tensor_uniform )
# 정규 분포로 초기화
torch.nn.init.xavier_normal_(tensor_normal, gain=1.0)
print(f'정규 분포',tensor_normal)
------------------------------------------------------
균등 분포 tensor([[-0.5576, -0.7264, 0.2654, -0.7511, -0.3088],
[ 0.7059, 0.6009, -0.6884, -0.0664, 0.7435],
[ 0.5134, 0.6118, 0.2403, 0.0801, 0.7607]])
정규 분포 tensor([[ 0.9741, 0.1742, -1.0699, -0.1486, 0.2731],
[-0.2109, -0.2306, 0.7515, -0.9540, 1.0228],
[-0.4853, 0.2636, 0.6432, -0.1358, -0.4924]])
3.4.He 초기화(Kaiming 초기화)
He 초기화는 신경망에서 ReLU 활성화 함수를 사용할 때 흔히 발생하는 그래디언트 소실 문제를 완화하기 위해 개발되었습니다. 이 초기화 방법은 각 층의 가중치를 입력 노드 수의 역수에 비례하는 분산을 가진 분포에서 무작위로 선택하여 초기화합니다. He 초기화는 균등 분포(`kaiming_uniform_`) 또는 정규 분포(`kaiming_normal_`)를 이용할 수 있습니다.
3.4.1. He 초기화의 장점과 단점
장점
- 비선형 활성화 함수 적합: ReLU 및 그 변형(예: Leaky ReLU, PReLU 등)에 적합하게 설계되어, 이들 활성화 함수를 사용할 때 효과적입니다.
- 효율적인 그래디언트 전파: 초기화 시점에서 네트워크의 각 층을 통과하는 그래디언트의 크기를 유지하도록 도와줍니다.
단점
- 특정 활성화 함수에 최적화: He 초기화는 비선형 활성화 함수에 최적화되어 있어, 다른 유형의 활성화 함수에는 적합하지 않을 수 있습니다.
3.4.2. He 초기화 코드 적용
# 균등 분포
torch.nn.init.kaiming_uniform_(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu', generator=None)
# 정규 분포
torch.nn.init.kaiming_normal_(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu', generator=None)
- tensor: 초기화할 텐서입니다.
- a (기본값: 0): 활성화 함수의 음의 기울기를 지정하는 값입니다. 이 값은 주로 `leaky_relu` 같은 활성화 함수에서 음의 부분의 기울기를 나타냅니다. 기본값인 0은 ReLU에 대한 표준 케이스를 의미하며, `leaky_relu`에서는 해당 함수의 음의 영역 기울기를 나타내야 합니다.
- mode (기본값: 'fan_in'): 가중치 텐서의 팬(fan) 모드를 지정합니다. `'fan_in'`은 텐서의 입력 연결 수에 기반한 초기화를, `'fan_out'`은 출력 연결 수에 기반한 초기화를 의미합니다. `'fan_in'`은 주로 입력의 분산을 유지하는 데 사용되며, 일반적으로 심층 신경망에서 권장됩니다.
- nonlinearity (기본값: 'leaky_relu'): 사용할 활성화 함수의 유형을 지정합니다. 이 값은 초기화 과정에서 가중치의 분산 계산에 사용되는 `gain` 값을 결정하는 데 중요합니다. 예를 들어 'relu' 또는 'leaky_relu' 같은 다양한 활성화 함수들이 지원됩니다
🙌 예를 들어, 만약 nonlinearity='relu'라면, He 초기화 공식에서 사용되는 gain 값은 2로 설정됩니다. 이는 ReLU 활성화 함수를 사용할 때 통상적으로 나타나는 신호의 분산 감소를 보상하기 위한 것입니다. 반면 nonlinearity='leaky_relu'와 같은 경우, 해당 활성화 함수의 음의 기울기 파라미터 a에 따라 적절한 gain 값을 조정합니다. - generator(기본값: None): 난수 생성기입니다. 이 인자를 통해 실험의 재현성을 보장하기 위해 특정한 난수 생성기를 지정할 수 있습니다.
3.4.3. He 초기화를 적용하는 방법
입력 노드의 수를 기반으로 초기화 범위를 계산하며, ReLU 활성화 함수의 특성을 반영하여 초기화 범위를 조절하는 코드입니다.
import torch
tensor_uniform = torch.empty(3, 5)
torch.nn.init.kaiming_uniform_(tensor_uniform, mode='fan_in', nonlinearity='relu')
print(f'균등 분포 ', tensor_uniform )
tensor_normal = torch.empty(3, 5)
torch.nn.init.kaiming_normal_(tensor_normal, mode='fan_in', nonlinearity='relu')
print(f'정규 분포',tensor_normal)
Pytorch 실제 사용 예시
class BaseModel(nn.Module):
def __init__(self, num_classes=len(le.classes_)):
super(BaseModel, self).__init__()
self.backbone = models.efficientnet_b0(pretrained=True)
self.classifier = nn.Linear(1000, num_classes)
# 가중치 초기화 적용 x - 0.44 / 0.47
# torch.nn.init.uniform_(self.classifier.weight, a=0, b=1) - 0.38 / 0.42
# torch.nn.init.normal_(self.classifier.weight, mean=0, std=1) - 0.39 / 0.42
# torch.nn.init.xavier_normal_(self.classifier.weight,gain=1.0) - 0.46 / 0.49
# torch.nn.init.xavier_uniform_(self.classifier.weight,gain=1.0) # - 0.44 / 0.47
# torch.nn.init.kaiming_normal_(self.classifier.weight,a=0,mode='fan_in',nonlinearity='leaky_relu') - 0.45 / 0.49
# torch.nn.init.kaiming_uniform_(self.classifier.weight,a=0,mode='fan_in',nonlinearity='leaky_relu') - 0.44 / 0.47
def forward(self, x):
x = self.backbone(x)
x = self.classifier(x)
return x
.
'Deep learning' 카테고리의 다른 글
[pytorch] 학습의 가속화를 위한 배치 정규화(Batch Normalization) (0) | 2024.05.09 |
---|---|
[Deep Learning] 인공지능의 기초: 퍼셉트론부터 인공 신경망까지 (2) | 2024.02.06 |
[Deep learning] 정제와 정규화 (cleaning and normalization) (0) | 2023.03.03 |
[Deep learning] Callback 함수 (0) | 2023.02.06 |