[PyTorch] 정칙화(Regularization)
Posted: Updated:
AI 스터디를 하며 ‘파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습’ 교재를 정리한 글입니다.
정칙화(Regularization)
모델 학습 시 발생하는 과대적합 문제를 방지하기 위해 사용되는 기술
모델이 암기(Memorization)가 아니라 일반화(Generalization)할 수 있도록 손실 함수에 규제(Penalty)를 가하는 방식
모델이 데이터의 학습 데이터의 노이즈나 특정 패턴을 파악하지 않고 일반화되어 새로운 데이터에서도 정확한 예측이 가능하게 하도록 하기 위함
학습 데이터들이 가진 작은 차이점에 덜 민감해져 모델의 분산 값이 낮아짐
데이터 학습 시 의존하는 특징 수를 줄여 추론 능력 개선하는 방식
모델이 단순하면 모델 매개변수가 적어 정칙화가 필요하지 않고, 데이터의 수가 많거나 잘 정제되어 있어 노이즈가 거의 없는 경우에는 사용하지 않음
모델이 비교적 복잡하고 학습에 사용된 데이터의 수가 적을 때 활용할 수 있음
import torch
import pandas as pd
from torch import nn
from torch import optim
from torch.utils.data import Dataset, DataLoader, random_split
class CustomDataset(Dataset):
def __init__(self, file_path):
df = pd.read_csv(file_path)
self.x1 = df.iloc[:, 0].values
self.x2 = df.iloc[:, 1].values
self.x3 = df.iloc[:, 2].values
self.y = df.iloc[:, 3].values
self.length = len(df)
def __getitem__(self, index):
x = torch.FloatTensor([self.x1[index], self.x2[index], self.x3[index]])
y = torch.FloatTensor([self.y[index]])
return x, y
def __len__(self):
return self.length
dataset = CustomDataset("datasets/binary.csv")
train_dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = nn.Sequential(
nn.Linear(3, 50),
nn.ReLU(),
nn.Linear(50, 1)
).to(device)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
L1 정칙화(L1 Regularization)
L1 노름(L1 Norm) 방식을 사용해 규제하는 방법으로, 라쏘 정칙화(Lasso Regularization)라고도 함
주로 선형 모델에 사용하며, 선형 회귀 모델에 적용하면 라쏘 회귀(Least Absolute Shrinkage and Selection Operator Regression)라고 함
L1 노름은 벡터 또는 행렬값의 절댓값 합계를 계산
손실 함수에 가중치 절댓값의 합을 추가해 과대적합 방지
손실 함수에 값이 추가되면 오차가 더 커지므로, 모델은 추가된 값까지 최소화하는 방향으로 학습이 진행될 것
모델 학습 시 불필요한 가중치는 0으로 수렴하여 특징 선택(Feature Selection) 효과를 얻을 수 있음
예측에 사용되는 특징 수가 줄어 정보 손실 발생 가능
모델 가중치를 모두 계산해 모델을 갱신하므로 계산 복잡도가 높음
미분이 불가능해 역전파에 많은 리소스 필요
_lambda
값이 적절하지 않으면 가중치 값들이 너무 작아져 모델 해석하기 어려울 수 있음
$\lambda$: 규제 강도, 0보다 큰 값으로 설정 (0에 가까워질수록 많은 특징 사용)
for x, y in train_dataloader:
x = x.to(device)
y = y.to(device)
output = model(x)
_lambda = 0.5 # 여러번 반복하여 최적의 값을 찾아야 함
l1_loss = sum(p.abs().sum() for p in model.parameters())
loss = criterion(output, y) + _lambda * l1_loss
L2 정칙화 (L2 Regularization)
L2 노름(L1 Norm) 방식을 사용해 규제하는 방법으로, 릿지 정칙화(Ridge Regularization)라고도 함
과대적합을 효과적으로 방지하기 위해 조기 중지 또는 드롭아웃과 함께 사용할 수 있음
주로 심층 신경망 모델에서 사용하며, 선형 회귀 모델에서 적용하는 경우를 릿지 회귀(Ridge Regression)라고 함
L2 노름은 벡터 또는 행렬 값의 크기를 계산하는데, L2 정칙화는 손실 함수에 가중치 제곱의 합을 추가하는 방식
하나의 특징이 너무 중요한 요소가 되지 않도록 규제하는 것이 특징
L1 정칙화에 비해 가중치 값들이 비교적 균일하게 분포되며, 가중치를 0으로 만들지 않고 0에 가깝게 만듦
모델 매개변수의 제곱 값을 계산하고 저장해야 하므로 L1 처럼 많은 리소스를 소모
\[L_2 = \lambda * \sum_{i=0}^n |w_i^2|\]for x, y in train_dataloader:
x = x.to(device)
y = y.to(device)
output = model(x)
_lambda = 0.5 # 여러번 반복하여 최적의 값을 찾아야 함
l2_loss = sum(p.pow(2.0).sum() for p in model.parameters())
loss = criterion(output, y) + _lambda * l2_loss
L1 정칙화와 L2 정칙화 비교
L1 정칙화 | L2 정칙화 | |
---|---|---|
기술 방식 | 가중치 절대값의 합 | 가중치 제곱의 합 |
모델링 | 희소함(Sparse Solution) | 희소하지 않음(Non-sparse Solution) |
특징 선택 | 있음 | 없음 |
이상치 | 강함 | 약함 |
가중치 | 0이 될 수 있음 | 0에 가깝게 됨 |
효과 | 비교적 복잡한 데이터 패턴을 학습할 수 없음 | 비교적 복잡한 데이터 패턴을 학습할 수 있음 |
가중치 감쇠(Weight Decay)
일반적으로 L2 정칙화와 동의어로 사용되지만, 가중치 감쇠는 손실 함수에 규제 항을 추가하는 기술 자체를 의미
파이토치의 가중치 감쇠는 L2 정규화와 동일하며 weight_decay
하이퍼파라미터로 설정 가능
L2 정칙화의 장단점을 동일하게 갖지만, 간단하게 적용할 수 있음
조기 중지, 드롭아웃과 함께 사용 가능
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, weight_decay=0.01)
모멘텀(Momentum)
경사 하강법 알고리즘의 변형 중 하나로, 이전에 이동했던 방향과 기울기의 크기를 고려하여 가중치 갱신
지수 가중 이동평균 사용, 이전 기울기 값의 일부를 현재 기울기 값에 추가하여 가중치 갱신
이전 모멘텀 값에 모멘텀 계수 $\gamma$를 곱한 값과 경사 하강법 갱신 값의 합으로 계산
모멘텀 계수는 0.0~1.0 사이의 값으로 설정할 수 있고, 일반적으로 0.9 사용
계수를 0으로 설정하면 경사 하강법 수식과 동일
\(v_i = \gamma v_{i-1} + \alpha \nabla f(W_i)\) \(W_{i+1} = W_i - v_i\)
$v$: $i$번째 모멘텀 값, 이동 벡터
$\gamma$ = 하이퍼파라미터, 모멘텀 계수
최적화 함수의 momentum
하이퍼파라미터로 설정 가능
엘라스틱 넷(Elastic-Net)
L1 정칙화와 L2 정칙화 방식을 선형 조합으로 사용해 가중치 규제
$\alpha$라는 혼합 비율을 사용해 어떤 정칙화를 많이 반영할지 설정
혼합 비율은 0-1 사이의 값을 사용하며, 1일 때 L1 정칙화, 0일 때 L2정칙화가 됨
특징 수가 샘플 수보다 더 많을 때 유의미한 결과를 가져올 수 있어 상관관계가 있는 특징을 잘 처리함
혼합 비율 조정을 위해 튜닝이 많이 필요하며, 계산 복잡도 문제를 가진 정칙화를 활용하기 때문에 리소스를 많이 소모함
\[\text{Elastic-Net} = \alpha \times L_1 + (1 - \alpha) \times L_2\]드롭아웃(Dropout)
동조화(Co-adaptation) 현상: 모델 학습 중 특정 노드의 가중치나 편향이 큰 값을 갖게 될 때, 다른 노드가 큰 값을 갖는 노드에 의존하는 것
동조화 현상으로 인해 특정 노드에 의존성이 생기면 과대적합이 발생할 수 있음
학습 과정에서 일부 노드를 제거해 노드 간 의존성을 억제하는 방식
일부 노드를 일정 비율로 제거하거나 0으로 설정함
투표(Voting) 효과를 얻을 수 있고, 모델 평균화(Model Averaging)됨
충분한 데이터셋이 필요하며, 비교적 깊은 모델에 사용
Dropout
클래스로 쉽게 구현 가능
p
는 베르누이 분포의 모수로, 각 노드의 제거 여부를 확률적으로 선택
배치 정규화와 동시에 사용할 경우 서로의 정칙화 효과를 방해하므로 주의
함께 사용해야 할 경우, 드롭아웃, 배치 정규화 순으로 적용
class Net(nn.Module):
def __init__(self):
super().__init__()
self.layer1 = nn.Linear(10, 10)
self.dropout = nn.Dropout(p=0.5)
self.layer2 = nn.Linear(10, 10)
def forward(self, x):
x = self.layer1(x)
x = self.dropout(x)
x = self.layer2(x)
return x
그레이디언트 클리핑(Gradient Clipping)
\[w = r\frac{w}{\|w\|} \quad \text{if:} \quad \|w\| > r\]가중치 최댓값을 규제해 임곗값을 초과하지 않도록 기울기를 잘라 모델 학습 시 기울기가 너무 커지는 현상 방지
가중치 노름이 최대 임곗값 $r$보다 높은 경우 수행하며, 일반적으로 L2 노름 사용
$r$은 사용자가 설정하는 하이퍼파라미터로, 0.1이나 1 등 작은 크기의 값 적용
최대 임곗값이 높으면 모델 표현력이 떨어지며, 낮으면 학습이 불안정해질 수 있으므로 경험적으로 선택해야 함
기울기 폭주에 취약한 순환 신경망(RNN), LSTM 모델 학습 시 주로 사용
for x, y in train_dataloader:
x = x.to(device)
y = y.to(device)
output = model(x)
loss = criterion(output, y)
optimizer.zero_grad()
loss.backward()
# 역전파 수행 후 최적화 함수 반영 전 호출
torch.nn.utils.clip_grad_norm_(model.parameters(), 0.1) # 그레이디언트 클리핑 함수: 기울기 정규화하려는 파라미터 전달, 0.1은 최대 노름
optimizer.step()
댓글남기기