챕터 3 - Loss Functions and Optimization
1. Loss Function이란?
Lecture 2에서 Linear Classifier \(f(x, W) = Wx + b\)를 배웠는데, 남은 문제가 "그래서 W를 어떻게 학습시키냐"였다. 이번 강의가 바로 그 답이다.
핵심 흐름은 이렇다. W가 좋은지 나쁜지를 수치로 표현하는 게 Loss Function이고, 그 Loss를 최소화하는 W를 찾아가는 과정이 Optimization이다.
전체 Loss는 두 항으로 구성된다:
- 앞의 \(\frac{1}{N} \sum L_i\) - Data Loss: 모델이 학습 데이터를 얼마나 잘 맞추는지
- 뒤의 \(\lambda R(W)\) - Regularization: 모델이 너무 복잡해지지 않도록 잡아주는 항
2. Multiclass SVM Loss (Hinge Loss)
Data Loss로 쓸 수 있는 첫 번째 함수가 Multiclass SVM Loss다. 아이디어는 단순한데, 정답 클래스의 score가 나머지 클래스보다 일정 margin(여기서는 1)만큼 높으면 loss = 0, 그렇지 않으면 그 차이만큼 패널티를 준다.
여기서 \(s_j\)는 j번째 클래스의 score, \(s_{y_i}\)는 정답 클래스의 score다.
계산 예시
강의에서 cat/car/frog 3클래스 예시를 보여준다. cat이 정답이고 각 score가 3.2, 5.1, -1.7일 때:
- car 클래스: \(\max(0,\ 5.1 - 3.2 + 1) = 2.9\)
- frog 클래스: \(\max(0,\ -1.7 - 3.2 + 1) = 0\)
- \(L_{cat} = 2.9 + 0 = 2.9\)
이걸 전체 학습 데이터에 대해 계산하고 평균 낸 게 최종 Data Loss다.
SVM Loss의 특징들
강의에서 Q&A 형식으로 몇 가지 성질을 짚어준다.
- Loss의 범위: 최솟값 0, 최댓값 ∞
- 초기화 직후 Loss 값? - W가 아주 작게 초기화되면 모든 score가 거의 0에 가까워서 Loss ≈ C-1 (C는 클래스 수). CIFAR-10이면 약 9. 학습 초기에 Loss가 이 값이 아니면 버그가 있다는 신호로 쓸 수 있다.
- 정답 클래스는 합산에서 제외 - j ≠ y_i 조건 때문에 자기 자신이랑 비교하는 건 없다. 만약 포함하면 Loss가 0이 아니라 1이 최솟값이 돼서 의미가 없다.
- margin(1)은 사실 별로 중요한 숫자가 아니다 - W를 스케일링하면 score 차이도 같이 커지거나 작아지기 때문에, 중요한 건 절대적인 score 값이 아니라 정답 score와 나머지 score 간의 상대적 차이다. margin은 결국 W의 스케일에 흡수된다.
- Loss = 0이 되는 W는 유일하지 않다 - 2W도 마찬가지로 Loss = 0을 만족한다. 이게 Regularization이 필요한 이유 중 하나다.
그래프로 그리면 hinge 모양이 나와서 Hinge Loss라고도 부른다. 정답 score가 충분히 높아서 margin을 만족하면 loss가 0에서 flat해진다. 즉 "충분히 맞으면 더 신경 안 쓴다"는 성질이 있다.
def svm_loss(scores, correct_class):
margins = np.maximum(0, scores - scores[correct_class] + 1)
margins[correct_class] = 0 # 정답 클래스 제외
loss = np.sum(margins)
return loss
3. Regularization
Data Loss만으로 학습하면 극단적으로는 학습 데이터를 통째로 외워버리는 W를 찾게 된다. Data Loss는 0이 되지만, 새로운 데이터에서는 성능이 박살난다. 이게 Overfitting이다.
이걸 막기 위해 Loss에 Regularization Term을 추가한다. W가 너무 복잡해지면 페널티를 주는 것이다. 오컴의 면도날(Occam's Razor)이랑 같은 맥락인데, 여러 가설이 경쟁할 때 더 단순한 것을 선택하는 게 일반화에 유리하다는 원칙이다.
\(\lambda\)는 두 항의 균형을 조절하는 하이퍼파라미터다. \(\lambda\)가 크면 regularization이 강해지고, 작으면 Data Loss에 더 집중한다.
Regularization 종류
| 종류 | 수식 | 특징 |
|---|---|---|
| L2 (Weight Decay) | \(R(W) = \sum_k \sum_l W_{k,l}^2\) | W를 전체적으로 작게. 가장 많이 씀 |
| L1 | \(R(W) = \sum_k \sum_l |W_{k,l}|\) | W를 sparse하게 만듦 |
| Elastic Net | \(\beta W^2 + |W|\) | L1 + L2 혼합 |
| Dropout | - | 나중에 다룸 |
L1 vs L2 Regularization - 어떤 걸 선택할까
\(x = [1, 1, 1, 1]\)이고 두 가중치 벡터 \(w_1 = [1, 0, 0, 0]\), \(w_2 = [0.25, 0.25, 0.25, 0.25]\)가 있다고 하자. 둘 다 \(w^T x = 1\)로 내적값은 같다.
- L1은 \(w_1\) 선호 - sparse한 해를 좋아하기 때문. 중요한 피처 몇 개에만 집중하는 방식
- L2는 \(w_2\) 선호 - 모든 피처에 영향을 골고루 퍼뜨리는 걸 좋아하기 때문 (diffuse over everything)
어느 쪽이 항상 좋다는 건 없다. 피처 간 독립성이 있으면 L1, 전체적으로 영향을 분산시키고 싶으면 L2를 쓰는 경향이 있다. 딥러닝에서는 보통 L2(Weight Decay)를 디폴트로 쓴다.
4. Softmax Classifier (Cross-Entropy Loss)
SVM은 score를 그냥 숫자로만 다뤘는데, Softmax는 score를 확률로 바꾼다.
먼저 각 score에 exp를 취하고 정규화해서 확률로 만드는 게 Softmax 함수다:
Loss는 정답 클래스의 확률에 -log를 취한 값이다:
이걸 Cross-Entropy Loss라고 부른다. -log를 쓰는 이유는 확률값을 직접 최대화하는 것보다 log를 취한 값을 최대화하는 게 수치적으로 편하기 때문이고, Loss는 "나쁜 정도"를 나타내야 하니까 마이너스를 붙인다.
계산 예시
cat score 3.2, car 5.1, frog -1.7이라고 하면:
- exp 취하기: \(e^{3.2} \approx 24.5\), \(e^{5.1} \approx 164.0\), \(e^{-1.7} \approx 0.18\)
- 합산: \(24.5 + 164.0 + 0.18 = 188.68\)
- cat 확률: \(24.5 / 188.68 \approx 0.13\)
- Loss: \(-\log(0.13) \approx 0.89\)
Softmax의 특징들
- Loss 범위: 최솟값 0, 최댓값 ∞
- 초기화 직후 Loss? - W가 작으면 모든 score가 비슷해서 확률이 균등하게 1/C씩. CIFAR-10이면 -log(1/10) = log(10) ≈ 2.3. 초기 Loss가 이 값이 아니면 구현 버그 신호다.
- 확률로 해석 가능 - SVM과 달리 출력값이 0~1 사이이고 합이 1이라서 "이 이미지가 고양이일 확률 13%" 식으로 해석할 수 있다.
- 항상 더 나아지려 한다 - SVM은 margin만 넘으면 만족하고 멈추는데, Softmax는 정답 클래스 확률을 1로 만드는 게 목표라서 score가 아무리 높아도 계속 더 올리려 한다. 이론적으로는 정답 score → ∞, 나머지 → -∞로 수렴한다.
5. SVM vs Softmax
| SVM (Hinge Loss) | Softmax (Cross-Entropy) | |
|---|---|---|
| 출력 | 임의의 숫자 (score) | 확률 (0~1, 합=1) |
| 목표 | 정답 score가 나머지보다 margin 이상 | 정답 클래스 확률을 1로 |
| 만족하면? | loss=0, 더 신경 안 씀 | 계속 더 올리려 함 |
| gradient | piecewise linear (꺾임) | 어디서든 smooth |
| 해석 | 상대적 score 차이 | 확률로 해석 가능 |
| 실전 성능 차이 | 보통 크지 않음. 둘 다 쓰이지만 Softmax가 더 흔함 | |
성능 차이는 크지 않지만 Softmax가 확률로 해석 가능하고 gradient가 어디서든 smooth하다는 장점 때문에 실무에서 더 자주 쓰인다.
6. Optimization
Loss Function을 정의했으면, 이 Loss를 최소화하는 W를 어떻게 찾냐가 문제다. 이게 Optimization이다.
비유하면, W가 가질 수 있는 모든 경우의 수를 거대한 산악 지형이라고 보고, Loss가 그 지형의 높이라고 생각하면 된다. 우리가 원하는 건 가장 낮은 골짜기를 찾는 것이다.
시도 1: Random Search
W를 그냥 랜덤하게 뽑아보면서 제일 좋은 걸 고르는 방법. 강의에서 이걸로 CIFAR-10 15.5% 정확도가 나왔다고 한다. 10개 클래스 랜덤 찍기가 10%이니 그것보다야 낫지만, 당연히 실용적이지 않다.
시도 2: Gradient 활용
훨씬 스마트한 방법이다. 현재 W에서 Loss를 미분하면, 어느 방향으로 W를 움직여야 Loss가 줄어드는지를 수학적으로 알 수 있다. 이게 Gradient(기울기)를 이용하는 방법이다.
1차원에서 기울기는 그냥 미분이다:
W가 행렬이면 각 원소마다 편미분을 계산한 것이 gradient다. Gradient의 반대 방향으로 W를 업데이트하면 Loss가 줄어든다.
Numerical vs Analytic Gradient
Gradient를 계산하는 방법이 두 가지 있다.
| Numerical Gradient | Analytic Gradient | |
|---|---|---|
| 방법 | h를 아주 작게 해서 수치적으로 근사 | 수식을 직접 미분해서 계산 |
| 속도 | 매우 느림 (파라미터 수만큼 반복) | 빠름 |
| 정확도 | 근사값 (오차 있음) | 정확 |
| 실제 사용 | gradient check 디버깅 용도 | 실제 학습에 사용 |
실제로는 analytic gradient로 학습하고, 구현이 맞는지 확인할 때 numerical gradient로 Gradient Check를 한다. 두 값이 비슷하게 나오면 구현이 맞다는 뜻이다. 이건 나중에 직접 구현할 때 디버깅에 꽤 유용하다.
7. Gradient Descent & SGD
Gradient를 이용해서 W를 반복적으로 업데이트하는 게 Gradient Descent(경사하강법)다.
\(\alpha\)가 Learning Rate(학습률)인데, 한 번에 얼마나 크게 이동할지를 결정하는 하이퍼파라미터다. 너무 크면 발산하고, 너무 작으면 수렴이 너무 느리다. 실제로는 \(10^{-3}\) ~ \(10^{-5}\) 사이에서 시작하는 경우가 많다.
# Vanilla Gradient Descent
while True:
weights_grad = evaluate_gradient(loss_fn, data, weights)
weights -= learning_rate * weights_grad
Stochastic Gradient Descent (SGD)
근데 실제로는 N이 수백만 개인 데이터셋에서 매번 전체 Loss를 계산하면 너무 느리다. 그래서 전체 데이터 대신 작은 미니배치만 뽑아서 gradient를 근사하는 게 SGD다.
# Stochastic Gradient Descent (Mini-batch)
while True:
data_batch = sample_training_data(data, 256) # 256개 미니배치
weights_grad = evaluate_gradient(loss_fn, data_batch, weights)
weights -= learning_rate * weights_grad
미니배치 사이즈는 보통 32, 64, 128, 256을 많이 쓴다. 이것도 하이퍼파라미터다.
전체 데이터로 계산한 gradient의 "노이즈 낀 추정치"지만 실제로는 잘 동작한다. 오히려 노이즈가 local minima에서 빠져나오는 데 도움이 된다는 얘기도 있다. 또한 미니배치를 랜덤하게 샘플링하면 전체 데이터셋 gradient와 expectation이 같아서 결국 비슷하게 수렴한다.
Learning Rate 선택 팁
학습 초기에 Loss가 전혀 안 내려가면 learning rate가 너무 낮은 것이고, Loss가 NaN이 나오면 너무 높은 것이다. 처음엔 regularization을 0으로 두고 Data Loss만 보면서 learning rate를 잡는 게 좋다. regularization을 키우면 Loss가 올라가는지도 확인해보면 구현이 맞는지 검증할 수 있다.
핵심 요약
| 개념 | 한 줄 요약 |
|---|---|
| Loss Function | W가 얼마나 나쁜지를 수치로 나타낸 함수. 작을수록 좋은 W |
| SVM Loss (Hinge) | 정답 score가 나머지보다 margin 이상 높으면 loss=0. 충분하면 더 안 함 |
| 초기 Loss 체크 | SVM은 C-1, Softmax는 log(C). 다르면 버그 신호 |
| Regularization | 모델 복잡도에 패널티. Overfitting 방지. L2가 디폴트 |
| Softmax | score → 확률 변환. 정답 클래스 확률을 1로 만드는 게 목표 |
| SVM vs Softmax | 성능 차이 크지 않음. Softmax가 확률 해석 가능 + gradient smooth |
| Gradient | Loss를 W로 편미분한 것. 반대 방향으로 W를 업데이트하면 Loss 감소 |
| Gradient Check | numerical gradient로 analytic gradient 구현이 맞는지 검증 |
| SGD | 미니배치로 gradient 근사. 32~256 배치 사이즈 주로 사용 |
| Learning Rate | 너무 크면 발산, 너무 작으면 느림. 보통 1e-3~1e-5에서 시작 |
참고
- cs231n 공식 강의 노트 - Linear Classification
- cs231n 공식 강의 노트 - Optimization
- Stanford cs231n YouTube - Lecture 3
다음: Lecture 4 - Backpropagation and Neural Networks