[AI 101] 모델 학습과 최적화 – AI가 스스로 똑똑해지는 4가지 비밀
핵심 요약
“AI는 어떻게 실수에서 배울까?” 답은 손실함수, 경사하강법, 학습률, 역전파라는 4가지 핵심 메커니즘에 있습니다. 손실함수(Loss Function)는 AI의 “성적표”로 예측 오차를 측정하고, 경사하강법(Gradient Descent)은 “산을 내려가듯” 최적값을 찾습니다. 학습률(Learning Rate)은 “걸음 크기”를 결정하고, 역전파(Backpropagation)는 “오류를 거꾸로 추적”하여 수천 개의 가중치를 동시에 업데이트합니다. 이 4가지가 없다면 딥러닝은 존재할 수 없습니다. 수식부터 Python 구현, 실전 최적화까지 완벽하게 설명합니다.
📍 목차
- 손실함수(Loss Function)와 비용함수(Cost Function)
- 경사하강법(Gradient Descent) 원리
- 학습률(Learning Rate)과 하이퍼파라미터
- 역전파(Backpropagation) 알고리즘
- 실전 최적화 전략
1. 손실함수(Loss Function)와 비용함수(Cost Function)
1-1. 손실함수란?
손실함수(Loss Function)는 AI의 예측이 얼마나 틀렸는지 측정하는 함수입니다.
비유: 시험 채점
| 학생 | 정답 | AI 예측 | 오차 |
|---|---|---|---|
| 1번 | 90점 | 85점 | -5점 |
| 2번 | 70점 | 75점 | +5점 |
| 3번 | 80점 | 80점 | 0점 |
손실함수 = 각 학생의 오차를 수치화
1-2. 손실함수 vs 비용함수
| 항목 | 손실함수 (Loss Function) | 비용함수 (Cost Function) |
|---|---|---|
| 정의 | 1개 데이터의 오차 | 전체 데이터의 평균 오차 |
| 수식 | (L(y, \hat{y})) | (J = \frac{1}{n}\sum_{i=1}^{n}L(y_i, \hat{y}_i)) |
| 예시 | 학생 1명의 점수 차이 | 반 전체의 평균 점수 차이 |
| 사용 | 개별 예측 평가 | 모델 전체 성능 평가 |
핵심:
- Loss: 개별 (Individual)
- Cost: 전체 (Total/Average)
하지만 실무에서는 두 용어를 거의 혼용합니다.
1-3. 주요 손실함수 종류
1️⃣ 회귀 문제: MSE (Mean Squared Error)
평균 제곱 오차
수식:
특징:
- 오차를 제곱 → 큰 오차에 큰 페널티
- 미분 가능 → 경사하강법 사용 가능
Python 구현:
import numpy as np
def mse_loss(y_true, y_pred):
"""평균 제곱 오차"""
return np.mean((y_true - y_pred) ** 2)
# 예시
y_true = np.array([100, 200, 150])
y_pred = np.array([110, 190, 160])
loss = mse_loss(y_true, y_pred)
print(f"MSE Loss: {loss:.2f}") # 116.67장점: 미분 쉬움, 수학적으로 우아함
단점: 이상치에 매우 민감 (오차² 때문)
2️⃣ 회귀 문제: MAE (Mean Absolute Error)
평균 절대 오차
수식:
특징:
- 오차의 절댓값 → 이상치에 덜 민감
def mae_loss(y_true, y_pred):
"""평균 절대 오차"""
return np.mean(np.abs(y_true - y_pred))
loss = mae_loss(y_true, y_pred)
print(f"MAE Loss: {loss:.2f}") # 10.00비교:
- MSE: 큰 오차를 크게 처벌 → 이상치 민감
- MAE: 모든 오차를 동등하게 처리 → 이상치 강건
3️⃣ 분류 문제: Binary Cross-Entropy
이진 분류 손실함수
수식:
직관적 이해:
| 실제 (y) | 예측 (ŷ) | Loss |
|---|---|---|
| 1 (스팸) | 0.9 | 0.11 (작음) |
| 1 (스팸) | 0.1 | 2.30 (큼!) |
| 0 (정상) | 0.1 | 0.11 (작음) |
| 0 (정상) | 0.9 | 2.30 (큼!) |
Python 구현:
def binary_cross_entropy(y_true, y_pred, epsilon=1e-15):
"""이진 교차 엔트로피"""
# 수치 안정성을 위해 epsilon 추가
y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
return -np.mean(y_true * np.log(y_pred) +
(1 - y_true) * np.log(1 - y_pred))
y_true = np.array([1, 0, 1, 0])
y_pred = np.array([0.9, 0.1, 0.8, 0.2])
loss = binary_cross_entropy(y_true, y_pred)
print(f"BCE Loss: {loss:.4f}") # 0.16444️⃣ 다중 분류: Categorical Cross-Entropy
3개 이상 클래스 분류
수식:
- : 클래스 개수
- : 클래스 의 실제 레이블 (One-Hot)
예시: 3개 클래스 (고양이, 개, 새)
from scipy.special import softmax
# 실제: 고양이 (One-Hot: [1, 0, 0])
y_true = np.array([1, 0, 0])
# 모델 출력 (Logits)
logits = np.array([2.0, 1.0, 0.5])
# Softmax로 확률 변환
y_pred = softmax(logits)
print(f"예측 확률: {y_pred}") # [0.66, 0.24, 0.10]
# CCE 계산
loss = -np.sum(y_true * np.log(y_pred))
print(f"CCE Loss: {loss:.4f}") # 0.41701-4. 손실함수 선택 가이드
| 문제 유형 | 출력 형태 | 추천 손실함수 |
|---|---|---|
| 회귀 | 연속값 | MSE (일반), MAE (이상치 많음), Huber (절충) |
| 이진 분류 | 0 or 1 | Binary Cross-Entropy |
| 다중 분류 | One-Hot | Categorical Cross-Entropy |
| 다중 레이블 | [1, 0, 1] | Binary Cross-Entropy (각 레이블) |
2. 경사하강법(Gradient Descent) 원리
2-1. 경사하강법이란?
경사하강법(Gradient Descent)은 손실함수를 최소화하는 파라미터를 찾는 최적화 알고리즘입니다.
비유: 안개 낀 산에서 가장 낮은 곳 찾기
1. 현재 위치에서 경사(기울기) 확인
2. 가장 가파른 아래 방향으로 한 걸음
3. 새 위치에서 다시 경사 확인
4. 반복 → 골짜기(최저점) 도달2-2. 경사하강법 수식
목표: 손실함수 를 최소화하는 가중치 찾기
업데이트 규칙:
- : 가중치 (Weight)
- : 학습률 (Learning Rate)
- : 손실함수의 미분 (Gradient)
직관적 이해:
| 기울기 | 의미 | 업데이트 방향 |
|---|---|---|
| 양수 (+) | 오른쪽으로 갈수록 Loss ↑ | 왼쪽으로 이동 (w 감소) |
| 음수 (-) | 왼쪽으로 갈수록 Loss ↑ | 오른쪽으로 이동 (w 증가) |
| 0 | 최저점 도달 | 정지 |
2-3. Python으로 구현하기
예시: 단순 선형회귀 (y = wx + b)
import numpy as np
import matplotlib.pyplot as plt
# 데이터 생성
np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
# 파라미터 초기화
w = 0.0
b = 0.0
learning_rate = 0.01
epochs = 1000
# 경사하강법
losses = []
for epoch in range(epochs):
# 1. 예측
y_pred = w * X + b
# 2. 손실 계산
loss = np.mean((y - y_pred) ** 2)
losses.append(loss)
# 3. 기울기 계산
dw = -2 * np.mean(X * (y - y_pred))
db = -2 * np.mean(y - y_pred)
# 4. 파라미터 업데이트
w = w - learning_rate * dw
b = b - learning_rate * db
if epoch % 100 == 0:
print(f"Epoch {epoch:4d} | Loss: {loss:.4f} | w: {w:.4f}, b: {b:.4f}")
print(f"\n최종 파라미터: w={w:.4f}, b={b:.4f}")
print(f"실제 값: w=3.0, b=4.0")출력:
Epoch 0 | Loss: 28.5472 | w: 0.5267, b: 0.1347
Epoch 100 | Loss: 0.9821 | w: 2.8456, b: 3.7234
Epoch 200 | Loss: 0.9456 | w: 2.9234, b: 3.8901
...
Epoch 1000 | Loss: 0.9112 | w: 2.9989, b: 4.0012
최종 파라미터: w=2.9989, b=4.0012
실제 값: w=3.0, b=4.02-4. 경사하강법의 3가지 변형
1️⃣ 배치 경사하강법 (Batch GD)
전체 데이터로 한 번에 업데이트
# 전체 데이터 사용
for epoch in range(epochs):
y_pred = model(X) # 모든 데이터
loss = compute_loss(y, y_pred)
gradients = compute_gradients(X, y, y_pred)
update_parameters(gradients)장점: 안정적, 정확한 기울기
단점: 느림 (데이터 100만 개면 한 번에 처리)
2️⃣ 확률적 경사하강법 (Stochastic GD)
데이터 1개씩 업데이트
# 1개씩 처리
for epoch in range(epochs):
for i in range(len(X)):
xi, yi = X[i], y[i]
y_pred = model(xi)
loss = compute_loss(yi, y_pred)
gradients = compute_gradients(xi, yi, y_pred)
update_parameters(gradients)장점: 빠름, 메모리 효율적
단점: 불안정 (노이즈 많음)
3️⃣ 미니배치 경사하강법 (Mini-batch GD)
일부 데이터(예: 32개)씩 업데이트
batch_size = 32
for epoch in range(epochs):
for i in range(0, len(X), batch_size):
X_batch = X[i:i+batch_size]
y_batch = y[i:i+batch_size]
y_pred = model(X_batch)
loss = compute_loss(y_batch, y_pred)
gradients = compute_gradients(X_batch, y_batch, y_pred)
update_parameters(gradients)장점: 속도 + 안정성 균형
단점: 배치 크기 조정 필요
실무 표준: Mini-batch GD (배치 크기 32~256)
3. 학습률(Learning Rate)과 하이퍼파라미터
3-1. 학습률이란?
학습률(Learning Rate, (\alpha))은 한 번에 파라미터를 얼마나 크게 바꿀지 결정합니다.
비유: 산 내려가기 걸음 크기
| 학습률 | 걸음 크기 | 결과 |
|---|---|---|
| 너무 큼 (0.9) | 10m씩 | 골짜기 지나쳐서 다른 산으로 |
| 적당 (0.01) | 1m씩 | 안정적으로 도달 |
| 너무 작음 (0.0001) | 1cm씩 | 평생 걸려도 도달 못함 |
3-2. 학습률의 영향
실험: 같은 데이터, 다른 학습률
learning_rates = [0.001, 0.01, 0.1, 1.0]
for lr in learning_rates:
w = 0.0
losses = []
for epoch in range(100):
y_pred = w * X
loss = np.mean((y - y_pred) ** 2)
losses.append(loss)
dw = -2 * np.mean(X * (y - y_pred))
w = w - lr * dw
plt.plot(losses, label=f'LR={lr}')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.title('학습률에 따른 손실 변화')
plt.show()결과:
LR=0.001: 천천히 감소 (100 epoch 후에도 높음)
LR=0.01: 적당히 빠르게 수렴 ✅
LR=0.1: 빠르게 수렴하지만 불안정
LR=1.0: 발산! (Loss가 무한대로) ❌3-3. 최적 학습률 찾기
방법 1: Learning Rate Range Test
1e-6 ~ 1까지 점진적으로 증가시키며 Loss 관찰
lrs = np.logspace(-6, 0, 100) # 10^-6 ~ 10^0
losses = []
for lr in lrs:
# 한 epoch 학습
loss = train_one_epoch(lr)
losses.append(loss)
plt.plot(lrs, losses)
plt.xscale('log')
plt.xlabel('Learning Rate')
plt.ylabel('Loss')
plt.title('LR Range Test')
plt.show()해석:
- Loss가 급격히 감소하는 구간 → 최적 LR
- Loss가 증가하기 시작 → 너무 큼
방법 2: Learning Rate Scheduler
학습 진행에 따라 LR 조정
Step Decay:
def step_decay(epoch, initial_lr=0.1, drop=0.5, epochs_drop=10):
"""10 epoch마다 LR을 절반으로"""
return initial_lr * (drop ** (epoch // epochs_drop))
# 예시
# Epoch 0-9: LR = 0.1
# Epoch 10-19: LR = 0.05
# Epoch 20-29: LR = 0.025Exponential Decay:
def exp_decay(epoch, initial_lr=0.1, decay_rate=0.95):
"""지수적으로 감소"""
return initial_lr * (decay_rate ** epoch)PyTorch 구현:
import torch.optim as optim
optimizer = optim.SGD(model.parameters(), lr=0.1)
# Step Decay Scheduler
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)
for epoch in range(100):
train()
scheduler.step() # LR 업데이트3-4. 하이퍼파라미터
하이퍼파라미터: 학습 전 사람이 설정하는 값
| 하이퍼파라미터 | 설명 | 일반적 범위 |
|---|---|---|
| Learning Rate | 업데이트 속도 | 0.001 ~ 0.1 |
| Batch Size | 미니배치 크기 | 32 ~ 256 |
| Epochs | 전체 데이터 반복 횟수 | 10 ~ 1000 |
| Optimizer | 최적화 알고리즘 | SGD, Adam, RMSprop |
| Weight Decay | 가중치 정규화 | 1e-5 ~ 1e-3 |
하이퍼파라미터 튜닝 방법:
- Grid Search: 모든 조합 시도
- Random Search: 랜덤하게 샘플링
- Bayesian Optimization: 효율적 탐색
4. 역전파(Backpropagation) 알고리즘
4-1. 역전파란?
역전파(Backpropagation)는 출력층의 오차를 거꾸로 전파하여 모든 가중치의 기울기를 계산하는 알고리즘입니다.
핵심 질문:
“신경망에 가중치가 1,000개인데 어떻게 각각의 기울기를 계산할까?”
답: 연쇄 법칙(Chain Rule)
4-2. 순전파 vs 역전파
순전파 (Forward Pass):
입력 → 은닉층 1 → 은닉층 2 → 출력 → Loss역전파 (Backward Pass):
Loss → 출력층 기울기 → 은닉층 2 기울기 → 은닉층 1 기울기4-3. 간단한 신경망 예제
구조: 입력(1) → 은닉(2) → 출력(1)
Input (x)
↓
Hidden Layer (h)
↓
Output (y)순전파:
import numpy as np
# 입력
x = 2.0
# 가중치
w1 = 0.5 # 입력 → 은닉
w2 = 0.3 # 은닉 → 출력
# 순전파
h = x * w1 # 은닉층 (활성화 함수 없음)
y_pred = h * w2 # 출력
# 실제 값
y_true = 1.0
# 손실 (MSE)
loss = (y_true - y_pred) ** 2
print(f"순전파:")
print(f" h = {h:.2f}")
print(f" y_pred = {y_pred:.2f}")
print(f" loss = {loss:.4f}")출력:
순전파:
h = 1.00
y_pred = 0.30
loss = 0.4900역전파: 연쇄 법칙
목표: 계산
수식:
Python 구현:
# 역전파
# 1. ∂L/∂y_pred
dL_dy = -2 * (y_true - y_pred) # MSE 미분
# 2. ∂L/∂w2
dy_dw2 = h
dL_dw2 = dL_dy * dy_dw2
# 3. ∂L/∂h
dy_dh = w2
dL_dh = dL_dy * dy_dh
# 4. ∂L/∂w1
dh_dw1 = x
dL_dw1 = dL_dh * dh_dw1
print(f"\n역전파:")
print(f" ∂L/∂w2 = {dL_dw2:.4f}")
print(f" ∂L/∂w1 = {dL_dw1:.4f}")
# 파라미터 업데이트
learning_rate = 0.1
w2_new = w2 - learning_rate * dL_dw2
w1_new = w1 - learning_rate * dL_dw1
print(f"\n업데이트:")
print(f" w2: {w2:.2f} → {w2_new:.4f}")
print(f" w1: {w1:.2f} → {w1_new:.4f}")출력:
역전파:
∂L/∂w2 = -1.4000
∂L/∂w1 = -0.8400
업데이트:
w2: 0.30 → 0.4400
w1: 0.50 → 0.58404-4. 활성화 함수와 역전파
ReLU 활성화 함수:
순전파:
역전파:
def relu(z):
return np.maximum(0, z)
def relu_derivative(z):
return (z > 0).astype(float)
# 예시
z = np.array([-1, 0, 1, 2])
print(f"ReLU({z}) = {relu(z)}") # [0 0 1 2]
print(f"ReLU'({z}) = {relu_derivative(z)}") # [0 0 1 1]4-5. PyTorch 자동 미분
PyTorch는 역전파를 자동으로 계산합니다.
import torch
# 가중치 (requires_grad=True로 미분 추적)
w = torch.tensor([0.5], requires_grad=True)
# 순전파
x = torch.tensor([2.0])
y_true = torch.tensor([1.0])
y_pred = w * x
loss = (y_true - y_pred) ** 2
print(f"Loss: {loss.item():.4f}")
# 역전파 (자동!)
loss.backward()
# 기울기 확인
print(f"∂L/∂w: {w.grad.item():.4f}")
# 파라미터 업데이트
learning_rate = 0.1
with torch.no_grad(): # 미분 추적 끄기
w -= learning_rate * w.grad
print(f"Updated w: {w.item():.4f}")출력:
Loss: 0.0000
∂L/∂w: -4.0000
Updated w: 0.90005. 실전 최적화 전략
5-1. 고급 최적화 알고리즘
1️⃣ Momentum
문제: 일반 SGD는 진동(oscillation)이 심함
해결: 이전 기울기의 관성 추가
수식:
- : 관성 계수 (보통 0.9)
효과: 골짜기를 따라 빠르게 수렴
2️⃣ RMSprop
문제: 학습률이 모든 파라미터에 동일
해결: 각 파라미터마다 적응적 학습률
수식:
효과: 큰 기울기 → 작은 LR, 작은 기울기 → 큰 LR
3️⃣ Adam (Adaptive Moment Estimation)
최고의 조합: Momentum + RMSprop
수식:
기본값:
- = 0.001
- = 0.9
- = 0.999
PyTorch 구현:
import torch.optim as optim
optimizer = optim.Adam(model.parameters(), lr=0.001)
for epoch in range(epochs):
optimizer.zero_grad() # 기울기 초기화
output = model(X)
loss = criterion(output, y)
loss.backward() # 역전파
optimizer.step() # 파라미터 업데이트5-2. 최적화 알고리즘 비교
| 알고리즘 | 속도 | 안정성 | 하이퍼파라미터 | 추천 용도 |
|---|---|---|---|---|
| SGD | ⭐⭐ | ⭐⭐⭐ | LR만 | 간단한 문제 |
| SGD + Momentum | ⭐⭐⭐ | ⭐⭐⭐⭐ | LR, β | 컴퓨터 비전 |
| RMSprop | ⭐⭐⭐⭐ | ⭐⭐⭐ | LR, β | RNN |
| Adam | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | LR, β1, β2 | 대부분의 경우 |
실무 가이드:
- 처음 시도: Adam (lr=0.001)
- 성능 안 나오면: SGD + Momentum (lr=0.01)
5-3. 과적합 방지
1️⃣ Early Stopping
Validation Loss가 증가하면 학습 중단
best_loss = float('inf')
patience = 5
counter = 0
for epoch in range(epochs):
train_loss = train()
val_loss = validate()
if val_loss = patience:
print(f"Early stopping at epoch {epoch}")
break2️⃣ L2 정규화 (Weight Decay)
가중치가 너무 커지지 않도록 제한
수식:
optimizer = optim.Adam(model.parameters(),
lr=0.001,
weight_decay=1e-5) # L2 정규화3️⃣ Dropout
학습 중 일부 뉴런을 랜덤하게 끄기
import torch.nn as nn
class Model(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(100, 50)
self.dropout = nn.Dropout(p=0.5) # 50% 끄기
self.fc2 = nn.Linear(50, 10)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.dropout(x) # 학습 시에만 작동
x = self.fc2(x)
return xFAQ: 초보자가 자주 묻는 질문
Q1. Loss가 NaN(Not a Number)이 나옵니다!
A. 원인: (1) 학습률이 너무 큼 → 기울기 폭발, (2) 수치 불안정 → log(0) 계산. 해결: (1) 학습률 10배 낮추기 (0.1 → 0.01), (2) Gradient Clipping 적용.
Q2. 경사하강법이 너무 느립니다.
A. 해결책: (1) 배치 크기 증가 (32 → 128), (2) Adam 사용 (SGD 대신), (3) GPU 사용, (4) 학습률 증가 (신중하게).
Q3. Validation Loss는 줄어드는데 Train Loss는 늘어납니다?
A. 이는 정상입니다. 정규화(Dropout, Weight Decay)가 Train 시에만 적용되어 Train Loss가 Validation보다 높을 수 있습니다.
Q4. 손실함수와 비용함수를 꼭 구분해야 하나요?
A. 실무에서는 거의 구분 안 합니다. 둘 다 “Loss”로 통칭합니다. 학술적으로만 구분하며, 코딩할 때는 신경 쓰지 않아도 됩니다.
Q5. 학습률을 어떻게 설정하나요?
A. 기본값: Adam이면 0.001, SGD면 0.01로 시작. 실험: Learning Rate Range Test 실행. 조정: Validation Loss 보고 너무 느리면 증가, 불안정하면 감소.
외부 참고 자료
모델 학습과 최적화를 더 깊게 배우고 싶다면:
- IBM – 경사 하강법이란? – 개념 설명
- IBM – 학습률이란? – Learning Rate 가이드
- PyTorch – Optimizers – 최적화 알고리즘
- TensorFlow – Training – 학습 방법
- Gradient Descent Visualization – 시각화
정리: 이 글에서 배운 것
✅ 손실함수: AI의 성적표, MSE(회귀), Cross-Entropy(분류)
✅ 비용함수: 전체 데이터의 평균 손실
✅ 경사하강법: 기울기 반대 방향으로 이동, (w = w – \alpha \nabla J)
✅ 학습률: 걸음 크기, 너무 크면 발산, 너무 작으면 느림
✅ 역전파: 연쇄 법칙으로 모든 가중치 기울기 계산
✅ Adam: 실무 표준 최적화 알고리즘
다음 편에서는 “신경망의 구조 – 퍼셉트론에서 딥러닝까지“에 대해 자세히 알아봅니다. 특히 활성화 함수, 은닉층, CNN, RNN을 완벽 설명하겠습니다.
