GELU
Gaussian Error Linear Unit
Transformer에서 사용하는 활성화 함수. 확률적 정규화 효과.
Gaussian Error Linear Unit
Transformer에서 사용하는 활성화 함수. 확률적 정규화 효과.
GELU(Gaussian Error Linear Unit)는 2016년 Dan Hendrycks와 Kevin Gimpel이 제안한 활성화 함수로, 입력값에 표준 정규분포의 누적분포함수(CDF)를 곱한 형태입니다. 수식은 GELU(x) = x * Phi(x)이며, 여기서 Phi는 표준 정규분포의 CDF입니다.
GELU의 핵심 아이디어는 확률적 정규화입니다. 입력값이 클수록 통과할 확률이 높고, 작을수록 0에 가까워집니다. 이는 ReLU의 하드한 0 처리와 달리 부드러운 비선형성을 제공하며, 음수 영역에서도 작은 그래디언트를 허용합니다.
GELU는 BERT, GPT, ViT 등 대부분의 Transformer 기반 모델에서 표준 활성화 함수로 사용됩니다. 연구에 따르면 GELU는 NLP 태스크에서 ReLU 대비 평균 0.3-0.5% 성능 향상을 보이며, 특히 사전학습 모델에서 효과적입니다.
실무에서는 정확한 GELU 대신 근사 버전이 자주 사용됩니다. PyTorch의 tanh 근사(x * 0.5 * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3))))는 계산 효율이 높으면서 원본과 거의 동일한 성능을 보입니다.
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
# PyTorch 내장 GELU
gelu_layer = nn.GELU()
# 정확한 GELU 구현
def gelu_exact(x):
"""GELU(x) = x * Phi(x) where Phi is CDF of standard normal"""
return x * 0.5 * (1.0 + torch.erf(x / np.sqrt(2.0)))
# 빠른 근사 GELU (tanh approximation)
def gelu_tanh_approx(x):
"""Fast GELU approximation using tanh"""
return 0.5 * x * (1.0 + torch.tanh(
np.sqrt(2.0 / np.pi) * (x + 0.044715 * torch.pow(x, 3))
))
# Transformer 블록에서 GELU 사용 예시
class TransformerFFN(nn.Module):
def __init__(self, d_model=768, d_ff=3072, dropout=0.1):
super().__init__()
self.linear1 = nn.Linear(d_model, d_ff)
self.linear2 = nn.Linear(d_ff, d_model)
self.dropout = nn.Dropout(dropout)
self.gelu = nn.GELU()
def forward(self, x):
# FFN: Linear -> GELU -> Dropout -> Linear -> Dropout
x = self.linear1(x)
x = self.gelu(x)
x = self.dropout(x)
x = self.linear2(x)
x = self.dropout(x)
return x
# 활성화 함수 비교 시각화
x = torch.linspace(-4, 4, 1000)
plt.figure(figsize=(10, 6))
plt.plot(x.numpy(), F.relu(x).numpy(), label='ReLU', linewidth=2)
plt.plot(x.numpy(), gelu_exact(x).numpy(), label='GELU (exact)', linewidth=2)
plt.plot(x.numpy(), gelu_tanh_approx(x).numpy(), label='GELU (approx)',
linewidth=2, linestyle='--')
plt.axhline(y=0, color='gray', linestyle='-', alpha=0.3)
plt.axvline(x=0, color='gray', linestyle='-', alpha=0.3)
plt.xlabel('Input')
plt.ylabel('Output')
plt.title('Activation Function Comparison')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# BERT/GPT 스타일 모델에서 사용
class BertConfig:
hidden_size = 768
intermediate_size = 3072
hidden_act = "gelu" # GPT-2는 "gelu_new" 사용
print(f"GELU(-1.0) = {gelu_exact(torch.tensor(-1.0)).item():.4f}") # ~-0.1587
print(f"GELU(0.0) = {gelu_exact(torch.tensor(0.0)).item():.4f}") # 0.0
print(f"GELU(1.0) = {gelu_exact(torch.tensor(1.0)).item():.4f}") # ~0.8413
"GELU가 ReLU보다 Transformer에서 더 좋은 성능을 보이는 이유가 뭔가요?" - "GELU는 음수 입력에서도 작은 그래디언트를 허용해서 dying neuron 문제가 적고, 부드러운 비선형성이 attention 메커니즘과 잘 맞습니다. 특히 사전학습 과정에서 더 안정적인 학습이 가능합니다."
"Hugging Face 모델에서 gelu와 gelu_new의 차이점이 뭔가요?" - "gelu_new는 GPT-2에서 사용하는 tanh 근사 버전입니다. 정확한 GELU와 거의 동일한 성능을 보이면서 계산이 더 빠르죠. 대부분의 경우 어떤 것을 써도 무방하지만, 사전학습된 모델과 동일한 버전을 사용하는 것이 중요합니다."
"FFN 레이어에서 ReLU 대신 GELU를 쓴 이유가 있나요?" - "BERT 논문을 따라서 GELU를 사용했습니다. 저희 실험에서도 GELU가 validation loss 기준 약 2% 더 낮았고, 특히 마스크드 언어 모델링 태스크에서 차이가 컸습니다."
PyTorch에서 nn.GELU(approximate='tanh')와 기본 버전은 미세하게 다릅니다. 사전학습 모델을 파인튜닝할 때는 원본과 동일한 버전을 사용해야 재현성이 보장됩니다.
정확한 GELU는 erf 함수를 사용해 ReLU보다 약 3-4배 느립니다. 추론 속도가 중요한 경우 tanh 근사나 SiLU(Swish) 사용을 고려하세요.
매우 큰 음수 입력에서 GELU 그래디언트가 거의 0이 됩니다. mixed precision 학습 시 underflow 문제가 발생할 수 있으니 그래디언트 클리핑을 권장합니다.