[딥러닝] 정칙화(regularization)
과적합(Overfitting)의 문제
과적합은 모델이 훈련 데이터에 너무 잘 맞추어져서 새로운 데이터나 테스트 데이터에 대해 제대로된 예측을 하지 못하는 현상을 의미합니다. 이는 모델이 훈련 데이터의 노이즈까지 학습해버려, 실제 세계의 다양한 상황을 처리하는데 실패하는 주요 원인이 됩니다. 결국, 모델의 일반화 능력이 떨어지고, 실제 세계에서의 성능이 저하됩니다.
일반화는 모델이 처음 보는 데이터에 대해 얼마나 잘 예측을 하는지를 나타냅니다. 즉, 모델이 학습 단계에서 보지 못한 새로운 데이터에 대해서도 정확하게 작동하는 능력을 말합니다. 정칙화 기법은 모델이 훈련 데이터에 과도하게 맞추어지는 것을 방지하고, 모델의 복잡도를 줄임으로써 일반화 능력을 증가시키는 데 목적이 있습니다.
정칙화를 구현하는 방법은 여러 가지가 있으며, 그 중 몇 가지는 다음과 같습니다.
- 드롭아웃(Dropout): 드롭아웃은 학습 과정에서 모델의 일부 뉴런을 임의로 비활성화시켜 모델이 일부 특징에만 의존하지 않게 함으로써 과적합을 방지하는 기법입니다.
- L1/L2 정규화: L1 정규화는 가중치의 절대값의 합을, L2 정규화는 가중치의 제곱의 합을 손실함수에 추가하여 가중치가 너무 커지지 않도록 제한합니다.
- 배치 정규화(Batch Normalization): 배치 정규화는 학습 중 각 레이어의 입력 분포가 변경되는 현상인 내부 공변량 변화(internal covariate shift)를 줄이고, 모델의 학습 속도를 높이며 과적합 문제를 완화시키는 기법입니다.
드롭아웃
드롭아웃(Dropout)은 딥러닝 모델 학습 과정에서 무작위로 일부 뉴런을 제외함으로써, 모델이 특정 뉴런에 과도하게 의존하는 것을 방지하는 방법입니다. 이는 과적합을 줄이고, 모델이 더 일반화되도록 돕습니다. 아래는 드롭아웃에 대한 세부 내용을 담고 있습니다.
드롭아웃은 매번의 학습 단계에서, 설정된 비율에 따라 무작위로 뉴런을 "삭제"하는 것을 의미합니다. 여기서 삭제라 함은 해당 뉴런을 학습 과정에서 임시적으로 제외한다는 의미입니다. 이때 설정된 비율을 드롭아웃 비율이라 합니다.
드롭아웃은 훈련 과정 중에만 적용되며, 매 학습 단계마다 무작위로 선택된 뉴런들이 활성화되지 않도록 합니다. 이는 네트워크가 학습 데이터의 작은 변화나 노이즈에 덜 민감하게 만들어줍니다. 테스트 또는 평가 시에는 드롭아웃이 적용되지 않으며, 모든 뉴런이 활성화 상태로 유지됩니다. 이때, 뉴런의 출력을 드롭아웃 비율에 비례하여 조정함으로써, 훈련 시에 무작위로 제거된 뉴런의 기여도를 보정합니다.
드롭아웃은 네트워크의 복잡성을 감소시키고, 훈련 과정에서 다양한 네트워크 구조를 탐색하는 것과 유사한 효과를 가져옵니다. 이로 인해 모델이 더 강건해지고, 과적합을 방지하는 데 도움을 줍니다.
Pytorch에서 드롭아웃을 적용하는 방법은 매우 간단합니다. 각 층의 뒤에 torch.nn.Dropout
을 추가하면 됩니다. 드롭아웃 비율은 0과 1 사이의 값으로 설정하며, 이 비율에 따라 해당 층의 뉴런 중 일부가 무작위로 제외됩니다.
import torch
import torch.nn as nn
import torch.nn.functional as F
class MyModel(nn.Module):
def __init__(self, input_size, hidden_size, num_classes):
super(MyModel, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size)
self.dropout = nn.Dropout(0.5) # 드롭아웃 비율을 50%로 설정
self.fc2 = nn.Linear(hidden_size, num_classes)
def forward(self, x):
x = F.relu(self.fc1(x))
x = self.dropout(x) # 활성화 함수 적용 후 드롭아웃 적용
x = self.fc2(x)
return x
# 모델 초기화
model = MyModel(input_size=784, hidden_size=500, num_classes=10)
# 예시 데이터 (랜덤으로 생성)
input_data = torch.randn(1, 784)
# 모델을 통해 예시 데이터 전달
output = model(input_data)
위의 예시에서는 드롭아웃을 하나의 층으로 추가하여 사용했습니다. 이 방법은 모델의 과적합을 방지하고 일반화 성능을 향상시키는 데 중요한 역할을 합니다.
배치 정규화의 개념
학습 과정에서 신경망의 각 층으로의 입력은 가중치의 업데이트에 따라 끊임없이 변화합니다. 이런 변화는 특히 딥러닝 모델에서 더 극심하며, 각 층의 입력 분포가 계속 변한다는 의미의 "내부 공변량 변화"를 야기합니다. 배치 정규화는 이 문제를 해결하기 위해 도입되었으며, 각 층에서의 입력을 정규화하는 기법으로, 학습 과정에서 mini-batch의 평균과 분산을 사용하여 정규화를 실시합니다.
배치 정규화의 주 목적은 내부 공변량 변화를 줄이는 것입니다. 이를 통해 학습 과정이 안정화되며, 학습률를 높일 수 있는 가능성이 생깁니다. 결과적으로, 학습이 더 빨리 수렴하도록 도와줍니다.
배치 정규화의 이점:
- 더 높은 학습률 사용 가능: 내부 공변량 변화를 줄임으로써, 높은 학습률에서도 모델이 안정적으로 학습될 수 있게 됩니다.
- 초기 가중치 선택의 중요성 감소: 초기 가중치에 대한 모델의 민감도가 감소하여, 다양한 초기화 방법이 유효하게 됩니다.
- 과적합에 대한 경향 감소: 미니 배치마다 입력 분포를 정규화함으로써, 모델이 일종의 규제 효과를 경험하게 됩니다. 이는 과적합을 완화하는 역할을 합니다.
PyTorch에서는 torch.nn
모듈 아래 BatchNorm1d
, BatchNorm2d
, BatchNorm3d
등을 통해 쉽게 배치 정규화를 적용할 수 있습니다. 예를 들어, 2D 합성곱 층의 배치 정규화는 다음과 같이 구현할 수 있습니다:
import torch
import torch.nn as nn
# 2D 합성곱 레이어 예시
conv_layer = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1)
# 배치 정규화 레이어
bn_layer = nn.BatchNorm2d(num_features=64)
# 예제 입력 데이터
input_tensor = torch.randn(10, 32, 28, 28) # 배치 크기 10, 채널 32, 28x28 이미지
# 합성곱 층을 통과
output_tensor = conv_layer(input_tensor)
# 배치 정규화 적용
output_tensor_bn = bn_layer(output_tensor)
print("원래 텐서 크기:", output_tensor.size())
print("배치 정규화 후 텐서 크기:", output_tensor_bn.size())
위 코드는 2D 이미지에 대한 합성곱 레이어를 생성하고, 이를 통과한 후, 결과 텐서에 배치 정규화를 적용합니다. 이 과정은 신경망에서 일반적으로 층 사이에 배치 정규화 레이어를 삽입하여 사용하게 됩니다.
L1 정규화
L1 정규화, 또는 Lasso 정규화는 가중치의 절대값의 합에 비례하는 비용을 손실 함수에 추가합니다. 이는 다음과 같이 표현할 수 있습니다:
여기서 는 정규화 강도를 조절하는 매개변수이고, 는 모델의 가중치입니다. L1 정규화의 주요 특징은 모델의 일부 가중치를 정확히 0으로 만들어 희소성(sparsity)을 높이는 것입니다. 이는 불필요한 특성을 모델에서 제거하여, 모델이 중요한 특성에만 집중하게 합니다.
import torch
import torch.nn as nn
# L1 정규화 예시
loss_function = nn.MSELoss()
lambda_1 = 0.01 # 정규화 매개 변수
# 예시 모델 파라미터
weights = torch.randn(10, requires_grad=True)
target = torch.randn(10)
# 예시 손실 계산
output_loss = loss_function(weights, target)
l1_regularization = lambda_1 * torch.sum(torch.abs(weights))
total_loss = output_loss + l1_regularization
print("Total Loss with L1 Regularization:", total_loss.item())
L2 정규화
L2 정규화, 또는 Ridge 정규화는 가중치의 제곱에 비례하는 비용을 손실 함수에 추가합니다. L2 정규화는 아래와 같이 표현됩니다:
L2 정규화는 모든 가중치를 작게 만들어 규제하는 효과가 있어 과적합을 방지하는데 도움이 됩니다. 학습 과정에서 가중치가 너무 크게 증가하는 것을 방지하여 모델의 일반화 능력을 향상시킵니다.
예시 코드 (PyTorch):
# L2 정규화 예시
# PyTorch는 L2 정규화를 고유하게 지원하므로 별도로 구현할 필요가 없음.
# 대부분의 최적화 알고리즘에서 weight_decay 매개변수로 적용 가능.
optimizer = torch.optim.SGD(weights, lr=0.01, weight_decay=0.01)
# 학습 루프 안에서
optimizer.zero_grad()
output_loss = loss_function(weights, target)
output_loss.backward()
optimizer.step()
L1 대 L2
L1과 L2 정규화는 모두 모델의 과대적합을 막아주는 역할을 하지만, 그 방식에서 차이가 있습니다.
- L1 정규화는 가중치의 절대값에 비례하는 비용을 추가하여 특정 가중치를 0으로 만들어 희소한 모델을 생성합니다. 이는 모델의 해석성을 높여줍니다.
- L2 정규화는 가중치가 큰 값으로 증가하는 것을 제한하여 과적합을 방지합니다. 가중치가 완전히 0이 되지는 않아 모델이 더 매끄러워지게 합니다.
적절한 정규화 기법의 선택은 문제의 특성, 데이터의 차원성 및 모델의 복잡성 등 여러 요소에 따라 달라집니다. 일반적으로 L1 정규화는 특성 선택의 목적으로, L2 정규화는 모델의 과적합을 방지하기 위한 목적으로 사용됩니다. 때로는 L1과 L2를 결합한 엘라스틱 넷(Elastic Net) 정규화가 사용되기도 합니다.