🤖AI/ML

경사하강법

Gradient Descent

손실 함수를 최소화하기 위해 기울기(Gradient) 반대 방향으로 파라미터를 반복적으로 업데이트하는 최적화 알고리즘입니다. SGD, Momentum, Adam 등 다양한 변형이 있으며, 딥러닝의 모든 학습 과정의 핵심입니다.

📖 상세 설명

경사하강법(Gradient Descent)은 손실 함수(Loss Function)의 값을 최소화하기 위해 파라미터를 반복적으로 조정하는 최적화 알고리즘입니다. 함수의 기울기(Gradient)를 계산하여 기울기가 가장 가파르게 감소하는 방향으로 이동하며, 머신러닝과 딥러닝의 모든 학습 과정의 핵심입니다.

경사하강법은 19세기 수학자 Augustin-Louis Cauchy가 처음 제안했습니다. 딥러닝에서는 역전파(Backpropagation)와 결합되어 신경망의 가중치를 학습하는 데 사용됩니다. 1986년 Rumelhart의 역전파 논문 이후 주류가 되었고, 2014년 Adam 옵티마이저의 등장으로 현대 딥러닝의 표준이 되었습니다.

θ = θ - η × ∇L(θ)

θ: 파라미터, η(eta): 학습률(Learning Rate), ∇L: 손실 함수의 기울기

Batch GD는 전체 데이터로 기울기를 계산하여 안정적이지만 느립니다. SGD(Stochastic)는 하나의 샘플로 계산해 빠르지만 노이즈가 큽니다. Mini-batch GD는 둘의 절충안으로 가장 널리 사용됩니다. Momentum은 이전 기울기를 활용해 빠르게 수렴하고, Adam은 적응적 학습률로 가장 범용적인 옵티마이저입니다.

실무에서 학습률 설정은 가장 중요한 하이퍼파라미터입니다. 너무 크면 발산하고, 너무 작으면 학습이 느립니다. GPT, BERT 같은 LLM은 AdamW + Warmup + Cosine Decay 조합을 표준으로 사용합니다. 학습률 스케줄러와 Gradient Clipping은 안정적인 학습을 위한 필수 기법입니다.

📊 옵티마이저 비교

옵티마이저 특징 권장 학습률 사용 사례
SGD 단순, 메모리 효율적 0.01 ~ 0.1 CNN, 이미지 분류
SGD + Momentum 진동 감소, 빠른 수렴 0.01 (momentum 0.9) ResNet, VGG
Adam 적응적 학습률, 범용 1e-3 ~ 1e-4 대부분의 딥러닝
AdamW Weight Decay 분리 1e-4 ~ 5e-5 BERT, GPT, LLM
LAMB/LARS 대규모 배치 학습 레이어별 조정 분산 학습, TPU

💻 코드 예제

PyTorch와 NumPy를 사용한 경사하강법 구현 및 비교 예제입니다.

# pip install torch numpy matplotlib

import torch
import torch.nn as nn

# 간단한 신경망
class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(10, 64)
        self.fc2 = nn.Linear(64, 1)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        return self.fc2(x)

# 데이터 준비
X = torch.randn(1000, 10)
y = torch.randn(1000, 1)

# 옵티마이저 비교
optimizers = {
    'SGD': torch.optim.SGD,
    'SGD+Momentum': lambda p, lr: torch.optim.SGD(p, lr=lr, momentum=0.9),
    'Adam': torch.optim.Adam,
    'AdamW': torch.optim.AdamW,
}

for name, opt_class in optimizers.items():
    model = SimpleNet()
    if callable(opt_class) and name == 'SGD+Momentum':
        optimizer = opt_class(model.parameters(), 0.01)
    else:
        optimizer = opt_class(model.parameters(), lr=0.01)

    criterion = nn.MSELoss()

    # 학습 루프
    for epoch in range(100):
        optimizer.zero_grad()        # 1. 기울기 초기화
        output = model(X)            # 2. 순전파
        loss = criterion(output, y)  # 3. 손실 계산
        loss.backward()              # 4. 역전파 (기울기 계산)
        optimizer.step()             # 5. 파라미터 업데이트

    print(f"{name:15} | Final Loss: {loss.item():.6f}")

# 출력 예시:
# SGD             | Final Loss: 0.987234
# SGD+Momentum    | Final Loss: 0.956123
# Adam            | Final Loss: 0.012456
# AdamW           | Final Loss: 0.011234
# NumPy로 경사하강법 처음부터 구현
import numpy as np

def gradient_descent_from_scratch():
    """선형 회귀를 위한 경사하강법 구현"""

    # 1. 데이터 생성 (y = 2x + 1 + noise)
    np.random.seed(42)
    X = np.random.randn(100, 1)
    y = 2 * X + 1 + np.random.randn(100, 1) * 0.3

    # 2. 파라미터 초기화
    w = np.random.randn(1, 1)  # 가중치
    b = np.zeros((1, 1))        # 편향
    lr = 0.1                    # 학습률
    epochs = 100

    # 3. 경사하강법
    for epoch in range(epochs):
        # 순전파
        y_pred = X @ w + b

        # 손실 계산 (MSE)
        loss = np.mean((y_pred - y) ** 2)

        # 기울기 계산
        dw = (2 / len(X)) * (X.T @ (y_pred - y))
        db = (2 / len(X)) * np.sum(y_pred - y)

        # 파라미터 업데이트 (핵심!)
        w = w - lr * dw  # θ = θ - η × ∇L
        b = b - lr * db

        if epoch % 20 == 0:
            print(f"Epoch {epoch:3d} | Loss: {loss:.4f} | w: {w[0,0]:.3f} | b: {b[0,0]:.3f}")

    print(f"\n최종 결과: y = {w[0,0]:.3f}x + {b[0,0]:.3f}")
    print(f"실제 함수: y = 2.000x + 1.000")

gradient_descent_from_scratch()

# 출력:
# Epoch   0 | Loss: 4.2134 | w: 1.234 | b: 0.156
# Epoch  20 | Loss: 0.1234 | w: 1.912 | b: 0.978
# ...
# 최종 결과: y = 1.998x + 1.003
# 학습률 스케줄러 - LLM 학습의 필수 요소
import torch
import torch.nn as nn
from torch.optim.lr_scheduler import (
    CosineAnnealingLR,
    OneCycleLR,
    LinearLR,
    SequentialLR
)

model = nn.Linear(10, 1)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3)

# 1. Cosine Annealing (가장 널리 사용)
scheduler = CosineAnnealingLR(optimizer, T_max=100, eta_min=1e-6)

# 2. Warmup + Cosine Decay (BERT, GPT 표준)
warmup_epochs = 10
total_epochs = 100

warmup = LinearLR(optimizer, start_factor=0.1, total_iters=warmup_epochs)
decay = CosineAnnealingLR(optimizer, T_max=total_epochs - warmup_epochs)
scheduler = SequentialLR(optimizer, [warmup, decay], milestones=[warmup_epochs])

# 3. OneCycleLR (빠른 학습에 효과적)
# scheduler = OneCycleLR(optimizer, max_lr=1e-3, total_steps=1000)

# 학습 루프
for epoch in range(total_epochs):
    # ... 학습 코드 ...

    scheduler.step()  # 에포크마다 학습률 조정

    if epoch % 10 == 0:
        current_lr = scheduler.get_last_lr()[0]
        print(f"Epoch {epoch:3d} | LR: {current_lr:.2e}")

# 출력:
# Epoch   0 | LR: 1.00e-04  (warmup)
# Epoch  10 | LR: 1.00e-03  (peak)
# Epoch  50 | LR: 5.00e-04  (decay)
# Epoch  99 | LR: 1.00e-06  (minimum)

🗣️ 실무 대화 예시

💬 학습 문제 해결 회의에서

"학습이 수렴하지 않으면 learning rate를 1e-4로 낮춰보세요. Adam 기본값 1e-3이 우리 모델에는 너무 클 수 있어요. Warmup 10% 추가하면 초기 불안정성도 해결됩니다."

💬 면접에서

"Adam은 1차 모멘트(평균)와 2차 모멘트(분산)를 이용해 파라미터별 적응적 학습률을 계산합니다. bias correction으로 초기 불안정성도 해결하죠. AdamW는 weight decay를 옵티마이저 외부에서 처리해서 regularization이 더 정확합니다."

💬 LLM 학습 논의에서

"GPT-4 급 모델은 AdamW + Linear Warmup + Cosine Decay 조합을 씁니다. Gradient Clipping은 1.0으로 설정하고, 배치 사이즈가 크면 LAMB이나 8-bit Adam도 고려해보세요. 메모리 절약됩니다."

⚠️ 주의사항

학습률 고정 사용

전체 학습 과정에서 동일한 학습률을 사용하면 후반에 수렴이 어렵습니다. 반드시 Scheduler를 사용해 점진적으로 감소시켜야 합니다.

Local Minima 과도한 우려

고차원에서는 local minima보다 saddle point가 문제입니다. Adam이나 Momentum이 이를 잘 탈출합니다. 실제로는 loss landscape이 생각보다 평탄합니다.

올바른 접근

AdamW + Warmup + Cosine Annealing이 대부분의 경우 좋습니다. Gradient Clipping(max_norm=1.0)으로 exploding gradient를 방지하고, Loss가 NaN이면 학습률을 10배 낮춰보세요.

🔥 실제 장애 사례

OpenAI GPT-3 2020

학습 불안정성으로 수천 달러 GPU 비용 낭비

원인: 175B 파라미터 모델에서 학습률 설정 실패, Loss spike 발생

영향: 학습 재시작으로 수천 GPU 시간 손실

해결: Gradient Clipping 강화, Warmup 기간 연장, 학습률 3e-4로 보수적 설정

📎 GPT-3 논문
DeepMind AlphaFold 2021

기울기 폭발로 학습 실패

원인: Attention 레이어에서 기울기 폭발(Exploding Gradient)

영향: NaN loss로 학습 중단, 재시작 필요

해결: Pre-LayerNorm 구조 채택, Gradient Clipping, 학습률 warmup 적용

📎 AlphaFold 논문

📝 이해도 퀴즈

Q1. 경사하강법에서 학습률(Learning Rate)이 너무 크면 발생하는 문제는?

Q2. Adam 옵티마이저가 SGD보다 널리 사용되는 이유는?

Q3. LLM 학습에서 AdamW가 Adam보다 선호되는 이유는?

🔗 관련 용어

📚 더 배우기