Deep Learning (딥러닝)¶
개요¶
딥러닝은 다층 신경망을 통해 데이터의 계층적 표현을 자동으로 학습하는 기계학습 분야이다. 수동 특성 엔지니어링 없이 원시 데이터로부터 복잡한 패턴을 추출한다.
신경망 기초¶
퍼셉트론 (Perceptron)¶
단일 뉴런의 기본 구조:
\[y = f\left(\sum_{i=1}^{n} w_i x_i + b\right) = f(\mathbf{w}^T \mathbf{x} + b)\]
| 구성 요소 | 설명 |
|---|---|
| \(x_i\) | 입력 특성 |
| \(w_i\) | 가중치 |
| \(b\) | 편향 (bias) |
| \(f\) | 활성화 함수 |
| \(y\) | 출력 |
다층 퍼셉트론 (MLP)¶
Input Layer Hidden Layer(s) Output Layer
x_1 ───┐
├─── h_1 ───┐
x_2 ───┤ ├─── y_1
├─── h_2 ───┤
x_3 ───┤ ├─── y_2
├─── h_3 ───┘
x_4 ───┘
층별 순전파 (Forward Propagation):
\[\mathbf{h}^{(l)} = f^{(l)}\left(\mathbf{W}^{(l)} \mathbf{h}^{(l-1)} + \mathbf{b}^{(l)}\right)\]
활성화 함수¶
주요 활성화 함수¶
| 함수 | 수식 | 범위 | 특징 |
|---|---|---|---|
| Sigmoid | \(\sigma(x) = \frac{1}{1+e^{-x}}\) | (0, 1) | 기울기 소실 문제 |
| Tanh | \(\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}\) | (-1, 1) | Zero-centered |
| ReLU | \(\text{ReLU}(x) = \max(0, x)\) | [0, \(\infty\)) | 현재 표준 |
| Leaky ReLU | \(f(x) = \max(\alpha x, x)\) | (\(-\infty\), \(\infty\)) | Dead ReLU 해결 |
| GELU | \(x \cdot \Phi(x)\) | (\(-\infty\), \(\infty\)) | Transformer에서 사용 |
| Swish | \(x \cdot \sigma(x)\) | (\(-\infty\), \(\infty\)) | 자가 게이팅 |
ReLU와 변형¶
import torch
import torch.nn as nn
# 활성화 함수 비교
x = torch.linspace(-3, 3, 100)
relu = nn.ReLU()(x)
leaky_relu = nn.LeakyReLU(0.01)(x)
elu = nn.ELU()(x)
gelu = nn.GELU()(x)
출력층 활성화 함수¶
| 문제 유형 | 활성화 함수 | 손실 함수 |
|---|---|---|
| 이진 분류 | Sigmoid | Binary Cross-Entropy |
| 다중 분류 | Softmax | Categorical Cross-Entropy |
| 회귀 | None (Linear) | MSE, MAE |
Softmax:
\[\text{softmax}(z_i) = \frac{e^{z_i}}{\sum_{j=1}^{K} e^{z_j}}\]
역전파 (Backpropagation)¶
Chain Rule¶
손실 함수 \(\mathcal{L}\)에 대한 가중치 기울기:
\[\frac{\partial \mathcal{L}}{\partial w_{ij}^{(l)}} = \frac{\partial \mathcal{L}}{\partial a_j^{(l)}} \cdot \frac{\partial a_j^{(l)}}{\partial z_j^{(l)}} \cdot \frac{\partial z_j^{(l)}}{\partial w_{ij}^{(l)}}\]
여기서: - \(z_j^{(l)} = \sum_i w_{ij}^{(l)} h_i^{(l-1)} + b_j^{(l)}\) (선형 결합) - \(a_j^{(l)} = f(z_j^{(l)})\) (활성화 후 출력)
역전파 알고리즘¶
Algorithm: Backpropagation
1. Forward Pass:
for l = 1 to L:
z^(l) = W^(l) @ h^(l-1) + b^(l)
h^(l) = f(z^(l))
2. Compute output error:
delta^(L) = dL/dh^(L) * f'(z^(L))
3. Backward Pass:
for l = L-1 to 1:
delta^(l) = (W^(l+1).T @ delta^(l+1)) * f'(z^(l))
dL/dW^(l) = delta^(l) @ h^(l-1).T
dL/db^(l) = delta^(l)
4. Update:
W^(l) = W^(l) - lr * dL/dW^(l)
b^(l) = b^(l) - lr * dL/db^(l)
PyTorch 자동 미분¶
import torch
import torch.nn as nn
# 간단한 신경망
model = nn.Sequential(
nn.Linear(784, 256),
nn.ReLU(),
nn.Linear(256, 128),
nn.ReLU(),
nn.Linear(128, 10)
)
# 손실 함수
criterion = nn.CrossEntropyLoss()
# 순전파
x = torch.randn(32, 784) # 배치
y = torch.randint(0, 10, (32,))
output = model(x)
loss = criterion(output, y)
# 역전파 (자동 미분)
loss.backward()
# 기울기 확인
for name, param in model.named_parameters():
if param.grad is not None:
print(f"{name}: grad shape = {param.grad.shape}")
최적화 알고리즘¶
Gradient Descent 변형¶
Stochastic Gradient Descent (SGD):
\[\theta_{t+1} = \theta_t - \eta \nabla_\theta \mathcal{L}(\theta_t; x^{(i)}, y^{(i)})\]
SGD with Momentum:
\[v_t = \gamma v_{t-1} + \eta \nabla_\theta \mathcal{L}(\theta_t)$$
$$\theta_{t+1} = \theta_t - v_t\]
Adam (Adaptive Moment Estimation)¶
1차 모멘트(평균)와 2차 모멘트(분산) 추정치 사용:
\[m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t$$
$$v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2\]
편향 보정:
\[\hat{m}_t = \frac{m_t}{1 - \beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1 - \beta_2^t}\]
파라미터 업데이트:
\[\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t\]
옵티마이저 비교¶
| 옵티마이저 | 하이퍼파라미터 | 특징 | 권장 사용 |
|---|---|---|---|
| SGD | lr, momentum | 일반화 좋음, 수렴 느림 | 컴퓨터 비전 |
| Adam | lr, beta1, beta2 | 빠른 수렴, 적응적 학습률 | NLP, 일반적 |
| AdamW | + weight_decay | Adam + 분리된 가중치 감쇠 | Transformer |
| RMSprop | lr, alpha | Adam의 전신 | RNN |
PyTorch 옵티마이저 사용¶
import torch.optim as optim
model = nn.Linear(10, 2)
# SGD with Momentum
optimizer_sgd = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=1e-4)
# Adam
optimizer_adam = optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999))
# AdamW (권장)
optimizer_adamw = optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.01)
# 학습 루프
for epoch in range(100):
optimizer.zero_grad() # 기울기 초기화
output = model(x)
loss = criterion(output, y)
loss.backward() # 역전파
optimizer.step() # 파라미터 업데이트
학습률 스케줄러¶
from torch.optim.lr_scheduler import StepLR, CosineAnnealingLR, OneCycleLR
# Step decay
scheduler = StepLR(optimizer, step_size=30, gamma=0.1)
# Cosine annealing
scheduler = CosineAnnealingLR(optimizer, T_max=100)
# One Cycle (Super Convergence)
scheduler = OneCycleLR(optimizer, max_lr=0.01, total_steps=1000)
# 학습 루프에서 사용
for epoch in range(epochs):
train()
scheduler.step()
정규화 기법¶
Dropout¶
학습 시 무작위로 뉴런 비활성화:
\[\tilde{h}^{(l)} = r^{(l)} \odot h^{(l)}, \quad r_j^{(l)} \sim \text{Bernoulli}(p)\]
model = nn.Sequential(
nn.Linear(784, 256),
nn.ReLU(),
nn.Dropout(p=0.5), # 50% 비활성화
nn.Linear(256, 10)
)
model.train() # Dropout 활성화
model.eval() # Dropout 비활성화 (추론 시)
Batch Normalization¶
미니배치 단위로 정규화:
\[\hat{x}_i = \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}}$$
$$y_i = \gamma \hat{x}_i + \beta\]
Layer Normalization¶
배치 차원 대신 특성 차원으로 정규화 (Transformer에서 사용):
전체 학습 파이프라인¶
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
# 모델 정의
class MLP(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim, dropout=0.5):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.BatchNorm1d(hidden_dim),
nn.ReLU(),
nn.Dropout(dropout),
nn.Linear(hidden_dim, hidden_dim),
nn.BatchNorm1d(hidden_dim),
nn.ReLU(),
nn.Dropout(dropout),
nn.Linear(hidden_dim, output_dim)
)
def forward(self, x):
return self.layers(x)
# 초기화
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = MLP(784, 256, 10).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)
# 학습 루프
def train_epoch(model, loader, criterion, optimizer, device):
model.train()
total_loss = 0
for x, y in loader:
x, y = x.to(device), y.to(device)
optimizer.zero_grad()
output = model(x)
loss = criterion(output, y)
loss.backward()
# Gradient clipping (선택)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
total_loss += loss.item()
return total_loss / len(loader)
# 검증 루프
@torch.no_grad()
def evaluate(model, loader, criterion, device):
model.eval()
total_loss = 0
correct = 0
total = 0
for x, y in loader:
x, y = x.to(device), y.to(device)
output = model(x)
loss = criterion(output, y)
total_loss += loss.item()
pred = output.argmax(dim=1)
correct += (pred == y).sum().item()
total += y.size(0)
return total_loss / len(loader), correct / total
# 학습 실행
for epoch in range(100):
train_loss = train_epoch(model, train_loader, criterion, optimizer, device)
val_loss, val_acc = evaluate(model, val_loader, criterion, device)
scheduler.step()
print(f"Epoch {epoch+1}: Train Loss={train_loss:.4f}, Val Loss={val_loss:.4f}, Val Acc={val_acc:.4f}")
관련 문서¶
| 주제 | 링크 |
|---|---|
| CNN | cnn.md |
| RNN/LSTM | rnn-lstm.md |
| Attention | attention.md |
| Transformer | ../../architecture/transformer.md |
| 앙상블 | ../ensemble/README.md |