Posted:      Updated:

AI 스터디를 하며 ‘파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습’ 교재를 정리한 글입니다.

과대적합(Overfitting)과 과소적합(UnderFitting)

  • 과대적합: 훈련 데이터에서 우수하고 새로운 데이터는 제대로 예측하지 못하는 것
  • 과소적합: 훈련 데이터에서도 성능이 좋지 않고 새로운 데이터에서도 성능이 좋지 않음

모델을 변경해 문제 완화 가능
과대적합은 모델의 구조가 복잡하여 훈련 데이터에 의존하게되어 발생
과소적합은 모델의 구조가 너무 단순하여 데이터의 특징을 제대로 학습하지 못함

편향-분산 트레이드오프

훈련 데이터와 새로운 데이터 모두 우수한 성능을 보이기 위해서는 낮은 편향과 낮은 분산을 가져야 함

분산이 높으면 추정치(Estimate)에 대한 변동 폭이 커지며, 데이터가 가진 노이즈도 학습에 포함돼 과대적합 문제가 발생
편향이 높으면 추정치가 항상 일정한 값을 갖게 될 확률이 높아져 데이터의 특징을 제대로 학습하지 못해 과소적합 문제 발생
편향과 분산은 서로 반비례하므로, 절충해 높은 성능을 끌어내야 함

모델이 복잡할수록 분산이 커지고 편향이 작아짐
단순해질수록 분산은 작아지고 편향이 커짐

해결 방안

  • 데이터 수집: 학습 데이터 수 늘리기
  • 피처 엔지니어링: 기존 훈련 데이터에서 변수나 특징 추출, 피처를 작은 차원으로 축소
  • 모델 변경
  • 조기 중단: 과대적합 발생 전에 학습을 중단
  • 배치 정규화(Batch Normalization): 모델 계층마다 평균과 분산을 조정해 내부 공변량 변화를 줄여 과대적합 방지
  • 가중치 초기화(Weight Initialization): 모델의 매개변수 최적화 전에 가중치 초깃값 설정
  • 정칙화(Regularization): 목적 함수에 페널티 부여

배치 정규화(Batch Normalization)

내부 공변량 변화(Internal Covariate Shift)를 줄여 과대적합을 방지하는 기술
내부 공변량 변화: 배치 단위로 나눠 학습할 때, 상위 계층의 매개변수가 갱신될 때마다 현재 계층에 전달되는 데이터 분포가 변경되는 것

내부 공변량 변화 발생 시 은닉층에서 다음 은닉층으로 값이 전달될 때 입력값이 균일하지 않아 가중치가 제대로 갱신되지 않을 수 있음
학습이 불안정해지고 느려져 가중치가 일정한 값으로 수렴하기 어려워짐
초기 가중치 값에 민감해져 일반화하기가 어려워짐

미니 배치의 입력을 정규화하는 방식으로 동작
각 계층에 대한 입력이 일반화되고 독립적으로 정규화가 수행되므로 빠르게 값 수렴 가능
초기 가중치에 대한 영향을 줄일 수 있음

\[y_i = \frac{x_i - E[X]}{\sqrt{\text{Var}[X] + \epsilon}} \cdot \gamma + \beta\]

$x_i$: 입력값
$y_i$: 배치 정규화가 적용된 결괏값
$E[X]$: 산술 평균(Arithmetic Mean)
$Var[X]$: 분산(Variance)
$X$: 전체 모집단, 미니 배치의 은닉층 출력값
$\epsilon$: 분모가 0이 되는 현상을 방지하는 작은 상수
$\gamma$, $\beta$: 학습 가능한 매개변수로, 활성화 함수에서 발생하는 음수 영역 처리를 위해 조절 하는 스케일(Scale) 값과 시프트(Shift) 값

배치 정규화 클래스로 간단하게 수행 가능

import torch
from torch import nn
x = torch.FloatTensor([
    [-0.6577, -0.5797, 0.6360],
    [0.7392, 0.2145, 1.523],
    [0.2432, 0.5662, 0.322]
])
nn.BatchNorm1d(3)(x)  # 1차원 배치 정규화. 특징 개수를 입력 받음

출력

tensor([[-1.3246, -1.3492, -0.3756],
        [ 1.0912,  0.3077,  1.3685],
        [ 0.2334,  1.0415, -0.9930]], grad_fn=<NativeBatchNormBackward0>)

정규화 종류

배치 정규화: 미니 배치 기반 정규화, 합성곱 신경망(CNN)이나 다중 퍼셉트론(MLP)과 같은 순방향 신경망(Feedforward Neural Network)에서 주로 사용
계층 정규화(Layer Normalization): 채널 축으로 계산, 샘플 길이가 달라도 정규화 수행 가능, NLP에서 주로 사용하며 순환 신경망(RNN)이나 트랜스포머 기반 모델에서 주로 사용
인스턴스 정규화(Instance Normalization): 채널과 샘플 기준으로 정규화, 입력이 다른 분포를 갖는 작업에 적합, 생성적 적대 신경망(GAN), 스타일 변환(Style Transfer) 모델에서 주로 사용
그룹 정규화(Group Normalization): 채널을 N개의 그룹으로 나눠 그룹 내에서 정규화, 배치 크기가 작거나 채널 수가 매우 많은 경우 사용, CNN의 배치 크기가 작을 때 배치 정규화의 대안으로 사용

PyTorch 정규화 레이어

정규화 종류 클래스 입력 설명
배치 정규화 torch.nn.BatchNorm1d(num_features) 2D/3D 입력 데이터 배치 정규화 적용
배치 정규화 torch.nn.BatchNorm2d(num_features) 4D 입력 데이터 배치 정규화 적용
배치 정규화 torch.nn.BatchNorm3d(num_features) 5D 입력 데이터 배치 정규화 적용
레이어 정규화 torch.nn.LayerNorm(normalized_shape) 정규화할 특정 레이어 레이어 정규화 적용
인스턴스 정규화 torch.nn.InstanceNorm1d(num_features) 2D/3D 입력 데이터 인스턴스 정규화 적용
인스턴스 정규화 torch.nn.InstanceNorm2d(num_features) 4D 입력 데이터 인스턴스 정규화 적용
인스턴스 정규화 torch.nn.InstanceNorm3d(num_features) 5D 입력 데이터 인스턴스 정규화 적용
그룹 정규화 torch.nn.GroupNorm(num_groups, num_channels) 그룹별 채널 내 그룹 정규화 적용

가중치 초기화(Weight Initialization)

모델의 초기 가중치 값 설정
적절한 초깃값을 설정하면 기울기 폭주나 기울기 소실 문제를 완화할 수 있음

from torch import nn

가중치 초기화 함수 1

class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer = nn.Sequential(
            nn.Linear(1, 2),
            nn.Sigmoid()
        )
        self.fc = nn.Linear(2, 1)
        self._init_weights()  # 사용자 정의 메서드

    def _init_weights(self):  # 메서드 이름 앞에 언더바(_)를 붙여 protected 메서드로 사용
        nn.init.xavier_uniform_(self.layer[0].weight)  # 가중치에는 제이비어 초기화 적용, 선형 변환 함수는 첫 번째(index 0) 값을 불러와 적용
        self.layer[0].bias.data.fill_(0.01)  # 편향은 fill 함수로 간단하게 값 채울 수 있음

        # fc 변수는 시퀀셜 클래스를 적용하지 않았으므로 원소 호출 없음
        nn.init.xavier_uniform_(self.fc.weight)
        self.fc.bias.data.fill_(0.01)

가중치 초기화 함수 2

class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer = nn.Sequential(
            nn.Linear(1, 2),
            nn.Sigmoid()
        )
        self.fc = nn.Linear(2, 1)
        self.apply(self._init_weights)  # 모델의 모든 하위 모듈에 재귀적으로 적용: 텐서의 각 요소에 함수 적용, 결과와 함께 새 텐서 반환

    def _init_weights(self, module):  # module 매개변수 추가, 초기화 메서드에서 선언한 모델의 매개변수
        if isinstance(module, nn.Linear):  # 객체 식별 함수: 선형 변환 함수인지 확인하고 선형 변환일 경우 가중치, 편향 초기화
            nn.init.xavier_uniform_(module.weight)
            nn.init.constant_(module.bias, 0.01)  # 상수 초기화
        print(f"Apply : {module}")

실행

model = Net()

출력

Apply : Linear(in_features=1, out_features=2, bias=True)
Apply : Sigmoid()
Apply : Sequential(
  (0): Linear(in_features=1, out_features=2, bias=True)
  (1): Sigmoid()
)
Apply : Linear(in_features=2, out_features=1, bias=True)
Apply : Net(
  (layer): Sequential(
    (0): Linear(in_features=1, out_features=2, bias=True)
    (1): Sigmoid()
  )
  (fc): Linear(in_features=2, out_features=1, bias=True)
)

상수 초기화

초기 가중치 값을 모두 같은 값으로 초기화함
0, 1, 특정 값(Constant), 단위행렬(Unit Matrix), 디랙 델타 함수(Dirac Delta Function) 값 등이 있음

구현이 간단하고 계산 비용이 거의 들지 않으나 일반적으로 사용하지 않음
대칭 파괴(Breaking Symmetry) 현상으로 모델을 학습하기 어렵거나 불가능하게 함
모든 노드가 동일한 출력을 생성하여 모델이 학습되지 않음

스칼라값을 입력으로 받는 매우 작은 모델이나 퍼셉트론 등에 적용
편향을 초기화 하는 경우 0, 0.01 등 형태로 초기화하는 데 사용

무작위 초기화

초기 가중치 값을 무작위 값이나 균등 분포, 정규 분포, 잘린 정규 분포, 희소 정규 분포 등으로 초기화하는 것

무작위 초기화는 계층이 적거나 하나만 있는 경우 간단하여 보편적으로 가장 많이 사용함
노드의 가중치와 편향을 무작위로 할당해 네트워크가 학습할 수 있게 하여 대칭 파괴 문제 방지
계층이 많아지고 깊어질 수록 활성화 값이 양 끝단에 치우쳐 기울기 소실 현상 발생

카이밍(Kaiming) & 허(He) 초기화

균등 분포나 정규 분포를 사용하지만 현재 계층의 입력 뉴런 수를 기반으로만 가중치를 초기화
각 노드의 출력 분산이 입력 분산과 동일하게 만들어 ReLU 함수의 죽은 뉴런 문제 최소화 가능
순방향 신경망 네트워크에서 가중치 초기화 시 효과적

직교 초기화(Orthogonal Initialization)

특잇값 분해(Singular Value Decomposition, SVD)를 활용해 자기 자신을 제외한 모든 열, 행 벡터들과 직교이면서 동시에 단위 벡터인 행렬을 만드는 방법
장단기 메모리(LSTM) 및 게이트 순환 유닛(GRU)와 같은 순환 신경망(RNN)에서 주로 사용

직교 행렬의 고윳값의 절댓값은 1이므로 행렬 곱을 여러번 수행해도 기울기 폭주나 소실이 발생하지 않음
가중치 행렬의 고윳값이 1에 가까워지도록 하여 RNN에서 기울기가 사라지는 문제를 방지하기 위해 사용

모델이 특정 초기화 값에 지나치게 민감해지므로 순방향 신경망에서는 사용하지 않음

PyTorch 초기화 함수

초기화 종류 함수명 인자 설명
상수 초기화 torch.nn.init.constant_ tensor, val tensor에 val 값을 상수로 초기화
1로 초기화 torch.nn.init.ones_ tensor tensor를 1로 초기화
0으로 초기화 torch.nn.init.zeros_ tensor tensor를 0으로 초기화
단위 행렬 초기화 torch.nn.init.eye_ tensor tensor를 단위 행렬로 초기화 (정사각형 텐서에 가능)
다람쥐 필터 초기화 torch.nn.init.dirac_ tensor tensor를 다람쥐 필터 형상으로 초기화 (3, 4, 5차원 텐서 가능)
균등 분포 초기화 torch.nn.init.uniform_ tensor, a=0.0, b=1.0 tensor를 [a, b] 균등 분포로 초기화
정규 분포 초기화 torch.nn.init.normal_ tensor, mean=0.0, std=1.0 tensor를 N(mean, std) 정규 분포로 초기화
절단 정규 분포 초기화 torch.nn.init.trunc_normal_ tensor, mean=0.0, std=1.0, a=-2.0, b=2.0 tensor를 N(mean, std)로 초기화하고 [a, b]로 절단
희소 행렬 초기화 torch.nn.init.sparse_ tensor, sparsity tensor를 sparsity 밀도의 N(0, 0.01) 정규 분포로 초기화
제이비어 균등 초기화 torch.nn.init.xavier_uniform_ tensor, gain=1.0 tensor를 제이비어 균등 분포로 초기화 (게인 사용)
제이비어 정규 초기화 torch.nn.init.xavier_normal_ tensor, gain=1.0 tensor를 제이비어 정규 분포로 초기화 (게인 사용)
카이밍 균등 초기화 torch.nn.init.kaiming_uniform_ tensor, a=0 tensor를 카이밍 균등 분포로 초기화 (게인 사용)
카이밍 정규 초기화 torch.nn.init.kaiming_normal_ tensor, a=0 tensor를 카이밍 정규 분포로 초기화 (게인 사용)
직교 초기화 torch.nn.init.orthogonal_ tensor, gain=1 tensor를 직교 초기화 (게인을 사용해 게인스케일 적용)

Categories:

댓글남기기