從經典案例學習 CNN:實戰技巧與完整總結 (Part 4)

本文件是 CNN 完整實戰指南的第四部分(最終篇),總結實戰經驗與進階技巧。

前置閱讀:建議先完成 Part 1-3。


本文件內容


第五部分:實戰技巧與疑難排解

常見問題與解決方案

問題 1:準確率卡在某個值不上升

症狀

Epoch 1: Train Acc 65%, Test Acc 63%
Epoch 5: Train Acc 75%, Test Acc 72%
Epoch 10: Train Acc 76%, Test Acc 72%  ← 停滯
Epoch 20: Train Acc 76%, Test Acc 72%

可能原因與解決方案

graph TD Problem[準確率停滯] --> Check1{學習率} Problem --> Check2{模型容量} Problem --> Check3{資料品質} Problem --> Check4{損失函數} Check1 -->|過大| Sol1[降低學習率<br/>0.001 → 0.0001] Check1 -->|過小| Sol2[提高學習率<br/>使用學習率調度] Check2 -->|太小| Sol3[增加層數/濾波器<br/>64→128→256] Check2 -->|太大| Sol4[減少參數<br/>加強正規化] Check3 -->|標籤錯誤| Sol5[檢查資料標註<br/>清理異常樣本] Check3 -->|不平衡| Sol6[使用加權損失<br/>過採樣/欠採樣] Check4 -->|不適合| Sol7[換損失函數<br/>Focal Loss] style Problem fill:#FFB6C1 style Sol1 fill:#90EE90 style Sol2 fill:#90EE90 style Sol3 fill:#90EE90 style Sol4 fill:#90EE90 style Sol5 fill:#90EE90 style Sol6 fill:#90EE90 style Sol7 fill:#90EE90

診斷步驟

# ===== 步驟 1: 檢查學習率 =====
# 畫出損失曲線,觀察震盪情況
plt.plot(history['loss'])
plt.title('訓練損失曲線')
plt.show()

# 如果損失劇烈震盪 → 學習率過大
# 如果損失幾乎不動 → 學習率過小

# ===== 步驟 2: 嘗試更大的學習率範圍 =====
# Learning Rate Finder
learning_rates = [1e-5, 1e-4, 1e-3, 1e-2, 1e-1]
for lr in learning_rates:
    model = create_model()
    optimizer = Adam(lr=lr)
    # 訓練 2-3 個 epoch
    # 記錄最終損失

# ===== 步驟 3: 檢查模型容量 =====
# 計算訓練集和測試集的差距
train_acc = 76%
test_acc = 72%
gap = 4%

if gap < 5%:
    print("模型容量可能不足,試著加深/加寬網路")
else:
    print("模型容量充足,問題可能在學習率或資料")

# ===== 步驟 4: 視覺化預測 =====
# 看看錯誤的案例,是否有規律?
wrong_indices = np.where(y_pred != y_true)[0]
for idx in wrong_indices[:10]:
    plt.imshow(X_test[idx])
    plt.title(f"True: {y_true[idx]}, Pred: {y_pred[idx]}")
    plt.show()

問題 2:訓練集準確率高,測試集低(過擬合)

症狀

Epoch 20: Train Acc 95%, Test Acc 75%  ← 差距 20%

解決方案優先級

方法 效果 實作難度 推薦指數
資料增強 +++++ ★☆☆☆☆ ⭐⭐⭐⭐⭐
Dropout ++++ ★☆☆☆☆ ⭐⭐⭐⭐⭐
L2 正規化 +++ ★☆☆☆☆ ⭐⭐⭐⭐☆
早停 (Early Stopping) ++++ ★☆☆☆☆ ⭐⭐⭐⭐⭐
Batch Normalization +++ ★★☆☆☆ ⭐⭐⭐⭐☆
減少模型容量 +++ ★★☆☆☆ ⭐⭐⭐☆☆
收集更多資料 +++++ ★★★★★ ⭐⭐⭐⭐⭐

完整解決方案

# ===== 方案 1: 資料增強(最有效) =====
datagen = ImageDataGenerator(
    rotation_range=20,
    width_shift_range=0.2,
    height_shift_range=0.2,
    horizontal_flip=True,
    zoom_range=0.15,
    shear_range=0.15
)

# ===== 方案 2: 加強 Dropout =====
model = Sequential([
    Conv2D(64, (3,3), activation='relu'),
    Dropout(0.3),  # 從 0.25 提高到 0.3-0.4
    # ...
    Dense(128, activation='relu'),
    Dropout(0.5),  # 全連接層用更高的 Dropout
    Dense(10, activation='softmax')
])

# ===== 方案 3: L2 正規化 =====
from tensorflow.keras.regularizers import l2

model.add(Dense(128, activation='relu',
                kernel_regularizer=l2(0.001)))  # L2 正規化

# PyTorch: 在優化器中設定 weight_decay
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)

# ===== 方案 4: 早停 =====
early_stop = EarlyStopping(
    monitor='val_loss',
    patience=10,         # 10 個 epoch 沒改善就停
    restore_best_weights=True
)

# ===== 方案 5: 減少模型大小 =====
# 將濾波器數量減半
# 原本: 64 → 128 → 256 → 512
# 改為: 32 → 64 → 128 → 256

問題 3:訓練速度太慢

速度優化檢查清單

# ===== 1. 確認 GPU 已啟用 =====
# TensorFlow
print(tf.config.list_physical_devices('GPU'))

# PyTorch
print(torch.cuda.is_available())

# 如果顯示 GPU 但沒加速,檢查資料是否在 GPU 上:
data = data.to(device)  # PyTorch
model = model.to(device)

# ===== 2. 增加 Batch Size =====
# GPU 記憶體允許的情況下,越大越快
BATCH_SIZE = 256  # 從 128 提高到 256
# 注意:Batch Size 提高可能需要調整學習率

# ===== 3. 使用 Mixed Precision 訓練 =====
# TensorFlow
from tensorflow.keras.mixed_precision import Policy
policy = Policy('mixed_float16')
tf.keras.mixed_precision.set_global_policy(policy)

# PyTorch
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()

for data, target in train_loader:
    optimizer.zero_grad()
    with autocast():  # 自動混合精度
        output = model(data)
        loss = criterion(output, target)
    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()

# 效果:速度提升 2-3 倍,記憶體減少 50%

# ===== 4. 優化 DataLoader =====
# 增加 num_workers
train_loader = DataLoader(
    train_dataset,
    batch_size=BATCH_SIZE,
    shuffle=True,
    num_workers=4,      # 從 2 提高到 4
    pin_memory=True,    # 加速 CPU→GPU 傳輸
    persistent_workers=True  # PyTorch 1.7+
)

# ===== 5. 使用更快的優化器 =====
# AdamW 通常比 Adam 更快收斂
from tensorflow.keras.optimizers import AdamW  # Keras
import torch.optim as optim  # PyTorch
optimizer = optim.AdamW(model.parameters(), lr=0.001)

速度對比(CIFAR-10, 50 epochs):

設定 訓練時間 加速比
CPU, batch=128 120 分鐘
GPU, batch=128 15 分鐘
GPU, batch=256 10 分鐘 12×
GPU, batch=256, Mixed Precision 5 分鐘 24×
GPU, batch=256, MP, num_workers=4 4 分鐘 30×

問題 4:CUDA Out of Memory

錯誤訊息

RuntimeError: CUDA out of memory. Tried to allocate 1.50 GiB
(GPU 0; 15.75 GiB total capacity; 14.23 GiB already allocated)

解決方案

# ===== 方案 1: 減少 Batch Size(最有效) =====
BATCH_SIZE = 64  # 從 128 減到 64 或 32

# ===== 方案 2: 清空 GPU 快取 =====
import torch
torch.cuda.empty_cache()

# TensorFlow
from tensorflow.keras import backend as K
K.clear_session()

# ===== 方案 3: 使用梯度累積(保持有效 Batch Size) =====
# 模擬 batch_size=128 的效果
ACCUMULATION_STEPS = 4
effective_batch_size = 32  # 32 × 4 = 128

optimizer.zero_grad()
for i, (data, target) in enumerate(train_loader):
    output = model(data)
    loss = criterion(output, target) / ACCUMULATION_STEPS
    loss.backward()

    if (i + 1) % ACCUMULATION_STEPS == 0:
        optimizer.step()
        optimizer.zero_grad()

# ===== 方案 4: 減少模型大小 =====
# 將濾波器數量減半
# 或使用 DepthwiseSeparable Convolution

# ===== 方案 5: 使用 Gradient Checkpointing =====
# PyTorch
from torch.utils.checkpoint import checkpoint

def forward(self, x):
    x = checkpoint(self.block1, x)  # 重新計算而非儲存
    x = checkpoint(self.block2, x)
    return x

模型不收斂的診斷流程

graph TD Start[模型不收斂] --> Q1{損失是否下降?} Q1 -->|完全不降| Path1[檢查基礎設定] Q1 -->|下降但停滯| Path2[優化超參數] Q1 -->|劇烈震盪| Path3[學習率過大] Path1 --> Check1[1. 資料是否正規化?] Check1 --> Check2[2. 標籤格式正確?] Check2 --> Check3[3. 優化器設定正確?] Check3 --> Check4[4. 損失函數適合?] Path2 --> Opt1[1. 提高學習率] Opt1 --> Opt2[2. 增加模型容量] Opt2 --> Opt3[3. 檢查資料品質] Path3 --> Fix1[降低學習率<br/>0.001 → 0.0001] Check1 --> Fix2[正規化到 [0,1]<br/>或標準化] Check2 --> Fix3[Keras: One-Hot<br/>PyTorch: 類別索引] Check3 --> Fix4[檢查 zero_grad<br/>檢查 optimizer.step] Check4 --> Fix5[分類: CrossEntropy<br/>回歸: MSE] style Start fill:#FFB6C1 style Fix2 fill:#90EE90 style Fix3 fill:#90EE90 style Fix4 fill:#90EE90 style Fix5 fill:#90EE90 style Fix1 fill:#90EE90

診斷代碼

# ===== 診斷清單 =====
def diagnose_model(model, X_train, y_train, X_test, y_test):
    """診斷模型訓練問題"""

    print("=" * 70)
    print("模型診斷報告")
    print("=" * 70)

    # 1. 檢查資料範圍
    print(f"\n1. 資料範圍檢查:")
    print(f"   X_train: [{X_train.min():.4f}, {X_train.max():.4f}]")
    print(f"   ✓ 正常範圍: [0, 1] 或 [-1, 1]" if X_train.max() <= 1.5 else "   ✗ 可能需要正規化!")

    # 2. 檢查標籤格式
    print(f"\n2. 標籤格式檢查:")
    print(f"   y_train shape: {y_train.shape}")
    print(f"   y_train unique: {np.unique(y_train)}")

    # 3. 檢查類別平衡
    print(f"\n3. 類別平衡檢查:")
    unique, counts = np.unique(y_train, return_counts=True)
    max_count = counts.max()
    min_count = counts.min()
    imbalance_ratio = max_count / min_count
    print(f"   最多: {max_count}, 最少: {min_count}, 比例: {imbalance_ratio:.2f}")
    if imbalance_ratio > 3:
        print(f"   ⚠️ 類別不平衡!考慮使用加權損失")

    # 4. 過擬合檢測(需要訓練後)
    print(f"\n4. 過擬合檢測:")
    train_loss, train_acc = model.evaluate(X_train[:1000], y_train[:1000], verbose=0)
    test_loss, test_acc = model.evaluate(X_test, y_test, verbose=0)
    gap = train_acc - test_acc
    print(f"   Train Acc: {train_acc*100:.2f}%")
    print(f"   Test Acc:  {test_acc*100:.2f}%")
    print(f"   Gap: {gap*100:.2f}%")

    if gap > 0.15:
        print(f"   ⚠️ 過擬合!建議:")
        print(f"      - 資料增強")
        print(f"      - 增加 Dropout")
        print(f"      - L2 正規化")
    elif gap < 0.05 and test_acc < 0.85:
        print(f"   ⚠️ 模型容量不足!建議:")
        print(f"      - 增加層數")
        print(f"      - 增加濾波器數量")

    # 5. 檢查梯度
    print(f"\n5. 梯度檢查:")
    # 訓練一個 batch
    sample_batch = X_train[:32]
    sample_labels = y_train[:32]

    with tf.GradientTape() as tape:
        predictions = model(sample_batch)
        loss = tf.keras.losses.categorical_crossentropy(sample_labels, predictions)

    gradients = tape.gradient(loss, model.trainable_variables)

    # 檢查是否有 NaN 或過大/過小的梯度
    for i, grad in enumerate(gradients):
        if grad is not None:
            grad_mean = tf.reduce_mean(tf.abs(grad)).numpy()
            if np.isnan(grad_mean):
                print(f"   ✗ 層 {i}: 梯度為 NaN!")
            elif grad_mean < 1e-8:
                print(f"   ⚠️ 層 {i}: 梯度過小 ({grad_mean:.2e}) - 可能梯度消失")
            elif grad_mean > 1e2:
                print(f"   ⚠️ 層 {i}: 梯度過大 ({grad_mean:.2e}) - 可能梯度爆炸")

    print("\n" + "=" * 70)

# 使用範例
diagnose_model(model, X_train, y_train_cat, X_test, y_test_cat)

過擬合與欠擬合

完整對比

graph LR subgraph 欠擬合 U1[訓練準確率 低<br/>測試準確率 低] U2[模型太簡單] U3[學習不足] end subgraph 良好擬合 G1[訓練準確率 高<br/>測試準確率 高] G2[泛化能力強] end subgraph 過擬合 O1[訓練準確率 高<br/>測試準確率 低] O2[記住訓練資料] O3[泛化能力弱] end U1 --> Solution1[增加模型容量<br/>訓練更久<br/>更好的特徵] G1 --> Maintain[保持現狀<br/>或繼續優化] O1 --> Solution2[資料增強<br/>Dropout<br/>正規化<br/>早停] style U1 fill:#FFE4B5 style G1 fill:#90EE90 style O1 fill:#FFB6C1

視覺化診斷

# 視覺化過擬合/欠擬合
def plot_learning_curves(history):
    """視覺化學習曲線並診斷問題"""

    train_acc = history['accuracy']
    val_acc = history['val_accuracy']
    train_loss = history['loss']
    val_loss = history['val_loss']

    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))

    # 準確率
    ax1.plot(train_acc, 'b-', label='訓練', linewidth=2)
    ax1.plot(val_acc, 'r-', label='驗證', linewidth=2)
    ax1.set_title('準確率曲線', fontsize=14, fontweight='bold')
    ax1.set_xlabel('Epoch')
    ax1.set_ylabel('準確率')
    ax1.legend()
    ax1.grid(True, alpha=0.3)

    # 損失
    ax2.plot(train_loss, 'b-', label='訓練', linewidth=2)
    ax2.plot(val_loss, 'r-', label='驗證', linewidth=2)
    ax2.set_title('損失曲線', fontsize=14, fontweight='bold')
    ax2.set_xlabel('Epoch')
    ax2.set_ylabel('損失')
    ax2.legend()
    ax2.grid(True, alpha=0.3)

    # 診斷
    final_train_acc = train_acc[-1]
    final_val_acc = val_acc[-1]
    gap = final_train_acc - final_val_acc

    print("\n診斷結果:")
    print("=" * 50)

    if final_val_acc < 0.7:
        if gap < 0.05:
            print("❌ 欠擬合 (Underfitting)")
            print("   訓練和驗證準確率都很低,但差距小")
            print("\n建議:")
            print("   1. 增加模型容量(更多層/濾波器)")
            print("   2. 訓練更多 epochs")
            print("   3. 降低正規化強度")
            print("   4. 檢查學習率是否過小")
        else:
            print("⚠️ 欠擬合 + 輕微過擬合")
            print("   驗證準確率低,但訓練準確率更高")
            print("\n建議:")
            print("   1. 先解決欠擬合:增加模型容量")
            print("   2. 再處理過擬合:加資料增強")
    elif gap > 0.15:
        print("❌ 過擬合 (Overfitting)")
        print(f"   訓練準確率 {final_train_acc*100:.1f}%,驗證準確率 {final_val_acc*100:.1f}%")
        print(f"   差距 {gap*100:.1f}%")
        print("\n建議:")
        print("   1. 資料增強(最有效)")
        print("   2. 增加 Dropout (0.3-0.5)")
        print("   3. L2 正規化 (weight_decay=1e-4)")
        print("   4. 早停 (EarlyStopping)")
        print("   5. 減少模型容量")
    else:
        print("✅ 良好擬合 (Good Fit)")
        print(f"   訓練準確率 {final_train_acc*100:.1f}%,驗證準確率 {final_val_acc*100:.1f}%")
        print(f"   差距 {gap*100:.1f}%(健康範圍)")

    # 檢查驗證損失是否上升
    if len(val_loss) > 10:
        min_val_loss_epoch = np.argmin(val_loss)
        if min_val_loss_epoch < len(val_loss) - 5:
            print(f"\n⚠️ 驗證損失在 epoch {min_val_loss_epoch} 達到最低")
            print(f"   之後開始上升,建議在該點停止訓練")

    plt.tight_layout()
    plt.show()

# 使用範例
plot_learning_curves(history.history)

超參數調整指南

超參數重要性排序

graph LR P1[學習率<br/>⭐⭐⭐⭐⭐] --> Impact1[影響最大<br/>優先調整] P2[Batch Size<br/>⭐⭐⭐⭐☆] --> Impact2[影響訓練速度<br/>與穩定性] P3[優化器<br/>⭐⭐⭐☆☆] --> Impact3[Adam 通常足夠] P4[網路架構<br/>⭐⭐⭐⭐☆] --> Impact4[層數、濾波器數] P5[Dropout 率<br/>⭐⭐⭐☆☆] --> Impact5[防過擬合] P6[L2 正規化<br/>⭐⭐☆☆☆] --> Impact6[輔助防過擬合] style P1 fill:#FFD700 style P2 fill:#FFD700 style P4 fill:#FFD700 style P3 fill:#FFE4B5 style P5 fill:#FFE4B5 style P6 fill:#E0E0E0

學習率調整策略

# ===== 策略 1: 學習率範圍測試 (Learning Rate Finder) =====
def find_learning_rate(model, train_loader, criterion, device):
    """找出最佳學習率範圍"""
    learning_rates = []
    losses = []

    # 測試從 1e-6 到 1 的學習率
    lr = 1e-6
    for batch_idx, (data, target) in enumerate(train_loader):
        if batch_idx > 100:  # 只測試 100 個 batch
            break

        data, target = data.to(device), target.to(device)

        # 更新學習率
        optimizer = optim.SGD(model.parameters(), lr=lr)

        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()

        # 記錄
        learning_rates.append(lr)
        losses.append(loss.item())

        # 指數增長學習率
        lr *= 1.1

    # 視覺化
    plt.figure(figsize=(10, 6))
    plt.plot(learning_rates, losses)
    plt.xscale('log')
    plt.xlabel('Learning Rate')
    plt.ylabel('Loss')
    plt.title('Learning Rate Finder')
    plt.grid(True)
    plt.show()

    # 建議:選擇損失下降最快的點
    print("建議學習率:")
    min_loss_idx = np.argmin(losses)
    print(f"  最低損失點: {learning_rates[min_loss_idx]:.6f}")
    print(f"  建議值(除以10): {learning_rates[min_loss_idx]/10:.6f}")

# ===== 策略 2: 學習率調度 =====
# 方案 A: Step Decay(階梯式衰減)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
# 每 30 個 epoch,學習率乘以 0.1

# 方案 B: Exponential Decay(指數衰減)
scheduler = optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.95)
# 每個 epoch,學習率乘以 0.95

# 方案 C: Cosine Annealing(餘弦退火)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100, eta_min=1e-6)
# 餘弦曲線,從 lr_max 降到 eta_min

# 方案 D: ReduceLROnPlateau(自適應)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(
    optimizer, mode='min', factor=0.5, patience=5, verbose=True
)
# 驗證損失 5 個 epoch 沒改善,學習率減半

# ===== 策略 3: Warm-up + Cosine Annealing =====
def lr_lambda(epoch):
    if epoch < 10:  # Warm-up
        return (epoch + 1) / 10
    else:  # Cosine Annealing
        progress = (epoch - 10) / (100 - 10)
        return 0.5 * (1 + np.cos(np.pi * progress))

scheduler = optim.lr_scheduler.LambdaLR(optimizer, lr_lambda)

學習率調度對比

策略 曲線 適用場景 效果
Fixed 平坦 快速實驗 ★★☆☆☆
Step Decay 階梯 傳統訓練 ★★★☆☆
Exponential 指數下降 長時間訓練 ★★★☆☆
Cosine Annealing 餘弦曲線 現代推薦 ★★★★☆
ReduceLROnPlateau 自適應 不確定時 ★★★★★
Warm-up + Cosine 先升後降 大型模型 ★★★★★

Batch Size 的影響

# Batch Size 對比實驗
batch_sizes = [32, 64, 128, 256, 512]

results = {}
for bs in batch_sizes:
    model = create_model()
    optimizer = Adam(lr=0.001)

    # 訓練 10 epochs
    # ...

    results[bs] = {
        'train_time': ...,
        'final_acc': ...,
        'memory_usage': ...
    }

# 結果範例(CIFAR-10)
"""
Batch Size | 訓練時間 | 準確率 | GPU 記憶體
-----------|---------|-------|----------
32         | 25 min  | 86.2% | 2.5 GB
64         | 15 min  | 86.5% | 3.8 GB
128        | 10 min  | 86.8% | 6.2 GB  ← 推薦
256        | 8 min   | 86.3% | 10.5 GB
512        | 7 min   | 85.1% | OOM     ← 過大導致泛化變差
"""

Batch Size 選擇建議

graph TD Start{GPU 記憶體} Start -->|16GB+| Large[Batch Size 256-512<br/>需調高學習率] Start -->|8-16GB| Medium[Batch Size 128-256<br/>最佳選擇] Start -->|4-8GB| Small[Batch Size 64-128<br/>平衡效能] Start -->|<4GB| Tiny[Batch Size 32-64<br/>或用梯度累積] Large --> Note1[學習率 × 2-4] Medium --> Note2[學習率保持] Small --> Note2 Tiny --> Note3[考慮梯度累積] style Medium fill:#90EE90 style Small fill:#FFE4B5 style Large fill:#FFE4B5 style Tiny fill:#FFB6C1

效能優化技巧

完整優化清單

# ============================================
# 完整優化範例(PyTorch)
# 包含所有效能提升技巧
# ============================================

import torch
import torch.nn as nn
from torch.cuda.amp import autocast, GradScaler
from torch.utils.data import DataLoader

# ===== 1. 使用 Mixed Precision 訓練 =====
scaler = GradScaler()

# ===== 2. 優化 DataLoader =====
train_loader = DataLoader(
    train_dataset,
    batch_size=256,           # 較大 batch size
    shuffle=True,
    num_workers=4,            # 多執行緒
    pin_memory=True,          # 加速 CPU→GPU
    persistent_workers=True,  # 保持 workers 活著
    prefetch_factor=2         # 預載入
)

# ===== 3. 優化模型 =====
model = create_model()
model = model.to(device)

# 使用 torch.compile(PyTorch 2.0+)
model = torch.compile(model)  # 自動優化計算圖

# ===== 4. 優化優化器 =====
optimizer = torch.optim.AdamW(
    model.parameters(),
    lr=0.001,
    betas=(0.9, 0.999),
    weight_decay=0.01,
    foreach=True  # 向量化操作(PyTorch 1.12+)
)

# ===== 5. 訓練迴圈(完整優化版) =====
model.train()

for epoch in range(num_epochs):
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device, non_blocking=True), target.to(device, non_blocking=True)

        # Mixed Precision
        with autocast():
            output = model(data)
            loss = criterion(output, target)

        # 反向傳播(Mixed Precision)
        scaler.scale(loss).backward()
        scaler.step(optimizer)
        scaler.update()
        optimizer.zero_grad(set_to_none=True)  # 更快的梯度清空

# ===== 效能對比 =====
"""
優化前: 20 分鐘/epoch
優化後: 5 分鐘/epoch  ← 快 4 倍!

優化項目           | 提升
-------------------|------
Mixed Precision    | 2.5×
DataLoader 優化    | 1.3×
torch.compile      | 1.2×
綜合效果           | 4×
"""

進階主題導覽

當你完成本指南後,可以探索這些進階主題:

1. 遷移學習 (Transfer Learning)

# 使用預訓練模型(以 ResNet 為例)
from torchvision import models

# 載入預訓練模型
resnet = models.resnet18(pretrained=True)

# 凍結預訓練層
for param in resnet.parameters():
    param.requires_grad = False

# 替換最後一層
resnet.fc = nn.Linear(512, 10)  # CIFAR-10 有 10 類

# 只訓練最後一層
optimizer = optim.Adam(resnet.fc.parameters(), lr=0.001)

# 效果:
# - 訓練時間: 從 20 分鐘減少到 5 分鐘
# - 準確率: 從 87% 提升到 94%

進階閱讀
- ImageNet 預訓練模型
- Fine-tuning 策略
- Domain Adaptation

2. 進階 CNN 架構

graph LR Basic[基礎 CNN] --> Adv1[ResNet<br/>殘差連接] Basic --> Adv2[DenseNet<br/>密集連接] Basic --> Adv3[Inception<br/>多尺度特徵] Basic --> Adv4[EfficientNet<br/>複合縮放] Basic --> Adv5[Vision Transformer<br/>注意力機制] Adv1 --> Papers1[He et al., 2015] Adv2 --> Papers2[Huang et al., 2017] Adv3 --> Papers3[Szegedy et al., 2015] Adv4 --> Papers4[Tan & Le, 2019] Adv5 --> Papers5[Dosovitskiy et al., 2020] style Basic fill:#FFE4B5 style Adv1 fill:#90EE90 style Adv2 fill:#90EE90 style Adv3 fill:#90EE90 style Adv4 fill:#90EE90 style Adv5 fill:#87CEEB

3. 進階正規化技術

技術 原理 效果 適用性
Mixup 混合兩張影像和標籤 +2-3% 通用
Cutout 隨機遮擋區域 +1-2% 影像
CutMix 剪貼混合 +2-3% 影像
Label Smoothing 軟化標籤 +0.5-1% 分類
DropBlock 結構化 Dropout +1-2% CNN
# Mixup 範例
def mixup_data(x, y, alpha=0.2):
    lam = np.random.beta(alpha, alpha)
    index = torch.randperm(x.size(0))

    mixed_x = lam * x + (1 - lam) * x[index]
    y_a, y_b = y, y[index]

    return mixed_x, y_a, y_b, lam

# 訓練時使用
mixed_x, y_a, y_b, lam = mixup_data(x, y)
output = model(mixed_x)
loss = lam * criterion(output, y_a) + (1 - lam) * criterion(output, y_b)

4. 模型壓縮與加速

graph TD FullModel[完整模型<br/>準確率 87%<br/>100 MB<br/>50 ms] --> Compress{壓縮技術} Compress --> Pruning[剪枝<br/>Pruning] Compress --> Quantization[量化<br/>Quantization] Compress --> Distillation[知識蒸餾<br/>Distillation] Pruning --> Result1[準確率 86%<br/>30 MB<br/>20 ms] Quantization --> Result2[準確率 86.5%<br/>25 MB<br/>15 ms] Distillation --> Result3[準確率 85%<br/>10 MB<br/>10 ms] style FullModel fill:#FFE4B5 style Result1 fill:#90EE90 style Result2 fill:#90EE90 style Result3 fill:#90EE90

5. 實際應用領域

領域 應用 推薦資料集 難度
醫療影像 X光診斷、腫瘤偵測 ChestX-ray14, ISIC ⭐⭐⭐⭐☆
自動駕駛 物體偵測、車道線 KITTI, Cityscapes ⭐⭐⭐⭐⭐
人臉識別 身份驗證、表情辨識 LFW, CelebA ⭐⭐⭐☆☆
農業 病蟲害偵測、作物分類 PlantVillage ⭐⭐⭐☆☆
工業檢測 瑕疵偵測、品質控制 MVTec AD ⭐⭐⭐⭐☆
零售 商品識別、人流分析 ImageNet, OpenImages ⭐⭐⭐☆☆

完整學習路線圖

graph TB Start([開始 CNN 之旅]) --> Phase1[階段 1: 基礎<br/>1-2 週] Phase1 --> Task1[✓ MNIST + LeNet-5<br/>98%+ 準確率] Task1 --> Task2[✓ 理解卷積、池化<br/>手算特徵圖尺寸] Task2 --> Task3[✓ Keras 實作<br/>熟悉 Sequential API] Task3 --> Phase2[階段 2: 進階<br/>2-3 週] Phase2 --> Task4[✓ MNIST + PyTorch<br/>99%+ 準確率] Task4 --> Task5[✓ 手寫訓練迴圈<br/>理解 backward] Task5 --> Task6[✓ CIFAR-10 挑戰<br/>85%+ 準確率] Task6 --> Phase3[階段 3: 深化<br/>3-4 週] Phase3 --> Task7[✓ 資料增強技巧<br/>準確率 +5%] Task7 --> Task8[✓ Batch Norm, Dropout<br/>理解正規化] Task8 --> Task9[✓ 超參數調整<br/>學習率調度] Task9 --> Phase4[階段 4: 實戰<br/>4-8 週] Phase4 --> Task10[ResNet 實作<br/>殘差連接] Task10 --> Task11[遷移學習<br/>ImageNet 預訓練] Task11 --> Task12[實際專案<br/>自己的資料集] Task12 --> Master([CNN 專家]) style Start fill:#90EE90 style Phase1 fill:#FFE4B5 style Phase2 fill:#FFE4B5 style Phase3 fill:#FFE4B5 style Phase4 fill:#FFD700 style Master fill:#FFD700

學習時程建議

全職學習(每週 40 小時)
- 第 1-2 週:完成 MNIST,理解基礎概念
- 第 3-4 週:PyTorch 實作,CIFAR-10 入門
- 第 5-6 週:進階技術,超參數調整
- 第 7-8 週:ResNet、遷移學習
- 第 9-12 週:實際專案,找工作

兼職學習(每週 10 小時)
- 第 1-4 週:MNIST 基礎
- 第 5-8 週:PyTorch + CIFAR-10
- 第 9-12 週:進階技術
- 第 13-20 週:ResNet + 專案


CNN 理論與概念完整總結

本章節系統化地總結所有 CNN 的核心理論,幫助你建立完整的知識體系。

1. 卷積層的數學原理

1.1 卷積運算

定義:卷積是一種線性運算,透過滑動濾波器(filter/kernel)在輸入上進行元素乘法和加總。

數學表達式

對於 2D 卷積:

Y[i,j] = Σₘ Σₙ X[i+m, j+n] × K[m,n] + b

其中:
- Y: 輸出特徵圖(Feature Map)
- X: 輸入影像
- K: 卷積核(Kernel)
- b: 偏差項(Bias)
- m,n: 卷積核的索引

實例計算

# 輸入影像 (5×5)
X = [[1, 2, 3, 4, 5],
     [6, 7, 8, 9, 10],
     [11, 12, 13, 14, 15],
     [16, 17, 18, 19, 20],
     [21, 22, 23, 24, 25]]

# 卷積核 (3×3) - 邊緣偵測
K = [[-1, -1, -1],
     [-1,  8, -1],
     [-1, -1, -1]]

# 計算輸出的一個元素 Y[1,1](無 padding):
Y[1,1] = 1×(-1) + 2×(-1) + 3×(-1) +
         6×(-1) + 7×(8) + 8×(-1) +
         11×(-1) + 12×(-1) + 13×(-1)
       = -1 -2 -3 -6 +56 -8 -11 -12 -13
       = 0

1.2 輸出尺寸計算

完整公式

輸出高度 = floor((H + 2P - K) / S) + 1
輸出寬度 = floor((W + 2P - K) / S) + 1

其中:
- H, W: 輸入高度和寬度
- K: 卷積核大小
- P: Padding(填充)
- S: Stride(步幅)
- floor: 向下取整

範例

輸入 卷積核 Padding Stride 輸出 計算
32×32 3×3 0 1 30×30 (32-3)/1+1 = 30
32×32 3×3 1 1 32×32 (32+2-3)/1+1 = 32
32×32 5×5 2 1 32×32 (32+4-5)/1+1 = 32
32×32 3×3 0 2 15×15 (32-3)/2+1 = 15

1.3 參數共享與局部連接

參數數量對比

# 全連接層(假設輸入 32×32,輸出 64 個神經元)
參數量 = 32 × 32 × 64 + 64(bias) = 65,600

# 卷積層(64 個 3×3 濾波器,輸入通道 1)
參數量 = 3 × 3 × 1 × 64 + 64(bias) = 640

節省 = 65,600 / 640 = 102 

為什麼卷積有效?

  1. 參數共享:同一個濾波器掃描整張影像
  2. 局部連接:每個神經元只連接局部區域
  3. 平移不變性:偵測特徵不受位置影響
  4. 層次化特徵:淺層→邊緣,深層→複雜物體

2. 池化層的原理

2.1 最大池化(Max Pooling)

運作機制

# 輸入 (4×4)
X = [[1,  3,  2,  4],
     [5,  6,  7,  8],
     [3,  2,  1,  0],
     [1,  2,  3,  4]]

# Max Pooling 2×2, stride=2
# 將 4×4 分成 4 個 2×2 區域,取最大值

Y = [[max(1,3,5,6)=6,  max(2,4,7,8)=8],
     [max(3,2,1,2)=3,  max(1,0,3,4)=4]]

Y = [[6, 8],
     [3, 4]]

為什麼使用池化?

優勢 說明 數值範例
降維 減少計算量 4×4→2×2,減少 75%
平移不變性 物體小幅移動不影響輸出 特徵移動 1 像素,池化輸出不變
抗雜訊 保留最顯著特徵 雜訊通常不是最大值
擴大感受野 每層看到更大範圍 2 層池化 → 感受野 ×4

2.2 平均池化(Average Pooling)

# 輸入相同
# Average Pooling 2×2

Y = [[avg(1,3,5,6)=3.75,  avg(2,4,7,8)=5.25],
     [avg(3,2,1,2)=2.00,  avg(1,0,3,4)=2.00]]

Max Pooling vs Average Pooling

特性 Max Pooling Average Pooling
用途 特徵偵測(主流) 背景資訊、全局特徵
保留資訊 最顯著特徵 平均資訊
現代使用 卷積層之間 全局平均池化(GAP)
範例 ResNet, VGG GoogLeNet 最後一層

3. 激活函數的深度分析

3.1 各種激活函數對比

graph TB subgraph 激活函數特性對比 A[ReLU<br/>f(x)=max(0,x)] --> A1[優點: 計算快<br/>避免梯度消失] A --> A2[缺點: Dead ReLU<br/>負值神經元死亡] B[Leaky ReLU<br/>f(x)=max(0.01x,x)] --> B1[優點: 解決 Dead ReLU] B --> B2[缺點: 需調整斜率] C[Sigmoid<br/>f(x)=1/(1+e⁻ˣ)] --> C1[優點: 平滑、有界] C --> C2[缺點: 梯度消失<br/>計算成本高] D[Tanh<br/>f(x)=tanh(x)] --> D1[優點: 零中心化<br/>比 Sigmoid 好] D --> D2[缺點: 仍有梯度消失] E[Softmax<br/>f(xᵢ)=eˣⁱ/Σeˣʲ] --> E1[優點: 輸出機率分布] E --> E2[用途: 多分類輸出層] end style A fill:#90EE90 style B fill:#90EE90 style E fill:#FFD700 style C fill:#FFE4B5 style D fill:#FFE4B5

3.2 激活函數的數學性質

激活函數 數學表達式 導數 值域 優缺點
ReLU f(x) = max(0, x) f'(x) = 1 if x>0 else 0 [0, ∞) 快速、避免梯度消失
❌ Dead ReLU
Leaky ReLU f(x) = max(0.01x, x) f'(x) = 1 if x>0 else 0.01 (-∞, ∞) 解決 Dead ReLU
❌ 需調參
Sigmoid f(x) = 1/(1+e⁻ˣ) f'(x) = f(x)(1-f(x)) (0, 1) 有界、平滑
❌ 梯度消失
Tanh f(x) = (eˣ-e⁻ˣ)/(eˣ+e⁻ˣ) f'(x) = 1 - f(x)² (-1, 1) 零中心化
❌ 梯度消失

3.3 Dead ReLU 問題

什麼是 Dead ReLU?

# 神經元的輸入始終 ≤ 0
x = -5  # 負輸入
output = max(0, x) = 0  # 輸出永遠是 0
gradient = 0  # 梯度永遠是 0

# 結果:神經元永遠無法更新(「死亡」)

原因
1. 不當的權重初始化
2. 學習率過大
3. 輸入資料未正規化

解決方案
1. 使用 Leaky ReLUPReLU
2. 正確的權重初始化(He initialization)
3. Batch Normalization
4. 適當的學習率

4. 正規化技術的理論

4.1 Batch Normalization 的數學原理

算法步驟

對於 mini-batch B = {x₁, x₂, ..., xₘ}:

1. 計算 mini-batch 均值:
   μ_B = (1/m) Σ xᵢ

2. 計算 mini-batch 方差:
   σ²_B = (1/m) Σ (xᵢ - μ_B)²

3. 正規化:
   x̂ = (xᵢ - μ_B) / (σ²_B + ε)

4. 縮放與平移可學習參數 γ, β:
   yᵢ = γ × x̂ + β

為什麼有效?

graph LR A[輸入分布不穩定] --> B[內部協變量偏移<br/>Internal Covariate Shift] B --> C[每層輸入分布變化] C --> D[難以訓練] E[Batch Normalization] --> F[穩定輸入分布] F --> G[加速收斂] F --> H[允許更大學習率] F --> I[正規化效果] style A fill:#FFB6C1 style E fill:#90EE90 style G fill:#FFD700 style H fill:#FFD700 style I fill:#FFD700

4.2 Dropout 的理論基礎

運作機制

# 訓練時(Dropout rate = 0.5)
# 隨機丟棄 50% 的神經元
input = [1.0, 2.0, 3.0, 4.0]
mask = [1, 0, 1, 0]  # 隨機生成
output = [1.0, 0, 3.0, 0]  # element-wise 乘法

# 測試時(不使用 Dropout)
output = [1.0, 2.0, 3.0, 4.0] × 0.5  # 縮放補償

為什麼有效?

  1. 集成學習效果
  2. 訓練了 2ⁿ 個子網路(n = 神經元數)
  3. 測試時相當於模型平均

  4. 減少神經元依賴

  5. 強迫網路學習冗餘表示
  6. 防止過度依賴特定神經元

  7. 理論證明(Srivastava et al., 2014):

  8. 等價於 L2 正規化的近似
  9. 降低模型複雜度

Dropout 率選擇

層類型 推薦 Dropout 率 原因
卷積層 0.1-0.3 參數共享已提供正規化
全連接層 0.5-0.7 參數多,容易過擬合
輸出層 0 不應隨機丟棄最終預測
小資料集 0.5-0.7 需要更強正規化
大資料集 0.2-0.3 資料已提供正規化

5. 損失函數與優化器

5.1 交叉熵損失(Cross-Entropy Loss)

數學定義

對於多分類問題:

L = -Σᵢ yᵢ × log(ŷᵢ)

其中:
- yᵢ: 真實標籤(One-Hot)
- ŷᵢ: 預測機率(Softmax 輸出)

實例計算

# 真實標籤(類別 2)
y_true = [0, 0, 1, 0, 0]  # One-Hot

# 模型預測(經過 Softmax)
y_pred = [0.05, 0.10, 0.70, 0.10, 0.05]

# 交叉熵損失
Loss = -(0×log(0.05) + 0×log(0.10) + 1×log(0.70) + 0×log(0.10) + 0×log(0.05))
     = -log(0.70)
     = 0.357

# 如果預測很準確
y_pred_good = [0.01, 0.01, 0.96, 0.01, 0.01]
Loss = -log(0.96) = 0.041   損失低

# 如果預測錯誤
y_pred_bad = [0.01, 0.01, 0.02, 0.01, 0.95]
Loss = -log(0.02) = 3.912   損失高

為什麼使用交叉熵?

對比 均方誤差 (MSE) 交叉熵
數學形式 Σ(y - ŷ)² -Σ y log(ŷ)
梯度特性 飽和時梯度小 錯誤越大梯度越大 ✅
適用場景 回歸問題 分類問題 ✅
收斂速度 快 ✅

5.2 優化器的數學原理

梯度下降(Gradient Descent)

θₜ₊₁ = θₜ - η × ∇L(θₜ)

其中:
- θ: 參數
- η: 學習率
- ∇L: 損失函數的梯度

動量(Momentum)

vₜ = β × vₜ₋₁ + ∇L(θₜ)
θₜ₊₁ = θₜ - η × vₜ

效果: 累積過去的梯度,加速收斂

Adam (Adaptive Moment Estimation)

# 一階動量(梯度的指數移動平均)
mₜ = β₁ × mₜ₋₁ + (1-β₁) × ∇L(θₜ)

# 二階動量(梯度平方的指數移動平均)
vₜ = β₂ × vₜ₋₁ + (1-β₂) × (∇L(θₜ))²

# 偏差修正
m̂ₜ = mₜ / (1-β₁ᵗ)
v̂ₜ = vₜ / (1-β₂ᵗ)

# 參數更新
θₜ₊₁ = θₜ - η × m̂ₜ / (√v̂ₜ + ε)

其中: β₁=0.9, β₂=0.999, ε=10⁻⁸

優化器對比

優化器 優點 缺點 推薦場景
SGD 簡單、理論保證 收斂慢、需調參 經典論文複現
SGD + Momentum 加速、減少震盪 仍需調參 大型資料集
Adam 自適應、穩健 泛化能力稍差 大多數情況 ✅
AdamW Adam + 正確的 L2 略複雜 現代推薦 ✅

6. 反向傳播的數學原理

6.1 鏈式法則(Chain Rule)

反向傳播的核心是鏈式法則

對於複合函數 f(g(x)),導數為:
df/dx = (df/dg) × (dg/dx)

CNN 中的應用

損失函數 L 對參數 W 的梯度:

L = Loss(Softmax(Dense(Flatten(Pool(Conv(X, W))))))

∂L/∂W = (∂L/∂Softmax) × (∂Softmax/∂Dense) × ... × (∂Conv/∂W)

6.2 卷積層的反向傳播

前向傳播

Y = X ∗ K + b  (∗ 表示卷積)

反向傳播

1. 損失對輸出的梯度從上一層傳來:
   L/Y

2. 損失對卷積核的梯度:
   L/K = X  (L/Y)

3. 損失對輸入的梯度傳給下一層:
   L/X = (L/Y)  K_rotated

其中 K_rotated  K 旋轉 180 

實例計算

# 前向傳播
X = [[1, 2],    K = [[1, 0],    Y = [[7, 10],
     [3, 4]]         [0, 1]]         [10, 11]]

# 反向傳播(假設 ∂L/∂Y = [[1, 1], [1, 1]])
L/K = X  (L/Y)
      = [[1×1+2×1+3×1+4×1, ...],  # 簡化
         [..., ...]]
      = [[10, 10],
         [10, 10]]

# 卷積核更新
K_new = K - learning_rate × L/K

7. 超參數的系統性影響

7.1 學習率的影響

graph TD Start[模型不收斂] --> Q1{損失是否下降?} Q1 -->|完全不降| Path1[檢查基礎設定] Q1 -->|下降但停滯| Path2[優化超參數] Q1 -->|劇烈震盪| Path3[學習率過大] Path1 --> Check1[1. 資料是否正規化?] Check1 --> Check2[2. 標籤格式正確?] Check2 --> Check3[3. 優化器設定正確?] Check3 --> Check4[4. 損失函數適合?] Path2 --> Opt1[1. 提高學習率] Opt1 --> Opt2[2. 增加模型容量] Opt2 --> Opt3[3. 檢查資料品質] Path3 --> Fix1[降低學習率<br/>0.001 → 0.0001] Check1 --> Fix2[正規化到 [0,1]<br/>或標準化] Check2 --> Fix3[Keras: One-Hot<br/>PyTorch: 類別索引] Check3 --> Fix4[檢查 zero_grad<br/>檢查 optimizer.step] Check4 --> Fix5[分類: CrossEntropy<br/>回歸: MSE] style Start fill:#FFB6C1 style Fix2 fill:#90EE90 style Fix3 fill:#90EE90 style Fix4 fill:#90EE90 style Fix5 fill:#90EE90 style Fix1 fill:#90EE90
0

數學分析

梯度下降: θₜ₊₁ = θₜ - η × L(θₜ)

η 過大:
- 步長太大,跨過最優點
- 損失函數值劇烈震盪
- Loss: 0.5  1.2  0.3  2.1 (不穩定)

η 適中:
- 穩步向最優點前進
- Loss: 0.5  0.4  0.3  0.2  0.15 (穩定下降)

η 過小:
- 步長太小,移動緩慢
- Loss: 0.5  0.499  0.498 (幾乎不動)

7.2 Batch Size 的理論影響

數學視角

梯度估計的方差:
Var(L)  1 / batch_size

Batch Size   Var   梯度估計更準確
Batch Size   Var   更多隨機性(有時是好事)

實際影響

Batch Size 梯度準確性 泛化能力 訓練速度 記憶體 推薦
32 很好 小資料集
64 平衡選擇
128 中高 推薦 ✅
256 很高 很快 大資料集
512+ 極高 極快 很高 需特殊調整

大 Batch Size 的調整

根據線性縮放規則(Linear Scaling Rule, Goyal et al., 2017):

如果 Batch Size 增加 k 倍
則 Learning Rate 也應增加 k 倍

範例:
Batch Size 128, LR = 0.001
Batch Size 256, LR = 0.002  ← 2 倍
Batch Size 512, LR = 0.004  ← 4 倍

8. 過擬合與欠擬合的理論本質

8.1 偏差-方差權衡(Bias-Variance Tradeoff)

數學定義

預期誤差 = Bias² + Variance + 不可約誤差

其中:
- Bias (偏差): 模型的平均預測與真實值的差距
- Variance (方差): 模型對訓練資料的敏感程度
- 不可約誤差: 資料本身的雜訊
graph TD Start[模型不收斂] --> Q1{損失是否下降?} Q1 -->|完全不降| Path1[檢查基礎設定] Q1 -->|下降但停滯| Path2[優化超參數] Q1 -->|劇烈震盪| Path3[學習率過大] Path1 --> Check1[1. 資料是否正規化?] Check1 --> Check2[2. 標籤格式正確?] Check2 --> Check3[3. 優化器設定正確?] Check3 --> Check4[4. 損失函數適合?] Path2 --> Opt1[1. 提高學習率] Opt1 --> Opt2[2. 增加模型容量] Opt2 --> Opt3[3. 檢查資料品質] Path3 --> Fix1[降低學習率<br/>0.001 → 0.0001] Check1 --> Fix2[正規化到 [0,1]<br/>或標準化] Check2 --> Fix3[Keras: One-Hot<br/>PyTorch: 類別索引] Check3 --> Fix4[檢查 zero_grad<br/>檢查 optimizer.step] Check4 --> Fix5[分類: CrossEntropy<br/>回歸: MSE] style Start fill:#FFB6C1 style Fix2 fill:#90EE90 style Fix3 fill:#90EE90 style Fix4 fill:#90EE90 style Fix5 fill:#90EE90 style Fix1 fill:#90EE90
1

8.2 模型容量理論

VC 維(Vapnik-Chervonenkis Dimension)

模型能夠擬合的最大樣本數量

線性模型(2D): VC 維 = 3
  - 可以完美分類任意 3 個點
  - 無法保證分類 4 個點

深度神經網路: VC 維 ≈ O(W × log W)
  - W: 參數總數
  - 容量極大,容易過擬合

正規化的理論作用

優化目標(無正規化):
min L(θ) = Σᵢ Loss(f(xᵢ; θ), yᵢ)

優化目標(有正規化):
min L(θ) + λ × R(θ)

其中:
- R(θ): 正規化項(如 ||θ||²)
- λ: 正規化強度

效果: 限制模型容量,防止過擬合

9. 深度學習的理論基礎

9.1 萬有近似定理(Universal Approximation Theorem)

定理內容

具有單個隱藏層的神經網路,只要有足夠多的神經元,就可以以任意精度近似任何連續函數。

數學表達

對於任意連續函數 f: [0,1]ⁿ → ℝ
存在神經網路 N(x),使得:
|N(x) - f(x)| < ε  (對所有 x)

其中 ε 是任意小的正數

實際意義

理論上:單層網路可以表示任何函數
實際上
- 需要指數級數量的神經元
- 訓練極其困難
- 深層網路更高效

9.2 為什麼深度重要?

表示能力的指數增長

淺層網路(1 層隱藏層):
表示能力 ∝ O(n)  (n: 神經元數)

深層網路(L 層):
表示能力 ∝ O(nᴸ)  (指數增長!)

層次化特徵學習

輸入影像
    ↓
第 1 層: 邊緣、角點
    ↓
第 2 層: 簡單形狀(圓形、方形)
    ↓
第 3 層: 物體部件(眼睛、鼻子、輪胎)
    ↓
第 4 層: 完整物體(臉、汽車)
    ↓
輸出: 分類結果

10. CNN 的數學性質總結

10.1 平移不變性(Translation Invariance)

定義:物體在影像中的位置改變,CNN 的輸出不變(或變化很小)

實現機制
1. 參數共享:同一個濾波器掃描整張影像
2. 池化:降低空間解析度
3. 大感受野:深層神經元看到大範圍

數學表達

對於平移 τ:
f(T_τ(x)) ≈ f(x)

其中 T_τ 是平移操作

10.2 局部連接與稀疏交互

傳統神經網路

每個神經元與所有輸入連接
參數量 = 輸入數 × 輸出數

CNN

每個神經元只與局部感受野連接
參數量 = 卷積核大小 × 濾波器數

節省參數 = 1 - (K²/H×W) ≈ 99%
(K=3, H=W=32 的情況)

10.3 感受野的遞增

計算公式

第 l 層的感受野:
RF_l = RF_{l-1} + (K_l - 1) × Π_{i=1}^{l-1} S_i

其中:
- K_l: 第 l 層的卷積核大小
- S_i: 第 i 層的步幅

實例

輸入: 32×32
Conv1 (3×3, s=1)  RF = 3
Pool1 (2×2, s=2)  RF = 6
Conv2 (3×3, s=1)  RF = 10
Pool2 (2×2, s=2)  RF = 20
Conv3 (3×3, s=1)  RF = 28

最終感受野幾乎覆蓋整張影像!

理論總結的關鍵要點

graph TD Start[模型不收斂] --> Q1{損失是否下降?} Q1 -->|完全不降| Path1[檢查基礎設定] Q1 -->|下降但停滯| Path2[優化超參數] Q1 -->|劇烈震盪| Path3[學習率過大] Path1 --> Check1[1. 資料是否正規化?] Check1 --> Check2[2. 標籤格式正確?] Check2 --> Check3[3. 優化器設定正確?] Check3 --> Check4[4. 損失函數適合?] Path2 --> Opt1[1. 提高學習率] Opt1 --> Opt2[2. 增加模型容量] Opt2 --> Opt3[3. 檢查資料品質] Path3 --> Fix1[降低學習率<br/>0.001 → 0.0001] Check1 --> Fix2[正規化到 [0,1]<br/>或標準化] Check2 --> Fix3[Keras: One-Hot<br/>PyTorch: 類別索引] Check3 --> Fix4[檢查 zero_grad<br/>檢查 optimizer.step] Check4 --> Fix5[分類: CrossEntropy<br/>回歸: MSE] style Start fill:#FFB6C1 style Fix2 fill:#90EE90 style Fix3 fill:#90EE90 style Fix4 fill:#90EE90 style Fix5 fill:#90EE90 style Fix1 fill:#90EE90
2

核心公式速查表

概念 公式 應用
卷積輸出 H_out = (H + 2P - K)/S + 1 計算特徵圖尺寸
參數數量 params = K×K×C_in×C_out + C_out 估算模型大小
感受野 RF_l = RF_{l-1} + (K_l-1)×∏S_i 設計網路深度
交叉熵 L = -Σ y_i log(ŷ_i) 分類損失
Adam 更新 θ = θ - η×m̂/√v̂ 參數優化
Batch Norm x̂ = (x-μ)/√(σ²+ε) 穩定訓練

總結與下一步

你已經學會了什麼

基礎概念
- 卷積、池化、全連接層的原理
- 前向傳播與反向傳播
- 損失函數與優化器

兩大框架
- Keras:快速原型開發
- PyTorch:深入理解原理

兩個經典任務
- MNIST:灰階影像,98%+ 準確率
- CIFAR-10:彩色影像,85%+ 準確率

核心技術
- 資料增強
- Batch Normalization
- Dropout
- 學習率調度

實戰技巧
- 診斷模型問題
- 超參數調整
- 效能優化

推薦學習資源

線上課程

  1. Coursera: Deep Learning Specialization (Andrew Ng)
  2. 系統化學習深度學習
  3. 包含 CNN 專題

  4. Fast.ai: Practical Deep Learning

  5. 實戰導向
  6. 從上到下的學習方式

  7. Stanford CS231n: Convolutional Neural Networks

  8. 斯坦福經典課程
  9. 理論深入

論文閱讀

按順序閱讀:

  1. LeNet-5 (1998) - 基礎
  2. "Gradient-Based Learning Applied to Document Recognition"

  3. AlexNet (2012) - 深度學習革命

  4. "ImageNet Classification with Deep CNNs"

  5. VGGNet (2014) - 更深的網路

  6. "Very Deep Convolutional Networks"

  7. ResNet (2015) - 殘差連接

  8. "Deep Residual Learning for Image Recognition"

  9. EfficientNet (2019) - 模型縮放

  10. "EfficientNet: Rethinking Model Scaling"

實戰專案建議

  1. 初級
  2. Fashion-MNIST 分類
  3. Dogs vs Cats 分類
  4. 手寫中文字辨識

  5. 中級

  6. CIFAR-100 (100 類別)
  7. Tiny ImageNet
  8. 自己收集資料集

  9. 高級

  10. Kaggle 競賽
  11. 醫療影像診斷
  12. 自動駕駛物體偵測

下一步行動計畫

本週
- [ ] 完整執行本指南所有程式碼
- [ ] 在 MNIST 達到 99%+ 準確率
- [ ] 在 CIFAR-10 達到 85%+ 準確率

下個月
- [ ] 實作 ResNet-18
- [ ] 嘗試遷移學習
- [ ] 完成一個小型專案

三個月內
- [ ] 參加一次 Kaggle 競賽
- [ ] 閱讀 5 篇經典論文
- [ ] 建立個人 GitHub 作品集


附錄:完整程式碼索引

檔案 內容 框架 難度
CNN_intro_b07.md 基礎知識、MNIST + LeNet-5 Keras ★☆☆☆☆
CNN_intro_b07_part2.md MNIST + SimpleCNN PyTorch ★★★☆☆
CNN_intro_b07_part3.md CIFAR-10 完整實戰 Both ★★★★☆
CNN_intro_b07_part4.md 實戰技巧與總結 Both ★★★★★

快速導航

我想...


本文件完成時間:2025-10-07 15:30:00
版本:b07_part4
系列完成:全四部分已完成 ✅

感謝閱讀!祝你學習愉快! 🎉