🤖 AI/ML

GELU

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% 더 낮았고, 특히 마스크드 언어 모델링 태스크에서 차이가 컸습니다."

⚠️ 흔한 실수 & 주의사항

1
근사 버전 선택 주의

PyTorch에서 nn.GELU(approximate='tanh')와 기본 버전은 미세하게 다릅니다. 사전학습 모델을 파인튜닝할 때는 원본과 동일한 버전을 사용해야 재현성이 보장됩니다.

2
연산 비용

정확한 GELU는 erf 함수를 사용해 ReLU보다 약 3-4배 느립니다. 추론 속도가 중요한 경우 tanh 근사나 SiLU(Swish) 사용을 고려하세요.

3
수치 안정성

매우 큰 음수 입력에서 GELU 그래디언트가 거의 0이 됩니다. mixed precision 학습 시 underflow 문제가 발생할 수 있으니 그래디언트 클리핑을 권장합니다.

🔗 관련 용어

📚 더 배우기