본문 바로가기

책/파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습

04 파이토치 심화 (3) 정칙화

정칙화

정칙화(Regularization)란 모델 학습시 발생하는 과대적합 문제를 방지하기 위해 사용되는 기술로, 모델이 암기(Memorization)가 아니라 일반화(Generalization)할 수 있도록 손실 함수에 규제를 가하는 방식이다.

 

우리가 보통 데이터를 $y=F(x) + \epsilon$으로 표현하는데, 노이즈인 $\epsilon$을 제외하고 모델이 F(x)를 학습하도록 하는 것을 목표로 삼는다. 암기란 모델이 데이터의 노이즈까지 학습한 경우이고 일반화란 데이터의 일반적인 패턴을 학습한 경우이다.

 

정칙화는 모델이 특정 피처나 특정 패턴에 너무 많은 비중을 할당하지 않도록 손실 함수에 규제를 가해 모델의 일반화 성능을 향상시킨다. 다시 말해 모델의 분산 값이 낮아지고 모델의 추론 능력을 개선한다.

 

정칙화는 모델이 복잡하고 학습 데이터가 적을 때 활용한다. 모델이 단순하다면 모델 매개변수의 수가 적어 정칙화가 필요하지 않고, 데이터의 수가 많거나 데이터가 잘 정제되어있어 노이즈가 거의 없는 경우에는 사용하지 않는다.

 

L1 정칙화

라쏘 정칙화(Lasso Regularization)라고도 하며, L1 노름(L1 Norm)방식을 사용해 규제하는 방법이다. L1 노름은 벡터 또는 행렬값의 절댓값 합계를 계산한다. L1 정칙화는 손실 함수에 가중치 절댓값의 합을 추가해 과대적합을 방지한다.

 

L1 정칙화는 손실 함수에 가중치 절댓값의 합으로 규제를 가하므로 모델은 가중치 절댓값의 합도 최소가 되는 방향으로 학습을 진행한다. 모델 학습 시 값이 크지 않은 가중치들은 0으로 수렴하게되어 예측에 필요한 특징의 수가 줄어든다.

 

불필요한 가중치가 0이 되므로 L1 정칙화를 적용한 모델은 특징 선택(Feature Selection) 효과를 얻을 수 있다.

 

$L_1 = \lambda * \sum \left\vert w_i \right\vert$

 

$\lambda$는 규제 강도이다. L1 정칙화는 모델의 가중치를 정확히 0으로 만드는 희소한 모델이 될 수 있다. 불필요한 특징을 처리하지 않으므로 모델의 성능이 올라갈 수도 있지만, 예측에 사용되는 특징의 수가 줄어들게 되므로 정보의 손실로 이어질 수 있다.

 

L1 정칙화 적용 방식

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
  • 모델의 가중치를 모두 계산해 모델을 갱신해야 하므로 계산 복잡도를 높인다.
  • 미분 불가능하므로 역전파를 게산하는데 더 많은 리소스를 소모한다.
  • $\lambda$ 값에 민감하다.

L1 정칙화는 주로 선형 모델에 적용한다. 선형 회귀 모델에 L1 정칙화를 적용하는 것을 라쏘 회귀라 한다.

 

L2 정칙화

릿지 정칙화(Ridge Regularization)라고도 하며 L2 노름(L2 Norm) 방식을 사용해 규제하는 방법이다. L2 노름은 벡터 또는 행렬 값의 크기를 계산한다. L2 정칙화는 손실 함수에 가중치 제곱의 합을 추가해 과대적합을 방지하도록 규제한다.

 

L2 정칙화는 가중치 값들이 비교적 균일하게 분포되며, 가중치를 0으로 만들지 않고 0에 가깝게 만든다. 오차를 최소화하면서 가중치를 작게 유지하고 골고루 분포되게끔 하므로 모델의 복잡도가 일부 조정된다.

 

$L_2 = \lambda * \sum \left\vert {w_i}^2 \right\vert$

 

L2 정칙화 적용 방식

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
  • 모델의 가중치를 모두 계산해 모델을 갱신해야 하므로 계산 복잡도를 높인다.
  • 미분 불가능하므로 역전파를 게산하는데 더 많은 리소스를 소모한다.
  • $\lambda$ 값에 민감하다.

L2 정칙화는 주로 심층 신경망 모델에서 사용하며, 선형 회귀 모델에서 L2 정칙화를 적용하는 경우를 릿지 회귀라고 한다.

 

가중치 감쇠

모델이 더 작은 가중치를 갖도록 손실 함수에 규제를 가하는 방법이다. 파이토치의 가중치 감쇠는 L2 정규화와 동일하며 최적화 함수에서 weight_decay 하이퍼파라미털르 설정해 구현할 수 있다.

 

가중치 감쇠 적용 방식

optimizer = torch.optim.SGD(model.parameters(), lr=0.01, weight_decay=0.01)

 

모멘텀

모멘텀은 경사 하강법 알고리즘의 변형 중 하나로, 이전에 이동했던 방향과 기울기의 크기를 고려하여 가중치를 갱신한다. 이를 위해 지수 가중 이동평균을 사용하며, 이전 기울기 값의 일부를 현재 기울기 값에 추가해 가중치를 갱신한다.

 

자세한 내용은 이전 포스팅 참고

2023.09.20 - [책/밑바닥부터 시작하는 딥러닝] - 6장 학습 관련 기술들 (1)

 

6장 학습 관련 기술들 (1)

매개변수 갱신 최적화(optimization) : 매개변수의 최적값을 찾는 문제 확률적 경사 하강법(SGD) 앞장에서 보았던 가장 간단한 방법 파이썬으로 구현해보자 class SGD: """확률적 경사 하강법 (Stochastic Gra

ai-junha.tistory.com

 

엘라스틱 넷

L1, L2 정칙화를 결합해 사용하는 방식이다. 이 두 정칙화 방식을 결합함으로써 희소성과 작은 가중치의 균형을 맞춘다.

 

두 정칙화 방식의 선형 조합으로 사용하며 혼합 비율 $\alpha$을 설정해 가중치를 규제한다. 혼합 비율은 0에서 1 사이의 값을 사용한다.

 

$Elastic-Net = \alpha  \times L_1 + (1- \alpha) \times L_2$

 

엘라스틱 넷은 트레이드 오프 문제를 더 유연하게 대처할 수 있다. 또한 특징의 수가 샘플의 수보다 더 많을 때 유의미한 결과를 가져온다. 이로 인해 상관관계가 있는 특징을 더 잘 처리할 수 있다. 당연히 정칙화의 단점인 계산 복잡도 문제와 더 많은 리소스를 소모한다.

 

드롭아웃

모델의 훈련 과정에서 일부 노드를 일정 비율로 제거하거나 0으로 설정해 과대적합을 방지하는 간단하고 효율적인 방법이다.

 

과대적합을 발생시키는 이유 중 하나는 모델 학습 시 발생하는 노드간 동조화(Co-adaptation) 현상이다. 동조화 현상이란 모델 학습 중 특정 노드의 가중치나 편향이 큰 값을 갖게 되면 다른 노드가 큰 값을 갖는 노드에 의존하는 것을 말한다.

 

 

모델이 일부 노드를 제거해 학습하므로 투표 효과를 얻을 수 있어 모델 평균화가 된다. 하진 모델 평균화 효과를 얻기 위해 다른 드롭아웃 마스크를 사용해 모델을 여러번 훈련해야 하므로 훈련 시간이 늘어난다. 모든 노드를 사용해 학습하지 않으므로 데이터세트가 많지 않다면 효과를 얻기 힘들며, 충분한 데이터세트와 학습이 없다며 모든 노드가 균일하게 학습될 수 없으므로 성능이 저하될 수 있다.

 

그러므로 드롭아웃을 적용할 때는 충분한 데이터세트와 비교적 깊은 모델에 적용한다.

 

드롭아웃 적용 방식

class Net(torch.nn.Module):
  def __init__(self):
    super().__init__()
    self.layer1 = torch.nn.Linear(10, 10)
    self.dropout = torch.nn.Dropout(p=0.5)
    self.layer2 = torch.nn.Linear(10, 10)

  def forward(self, x):
    x = self.layer1(x)
    x = self.dropout(x)
    x = self.layer2(x)
    return x

신경망 패키지에 있는 Dropout 클래스로 구현한다. p는 베르누이 분포의 모수를 의미하며, 이 분포로 각 노드의 제거 여부를 호가률적으로 선택한다.

 

드롭아웃과 배치 정규화는 서로의 정칙화 효과를 방해할 수 있다. 그러므로 드롭아웃과 배치 정규화를 사용하는 경우에는 드롭아웃, 배치 정규화 순으로 적용한다. 또한 드롭아웃은 배치 정규화와 마찬가지로 모델이 학습할 때만 적용되며 추론하는 과정에서는 일부 노드를 삭제하지 않고 모든 노드를 사용해 예측한다.

 

그레디언트 클리핑

모델을 학습할 때 기울기가 너무 커지는 현상을 방지하는데 사용되는 기술이다. 특정 노드의 가중치가 높다면 높은 분산을 갖게 하여 모델의 성능이 저하될 수 있다.

 

가중치 최댓값을 규제해 최대 임곗값을 초과하지 않도록 기울기를 클리핑하여 설정한 임곗값으로 변경한다.

 

$w=r {w \over ||w||} if: ||w|| > r$

 

r은 하이퍼파라미터로 0.1이나 1과 같이 작은 크기로 설정해야 한다. 그레디언트 클리핑은 RNN이나 LSTM 모델을 핛브하는데 주로 사용되며, 가중치 값에 대한 엄격한 제약 조건을 요구하는 상황이거나 모델이 큰 기울기에 민감한 상황에서 유용하게 활용할 수 있다.

 

그레디언트 클리핑 함수

grad_norm = torch.nn.utils.clip_grad_norm_(
    parameters,
    max_norm,
    norm_type=2.0,
)

기울기를 정규화 하려는 parameters를 전달하며 max_norm을 초과하는 경우 기울기를 잘라낸다. norm_type은 클리핑을 계산할 유형을 설정한다.

 

그레디언트 클리핑 적용 방식

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)

  optimizer.step()

역전파(loss.backward)를 수행한 이후화 최적화 함수(optimizer.step)를 반영하기 전에 호출한다.