開場:為什麼需要 CNN?
The Big Question
如果你今天要寫一個程式,告訴電腦「這張圖裡有沒有貓」,你會怎麼寫?
直覺做法?
「找到尖尖的耳朵 → 貓!」但尖耳朵的東西太多了:狐狸、兔子、紙做的角…
顏色判斷?
「橘色毛 → 貓!」那黑貓呢?那橘色的枕頭呢?人寫規則,永遠寫不完。
Template Matching?
存一張「標準貓」去比對?但貓有各種姿勢、各種距離、各種品種…
答案:讓機器自己學!
給大量「有貓 / 沒貓」的圖片,讓模型自己學出什麼特徵代表貓。這就是 CNN。
傳統方法 vs Deep Learning
❌ 傳統方法
人類手動設計特徵 (SIFT, HOG, Haar)
→ 每換一個任務就要重新設計
→ 永遠有 edge case
→ 規則越多越脆弱
✓ CNN / Deep Learning
模型自動學習特徵 (end-to-end)
→ 只要有足夠的資料
→ 特徵會自動變得越來越抽象
→ 第 1 層學邊緣 → 中間學紋理 → 最後學物件
CNN 為什麼是答案?
人類看圖也是先看局部:先注意到「尖尖的形狀」、「毛茸茸的紋理」、「圓圓的眼睛」。
CNN 的 kernel 也是一小塊一小塊地看,非常符合圖片的性質。比起 FC 把所有像素混在一起,Conv 保留了「空間關係」。
「貓的眼睛」不管出現在圖的左上角還是右下角,都應該被同一個偵測器抓到。
CNN 的同一個 kernel 滑過整張圖 = 相當於共享權重 = 大幅減少參數量。
一張 224×224 的圖,如果用 FC 需要 5000 萬參數;用 Conv 3×3 只需要 9 個。
多層 Conv 堆疊後:第一層學到邊緣和顏色,第二層組合出紋理和角,第三層以後逐漸出現「耳朵」「眼睛」等部件,最深層才是「貓」這個完整概念。這跟人類視覺皮層 V1→V2→V4→IT 的處理過程驚人地相似。
因為權重共享加上 pooling,CNN 天生具有一定的平移不變性。貓移到圖片另一邊,模型照樣認得出來。
這是 FC 完全做不到的 — FC 必須從零學習「同一個 pattern 出現在不同位置」的所有情況。
本堂課目標
從最基本的
卷積開始,一路走到
YOLO 物件偵測。
每一個概念都有互動 demo 可以玩。重點不是背公式,而是
建立直覺。
點上面的 Tab 開始探索吧!建議順序就是從左到右。
小測驗
CNN 相比 FC (全連接層) 最大的優勢是什麼?
精度永遠比較高
參數量大幅減少,利用了圖片的空間結構
訓練速度一定更快
不需要任何標記資料
卷積遊樂場
Convolution Playground
自己拖一個 3×3 kernel 的數值,看它對圖片做了什麼。
CNN 第一層就是在做這件事,只是 kernel 的值是模型自己學出來的。
新增了 stride 和 padding 可以調,還有 receptive field 動畫。
什麼是卷積?
一個小窗戶在圖上滑,每滑一次做一次點積運算,輸出一張 feature map。
為什麼是 3×3?
VGG 論文證明兩個 3×3 等於一個 5×5,但參數更少。3×3 從此成為共識。
什麼是 feature map?
每個 kernel 滑完整張圖後產生的 2D 矩陣,代表「某種特徵在哪裡出現」。
Padding & Stride
Padding 是邊緣補幾圈 0,stride 是一次滑幾格。下面可以調看看。
顯示 Receptive Field
看 kernel 怎麼滑過整張圖
Input · Kernel
3×3 Kernel(可以改數字)
Output Feature Map
PyTorch:
nn.Conv2d(1, 1, kernel_size=3, stride=1, padding=1)
Receptive Field 計算器
堆疊層看感受野怎麼長大
加層看 receptive field 怎麼成長。公式: RFnew = RFold + (kernel − 1) × stride_product
紅色區域 = 輸出一個 pixel 能「看到」的原圖範圍
為什麼這個很重要?
Sobel 是經典邊緣偵測器,人類花了幾十年才想出來。但 CNN 訓練幾個 epoch 後,
第一層的 kernel 自己就會長得很像 Sobel。
這就是 deep learning 的魔法 — 把 feature engineering 交給模型自己做。
池化的真相
Pooling Demystified
Pooling(池化)是 CNN 裡很常見的操作,但很多人只會背「會降低尺寸」,不知道為什麼。
下面動手調 window 看 Max、Avg、GAP 的差別。
為什麼要 Pool?
三個原因:減少計算量、增加 receptive field、提供平移不變性。
Max Pooling
取窗戶內最大值。最常用,因為保留最強特徵。「有偵測到 = 訊號強 = 數值大」。
Avg Pooling
取平均。比較少用於分類,但用在 GAP(全部平均成一個值)取代 FC 層。
趨勢
現代很多模型用 stride=2 conv 取代 pooling — 一樣下採樣但有可學參數。
Pool Type
不同 pool 對相同資料會輸出不同結果
Input(滑鼠移上看哪些 cell 在同一個 window)
黃色 = 當前 window · 紅色 = 被選中的 max
Output
PyTorch:
nn.MaxPool2d(2, 2)
三種 Pool 的視覺對比
同一張 feature map,經過三種 pool 後輸出的差別:
三種 pool 在做什麼
Max pool 會保留最尖銳的特徵(像把音量調到只聽最大聲那一刻)。
Avg pool 比較平滑,不會強調極值。
GAP 直接把整張圖壓成一個值,通常用在最後一層當分類前的「打包」。
小測驗
為什麼現代架構傾向用 stride=2 conv 取代 Max Pooling?
因為 Max Pooling 會讓圖片模糊
stride=2 conv 有可學參數,讓模型自己決定怎麼下採樣
因為 Max Pooling 計算量太大
stride=2 conv 不會減少尺寸
激活函數動物園
Activation Function Zoo
神經網路一定要有激活函數(activation function),不然多少層都跟一層一樣
(因為線性的疊加還是線性)。下面看每個 activation 長什麼樣子、套用在實際資料上會發生什麼。
為什麼要非線性?
沒有非線性,N 層 linear 化簡後跟 1 層 linear 一樣。神經網路的能力來自非線性的疊加。
ReLU 為什麼紅
計算超快(就是 if x<0 return 0),不會像 sigmoid 那樣飽和導致梯度消失。
Dying ReLU
ReLU 負半邊梯度為 0,如果某個 neuron 一直輸出負值就「死了」。Leaky ReLU 解決這個。
現代趨勢
YOLOv4 用 Mish,YOLOv5+、Transformer 用 SiLU/Swish。都是 ReLU 的平滑版本。
選擇激活函數(可以多選比較)
函數曲線(input → output)
x 軸 = input · y 軸 = output · 滑鼠移上去看數值
梯度曲線(d/dx output)
這就是 backward 時會用到的東西
梯度 ≈ 0 = 訓練不動了
套用在 feature map 上
💡 ReLU 把所有負值砍成 0,你會看到輸出的「黑色區域」變多 — 那些就是「死掉」的 neuron。
怎麼選 activation
新模型多半從
SiLU 或 GELU 開始(YOLOv5+, Transformer 都用)。
老模型用 ReLU 沒問題,簡單快速。
碰到 dying ReLU 改
Leaky ReLU。
Sigmoid / Tanh 現在只用在
輸出層(分類機率、座標限制),很少當隱藏層 activation 了。
小測驗
為什麼現代模型不再用 Sigmoid 當隱藏層的 activation?
因為計算太慢
因為會飽和,導致梯度消失
因為輸出不是 0~1
因為不能微分
Batch Normalization
標準化的力量
Batch Normalization 是現代 CNN 標配,但很多人講不清楚它在做什麼。
簡單說:把每一層的輸出分布「拉回來」,讓訓練更穩定。
下面實際看一看資料分布怎麼被改變。
公式
y = γ × (x − μ) / σ + β,把分布壓成 mean=0、std=1,再用 γ、β 去學適合的分布。
為什麼有用
讓每層收到的 input 分布穩定,可以用更大的 learning rate,訓練快很多。
Train vs Eval
新手雷:Train 用當前 batch 的 μ、σ,Eval 用訓練時的移動平均。忘記切換會慘。
放在哪
通常順序:Conv → BN → ReLU。有 BN 之後幾乎不用 Dropout 了。
為什麼要有 γ 和 β
只把分布壓到 mean=0, std=1 太死板,可能反而削弱模型表達力。所以 BN 多加兩個
可學參數 γ, β,
讓模型自己決定「這層的 activation 適合哪種分布」 — 等於最佳化標準化的程度。
FC vs Conv
為什麼不直接用全連接?
你可能會問:既然全連接層 (FC)能擬合任何函數,為什麼還要 Conv?
答案是參數量。下面動態算給你看,有圖有真相。
FC 是什麼
每個輸入都連到每個輸出,參數量 = input × output。彈性最大,但...也最貴。
Conv 為什麼省
同一個 kernel 在整張圖共用權重。一個 3×3 kernel = 9 個參數,不管圖多大。
Inductive Bias
Conv 內建「相鄰像素相關、特徵位置不變」的假設。FC 完全沒有,要從頭學起。
現代用法
Conv 提取特徵 → GAP 壓縮 → FC 做最後分類。FC 通常只剩最後一層。
參數量大比拼
💀 FC 比 Conv 多 — 倍參數!所以根本不用考慮直接接 FC。
為什麼 Conv 可以共用權重
FC 的世界觀
把整張圖攤平成一條線。左上角的像素跟右下角的像素「平等」 — 這完全違背圖片的本質
(相鄰像素其實相關性很高)。所以 FC 要從零學起「什麼是空間關係」。
Conv 的世界觀
「貓的眼睛」這種 pattern 不管出現在圖的哪個位置,都應該被同一個偵測器抓到。
所以一個 kernel 滑遍整張圖 = 共用權重 = 大幅減少參數。
參數量的概念
FC 隨輸入尺寸線性增長 — 224×224 比 32×32 多 49 倍參數。
Conv 跟輸入尺寸無關 — 一個 3×3 kernel 永遠 9 個參數,不管圖多大。
這就是為什麼現代視覺模型幾乎全是 Conv,FC 只剩最後一層當分類器。
梯度消失現場
Gradient Vanishing
拉動下面的 slider 改變網路深度,看左右兩種架構的梯度怎麼變化。
亮 = 梯度大(學得到),
暗 = 梯度接近 0(學不到)。
還可以按 ⏵ 看反向傳播的動畫。
梯度是什麼
告訴模型「往哪個方向調參數能讓 loss 變小」。沒有梯度 = 不知道怎麼學。
為什麼會消失
梯度從輸出層往輸入層傳時,每經過一層就乘一次偏導數。如果都小於 1,深層網路前幾層的梯度會指數衰減到 0。
ResNet 怎麼解
加 skip connection 讓梯度有捷徑可走,backward 時不會被中間層稀釋掉。
影響有多大
原本 30 層就訓練不起來,有 ResNet 後可以堆到 152 層、甚至 1000 層還能訓練。
反向傳播動畫
看梯度從輸出層怎麼一路衰減
Plain CNN(沒有 skip connection)
← input 前面
output 後面 →
※ 有效層 = 梯度 > 0.01 的層
ResNet(有 skip connection)
← input 前面
output 後面 →
※ 每個 residual block 都有 +1 的 identity path
數學直覺
梯度 ≈ factor^depth。當 factor < 1 且 depth 大,結果指數衰減到 0。
ResNet 的 skip connection 讓
反向傳播多一條 +1 的路徑,等於 (factor + 1)^depth。
這就是為什麼可以蓋到 152 層還訓練得起來。
殘差學習現場
y = F(x) + x · 一個 +x 改變世界
殘差學習是 ResNet 的核心發明。
下面動態看 F(x)、x、F(x)+x 三個訊號的關係,以及為什麼這條 skip connection 能解決梯度消失。
原本的學法
直接學 H(x) — 從 input 學到 output 該長什麼樣。難。
殘差的學法
學 F(x) = H(x) − x — 只學「需要在 x 上加什麼東西」。簡單多了。
退化解
如果這層沒用,模型可以把 F(x) 學成 0,等於 identity。不會比原本差。
梯度高速公路
∂(F+x)/∂x = ∂F/∂x + 1。那個 +1 保證梯度永遠有東西傳回去。
三條訊號的疊加
input x
F(x)
F(x) + x
Residual Block 結構
梯度視覺化:傳統 vs Residual
想像 backward 時梯度從輸出層往前傳。傳統 CNN 每經過一層就衰減,ResNet 因為有 +1 的 identity path,梯度可以直接「跳過」中間層。
為什麼這個發明這麼重要
ResNet 之前,網路深度被卡在 30 層左右(再深 train loss 反而變高)。
ResNet 之後,
深度不再是問題。Transformer、Diffusion、所有 LLM 也都用這個技巧。
可以說 deep learning 從 2015 年之後的發展都是建立在這條看似簡單的 +x 路徑上。
CNN 演進史
From LeNet to ConvNeXt
從 1998 年的 LeNet 到現代的 ConvNeXt,CNN 的發展像一場接力賽。每一代解決了上一代的痛點。
1998
LeNet-5 (Yann LeCun)
第一個成功的 CNN,用來辨識手寫數字。結構簡單:2 個 conv + 2 個 pool + 3 個 FC。證明了 CNN 可以學到有用的 feature。
2012
AlexNet (Alex Krizhevsky)
ImageNet 冠軍,error rate 從 26% 降到 16%。第一次用 GPU 訓練大型 CNN。引入 ReLU、Dropout、Data Augmentation。深度學習的「iPhone moment」。
2014
VGGNet (Simonyan & Zisserman)
證明「深度」很重要。統一用 3×3 kernel(兩個 3×3 = 一個 5×5 的感受野,參數更少)。VGG-16 / VGG-19 至今還是常用的 backbone。
2014
GoogLeNet / Inception
引入 Inception module:同時用 1×1、3×3、5×5 conv 平行處理,再 concat。用 1×1 conv 做 channel 壓縮,大幅減少計算量。22 層但參數只有 AlexNet 的 1/12。
2015
ResNet (Kaiming He)
Skip connection 解決梯度消失。152 層!Error rate 3.57%,超越人類(5.1%)。影響至今:所有現代模型都用 residual connection。
2017
DenseNet / SENet / MobileNet
DenseNet:每層連到所有後面的層。SENet:channel attention。MobileNet:depth-wise separable conv 讓 CNN 跑在手機上。
2020
EfficientNet / Vision Transformer
EfficientNet:用 NAS 自動找最佳架構。ViT:把 Transformer 搬到 vision,證明 attention 也能做圖片分類。CNN vs Transformer 大戰開始。
2022
ConvNeXt (Meta)
「如果把 ResNet 用 Transformer 的設計哲學重新設計會怎樣?」結果:純 CNN 也能跟 ViT 打平!證明 CNN 還沒死,只是需要現代化。
重要發明速查
在 AlexNet 之前,大家用 sigmoid / tanh。這些函數在輸入很大或很小時梯度趨近 0(飽和),深層網路訓練不起來。
ReLU: f(x) = max(0, x),正半邊梯度永遠 = 1,計算快 6 倍。從此成為默認 activation。
Ioffe & Szegedy 發現:每層的 input 分布會隨訓練改變(Internal Covariate Shift)。
BN 把每層輸出標準化到 mean=0, std=1,讓下一層收到穩定的 input。效果:可以用更大 LR,訓練快 14 倍。
核心: y = F(x) + x。多了一條 identity path,梯度可以直接流到前面的層。
沒有 ResNet 之前,超過 30 層 train loss 反而上升;有了之後可以 152 層、1000 層。
現在幾乎所有深度模型(包括 Transformer)都用這個技巧。
標準 Conv: 每個 output channel 的 kernel 都是 (Cin × K × K)。
Depthwise Separable: 先對每個 channel 獨立做 K×K conv,再用 1×1 conv 做跨 channel 混合。
計算量降低 8~9 倍,品質只掉一點點。MobileNet / EfficientNet / YOLOv5 都大量使用。
不是每個 channel(feature map)都一樣重要。SENet 用 GAP + FC + sigmoid 算出每個 channel 的「重要性權重」,再乘回去。
等於讓模型自己學「這張圖應該多注意哪些特徵」。後來的 ECA、CBAM 都是這個思路的延伸。
為什麼要知道演進?
現代模型不是憑空出現的,每一個設計都是為了解決某個問題。看懂演進,你就能理解為什麼 YOLO 的 backbone 長那樣,也能預測未來的趨勢。
小測驗
ResNet 的 skip connection 主要解決了什麼問題?
過擬合 (Overfitting)
梯度消失,讓深層網路可以訓練
計算量太大
訓練資料不足
YOLO 怎麼看圖
Anchor & Grid Visualizer
看 YOLO 怎麼把圖切成 grid,以及
anchor-based vs anchor-free 的差別。
點擊任一格子,可以看到該格子負責預測哪些 box。
Detection 是什麼
不只說「圖裡有貓」,還要說「貓在這個位置,框出來」。比分類難很多。
為什麼用 grid
YOLO 把圖切 SxS 格,每格「負責」預測中心落在它裡面的物件。
什麼是 anchor
預先定義的 box 形狀(瘦高、寬扁、正方形)。用 K-means 算出來。
Anchor-free 的勝利
YOLOv8 改回 anchor-free,簡化 pipeline,不用為每個資料集設計 anchor。
顯示 NMS 視覺化
NMS 把重複的 box 去掉
Image · Grid · Anchors
橘色框 = ground truth ·
綠色高亮 = 負責的格子 ·
藍色 = 你選的格子
Output Tensor 結構
Grid: 7 × 7
Anchors per cell: 3
Per-anchor outputs: 5 + 20 = 25
Output shape:
(B, 7, 7, 75)
B = batch size · 75 = 3 anchors × (5 + 20)
5 = (x, y, w, h, confidence) · 20 = 類別數
Selected cell info
點擊左邊任一格子來看 detail
IoU 跟 NMS 是什麼?
IoU 視覺化(拖預測框)
拖藍色預測框,看跟橘色 ground truth 的 IoU。IoU > 0.5 算 match。
NMS 互動模擬
模型常會對同一物件預測很多個 box(因為附近的格子都會輸出)。NMS (Non-Maximum Suppression) 一步步看:
點 Step 或 Auto Play 開始 NMS 流程。
為什麼 anchor-free 變主流
Anchor-based 需要先用 K-means 對訓練集算出最佳 anchor 形狀,
不同任務的最佳 anchor 完全不同(行人偵測 vs 衛星照片偵測 vs 人臉偵測)。
Anchor-free 把這負擔丟給模型自己學,簡化 pipeline。
YOLOv8 之後幾乎都改用這個。
YOLO 演進全紀錄
v1 → v8 · 每一代在解決什麼問題
YOLO 系列從 2016 的 v1 到 2023 的 v8,八代演進各有突破。下面用表格 + 手風琴逐一介紹。
版本比較總覽
| Version |
Year |
Backbone |
Key Innovation |
Grid |
Anchor |
mAP (COCO) |
| v1 | 2016 | 自定義 24-layer | One-stage 先驅 | 7×7 | 無 | 63.4 (VOC) |
| v2 | 2017 | Darknet-19 | Anchor boxes + BN | 13×13 | 5 | 48.1 |
| v3 | 2018 | Darknet-53 | Multi-scale (FPN-like) | 13/26/52 | 9 (3×3) | 55.3 |
| v4 | 2020 | CSPDarknet-53 | BoF + BoS + CIoU | 多尺度 | 9 | 65.7 |
| v5 | 2020 | CSPDarknet (PyTorch) | 工業部署、5 sizes | 多尺度 | 9 | ~68 |
| v6 | 2022 | EfficientRep | RepVGG 重參數化 | 多尺度 | Anchor-free | ~71 |
| v7 | 2022 | E-ELAN | Planned re-param | 多尺度 | Anchor-based | 56.8 AP |
| v8 | 2023 | C2f backbone | DFL + TAL + Multi-task | 多尺度 | Anchor-free | 53.9 AP |
各版本詳細解析
核心思想:把偵測問題轉成「回歸問題」,一次看完整張圖就預測所有物件。
架構:
- 輸入: 448 × 448
- 24 層 conv + 2 層 FC
- 輸出: 7 × 7 × 30 tensor
- 每個 cell 預測 2 個 box (x, y, w, h, conf) + 20 class probs
Loss 函數:MSE 形式,含有位置 loss、confidence loss、class loss,用 λ 平衡各項。
限制:
- 每格只能偵測 1 個物件 → 密集物件漏偵測
- 沒有 anchor → 對不同比例物件泛化差
- 小物件偵測差(7×7 grid 太粗)
意義:證明 real-time detection 可行,45 FPS!
Backbone: Darknet-19 (19 conv + 5 maxpool)
七大改進表:
| 改進 | 效果 |
| Batch Normalization | +2% mAP,可移除 Dropout |
| High Resolution Classifier | 先用 448×448 finetune backbone |
| Anchor Boxes | recall 從 81% → 88% |
| Dimension Clusters (K-means) | 找最佳 anchor 形狀 |
| Direct Location Prediction | 穩定訓練,用 sigmoid 限制偏移 |
| Fine-Grained Features | passthrough layer,小物件偵測改善 |
| Multi-Scale Training | 訓練時隨機切換輸入尺寸 (320~608) |
Anchor 機制:用 K-means 在訓練集的 GT box 上跑,找出 5 個最有代表性的 (w, h) 比例。模型預測相對 anchor 的偏移量 (tx, ty, tw, th),而非絕對座標。
Backbone: Darknet-53(53 層,含 residual connection)
核心改進:
- Multi-scale prediction: 在 3 個尺度做預測 (13×13, 26×26, 52×52)
- 類似 FPN 的結構:深層 feature 上採樣後跟淺層 concat
- 每個尺度 3 個 anchor → 共 9 個 anchor
- 用 logistic (BCE) 取代 softmax → 支援 multi-label
尺度分配:
- 13×13: 大物件 (anchor: 116×90, 156×198, 373×326)
- 26×26: 中物件 (anchor: 30×61, 62×45, 59×119)
- 52×52: 小物件 (anchor: 10×13, 16×30, 33×23)
效果:mAP-50 = 57.9%,FPS 跟 SSD 相當但精度更高。
Backbone: CSPDarknet-53(Cross Stage Partial)
Neck: SPP + PANet (Path Aggregation Network)
Bag of Freebies (訓練技巧,不增加推論成本):
- Mosaic Augmentation: 4 張圖拼成 1 張,大幅增加小物件樣本
- CutMix: 剪貼不同圖片的區域
- Label Smoothing: 防止 over-confident
- DropBlock: 結構化 Dropout
- CIoU Loss: 比 IoU 更好的 box loss,考慮中心距離和長寬比
Bag of Specials (推論時的額外計算):
- Mish activation (比 ReLU 平滑)
- CSP connections
- SAM (Spatial Attention Module)
- DIoU-NMS
效果:COCO AP = 43.5% @ 65 FPS (V100)。
開發者: Ultralytics (Glenn Jocher)
Backbone: CSPDarknet (PyTorch 重寫)
核心特色:
- C3 Module: CSP Bottleneck with 3 convolutions
- SPPF: 比 SPP 更快的空間金字塔池化
- 5 種大小: n / s / m / l / x (3.2M ~ 86.7M params)
- Auto Anchor: 自動計算最佳 anchor
- 極致工程: Mixed precision, EMA, Cosine LR
為什麼紅:
- PyTorch 原生,pip install 就能用
- 一行指令訓練自己的資料集
- TensorRT / ONNX / CoreML 一鍵匯出
- 工業界大量採用(工廠瑕疵檢測、自駕、無人機)
開發者: 美團 (Meituan)
Backbone: EfficientRep (基於 RepVGG)
核心創新:
- Structural Reparameterization: 訓練時用多分支(3×3 + 1×1 + identity),推論時融合成一個 3×3 conv
- Anchor-free: 直接預測距邊界的距離 (l, t, r, b)
- Decoupled Head: 分類和回歸用分開的 head
- SimOTA / TAL: 更好的正樣本分配策略
效果:YOLOv6-S 在 COCO 上 43.1% AP @ 520 FPS (T4)。
開發者: 原 YOLOv4 團隊 (WongKinYiu)
核心創新:
- E-ELAN: Extended-ELAN,不改 gradient path 但增加 learning capacity
- Planned Re-parameterization: 根據網路結構選擇性地做重參數化
- Auxiliary Head: 訓練時加輔助 head 提供更多監督訊號
- Coarse-to-fine lead guided: 精細的 label assignment
效果:56.8% AP @ 30 FPS (V100),當時最強。
開發者: Ultralytics (YOLOv5 同團隊)
Backbone: 改良 CSPDarknet (C2f module)
核心創新:
- C2f Module: C3 的改良版,更多 gradient flow
- Anchor-free: 不需要預設 anchor,直接預測
- DFL (Distribution Focal Loss): 把 box 回歸當作分布來預測
- TAL (Task-Aligned Learning): 分類和定位的 loss 一起優化正樣本分配
- Decoupled Head: 分類 / 回歸分開的輸出頭
- Multi-task: 同一個架構支援 Detection / Segmentation / Classification / Pose
5 種尺寸: n (3.2M) / s (11.2M) / m (25.9M) / l (43.7M) / x (68.2M)
為什麼重要:Anchor-free 讓 pipeline 更簡單;多任務讓一個模型做多件事;Ultralytics 生態系讓使用門檻極低。
演進脈絡:每一代解決了什麼
v1 證明 one-stage 可行
→ v2 加 anchor 解決多尺度
→ v3 多尺度預測解決小物件
→ v4 訓練技巧集大成
→ v5 工程化部署
→ v6 重參數化加速
→ v7 架構設計新高
→ v8 Anchor-free + Multi-task 統一框架
作業相關
你的作業是做一個自己的 YOLO 模型。不需要做到 v8 那麼複雜,但要理解基本原理:
grid 切割 → anchor/anchor-free 預測 → loss 計算 → NMS 後處理。
建議從 v1 的概念開始,再加入 v3 的多尺度。
PyTorch 模型組裝
拖拉式 nn.Module Builder
拖左邊的元件到中間組成一個 CNN,右邊會即時生成對應的 PyTorch code。
預設 input 是 (B, 3, 224, 224)。可以滑鼠移到 layer 名稱看每種 layer 在做什麼。
nn.Module
PyTorch 所有模型的基礎類別。繼承它就能管理參數、自動 backward。
__init__ vs forward
__init__ 定義「有什麼零件」,forward 定義「怎麼組起來」。backward 完全自動。
看 shape 變化
每加一層,看 channel 跟空間尺寸怎麼變。經典 pattern 是「channel 翻倍,空間減半」。
參數量 = 模型大小
下面會即時算總參數量,隨時看模型多大。Conv 比 FC 省非常多。
零件庫(可拖拉或點擊)
Conv2d 3×3
+padding=1, stride=1
Conv2d 3×3 ↓
stride=2 下採樣
Residual Block
含 skip connection
你的模型 · 每層 shape
Total parameters
0
PyTorch Code(自動生成)
# 拖 layer 進來,code 會自動生成
觀察 shape 變化
每加一層,看 channel 數和空間尺寸怎麼變。經典 CNN 的 pattern 是「
channel 翻倍,空間減半」 —
這樣每層的計算量大致守恆,而且高階特徵需要更多 channel 來表達。
試試看搭一個 Conv → BN → ReLU 的 block,然後加 MaxPool,觀察 shape 變化。
或直接點「Load ResNet-mini」看一個簡化版的 ResNet 長什麼樣。
Forward Pass 動畫
See Data Flow Through a CNN
點 Play 看一筆資料如何從輸入一路經過 Conv → ReLU → Pool → FC 變成分類結果。
觀察每一步的 tensor shape 怎麼變化。
Mini CNN: Input → Conv → ReLU → Pool → Conv → ReLU → Pool → FC → Output
點 Play 開始動畫,或用 Step 一步一步走。
為什麼要看 shape 變化
新手最常見的 RuntimeError 就是 shape mismatch。理解每一層怎麼改變 tensor 的 shape,
就能自己算出 FC 的 input features 要填多少,不用再靠猜。
PyTorch 實作指南
From Code to Training Loop
知道原理後,怎麼用 PyTorch 寫出來?這裡整理最核心的 code pattern,跟新手最常踩的雷。
nn.Module 基本結構
最簡單的 CNN
import torch
import torch.nn as nn
class SimpleCNN(nn.Module):
def __init__(self, num_classes=10):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True),
nn.MaxPool2d(2, 2),
nn.Conv2d(64, 128, kernel_size=3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.AdaptiveAvgPool2d(1),
)
self.classifier = nn.Linear(128, num_classes)
def forward(self, x):
x = self.features(x)
x = x.flatten(1)
x = self.classifier(x)
return x
Residual Block 寫法
ResNet 的核心 building block
class ResidualBlock(nn.Module):
def __init__(self, channels):
super().__init__()
self.block = nn.Sequential(
nn.Conv2d(channels, channels, 3, padding=1, bias=False),
nn.BatchNorm2d(channels),
nn.ReLU(inplace=True),
nn.Conv2d(channels, channels, 3, padding=1, bias=False),
nn.BatchNorm2d(channels),
)
self.relu = nn.ReLU(inplace=True)
def forward(self, x):
return self.relu(self.block(x) + x) # ← skip connection!
完整 Training Loop
PyTorch 訓練五步驟
# 1. 準備資料
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
# 2. 定義模型、Loss、Optimizer
model = SimpleCNN(num_classes=10).cuda()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
# 3. Training Loop
for epoch in range(50):
model.train() # ← 切到訓練模式 (BN 用 batch stats)
for images, labels in train_loader:
images, labels = images.cuda(), labels.cuda()
outputs = model(images) # Forward
loss = criterion(outputs, labels) # 算 Loss
optimizer.zero_grad() # 清梯度
loss.backward() # 反向傳播
optimizer.step() # 更新參數
# 4. Evaluation
model.eval() # ← 切到推論模式 (BN 用 running stats)
with torch.no_grad():
for images, labels in val_loader:
outputs = model(images.cuda())
_, predicted = outputs.max(1)
correct += (predicted == labels.cuda()).sum().item()
Training Simulator
調整超參數,看 loss curve 怎麼變化。體會 learning rate 太大太小、模型太深(overfitting)的差別。
Loss Curve 模擬器
Train Loss
Val Loss
Epoch: 0 / 100 | Train Loss: — | Val Loss: —
新手五大地雷
- 忘記 model.train() / model.eval() — BN 和 Dropout 在兩個模式行為不同!
- 忘記 optimizer.zero_grad() — 梯度會累積,loss 會爆炸
- 忘記 torch.no_grad() 在 eval 時 — 浪費記憶體建 graph
- Loss 用錯 — CrossEntropyLoss 已含 softmax,不要再自己加!
- Tensor device 不一致 — model 在 GPU,data 在 CPU → RuntimeError
YOLO vs 分類模型:差在哪?
| 面向 | 分類模型 (ResNet) | YOLO (偵測模型) |
| 輸出 | 單一 class 機率向量 (1000,) | 每格 × 每 anchor 的 (x,y,w,h,conf,classes) |
| Loss | CrossEntropyLoss | Box loss + Obj loss + Cls loss (複合) |
| 後處理 | argmax 取最大 | NMS 去除重複框 |
| 標註 | 一張圖一個 label | 每個物件一個 bbox + label |
| 難點 | 模型設計 | 正負樣本分配 + anchor 設計 + loss 平衡 |
YOLO 的 loss 有三個部分:
- Box loss: 預測框跟 GT 框的差異 (CIoU / DFL)
- Objectness loss: 這格有沒有物件 (BCE)
- Classification loss: 物件是哪個 class (BCE / CE)
Loss = λ₁ × box_loss + λ₂ × obj_loss + λ₃ × cls_loss
λ 的比例很重要,調不好模型會偏向某一個任務。YOLOv5 預設 box=0.05, obj=1.0, cls=0.5。
分類:每筆資料 = (image, label_int)
偵測:每筆資料 = (image, [多個 bbox])
因為每張圖的物件數量不同,需要自定義 collate_fn 來 padding。
常見格式 (YOLO txt format):
class_id x_center y_center width height (全部歸一化到 0~1)
Debug 心法
訓練到一半 loss 不動?先確認:
1.
印 shape — 每層 input/output shape 是否符合預期
2.
印 loss 各項 — 看是哪個 loss 卡住了
3.
overfit 一張圖 — 確認模型有能力學習(loss 能降到 0)
4.
視覺化預測 — 畫出預測的 bbox 跟 GT 比較
小測驗
PyTorch 的 CrossEntropyLoss 內部已經包含了什麼操作?
ReLU
LogSoftmax
Sigmoid
BatchNorm
作業說明
Homework · Build Your Own YOLO
這次作業的目標:設計一個自己的物件偵測模型,用 PyTorch 實作,並在提供的資料集上訓練。
作業要求
1. 模型設計
設計一個 CNN backbone + detection head。不限架構,但需要解釋設計理由。
2. 訓練腳本
完整的 training loop,包含 data loading, augmentation, loss, optimizer。
3. 實驗紀錄
記錄不同超參數的實驗結果,分析哪些設計有效、哪些沒用。
4. 推論展示
提供幾張測試圖的偵測結果視覺化,跟 mAP 數字。
評分標準
| 項目 | 比重 | 說明 |
| 模型架構設計 | 40% | Backbone 選擇、Head 設計是否合理、是否有創意 |
| 程式碼品質 | 30% | 結構清晰、有適當的 config、可重現 |
| 訓練策略 | 20% | Learning rate schedule、Augmentation、Loss 設計 |
| 最終精度 | 10% | mAP 越高越好,但不是唯一指標 |
重要提醒
精度只佔 10%! 重點是你對架構的理解和設計思路,不是跑出最高的數字。
用 pretrained backbone 是允許的(甚至推薦),但你需要說明為什麼選這個 backbone。
繳交內容
- 模型程式碼 (model.py 或 notebook)
- 訓練腳本 (train.py) 含完整 training loop
- 實驗報告:架構圖 + 設計理由 + 實驗結果表格
- 推論結果:至少 5 張測試圖的偵測視覺化
- README:如何安裝、如何訓練、如何推論
建議方向
自己設計 backbone + detection head,不使用任何 pretrained weights。
建議架構:3~5 個 Conv block (每個: Conv → BN → ReLU → Pool) + YOLO head。
優點:完全理解每個組件。缺點:精度可能不高,需要更多訓練時間。
使用 pretrained ResNet/EfficientNet 當 backbone,自己加 detection head。
步驟:取 ImageNet pretrained backbone → 接 FPN/PAN neck → 接 YOLO-style head。
優點:精度高、訓練快。需要理解 feature 怎麼從 backbone 取出。
Fork Ultralytics 的程式碼,修改某個模組(如換 backbone、改 loss、加 attention)。
需要:明確說明你改了什麼、為什麼改、改前改後的數據比較。
優點:接近工業實戰。需要理解框架原始碼。
常見問題 FAQ
可以用來當 baseline 比較,但你自己的模型需要有自己寫的部分。不能只跑一行 yolo train 就交差。
看資料集大小。如果用提供的小資料集,Google Colab 的免費 GPU 跑幾個小時就夠了。重點不在跑很多 epoch,而是觀察 loss 趨勢。
沒有固定門檻。重點是你的模型有在學(loss 下降、能產生合理的 bbox)。如果設計合理但精度不高,好好分析原因一樣可以拿高分。
個人作業。但歡迎互相討論概念,不要直接複製程式碼。