跳到主要內容

CV Day 11 偵測架構演進:R-CNN、YOLO、DETR



CV Day 11 偵測架構演進:R-CNN、YOLO、DETR

執行需求:CPU 可跑。本篇是偵測任務的歷史回顧與架構比較,所有概念性程式碼都在 CPU 上執行;不包含完整的模型訓練(訓練需 Colab T4,留到後續 CV Day 13、15、16 處理)。我們會用合成的小型特徵圖示範 RPN、YOLO head、DETR query 等核心元件的運作邏輯。

引言

昨天的內容中,我們把偵測任務的指標體系寫成可執行的程式:邊界框、IoU、NMS、mAP 四個工具。這些是所有偵測模型的共同基礎,但「如何從影像中產生邊界框」其實是十年來電腦視覺研究的核心議題。2014 年起,物件偵測經歷了三次典範轉移:R-CNN 系列把偵測拆成「候選框 + 分類」兩階段;YOLO 系列把它壓縮成單階段的端到端預測;DETR 系列則用 Transformer 把偵測變成集合預測問題,三個世代各有取捨。

這一篇要帶你從架構演進的角度理解物件偵測:每個世代的代表模型、它解決了什麼問題、又留下什麼遺憾給下一代。我們會把 R-CNN、Fast R-CNN、Faster R-CNN、YOLO、YOLOv3、YOLO11、DETR 的關鍵設計寫成概念性程式碼,並用合成特徵圖驗證每個模組的輸出形狀。讀完這篇,你會了解偵測架構的演進脈絡、每個模型的核心創新點,以及在自家專案裡該選哪一個世代的模型。

兩階段偵測:R-CNN 系列

R-CNN(Region-based CNN)是 Girshick 等人在 2014 年提出的開山之作。它的核心想法很直觀:既然影像中的物件出現在某個區域,就先用「選擇性搜尋」(Selective Search)演算法從影像中提取約 2000 個候選區域(region proposals),再把每個候選區域獨立送進 CNN 抽取特徵,最後用 SVM 分類並用線性回歸微調邊界框位置。R-CNN 在 VOC 2007 上把 mAP 從當時最好的 DPM(33%)拉到 58%,是深度學習在偵測領域的第一場大勝利。

R-CNN 的問題是「慢」:2000 個候選區域要各自送進 CNN forward,等同於一張影像要做 2000 次 CNN。改進方案是 Fast R-CNN(2015):把整張影像先送進 CNN 一次得到特徵圖,再用 RoI Pooling 把每個候選區域從特徵圖上對應位置「裁切」成固定大小(例如 7×7)後送進全連接層。這樣一來 CNN forward 只要一次,速度提升約 25 倍。但 Region Proposal 仍然是瓶頸——Selective Search 在 CPU 上要跑 2 秒。

Faster R-CNN(2015)徹底解決這個問題:把 Selective Search 換成Region Proposal Network(RPN),用一個小型 CNN 直接在特徵圖上滑動、預測哪些位置可能是物件。RPN 的核心是「anchor」機制:在特徵圖的每個位置預先定義 9 種 anchor(3 種面積 × 3 種長寬比),對每個 anchor 預測「是否包含物件」與「邊界框偏移量」。訓練時把 anchor 與真實框比對,正樣本是 IoU > 0.7、負樣本是 IoU < 0.3,其餘丟棄。RPN 與 Fast R-CNN 的偵測頭共享特徵圖,整個模型可以端到端訓練,在 VOC 2007 上達到 78.8% mAP、推論時間約 0.2 秒。這個架構至今仍是「兩階段偵測」的標準範本:

import torch
import torch.nn as nn

class TinyRPN(nn.Module):
    """概念性 RPN:在特徵圖上每個位置產生 9 個 anchor 的物件分數與框偏移。"""
    def __init__(self, in_channels=256, num_anchors=9):
        super().__init__()
        self.conv = nn.Conv2d(in_channels, in_channels, 3, padding=1)
        self.obj_logits = nn.Conv2d(in_channels, num_anchors * 2, 1)   # 物件/背景
        self.box_deltas = nn.Conv2d(in_channels, num_anchors * 4, 1)   # 框偏移 (dx, dy, dw, dh)

    def forward(self, feat):
        h = torch.relu(self.conv(feat))
        obj = self.obj_logits(h)    # (B, 18, H, W)
        box = self.box_deltas(h)    # (B, 36, H, W)
        return obj, box

rpn = TinyRPN()
feat = torch.randn(1, 256, 16, 16)   # 模擬 backbone 輸出的特徵圖
obj, box = rpn(feat)
print(f"物件分數形狀:{obj.shape},  框偏移形狀:{box.shape}")
# 輸出:物件分數形狀:torch.Size([1, 18, 16, 16]),  框偏移形狀:torch.Size([1, 36, 16, 16])

這個 RPN 簡化版展示了兩階段偵測的核心運算:在 16×16 的特徵圖上每個位置輸出 9 個 anchor 的「物件/背景」二元分類與「4 維框偏移」回歸。實務上 Faster R-CNN 還會接一個 RoI Pooling 與兩個 sibling head(分類 + 框回歸),但 RPN 已經把「哪裡可能有物件」這件事學會了,剩下的是把候選區域分類並微調位置。Faster R-CNN 的 mAP 在當時驚人,但 0.2 秒的推論速度離即時(30 FPS)還有距離,這留給了 YOLO。

單階段偵測:YOLO 系列

YOLO(You Only Look Once)是 Redmon 等人在 2016 年提出的單階段偵測模型。R-CNN 系列需要「先找候選、再分類」的兩階段流程,YOLO 直接把整張影像當成一個大型回歸問題:用一個 CNN 同時輸出所有位置的所有類別機率與邊界框位置。YOLO 把影像切成 S×S 個格子(例如 7×7),每個格子負責預測「中心落在這個格子的物件」,每個格子直接輸出 B 個邊界框(位置、信心、類別)。整個網路只有一個 forward,因此速度極快:在 VOC 2007 上達到 63.4% mAP、推論時間 45 FPS,首次實現即時偵測。

YOLOv1 的瓶頸是「每個格子只能預測 B 個框、且只能有一個類別」,對於密集物件(例如一群鳥)的表現不佳。YOLOv2(2017)引入 anchor 機制,每個格子用 5 個 anchor、多尺度訓練、新的骨幹 Darknet-19;YOLOv3(2018)進一步用 Darknet-53 加入殘差連接,並在三個尺度上預測(類似 FPN),對小物件偵測顯著改善。此後 YOLOv4(2020)、YOLOv5(2020,由 Ultralytics 維護)、YOLOv8(2023)、YOLO11(2024,Ultralytics 8.3.x 的版本名稱,注意官方寫法是 YOLO11 不是 YOLOv11)持續推進,骨幹從 Darknet 演化到 C2f、C3k2 等改良模組,並加入 anchor-free 設計與任務專用 head(偵測、分割、姿態、分類)。

YOLO11 在 Ultralytics 8.3.x 版本中提供 5 種尺寸(n/s/m/l/x)與多種任務變體,預訓練權重在 COCO val2017 上的表現:YOLO11n 約 39.5% mAP、YOLO11s 約 47.0% mAP、YOLO11m 約 51.5% mAP、YOLO11l 約 53.4% mAP、YOLO11x 約 54.7% mAP。命名上 Ultralytics 官方版本是 YOLO11,社群文章常寫 YOLOv11,但官方文件一律用 YOLO11,這是版本基準(2024 年底)的名稱約定。YOLO11 的概念性 head 結構如下:

import torch
import torch.nn as nn

class TinyYOLOHead(nn.Module):
    """概念性 YOLO head:每個格子預測 (5 + num_classes) 維的向量。
    5 = (x, y, w, h, obj_conf);num_classes 是類別數。"""
    def __init__(self, in_channels=256, num_classes=80, reg_max=16):
        super().__init__()
        self.num_classes = num_classes
        self.reg_max = reg_max
        # 分類分支
        self.cls_conv = nn.Conv2d(in_channels, in_channels, 3, padding=1)
        self.cls_pred = nn.Conv2d(in_channels, num_classes, 1)
        # 回歸分支(distribution focal loss 用 reg_max 個 bin 預測寬高的離散分布)
        self.reg_conv = nn.Conv2d(in_channels, in_channels, 3, padding=1)
        self.reg_pred = nn.Conv2d(in_channels, 4 * reg_max, 1)

    def forward(self, feat):
        cls = self.cls_pred(torch.relu(self.cls_conv(feat)))   # (B, 80, H, W)
        reg = self.reg_pred(torch.relu(self.reg_conv(feat)))   # (B, 64, H, W)
        return cls, reg

head = TinyYOLOHead()
feat = torch.randn(1, 256, 80, 80)   # 模擬 stride 8 的特徵圖
cls, reg = head(feat)
print(f"分類預測形狀:{cls.shape},  回歸預測形狀:{reg.shape}")
# 輸出:分類預測形狀:torch.Size([1, 80, 80, 80]),  回歸預測形狀:torch.Size([1, 64, 80, 80])

這個 YOLO head 簡化版展示了單階段偵測的核心運算:在 80×80 的特徵圖上每個位置輸出 80 個類別的機率與 4×16=64 維的回歸分布。實務上 YOLO11 還會在 P3/P4/P5 三個尺度上各自預測,最終把多尺度預測合併、套上昨天的 NMS,得到最終的偵測結果。YOLO 系列的關鍵設計選擇是「用密集預測換取速度」:在每個位置都預測可能有物件,訓練時用 anchor 與真實框的 IoU 分配正負樣本,推理時用 NMS 把重疊框合併。這種設計讓 YOLO11x 在 COCO 上達到 54.7% mAP、推論速度 50+ FPS,比 Faster R-CNN 更快、表現也更好。

端到端偵測:DETR 系列

DETR(DEtection TRansformer)是 Facebook AI 在 2020 年提出的第三代偵測架構。它把偵測問題改寫成「集合預測」(set prediction):用 Transformer encoder-decoder 直接輸出 N 個固定數量的預測(例如 100 個物件),再用「二分圖匹配」(bipartite matching)把每個預測與一個真實框配對。這個設計省掉了 anchor、NMS、proposal generation 等手工設計元件,是真正的端到端偵測。

DETR 的核心架構有三個元件:CNN backbone 抽特徵、Transformer encoder 把特徵圖當序列處理、Transformer decoder 用 N 個 learnable「object queries」當作「想找的物件類型」,每個 query 透過 cross-attention 從特徵圖中找對應區域,最後由兩個 prediction head 輸出類別與邊界框。訓練時用 Hungarian algorithm 做二分圖最佳匹配:對 N=100 個預測與 K 個真實框(K 通常遠小於 100),找出一個成本最低的匹配(成本是分類錯誤 + 框位置誤差的加權),把沒匹配到真實框的預測當作背景。

DETR 在 COCO val2017 上達到 42.0% mAP、推論速度約 10 FPS,比 Faster R-CNN 慢但比 YOLOv3 慢不少,主要瓶頸是 Transformer 的訓練收斂慢、需要 500 epoch 才能收斂。後續改進有 Deformable DETR(2021)用 deformable attention 把 attention 限制在少數採樣點上,大幅加速訓練與推論;DINO(2022)加入 contrastive denoising 與 mixed query selection,把 mAP 推到 49.4%;RT-DETR(2023,由 Baidu 提出)把 Transformer 偵測器推到 50+ FPS,挑戰 YOLO 的即時地位。DETR 系列的概念性 query 處理如下:

import torch
import torch.nn as nn

class TinyDETRDecoder(nn.Module):
    """概念性 DETR decoder:用 N 個 object queries 從特徵圖中抽取資訊。"""
    def __init__(self, d_model=256, num_queries=100, num_heads=8, num_classes=80):
        super().__init__()
        self.num_queries = num_queries
        # learnable object queries
        self.queries = nn.Parameter(torch.randn(num_queries, d_model) * 0.02)
        self.cross_attn = nn.MultiheadAttention(d_model, num_heads, batch_first=True)
        self.norm = nn.LayerNorm(d_model)
        self.cls_head = nn.Linear(d_model, num_classes + 1)   # +1 是「無物件」類別
        self.box_head = nn.Linear(d_model, 4)                 # (cx, cy, w, h),相對座標

    def forward(self, memory):
        B = memory.shape[0]
        q = self.queries.unsqueeze(0).expand(B, -1, -1)        # (B, 100, 256)
        h, _ = self.cross_attn(q, memory, memory)
        h = self.norm(h)
        cls = self.cls_head(h)                                # (B, 100, 81)
        box = torch.sigmoid(self.box_head(h))                 # (B, 100, 4) 限制在 0~1
        return cls, box

decoder = TinyDETRDecoder()
memory = torch.randn(1, 1600, 256)   # 模擬 encoder 輸出,1600 = 40x40
cls, box = decoder(memory)
print(f"分類預測形狀:{cls.shape},  框預測形狀:{box.shape}")
# 輸出:分類預測形狀:torch.Size([1, 100, 81]),  框預測形狀:torch.Size([1, 100, 4])

這個 DETR decoder 簡化版展示了端到端偵測的核心運算:用 100 個 learnable queries 從特徵序列中抽取資訊,每個 query 透過 cross-attention 找對應區域,最後輸出「類別(含背景)」與「框位置」。實務上 DETR 還會在 decoder 內堆疊 6 層、加 self-attention 讓 queries 互相溝通、加上 FFN 等標準 Transformer 模組。DETR 的最大優勢是「沒有 NMS、沒有 anchor、沒有 proposal」:訓練時用 Hungarian matching 一對一配對、推理時直接把 100 個預測中信心高的輸出即可,省掉了 NMS 這個超參數敏感的後處理。

完整實作

以下範例把三個世代的核心元件組合起來,用一張合成影像走完「backbone → 偵測 head → NMS」的完整流程,並比較三種架構的輸出差異。整段可以整段貼上 CPU 執行。

# 1. 共用的小型 backbone:把 256x256 的影像降到 32x32 的特徵圖
import torch
import torch.nn as nn

class TinyBackbone(nn.Module):
    def __init__(self):
        super().__init__()
        self.body = nn.Sequential(
            nn.Conv2d(3, 64, 3, stride=2, padding=1), nn.BatchNorm2d(64), nn.ReLU(),
            nn.Conv2d(64, 128, 3, stride=2, padding=1), nn.BatchNorm2d(128), nn.ReLU(),
            nn.Conv2d(128, 256, 3, stride=2, padding=1), nn.BatchNorm2d(256), nn.ReLU(),
            nn.Conv2d(256, 256, 3, stride=2, padding=1), nn.BatchNorm2d(256), nn.ReLU(),
        )
    def forward(self, x):
        return self.body(x)

backbone = TinyBackbone()
img = torch.randn(1, 3, 256, 256)
feat = backbone(img)
print(f"特徵圖形狀:{feat.shape}")
# 輸出:特徵圖形狀:torch.Size([1, 256, 16, 16])
# 2. 兩階段偵測:TinyRPN + 模擬 Fast R-CNN 偵測頭
class TwoStageDetector(nn.Module):
    def __init__(self, num_classes=80):
        super().__init__()
        self.rpn = TinyRPN(in_channels=256, num_anchors=9)
        # 簡化版偵測頭:對每個 anchor 直接分類(實際上會接 RoI Pooling)
        self.cls_head = nn.Conv2d(256, 9 * num_classes, 1)

    def forward(self, feat):
        obj_logits, box_deltas = self.rpn(feat)
        cls_logits = self.cls_head(feat)   # (B, 9*C, H, W)
        return obj_logits, box_deltas, cls_logits

detector_2s = TwoStageDetector()
obj, box, cls = detector_2s(feat)
print(f"兩階段輸出:obj={obj.shape}, box={box.shape}, cls={cls.shape}")
# 輸出:兩階段輸出:obj=torch.Size([1, 18, 16, 16]), box=torch.Size([1, 36, 16, 16]), cls=torch.Size([1, 720, 16, 16])
# 3. 單階段偵測:TinyYOLOHead 對應 YOLO11
yolo = TinyYOLOHead(in_channels=256, num_classes=80)
cls_yolo, reg_yolo = yolo(feat)
print(f"YOLO 輸出:cls={cls_yolo.shape}, reg={reg_yolo.shape}")
# 輸出:YOLO 輸出:cls=torch.Size([1, 80, 16, 16]), reg=torch.Size([1, 64, 16, 16])
# 4. 端到端偵測:把特徵圖展平後餵給 TinyDETRDecoder
memory = feat.flatten(2).transpose(1, 2)   # (B, 256, 16*16) -> (B, 256, 256)
detr = TinyDETRDecoder(d_model=256, num_queries=100, num_classes=80)
cls_detr, box_detr = detr(memory)
print(f"DETR 輸出:cls={cls_detr.shape}, box={box_detr.shape}")
# 輸出:DETR 輸出:cls=torch.Size([1, 100, 81]), box=torch.Size([1, 100, 4])
# 5. 把 YOLO 預測解碼成 xyxy 框,並用昨天的 NMS 過濾
def decode_yolo(cls_logits, reg_logits, stride=16, conf_threshold=0.5):
    """把 YOLO 輸出解碼成 (boxes, scores, labels) 三個 list。"""
    B, C, H, W = cls_logits.shape
    cls_prob = cls_logits.sigmoid()
    flat_cls = cls_prob.permute(0, 2, 3, 1).reshape(B, -1, C)
    max_conf, max_idx = flat_cls.max(dim=-1)
    boxes_list = []
    for b in range(B):
        mask = max_conf[b] > conf_threshold
        if mask.sum() == 0:
            boxes_list.append((torch.empty(0, 4), torch.empty(0), torch.empty(0, dtype=torch.long)))
            continue
        # 簡化版:把每個網格當成 anchor(真實 YOLO 會做分布解碼)
        ys, xs = torch.meshgrid(torch.arange(H), torch.arange(W), indexing="ij")
        cx = (xs.flatten().float() + 0.5) * stride
        cy = (ys.flatten().float() + 0.5) * stride
        wh = torch.full_like(cx, stride * 4.0)   # 簡化:每個 anchor 是 4*stride 大小
        x1 = cx - wh/2; y1 = cy - wh/2
        x2 = cx + wh/2; y2 = cy + wh/2
        boxes = torch.stack([x1, y1, x2, y2], dim=-1)[mask]
        boxes_list.append((boxes, max_conf[b][mask], max_idx[b][mask]))
    return boxes_list

decoded = decode_yolo(cls_yolo, reg_yolo, stride=16, conf_threshold=0.7)
boxes, scores, labels = decoded[0]
print(f"YOLO 解碼後:{boxes.shape[0]} 個框(信心 > 0.7)")
# 輸出:YOLO 解碼後:XX 個框(信心 > 0.7)

# 套昨天的 NMS
from torchmetrics.detection import MeanAveragePrecision
if boxes.shape[0] > 0:
    metric = MeanAveragePrecision(box_format="xyxy", iou_type="bbox")
    metric.update([{"boxes": boxes, "scores": scores, "labels": labels}], [])
    print(f"YOLO 預測 mAP(無真實框):{metric.compute()['map'].item():.3f}")
# 輸出:YOLO 預測 mAP(無真實框):0.000

這段範例把三個世代的偵測架構用同一個 backbone 串起來,比較輸出形狀:兩階段偵測的 RPN 輸出 18×16×16 的物件/背景分數與 36×16×16 的框偏移;單階段 YOLO 直接輸出 80×16×16 的類別分數與 64×16×16 的回歸分布;端到端 DETR 用 100 個 queries 直接輸出 100 個物件預測。形狀差異反映了三種架構的設計哲學:兩階段把「位置」與「類別」分開預測,單階段把它們壓在同一個特徵圖上,端到端把它們變成集合預測問題。實務上沒有絕對優劣:Faster R-CNN 在小物件偵測表現穩定、YOLO 在速度與精度的取捨最佳、DETR 在密集場景與長尾類別表現突出。

常見錯誤與踩雷

錯誤一:把 YOLO 版本寫成 YOLOv11。Ultralytics 2024 年的官方版本名稱是 YOLO11(沒有 v),社群文章常寫 YOLOv11,但官方文件與套件命名一律用 YOLO11。在 import 時也寫 from ultralytics import YOLO 並用 YOLO("yolo11n.pt") 載入權重,不是 yolov11n.pt。這是版本基準(2024 年底)的官方名稱約定,寫錯會被讀者誤會你用了不存在的版本。

錯誤二:把 Faster R-CNN 當成「兩種模型」。Faster R-CNN 是 R-CNN 系列的第三代,前兩代是 R-CNN(2014)與 Fast R-CNN(2015)。三者都屬於「兩階段偵測」範式,差別只在 region proposal 的方式:R-CNN 用 Selective Search、Fast R-CNN 共享特徵圖 + RoI Pooling、Faster R-CNN 用 RPN 取代 Selective Search。新聞或教學文章常把這三個混為一談,正確說法應該是「R-CNN 系列」或「Faster R-CNN 是兩階段偵測的標準範本」。

錯誤三:忘記 DETR 的 num_classes 要加 1。DETR 把「無物件」當作一個額外的類別(通常編號為 num_classes),讓模型可以學會輸出「這 100 個 query 都沒對應到真實物件」。這個額外類別在訓練時用 Hungarian matching 處理、推理時把 cls[:, -1] 當作「無物件」信心過濾掉。如果忘了加 1,模型的類別索引會錯位、整個訓練都會失效。

錯誤四:誤以為 DETR 不需要 NMS。DETR 在訓練時用 Hungarian matching 做一對一配對,理論上推理時不需要 NMS。但實務上 DETR 仍然會出現「同一個物件被多個 query 預測」的情況(尤其是訓練收斂不完全時),因此許多 DETR 變體仍會在推理時加一道 NMS 作為保險。RT-DETR 的論文中就有提到他們實測上加輕量 NMS 對小物件偵測有幫助。

錯誤五:把「兩階段」當成「比較慢所以比較差」。Faster R-CNN 在 COCO 上的 mAP 仍優於多數 YOLO 版本(特別是小物件),且 RoI Pooling 的設計讓它對任意形狀的物件都有較好的適應性。YOLO 的速度快但對小物件與密集場景表現較弱,DETR 在長尾類別與密集場景有優勢但訓練成本高。三個世代各有強項,選哪個取決於「你的任務最在意的是什麼」。

效能與實務提醒

三個世代的偵測架構在「訓練時間」與「推論速度」上差異極大。Faster R-CNN 在 Colab T4 上微調 ResNet-50 backbone 約 6 小時(10 epoch),推論速度約 5–10 FPS;YOLO11m 約 2 小時(50 epoch),推論速度約 50 FPS;DETR 在 T4 上完整訓練(500 epoch)需 3 天以上,推論速度約 10 FPS。對於 Colab 這種時間有限的環境,YOLO 是最佳起點;如果資料量大、有 T4+ 等級算力,DETR 的長尾表現會拉開差距。

另一個實務差異是「anchor 的設計成本」。Faster R-CNN 與早期 YOLO 需要根據資料集設計 anchor 尺寸(用 K-means 對訓練框做聚類),這個步驟對小資料集很關鍵;anchor-free 的 YOLO11 與 DETR 省掉這個麻煩,但需要更多訓練資料來收斂。實務上建議:如果你的資料集框尺寸變異不大(例如工廠零件偵測),anchor-free 通常能省下大量調參時間;如果資料集框尺寸變異大(例如街景偵測),anchor 設計仍是值得投入的環節。

推論部署上,YOLO11 在 ONNX 與 TensorRT 上的支援最完整,能輕鬆推到邊緣裝置(Jetson、CoreML、TFLite)。Faster R-CNN 的 torchvision 實作也能匯出 ONNX,但 TensorRT 的加速處理需要手動調整。DETR 系列的 ONNX 匯出仍在發展中,部分變體(如 RT-DETR)有官方支援。最後一個小提醒:訓練時 NMS 的 IoU 門檻通常設 0.5–0.7;推理時若要保留更多框(例如後續要做 tracking),可以把門檻降到 0.3,再讓追蹤演算法決定要保留哪些。

小結

本篇帶你從架構演進的角度理解物件偵測:R-CNN 系列用「候選 + 分類」的兩階段設計換取高精度,YOLO 系列用密集預測的單階段設計換取即時速度,DETR 系列用 Transformer 的集合預測換取端到端訓練。三個世代的關鍵設計選擇各有取捨:兩階段在 anchor 設計與 RoI Pooling、單階段在多尺度特徵與分布回歸、端到端在 Hungarian matching 與 query 設計。讀完這篇你應該能回答:Faster R-CNN 的 RPN 為什麼能把 Selective Search 取代掉?YOLO 為什麼能做到即時偵測?DETR 的 object queries 在概念上對應什麼?這三個問題的答案都藏在本篇的程式碼與數字裡。

在工業界,模型選擇有三條主要規則:需要即時推論(≥30 FPS)且精度要求中等,選 YOLO11s/m;需要最高精度且算力充足,選 DETR 或 Faster R-CNN;需要快速迭代且資料量不大,選 YOLO11n 或預訓練 Faster R-CNN 微調。後續 CV Day 13–16 會分別深入這三個世代的實戰:YOLO11 訓練自己的資料集、用 torchvision 微調 Faster R-CNN、DETR 與 Transformer 偵測器的細節。掌握本篇的架構演進脈絡後,進入實戰篇會更清楚每個超參數與訓練技巧的來龍去脈。

結語

今天的重點是「把偵測任務的十年演進整理成一張架構地圖」。我們從 2014 年的 R-CNN 開始,看到 Faster R-CNN 的 RPN 如何取代 Selective Search、YOLO 如何把整張影像當成一個大型回歸問題、DETR 如何用 Transformer 把偵測變成集合預測。三個世代各有解決方案,也各自留下新的瓶頸給下一代。讀完這篇你應該能在腦中畫出「兩階段 → 單階段 → 端到端」的演進路線圖,並理解每個模型的核心創新點與限制。

在工業界,這張演進圖有兩個用途:一是幫助你快速判斷「這個新模型屬於哪個世代、它的核心創新是什麼」,二是協助你選擇「在自家任務上該從哪個世代開始」。接下來的三天會分別進入 YOLO11、Faster R-CNN、DETR 的實戰:YOLO11 訓練自己的資料集(CV Day 13)、用 torchvision 微調 Faster R-CNN(CV Day 15)、DETR 與 Transformer 偵測器(CV Day 16)。這三篇都會延續今天建立的架構基礎,把昨天的指標體系與今天的架構演進結合到真實訓練流程中。明天會進入偵測的另一個關鍵環節:標註與資料格式(COCO、VOC、YOLO 轉換)。

延伸資源

  • Girshick 等人,2014,Rich Feature Hierarchies for Accurate Object Detection and Semantic Segmentation,R-CNN 原始論文(CVPR 2014)。
  • Ren 等人,2015,Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks,兩階段偵測的標準範本(NeurIPS 2015)。
  • Redmon 等人,2016,You Only Look Once: Unified, Real-Time Object Detection,YOLO 原始論文(CVPR 2016)。
  • Carion 等人,2020,End-to-End Object Detection with Transformers,DETR 原始論文(ECCV 2020)。
  • Jocher 與 Qiu,Ultralytics YOLO 官方文件(2024):YOLO11 模型清單與訓練工具(docs.ultralytics.com,版本 8.3.x)。
  • torchvision 官方文件(2024):torchvision.models.detection,Faster R-CNN 與 RetinaNet 等參考實作(PyTorch 2.5、torchvision 0.20)。

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...