하이퍼파라미터
Hyperparameter
학습 전 설정하는 파라미터. 학습률, 배치 크기, 에포크 수 등. 튜닝으로 성능 최적화.
Hyperparameter
학습 전 설정하는 파라미터. 학습률, 배치 크기, 에포크 수 등. 튜닝으로 성능 최적화.
하이퍼파라미터(Hyperparameter)는 머신러닝 모델의 학습 과정을 제어하는 외부 설정값입니다. 모델이 데이터로부터 학습하는 가중치(weight)와 달리, 하이퍼파라미터는 학습 전에 사람이 직접 설정해야 합니다. 대표적인 예로 학습률(learning rate), 배치 크기(batch size), 에포크 수(epochs), 은닉층 수(hidden layers), 드롭아웃 비율(dropout rate) 등이 있습니다. 하이퍼파라미터 선택은 모델 성능에 결정적인 영향을 미칩니다.
하이퍼파라미터의 종류는 크게 세 가지로 나눌 수 있습니다. 첫째, 모델 구조 관련 - 레이어 수, 뉴런 수, 필터 크기, Attention 헤드 수 등 아키텍처를 결정합니다. 둘째, 학습 알고리즘 관련 - 학습률, 옵티마이저 종류(Adam, SGD), 모멘텀, 가중치 감쇠(weight decay) 등이 있습니다. 셋째, 정규화 관련 - 드롭아웃, L1/L2 정규화 계수, 조기 종료(early stopping) 인내값 등 과적합을 방지합니다.
2024-2025년 LLM 시대에도 하이퍼파라미터는 여전히 중요합니다. GPT-4, Claude 같은 대형 모델은 수조 개의 파라미터를 가지지만, 이를 학습하거나 파인튜닝할 때 학습률 스케줄, 워밍업 스텝, 그래디언트 클리핑 임계값 같은 하이퍼파라미터가 성공을 좌우합니다. LoRA 파인튜닝에서는 rank, alpha, target modules 같은 새로운 하이퍼파라미터가 등장했습니다.
하이퍼파라미터와 파라미터의 차이를 명확히 이해해야 합니다. 파라미터(가중치)는 모델이 데이터로부터 학습하는 값으로, 신경망의 W와 b가 여기에 해당합니다. 하이퍼파라미터는 학습 과정 자체를 설정하는 값으로, 이 값에 따라 파라미터가 어떻게 업데이트될지가 결정됩니다. 예를 들어 학습률 0.001과 0.01은 동일한 아키텍처에서도 완전히 다른 최종 모델을 만들어냅니다.
PyTorch에서 주요 하이퍼파라미터를 설정하고 관리하는 예제입니다.
import torch
import torch.nn as nn
import torch.optim as optim
from dataclasses import dataclass
from typing import Optional
@dataclass
class HyperParameters:
"""모델 학습을 위한 하이퍼파라미터 설정"""
# 모델 구조 하이퍼파라미터
hidden_dim: int = 256
num_layers: int = 3
num_heads: int = 8 # Transformer용
dropout: float = 0.1
# 학습 알고리즘 하이퍼파라미터
learning_rate: float = 1e-4
weight_decay: float = 0.01
batch_size: int = 32
num_epochs: int = 100
# 옵티마이저 하이퍼파라미터
optimizer: str = "adamw" # adam, sgd, adamw
beta1: float = 0.9
beta2: float = 0.999
momentum: float = 0.9 # SGD용
# 학습률 스케줄러 하이퍼파라미터
scheduler: str = "cosine" # cosine, step, linear
warmup_steps: int = 1000
min_lr: float = 1e-6
# 정규화 하이퍼파라미터
gradient_clip: float = 1.0
early_stopping_patience: int = 10
label_smoothing: float = 0.1
# LoRA 파인튜닝 하이퍼파라미터 (LLM용)
lora_rank: int = 8
lora_alpha: int = 16
lora_dropout: float = 0.05
class SimpleMLP(nn.Module):
"""하이퍼파라미터 기반 MLP 모델"""
def __init__(self, input_dim: int, output_dim: int, hp: HyperParameters):
super().__init__()
layers = []
dims = [input_dim] + [hp.hidden_dim] * hp.num_layers + [output_dim]
for i in range(len(dims) - 1):
layers.append(nn.Linear(dims[i], dims[i+1]))
if i < len(dims) - 2: # 마지막 층 제외
layers.append(nn.ReLU())
layers.append(nn.Dropout(hp.dropout))
self.network = nn.Sequential(*layers)
def forward(self, x):
return self.network(x)
def create_optimizer(model: nn.Module, hp: HyperParameters) -> optim.Optimizer:
"""하이퍼파라미터에 따른 옵티마이저 생성"""
if hp.optimizer == "adam":
return optim.Adam(
model.parameters(),
lr=hp.learning_rate,
betas=(hp.beta1, hp.beta2),
weight_decay=hp.weight_decay
)
elif hp.optimizer == "adamw":
return optim.AdamW(
model.parameters(),
lr=hp.learning_rate,
betas=(hp.beta1, hp.beta2),
weight_decay=hp.weight_decay
)
elif hp.optimizer == "sgd":
return optim.SGD(
model.parameters(),
lr=hp.learning_rate,
momentum=hp.momentum,
weight_decay=hp.weight_decay
)
else:
raise ValueError(f"Unknown optimizer: {hp.optimizer}")
def create_scheduler(optimizer: optim.Optimizer, hp: HyperParameters, total_steps: int):
"""학습률 스케줄러 생성"""
if hp.scheduler == "cosine":
return optim.lr_scheduler.CosineAnnealingLR(
optimizer,
T_max=total_steps - hp.warmup_steps,
eta_min=hp.min_lr
)
elif hp.scheduler == "step":
return optim.lr_scheduler.StepLR(
optimizer,
step_size=total_steps // 3,
gamma=0.1
)
elif hp.scheduler == "linear":
return optim.lr_scheduler.LinearLR(
optimizer,
start_factor=1.0,
end_factor=hp.min_lr / hp.learning_rate,
total_iters=total_steps
)
def train_with_hyperparameters(
model: nn.Module,
train_loader,
hp: HyperParameters
):
"""하이퍼파라미터를 사용한 학습 루프"""
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
criterion = nn.CrossEntropyLoss(label_smoothing=hp.label_smoothing)
optimizer = create_optimizer(model, hp)
total_steps = len(train_loader) * hp.num_epochs
scheduler = create_scheduler(optimizer, hp, total_steps)
best_loss = float('inf')
patience_counter = 0
for epoch in range(hp.num_epochs):
model.train()
epoch_loss = 0.0
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
# 그래디언트 클리핑 적용
torch.nn.utils.clip_grad_norm_(model.parameters(), hp.gradient_clip)
optimizer.step()
scheduler.step()
epoch_loss += loss.item()
avg_loss = epoch_loss / len(train_loader)
print(f"Epoch {epoch+1}/{hp.num_epochs}, Loss: {avg_loss:.4f}, LR: {scheduler.get_last_lr()[0]:.2e}")
# 조기 종료 체크
if avg_loss < best_loss:
best_loss = avg_loss
patience_counter = 0
else:
patience_counter += 1
if patience_counter >= hp.early_stopping_patience:
print(f"Early stopping at epoch {epoch+1}")
break
# 사용 예시
if __name__ == "__main__":
# 하이퍼파라미터 설정
hp = HyperParameters(
hidden_dim=512,
num_layers=4,
learning_rate=3e-4,
batch_size=64,
dropout=0.2
)
print(f"하이퍼파라미터: {hp}")
# 모델 생성
model = SimpleMLP(input_dim=784, output_dim=10, hp=hp)
print(f"모델 파라미터 수: {sum(p.numel() for p in model.parameters()):,}")
| 하이퍼파라미터 | 일반적 범위 | 높으면 | 낮으면 |
|---|---|---|---|
| Learning Rate | 1e-5 ~ 1e-2 | 빠른 학습, 발산 위험 | 느린 수렴, 안정적 |
| Batch Size | 16 ~ 512 | 빠른 학습, 일반화 저하 | 노이즈, 일반화 향상 |
| Dropout | 0.1 ~ 0.5 | 과적합 방지, 언더피팅 | 과적합 위험 |
| Weight Decay | 1e-5 ~ 1e-2 | 작은 가중치, 과적합 방지 | 과적합 위험 |
| Hidden Dim | 64 ~ 4096 | 높은 표현력, 과적합 | 언더피팅 |
여러 하이퍼파라미터를 동시에 바꾸면 어떤 변경이 성능에 영향을 줬는지 알 수 없습니다. 한 번에 하나씩 변경하고 효과를 측정하세요.
테스트 세트 성능으로 하이퍼파라미터를 선택하면 테스트 세트에 과적합됩니다. 반드시 검증 세트(validation set)를 별도로 사용하세요.
모든 하이퍼파라미터와 결과를 기록하세요. Weights & Biases, MLflow 같은 도구로 실험을 추적하고, random seed를 고정해 재현 가능하게 만드세요.