Level 3: 딥러닝 핵심
🔮

Level 3

손실 함수

딥러닝의 시작점 - MSE와 Cross-Entropy

6분 0초
손실 함수 강의 영상
강의 영상 보기 (새 탭에서 재생)YouTube

📓Google Colab에서 실습하기

이 레슨은 PyTorch/GPU가 필요합니다. 노트북을 다운로드 후 Google Colab에서 열어주세요.

학습 내용

손실 함수 (Loss Function) - 모델의 성적표

학습 목표

이 레슨을 완료하면:

  • 손실 함수가 왜 필요한지 직관적으로 이해합니다
  • MSE(평균 제곱 오차)를 직접 구현하고 시각화할 수 있습니다
  • Cross-Entropy를 직접 구현하고 시각화할 수 있습니다
  • 문제 유형에 따라 올바른 손실 함수를 선택할 수 있습니다
  • 손실 지형(Loss Landscape)의 의미를 파악합니다
  • loss.backward()가 내부적으로 무엇을 하는지 연결합니다

핵심 메시지

"손실 함수는 모델의 시험 채점표입니다. 점수(손실)가 낮을수록 모델이 잘하고 있다는 뜻!"


1. 비유: 시험 채점 시스템

학교 시험을 떠올려 봅시다.

학생이 답안을 제출하면, 선생님이 채점을 합니다. 채점 결과가 나쁘면 학생은 "어디가 틀렸는지" 파악하고, 다음번에 더 잘하려고 노력하죠.

딥러닝도 똑같습니다:

시험 채점딥러닝
학생의 답안모델의 예측값
정답지실제 정답(label)
채점 기준손실 함수
점수(감점)손실값(loss)
오답 노트역전파(backpropagation)

핵심은 이것입니다: 채점 기준(손실 함수)이 없으면, 모델은 자기가 얼마나 틀렸는지 알 수 없습니다.


2. 왜 손실 함수가 필요한가?

모델이 학습하려면 반드시 "지금 얼마나 틀렸는지" 를 숫자로 표현해야 합니다.

예를 들어 집값을 예측하는 모델이 있다고 합시다:

구분
실제 집값5억 원
모델 예측4억 원
오차 (손실)1억 원 ← 이것이 "손실(loss)"

손실값이 크면 "많이 틀렸다", 작으면 "거의 맞았다"는 뜻입니다. 학습의 목표는 단 하나: 이 손실값을 최대한 줄이는 것!

그런데 "틀린 정도"를 측정하는 방법은 여러 가지가 있습니다. 문제 유형에 따라 다른 채점 기준(손실 함수)을 써야 합니다.


3. MSE (Mean Squared Error) - 회귀 문제의 채점표

MSE란?

MSE는 "오차를 제곱해서 평균 낸 것" 입니다. 숫자를 예측하는 회귀(regression) 문제에서 사용합니다.

수식:

MSE=1ni=1n(y^iyi)2\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (\hat{y}_i - y_i)^2

여기서 y^i\hat{y}_i는 예측값, yiy_i는 실제값, nn은 데이터 개수입니다.

왜 제곱할까?

두 가지 이유가 있습니다:

이유설명
방향 제거+1 오차와 -1 오차가 상쇄되면 안 됩니다. 제곱하면 둘 다 양수!
큰 오차에 패널티오차 1 -> 손실 1, 오차 2 -> 손실 4, 오차 3 -> 손실 9. 큰 실수에 더 큰 벌점!

🔬 실습: MSE 단계별 구현

아래 코드를 실행하면 MSE가 어떻게 계산되는지 단계별로 확인할 수 있습니다.

python
import numpy as np # ═══════════════════════════════════════════════════════════════ # 📊 MSE(Mean Squared Error) 단계별 계산 # ═══════════════════════════════════════════════════════════════ # 📌 데이터 준비: 실제값 vs 모델의 예측값 y_true = np.array([3.0, 5.0, 2.5, 7.0, 4.5]) # 정답 (실제 데이터) y_pred = np.array([2.8, 5.2, 2.0, 6.5, 4.8]) # AI 모델의 예측 print("🎯 정답값:", y_true) print("🤖 예측값:", y_pred) print() # ───────────────────────────────────────────────────────────────── # Step 1️⃣: 오차 계산 (예측 - 정답) # ───────────────────────────────────────────────────────────────── errors = y_pred - y_true print("Step 1️⃣ 오차 (예측-정답):", errors) # 결과: [-0.2, 0.2, -0.5, -0.5, 0.3] # → 양수면 과대예측, 음수면 과소예측 # ───────────────────────────────────────────────────────────────── # Step 2️⃣: 오차 제곱 (음수를 양수로 + 큰 오차에 패널티) # ───────────────────────────────────────────────────────────────── squared_errors = errors ** 2 print("Step 2️⃣ 제곱 오차:", squared_errors) # 결과: [0.04, 0.04, 0.25, 0.25, 0.09] # → 모든 값이 양수가 됨! # ───────────────────────────────────────────────────────────────── # Step 3️⃣: 평균 계산 → 이것이 MSE! # ───────────────────────────────────────────────────────────────── mse = np.mean(squared_errors) print("Step 3️⃣ 평균 (MSE):", round(mse, 4)) print() # ═══════════════════════════════════════════════════════════════ # 💡 한 줄로 쓰면 이렇게 됩니다 # ═══════════════════════════════════════════════════════════════ mse_oneline = np.mean((y_pred - y_true) ** 2) print("📝 한 줄 공식:", round(mse_oneline, 4)) # ═══════════════════════════════════════════════════════════════ # ✅ 완벽한 예측은? MSE = 0 # ═══════════════════════════════════════════════════════════════ y_perfect = y_true.copy() # 정답과 똑같이 예측 mse_perfect = np.mean((y_perfect - y_true) ** 2) print() print("🏆 완벽한 예측의 MSE:", mse_perfect, "(0이면 완벽!)")

📈 시각화: MSE 손실 곡선

MSE가 예측 오차에 어떻게 반응하는지 그래프로 확인해봅시다.

python
import numpy as np import matplotlib.pyplot as plt # ═══════════════════════════════════════════════════════════════ # 📊 MSE 손실 곡선 시각화 # 정답이 5.0일 때, 예측값에 따라 손실이 어떻게 변하는지 확인 # ═══════════════════════════════════════════════════════════════ y_true = 5.0 # 🎯 정답값 (고정) y_preds = np.linspace(0, 10, 200) # 예측값 범위: 0 ~ 10 # 각 예측값에 대한 MSE 계산 mse_values = (y_preds - y_true) ** 2 # ───────────────────────────────────────────────────────────────── # 그래프 그리기 # ───────────────────────────────────────────────────────────────── plt.figure(figsize=(10, 6)) plt.plot(y_preds, mse_values, linewidth=3, color='royalblue', label='MSE Loss') plt.axvline(x=y_true, color='red', linestyle='--', linewidth=2, label='Answer = 5.0') plt.scatter([y_true], [0], color='red', s=100, zorder=5) # 정답 지점 표시 # 그래프 꾸미기 plt.xlabel('Prediction', fontsize=14) plt.ylabel('MSE Loss', fontsize=14) plt.title('MSE Loss Curve: Loss Increases Quadratically with Distance', fontsize=15) plt.legend(fontsize=12, loc='upper right') plt.grid(True, alpha=0.3) # 핵심 포인트 표시 plt.annotate('Correct! Loss=0', xy=(5, 0), xytext=(6.5, 5), fontsize=11, arrowprops=dict(arrowstyle='->', color='red'), color='red', fontweight='bold') plt.annotate('Error 2 -> Loss 4', xy=(7, 4), xytext=(8, 8), fontsize=10, arrowprops=dict(arrowstyle='->', color='gray')) plt.annotate('Error 3 -> Loss 9', xy=(8, 9), xytext=(9, 15), fontsize=10, arrowprops=dict(arrowstyle='->', color='gray')) plt.tight_layout() plt.show()

💡 핵심 포인트: 정답(5.0)에서 멀어질수록 손실이 제곱으로 커집니다!

  • 오차 1 → 손실 1
  • 오차 2 → 손실 4 (2배가 아닌 4배!)
  • 오차 3 → 손실 9 (3배가 아닌 9배!)

이것이 MSE가 "큰 실수에 큰 벌점"을 주는 원리입니다.


4. Cross-Entropy - 분류 문제의 채점표

Cross-Entropy란?

분류(classification) 문제에서는 모델이 확률을 출력합니다. 예: "이 사진이 고양이일 확률 90%, 강아지일 확률 10%"

Cross-Entropy는 "정답 클래스의 확률이 얼마나 높은지" 를 측정합니다.

수식 (이진 분류):

BCE=1ni=1n[yilog(pi)+(1yi)log(1pi)]\text{BCE} = -\frac{1}{n} \sum_{i=1}^{n} \left[ y_i \log(p_i) + (1-y_i) \log(1-p_i) \right]

여기서 yiy_i는 정답(0 또는 1), pip_i는 모델이 예측한 확률입니다.

직관적 이해

🖼️ 이미지: 고양이 사진

모델예측판정손실
모델 A"고양이 확률 95%"✅ 자신 있고 정답!낮음
모델 B"고양이 확률 50%"⚠️ 반반이라고?중간
모델 C"고양이 확률 5%"❌ 완전히 틀림!매우 높음

log가 하는 일

정답 확률log(p)-\log(p) = 손실해석
0.95 (95%)0.05✅ 거의 0, 훌륭!
0.70 (70%)0.36⚠️ 조금 불안
0.50 (50%)0.69❌ 반반? 안 됨!
0.10 (10%)2.30❌ 큰 손실!
0.01 (1%)4.61❌ 엄청난 손실!

확률이 0에 가까워질수록 손실이 급격히 증가합니다. "자신 있게 틀리면" 엄청난 패널티를 받는 것이죠!

🔬 실습: Cross-Entropy 직접 구현

좋은 모델과 나쁜 모델의 손실 차이를 직접 비교해봅시다.

python
import numpy as np # ═══════════════════════════════════════════════════════════════ # 📊 Binary Cross-Entropy (BCE) 직접 구현 # ═══════════════════════════════════════════════════════════════ def binary_cross_entropy(y_true, y_pred): """ BCE 손실 함수 - y_true: 정답 (0 또는 1) - y_pred: 모델이 예측한 확률 (0~1 사이 값) """ # ⚠️ 안전 장치: log(0)은 무한대가 되므로 극단값 방지 y_pred = np.clip(y_pred, 1e-7, 1 - 1e-7) # BCE 공식 적용 bce = -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred)) return bce # ───────────────────────────────────────────────────────────────── # 📌 데이터: 5장의 사진을 분류하는 문제 # ───────────────────────────────────────────────────────────────── # 🏷️ 정답: [고양이, 강아지, 고양이, 고양이, 강아지] # 고양이 = 1, 강아지 = 0 y_true = np.array([1, 0, 1, 1, 0]) print("🏷️ 정답:", ["고양이" if x==1 else "강아지" for x in y_true]) # ───────────────────────────────────────────────────────────────── # 🏆 좋은 모델: 정답에 가까운 확률 예측 # ───────────────────────────────────────────────────────────────── y_pred_good = np.array([0.9, 0.1, 0.85, 0.95, 0.05]) print("\n🤖 좋은 모델의 예측 (고양이 확률):", y_pred_good) print(" → 손실 (BCE):", round(binary_cross_entropy(y_true, y_pred_good), 4), "✅ 낮음!") # ───────────────────────────────────────────────────────────────── # ❌ 나쁜 모델: 엉뚱한 확률 예측 # ───────────────────────────────────────────────────────────────── y_pred_bad = np.array([0.3, 0.8, 0.4, 0.2, 0.7]) print("\n🤖 나쁜 모델의 예측 (고양이 확률):", y_pred_bad) print(" → 손실 (BCE):", round(binary_cross_entropy(y_true, y_pred_bad), 4), "❌ 높음!") # ───────────────────────────────────────────────────────────────── # 🎯 완벽한 모델: 100% 정확한 예측 # ───────────────────────────────────────────────────────────────── y_pred_perfect = np.array([1.0, 0.0, 1.0, 1.0, 0.0]) print("\n🤖 완벽한 모델의 예측:", y_pred_perfect) print(" → 손실 (BCE):", round(binary_cross_entropy(y_true, y_pred_perfect), 4), "🏆 거의 0!")

📈 시각화: Cross-Entropy 손실 곡선

python
import numpy as np import matplotlib.pyplot as plt # ═══════════════════════════════════════════════════════════════ # 📊 Cross-Entropy 손실 곡선 비교 # 정답이 다를 때 손실이 어떻게 달라지는지 확인 # ═══════════════════════════════════════════════════════════════ p = np.linspace(0.01, 0.99, 200) # 예측 확률 (0.01 ~ 0.99) # 📐 손실 계산 ce_loss_1 = -np.log(p) # 정답=1 (고양이) 일 때 손실 ce_loss_0 = -np.log(1 - p) # 정답=0 (강아지) 일 때 손실 # ───────────────────────────────────────────────────────────────── # 그래프 그리기 (2개 나란히) # ───────────────────────────────────────────────────────────────── fig, axes = plt.subplots(1, 2, figsize=(14, 5)) # 왼쪽 그래프: 정답이 고양이(1)인 경우 axes[0].plot(p, ce_loss_1, linewidth=3, color='crimson') axes[0].set_xlabel('Prediction: P(cat)', fontsize=12) axes[0].set_ylabel('Loss', fontsize=12) axes[0].set_title('When answer is Cat', fontsize=14) axes[0].grid(True, alpha=0.3) axes[0].annotate('High prob\nLow loss', xy=(0.9, 0.1), fontsize=10, ha='center', color='green', fontweight='bold') axes[0].annotate('Low prob\nLoss explodes!', xy=(0.1, 2.3), fontsize=10, ha='center', color='red', fontweight='bold') # 오른쪽 그래프: 정답이 강아지(0)인 경우 axes[1].plot(p, ce_loss_0, linewidth=3, color='teal') axes[1].set_xlabel('Prediction: P(cat)', fontsize=12) axes[1].set_ylabel('Loss', fontsize=12) axes[1].set_title('When answer is Dog', fontsize=14) axes[1].grid(True, alpha=0.3) axes[1].annotate('Low prob\nLow loss', xy=(0.1, 0.1), fontsize=10, ha='center', color='green', fontweight='bold') axes[1].annotate('High prob\nLoss explodes!', xy=(0.9, 2.3), fontsize=10, ha='center', color='red', fontweight='bold') plt.tight_layout() plt.show()

💡 그래프 해석:

  • 왼쪽 (정답=고양이🐱): "고양이 확률"을 낮게 예측하면 손실 급증!
  • 오른쪽 (정답=강아지🐕): "고양이 확률"을 높게 예측하면 손실 급증!

Cross-Entropy는 **"자신 있게 틀리면 큰 벌점"**을 주는 것이 핵심입니다.


5. MSE vs Cross-Entropy 비교

선택 가이드

문제 유형손실 함수출력층 활성화예시
회귀 (숫자 예측)MSE없음 (선형)집값, 온도, 주가
이진 분류 (예/아니오)Binary Cross-EntropySigmoid스팸/정상, 합격/불합격
다중 분류 (여러 클래스)Categorical Cross-EntropySoftmax고양이/개/새, 숫자 0-9

왜 분류에 MSE를 쓰면 안 될까?

손실 함수확률 0.9 vs 0.99 비교학습 속도적합성
MSE0.01 vs 0.0001 (거의 같음!)❌ 느림분류에 부적합
Cross-Entropy0.10 vs 0.01 (10배 차이!)✅ 빠름분류에 최적

💡 핵심: Cross-Entropy는 "자신 있게 틀릴 때" 큰 패널티를 줘서 빠르게 학습합니다!

📈 실습: MSE vs Cross-Entropy 비교

왜 분류 문제에서 Cross-Entropy를 사용하는지 직접 확인해봅시다.

python
import numpy as np import matplotlib.pyplot as plt # ═══════════════════════════════════════════════════════════════ # 📊 MSE vs Cross-Entropy 손실 비교 # 분류 문제에서 왜 CE가 더 좋은지 시각화 # ═══════════════════════════════════════════════════════════════ p = np.linspace(0.01, 0.99, 200) # 예측 확률 범위 # 📐 정답이 1일 때 각 손실 함수 계산 mse_loss = (1 - p) ** 2 # MSE: (정답 - 예측)² ce_loss = -np.log(p) # CE: -log(예측) plt.figure(figsize=(8, 5)) plt.plot(p, mse_loss, linewidth=2, label='MSE', color='royalblue') plt.plot(p, ce_loss, linewidth=2, label='Cross-Entropy', color='crimson') plt.xlabel('predicted probability (label=1)', fontsize=12) plt.ylabel('Loss', fontsize=12) plt.title('MSE vs Cross-Entropy (when true label = 1)', fontsize=13) plt.legend(fontsize=12) plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() print('p=0.01 -> MSE:', round((1-0.01)**2, 3), ' CE:', round(-np.log(0.01), 3)) print('p=0.50 -> MSE:', round((1-0.50)**2, 3), ' CE:', round(-np.log(0.50), 3)) print('p=0.99 -> MSE:', round((1-0.99)**2, 3), ' CE:', round(-np.log(0.99), 3))

Cross-Entropy는 예측 확률이 0에 가까울 때 MSE보다 훨씬 더 큰 패널티를 줍니다. 그래서 분류 문제에서 학습이 더 빠르고 효과적입니다!


6. 손실 지형 (Loss Landscape) 시각화

가중치를 바꿀 때 손실값이 어떻게 변하는지를 지형(landscape) 으로 표현할 수 있습니다. 마치 산과 계곡처럼 생긴 3D 표면이죠.

핵심 용어

용어의미
전역 최솟값 (Global Minimum)전체에서 가장 낮은 곳. 이것이 목표!
지역 최솟값 (Local Minimum)주변보다 낮지만, 전체 최저는 아닌 곳. 함정!
안장점 (Saddle Point)한 방향은 최소, 다른 방향은 최대인 곳

실행해보기: 손실 지형 등고선 그래프

python
import numpy as np import matplotlib.pyplot as plt # 2개의 가중치(w1, w2)에 대한 손실 함수 지형 만들기 # 여러 극솟값이 있는 복잡한 표면 w1 = np.linspace(-3, 3, 200) w2 = np.linspace(-3, 3, 200) W1, W2 = np.meshgrid(w1, w2) # 복잡한 손실 함수 (여러 골짜기가 있음) Loss = (W1**2 + W2**2) * 0.5 + 2 * np.sin(W1 * 2) * np.cos(W2 * 2) + 3 plt.figure(figsize=(8, 6)) contour = plt.contourf(W1, W2, Loss, levels=30, cmap='RdYlBu_r') plt.colorbar(contour, label='Loss value') plt.xlabel('Weight 1 (w1)', fontsize=12) plt.ylabel('Weight 2 (w2)', fontsize=12) plt.title('Loss Landscape (contour plot)', fontsize=13) # 전역 최솟값 근처에 표시 min_idx = np.unravel_index(np.argmin(Loss), Loss.shape) plt.plot(W1[min_idx], W2[min_idx], 'w*', markersize=20, label='Global Minimum') plt.legend(fontsize=11) plt.tight_layout() plt.show() print('Global minimum at w1={:.2f}, w2={:.2f}'.format(W1[min_idx], W2[min_idx])) print('Minimum loss: {:.2f}'.format(Loss[min_idx]))

파란색이 손실이 낮은 곳(계곡), 빨간색이 높은 곳(산)입니다. 학습의 목표는 빨간색에서 시작해서 가장 깊은 파란색 계곡을 찾아가는 것입니다!


7. PyTorch에서의 손실 함수

실제 PyTorch 코드에서는 이렇게 사용합니다 (참고용):

import torch import torch.nn as nn # ═══════════════════════════════════════════════════════════════ # 🔧 PyTorch 손실 함수 사용법 # ═══════════════════════════════════════════════════════════════ # 📊 회귀 문제: MSE 사용 criterion = nn.MSELoss() loss = criterion(prediction, target) # 📊 분류 문제: CrossEntropy 사용 criterion = nn.CrossEntropyLoss() loss = criterion(prediction, target) # ───────────────────────────────────────────────────────────────── # 💡 핵심! loss.backward() 호출 시: # → "이 손실 함수의 기울기(gradient)"가 계산됨 # → 그 기울기로 가중치를 업데이트하는 것이 학습! # ───────────────────────────────────────────────────────────────── loss.backward() # ← 손실 함수의 미분값 계산

loss.backward()"이 손실 함수를 각 가중치로 미분해라" 라는 명령입니다. 그 미분값(gradient)이 다음 레슨에서 배울 경사하강법의 핵심 재료가 됩니다!


핵심 요약

개념설명비유
손실 함수모델이 얼마나 틀렸는지 측정하는 함수시험 채점 기준
MSE오차를 제곱해서 평균. 회귀 문제용감점 = (오답 크기)^2
Cross-Entropy정답 확률의 -log. 분류 문제용자신있게 틀리면 큰 감점
손실 지형가중치에 따른 손실값의 3D 지형산과 계곡
loss.backward()손실 함수의 기울기를 계산어느 방향이 내리막인지 확인

학습 체크리스트

  • 손실 함수가 왜 필요한지 설명할 수 있다
  • MSE를 numpy로 직접 구현할 수 있다
  • Cross-Entropy를 numpy로 직접 구현할 수 있다
  • 회귀에는 MSE, 분류에는 CE를 쓰는 이유를 안다
  • 손실 지형에서 전역 최솟값과 지역 최솟값의 차이를 안다
  • loss.backward()가 손실 함수의 기울기를 계산한다는 것을 안다

다음 강의 예고

"경사하강법 실전" - 손실 지형에서 가장 낮은 곳을 찾아가는 방법! SGD, Momentum, Adam 등 다양한 최적화 알고리즘을 직접 구현합니다.

레슨 정보

레벨
Level 3: 딥러닝 핵심
예상 소요 시간
6분 0초
참고 영상
YouTube 링크

💡실습 환경 안내

이 레벨은 PyTorch/GPU가 필요하여 Google Colab 사용을 권장합니다.

Colab은 무료 GPU를 제공하여 PyTorch, CNN, Transformer 등을 실행할 수 있습니다.