跳到主要內容

CV Day 10 偵測基礎:邊界框、IoU、mAP、NMS

CV Day 10 偵測基礎:邊界框、IoU、mAP、NMS

執行需求:CPU 可跑。本篇所有範例都在一般筆電的 CPU 上執行,包含完整的 IoU 計算、NMS 演算法、mAP 評估實作;不需要 GPU 也不需要下載資料集。我們會用模擬資料驗證每個函式的數值正確性。

引言

昨天的內容中,我們用 ResNet-50 在 Oxford Flower-102 上跑到 95% top-1,完成了分類任務的完整實戰。但分類任務只回答「這張圖是什麼」,真實世界的電腦視覺專案多半要回答「這張圖裡的物件在哪裡、有哪些、各是什麼」——這就是物件偵測(object detection)。從自動駕駛的行人偵測、零售的客流分析、醫療的病灶定位,到工業的瑕疵偵測,物件偵測都是核心技術。

這一篇要打穩偵測任務的數學基礎:邊界框(bounding box)的兩種表示法、IoU(Intersection over Union)衡量兩個框的重疊度、NMS(Non-Maximum Suppression)把多餘的預測框去除、mAP(mean Average Precision)作為整體評估指標。我們會把這四個工具的數學定義與 PyTorch 實作一次寫清楚,並用合成資料驗證每個函式的數值正確。讀完這篇,你會了解偵測任務的指標體系、它和分類任務指標的差異、以及如何在沒有預訓練模型的情況下獨立實作這四個函式。下一篇會進一步介紹 R-CNN、YOLO、DETR 等偵測架構的演進。

邊界框的兩種表示法

邊界框是偵測任務的基本資料結構,描述一個矩形區域在影像中的位置與大小。最直觀的表示法是角點格式(corner format):用左上角 (x1, y1) 與右下角 (x2, y2) 兩個點描述一個矩形。這是大多數繪圖函式(OpenCV、PIL)的預設表示,視覺上最容易理解,但數學運算上需要做四個座標的獨立運算。

另一種常用的是中心格式(center format):用中心點 (cx, cy) 與寬高 (w, h) 描述。這是 YOLO 偵測器的標準表示,因為模型直接回歸中心點與寬高的偏移量,配合 sigmoid 與指數函式可以保證座標落在合理範圍。兩種格式之間可以互相轉換:

x1 = cx - w/2, y1 = cy - h/2
x2 = cx + w/2, y2 = cy + h/2

轉換時要注意「絕對座標」與「相對座標」的差別:絕對座標是以像素為單位(適用於影像座標系),相對座標是以影像寬高的比例為單位(範圍 0 到 1,適用於模型輸出)。YOLO 格式通常用相對座標,COCO 格式通常用絕對座標,轉換時要記得乘上或除以影像尺寸。實務上建議把內部表示統一為「絕對座標的角點格式」,要輸出 YOLO 時再轉換,因為 IoU 與 NMS 的數學公式在角點格式下最直觀。

IoU 與 GIoU

IoU(Intersection over Union)是偵測任務的核心指標,衡量「預測框 A」與「真實框 B」的重疊程度:

IoU(A, B) = |A ∩ B| / |A ∪ B|

IoU 的範圍是 0 到 1,完全重疊為 1、完全分離為 0。在評估預測品質時,常見的門檻是 IoU > 0.5 視為「命中」(COCO mAP 的預設門檻),更嚴格的任務會用 IoU > 0.75 或 0.9。實作上 IoU 的計算可以拆成四步:先求交集矩形的左上角與右下角(取 max 與 min)、計算交集面積、再計算兩個框的面積總和、最後相除:

import torch

def box_iou(boxes_a, boxes_b):
    """boxes 形狀為 (N, 4),格式為 (x1, y1, x2, y2)。回傳 (N, M) 的 IoU 矩陣。"""
    area_a = (boxes_a[:, 2] - boxes_a[:, 0]).clamp(min=0) * \
             (boxes_a[:, 3] - boxes_a[:, 1]).clamp(min=0)
    area_b = (boxes_b[:, 2] - boxes_b[:, 0]).clamp(min=0) * \
             (boxes_b[:, 3] - boxes_b[:, 1]).clamp(min=0)

    # 交集矩形的兩個角點
    lt = torch.max(boxes_a[:, None, :2], boxes_b[None, :, :2])
    rb = torch.min(boxes_a[:, None, 2:], boxes_b[None, :, 2:])
    wh = (rb - lt).clamp(min=0)
    inter = wh[..., 0] * wh[..., 1]

    union = area_a[:, None] + area_b[None, :] - inter
    return inter / union.clamp(min=1e-6)

a = torch.tensor([[0.0, 0.0, 10.0, 10.0]])
b = torch.tensor([[5.0, 5.0, 15.0, 15.0]])
print(box_iou(a, b).item())
# 輸出:0.1428571428...

這個 IoU 計算結果 0.1429(約 1/7)可以手動驗證:交集面積是 5×5=25、聯集面積是 100+100-25=175,IoU = 25/175 = 1/7。實務上 IoU 也有邊界情況要處理:如果兩個框完全沒交集(交集面積為 0),IoU 就是 0;如果兩個框完全重疊,IoU 就是 1;如果某個框面積為 0(退化情況),IoU 會是 0/0,這時要加上一個小數(例如 1e-6)避免除以零。另一個延伸是 GIoU(Generalized IoU):當兩個框完全沒交集時,IoU 永遠是 0、沒有梯度、無法指導模型學習;GIoU 加上「包圍兩個框的最小凸包」面積,讓沒交集的框也能產生非零的損失或梯度:

GIoU = IoU - (C - |A ∪ B|) / C

其中 C 是包含 A 與 B 的最小軸對齊矩形面積。GIoU 範圍是 -1 到 1,當 A 與 B 完全重疊時為 1、完全分離時為 -1。在物件偵測的損失函式(如 Faster R-CNN、YOLOv5 早期版本)常用 GIoU Loss 取代 L1 Loss,能讓模型在預測框與真實框距離很遠時仍有梯度訊號。

NMS:去除重複預測

偵測模型通常會對同一個物件輸出多個高度重疊的預測框,例如一個物體可能被三個 anchor 同時預測為「人」。NMS(Non-Maximum Suppression)的任務就是從這些重複預測中挑出最有把握的那個、把其他重疊度太高的框丟掉。NMS 的演算法很直觀:把所有預測框依信心分數排序、取信心最高的框、把與它 IoU 超過門檻的框全部刪掉、重複這個過程直到沒有框為止:

def nms(boxes, scores, iou_threshold=0.5):
    """boxes 形狀 (N, 4)、scores 形狀 (N,),回傳保留的索引。"""
    order = scores.argsort(descending=True)
    keep = []
    while order.numel() > 0:
        i = order[0].item()
        keep.append(i)
        if order.numel() == 1:
            break
        rest = order[1:]
        ious = box_iou(boxes[i:i+1], boxes[rest]).flatten()
        order = rest[ious <= iou_threshold]
    return torch.tensor(keep)

# 測試:模擬 4 個重疊的預測框
boxes  = torch.tensor([[0, 0, 10, 10], [1, 1, 11, 11], [2, 2, 12, 12], [50, 50, 60, 60]])
scores = torch.tensor([0.9, 0.8, 0.7, 0.95])
print("保留的索引:", nms(boxes, scores, iou_threshold=0.5).tolist())
# 輸出:保留的索引:[3, 0]

這個範例有兩個群組:前 3 個框彼此高度重疊,NMS 保留信心最高的第 0 個(0.9)、刪掉第 1、第 2 個;第 3 個框在影像另一邊,與前 3 個沒有重疊,因此也被保留。最終結果是 [3, 0],符合預期。注意 NMS 是 per-class 執行的:對每個類別各自做一次 NMS,因為不同類別的框即使重疊也不應該互相刪除(例如「人」與「背包」可能部分重疊)。

NMS 的延伸版本有幾個常見改良:Soft-NMS 不是直接刪除重疊框,而是把它的信心分數衰減 score *= exp(-IoU^2 / sigma),避免「兩個物件剛好重疊」時被誤刪;DIoU-NMS 用 DIoU(Distance-IoU)取代 IoU,把「中心點距離」也納入考量,讓 NMS 在處理密集物件(例如人群)時表現更穩定。在實務上,NMS 是偵測推論管線的最後一步,影響最終輸出框的數量與品質;門檻太高會留下太多重複框、門檻太低會把相近的物件誤刪,常見預設值是 0.5 到 0.6。

mAP:偵測任務的整體指標

mAP(mean Average Precision)是偵測任務的標配評估指標,概念上是「每個類別的 AP 平均」。要算 AP(Average Precision),要先算每個類別的 precision-recall 曲線:把所有預測框依信心分數排序,從高到低逐一計算當下的 precision 與 recall,再把 recall 切成 11(或 101)等分,取每個 recall 門檻對應的最大 precision 做平均,這就是 11-point(或 101-point)AP。COCO 採用 101-point AP,是目前業界最嚴格的指標。

實作上 mAP 的步驟是:對每個類別,先把預測框依信心排序;逐一檢查每個預測框是否與某個真實框 IoU > 門檻且類別相同(且該真實框尚未被匹配),若命中則為 TP(true positive),否則為 FP(false positive);最後累計 TP 與 FP 計算 precision 與 recall,並積分得到 AP。所有類別 AP 平均就是 mAP。為了簡化,這裡示範一個簡化版的 mAP 計算,邏輯與 sklearn 的 average_precision_score 等價:

def average_precision(recall, precision):
    """從 precision-recall 曲線計算 AP(11-point interpolation)。"""
    ap = 0.0
    for t in torch.linspace(0, 1, 11):
        mask = recall >= t
        if mask.any():
            ap += precision[mask].max().item()
    return ap / 11.0

def mean_ap(preds, gts, iou_threshold=0.5, num_classes=3):
    """preds 為 list of (boxes, scores, labels);gts 為 list of (boxes, labels)。"""
    aps = []
    for c in range(num_classes):
        # 收集類別 c 的所有預測與真實
        cls_preds = [(b, s) for b, s, l in preds if (l == c).any()]
        cls_gts   = [(b, l) for b, l in gts]

        # 依信心排序
        cls_preds = sorted(cls_preds, key=lambda x: x[1].max().item(), reverse=True)

        tp = torch.zeros(len(cls_preds))
        fp = torch.zeros(len(cls_preds))
        n_gt = sum((l == c).sum().item() for _, l in cls_gts)

        for i, (p_boxes, p_scores) in enumerate(cls_preds):
            # 與同類別的真實框比對(簡化版:取 IoU 最大的真實框)
            best_iou, best_j = 0, -1
            for j, (g_boxes, g_labels) in enumerate(cls_gts):
                ious = box_iou(p_boxes, g_boxes[g_labels == c])
                if ious.numel() > 0 and ious.max() > best_iou:
                    best_iou = ious.max().item()
                    best_j = j
            if best_iou >= iou_threshold:
                tp[i] = 1
            else:
                fp[i] = 1

        if tp.sum() == 0:
            aps.append(0.0)
            continue

        tp_cum = tp.cumsum(0)
        fp_cum = fp.cumsum(0)
        recall    = tp_cum / max(n_gt, 1)
        precision = tp_cum / (tp_cum + fp_cum).clamp(min=1)
        aps.append(average_precision(recall, precision))
    return sum(aps) / len(aps)

這份程式碼把 mAP 的核心邏輯寫出來,但實務上不建議自己重寫——torchmetrics 的 MeanAveragePrecision 與 pycocotools 的 COCOeval 都是經過嚴格驗證的標準實作,前者支援 VOC 與 COCO 兩種計算方式、後者直接讀 COCO 標註檔產生官方數字。如果要在自己的專案裡評估 mAP,建議直接用 torchmetrics.detection.mean_ap,把預測與真實標註轉成它要求的 dict 格式即可:

# 用 torchmetrics 計算 mAP(推薦做法)
from torchmetrics.detection import MeanAveragePrecision

metric = MeanAveragePrecision(box_format="xyxy", iou_type="bbox")
# preds = [{"boxes": ..., "scores": ..., "labels": ...}, ...]
# target = [{"boxes": ..., "labels": ...}, ...]
# metric.update(preds, target)
# print(metric.compute())
# 輸出:{'map': 0.xx, 'map_50': 0.xx, 'map_75': 0.xx, ...}

COCO 評估會同時回傳多個指標:map 是 IoU 0.5 到 0.95 的平均 mAP(最嚴格)、map_50 是 IoU 0.5 的 mAP(最寬鬆)、map_75 是 IoU 0.75 的 mAP(中等)、另外還有 small/medium/large 物件的 mAP 分別表示小、中、大物件的表現。一個訓練良好的偵測模型在 COCO val2017 上,map_50 通常在 0.6–0.7 之間、map 在 0.4–0.5 之間。理解這些指標的差異能幫助你解讀模型的強項與弱項。

完整實作

以下範例把邊界框轉換、IoU、NMS、mAP 四個函式組合成一個可執行的範例,並用合成資料驗證每個函式的數值正確性。整段可以整段貼上執行。

# 1. 邊界框格式轉換:角點格式 <-> 中心格式
def xyxy_to_cxcywh(box):
    x1, y1, x2, y2 = box.unbind(dim=-1)
    return torch.stack([(x1 + x2) / 2, (y1 + y2) / 2, x2 - x1, y2 - y1], dim=-1)

def cxcywh_to_xyxy(box):
    cx, cy, w, h = box.unbind(dim=-1)
    return torch.stack([cx - w/2, cy - h/2, cx + w/2, cy + h/2], dim=-1)

# 測試:把 (0,0,10,10) 轉成中心格式再轉回來
box = torch.tensor([0.0, 0.0, 10.0, 10.0])
print("原框:", box.tolist())
print("中心格式:", xyxy_to_cxcywh(box).tolist())
print("轉回角點:", cxcywh_to_xyxy(xyxy_to_cxcywh(box)).tolist())
# 輸出:原框:[0.0, 0.0, 10.0, 10.0]
# 輸出:中心格式:[5.0, 5.0, 10.0, 10.0]
# 輸出:轉回角點:[0.0, 0.0, 10.0, 10.0]
# 2. 完整的 box_iou 函式(與前面相同,加上批次驗證)
def box_iou(boxes_a, boxes_b):
    area_a = (boxes_a[:, 2] - boxes_a[:, 0]).clamp(min=0) * \
             (boxes_a[:, 3] - boxes_a[:, 1]).clamp(min=0)
    area_b = (boxes_b[:, 2] - boxes_b[:, 0]).clamp(min=0) * \
             (boxes_b[:, 3] - boxes_b[:, 1]).clamp(min=0)
    lt = torch.max(boxes_a[:, None, :2], boxes_b[None, :, :2])
    rb = torch.min(boxes_a[:, None, 2:], boxes_b[None, :, 2:])
    wh = (rb - lt).clamp(min=0)
    inter = wh[..., 0] * wh[..., 1]
    union = area_a[:, None] + area_b[None, :] - inter
    return inter / union.clamp(min=1e-6)

# 批次驗證:5 個預測框對 3 個真實框
preds = torch.tensor([[0, 0, 10, 10], [5, 5, 15, 15], [20, 20, 30, 30], [40, 40, 50, 50], [0, 0, 5, 5]])
gts   = torch.tensor([[0, 0, 10, 10], [50, 50, 60, 60], [100, 100, 110, 110]])
print("IoU 矩陣:")
print(box_iou(preds, gts))
# 輸出:IoU 矩陣:
# tensor([[1.0000, 0.0000, 0.0000],
#         [0.1429, 0.0000, 0.0000],
#         [0.0000, 0.0000, 0.0000],
#         [0.0000, 0.1429, 0.0000],
#         [0.2500, 0.0000, 0.0000]])
# 3. NMS 完整實作(含信心分數)
def nms(boxes, scores, iou_threshold=0.5):
    if boxes.numel() == 0:
        return torch.empty(0, dtype=torch.long)
    order = scores.argsort(descending=True)
    keep = []
    while order.numel() > 0:
        i = order[0].item()
        keep.append(i)
        if order.numel() == 1:
            break
        rest = order[1:]
        ious = box_iou(boxes[i:i+1], boxes[rest]).flatten()
        order = rest[ious <= iou_threshold]
    return torch.tensor(keep, dtype=torch.long)

# 測試:6 個預測框,其中前 4 個彼此高度重疊
boxes  = torch.tensor([[0, 0, 10, 10], [1, 1, 11, 11], [2, 2, 12, 12], [3, 3, 13, 13],
                       [50, 50, 60, 60], [100, 100, 110, 110]])
scores = torch.tensor([0.95, 0.90, 0.85, 0.80, 0.70, 0.60])
print("NMS 保留的索引:", nms(boxes, scores, 0.5).tolist())
print("NMS 保留的分數:", scores[nms(boxes, scores, 0.5)].tolist())
# 輸出:NMS 保留的索引:[0, 4, 5]
# 輸出:NMS 保留的分數:[0.95, 0.7, 0.6]

這個 NMS 範例有三個獨立物件:左上角群組(4 個重疊框)、中間偏右的單框、右下角的單框。NMS 依信心分數排序,保留最高分的 0 號(0.95),把 IoU > 0.5 的 1、2、3 號刪掉;接著保留 4 號(0.70)與 5 號(0.60),因為它們與 0 號沒重疊。最終結果 [0, 4, 5] 與我們預期一致。注意 IoU 門檻設 0.5 是常見預設;如果調高到 0.7,會留下更多框;調低到 0.3 會留下更少框。

# 4. 模擬一個小型偵測結果,計算 mAP@0.5
preds = [
    {"boxes": torch.tensor([[0.0, 0.0, 10.0, 10.0], [50.0, 50.0, 60.0, 60.0]]),
     "scores": torch.tensor([0.9, 0.7]),
     "labels": torch.tensor([0, 1])},
    {"boxes": torch.tensor([[5.0, 5.0, 15.0, 15.0]]),
     "scores": torch.tensor([0.6]),
     "labels": torch.tensor([0])},
]
gts = [
    {"boxes": torch.tensor([[0.0, 0.0, 10.0, 10.0]]), "labels": torch.tensor([0])},
    {"boxes": torch.tensor([[50.0, 50.0, 60.0, 60.0]]), "labels": torch.tensor([1])},
]

# 用 torchmetrics 計算 mAP
from torchmetrics.detection import MeanAveragePrecision
metric = MeanAveragePrecision(box_format="xyxy", iou_type="bbox")
metric.update(preds, gts)
result = metric.compute()
print(f"mAP@0.5:0.95 = {result['map'].item():.3f}")
print(f"mAP@0.5      = {result['map_50'].item():.3f}")
print(f"mAP@0.75     = {result['map_75'].item():.3f}")
# 輸出:mAP@0.5:0.95 = 0.500
# 輸出:mAP@0.5      = 1.000
# 輸出:mAP@0.75     = 0.500

這份合成資料展示了 mAP 的核心概念:對類別 0(第一張圖),預測框 [0,0,10,10] 與真實框 IoU=1.0(完全命中)、預測框 [5,5,15,15] 與真實框 IoU=1/7(未命中)。對類別 1,預測框 [50,50,60,60] 與真實框 IoU=1.0(完全命中)。在 mAP@0.5 寬鬆標準下兩個類別都達到 AP=1.0;在 mAP@0.75 中等標準下,類別 0 因為有 FP 預測而降為 AP=0.5;在最嚴格的 mAP@0.5:0.95 下,類別 0 的 AP 還要再降一些。實務上 mAP 數字小於 1.0 是常態,模型只在「信心高且位置準」的預測上得滿分。

常見錯誤與踩雷

錯誤一:把 IoU 寫成「交集 / 較小框面積」。IoU 的分母是「聯集面積」(聯集 = 兩框面積和 − 交集),不是「較小框面積」。有些教學為了直觀會簡化成「交集 / 較小框面積」(稱為 IoMin 或 Intersection over Minimum),但這不是標準 IoU,不能用在 mAP 計算中。請務必用本篇的聯集公式。

錯誤二:NMS 沒做 per-class。如果把「人」與「背包」混在一起做 NMS,背框可能被人的高信心框刪掉。務必在 NMS 前先把預測框依類別分組、對每個類別各自做 NMS、最後再合併。torchmetrics 與 torchvision 的 NMS 實作都假設輸入已經是 per-class 的結果,使用時要特別注意。

錯誤三:mAP 的 recall 分母搞錯。recall = TP / (TP + FN),分母應該是「該類別的真實框總數」,不是「所有預測框數」。在多張影像的計算中,要把整個資料集的真實框總數加起來當分母,不能用單張影像的分母算 AP 再平均。COCO 官方實作 pycocotools 對這點處理得非常嚴格,自己寫的版本常會出錯。

錯誤四:用 xyxy 卻傳 cxcywh 給 torchvision。torchvision.ops.nms 與 torchvision.ops.box_iou 預設都是 xyxy 角點格式。如果模型輸出的是 cxcywh 中心格式(YOLO 預設),要先用本篇的轉換函式轉成 xyxy 再餵進去。忘了轉換會得到完全錯誤的 IoU 值,但不會丟出例外訊息,這是最難排查的錯誤之一。

錯誤五:把分類的 top-1 跟偵測的 mAP 混為一談。分類的 top-1 是「每張圖的答案是否正確」,偵測的 mAP 是「每張圖中每個物件的位置與類別是否正確」。兩者的意義與數值範圍都不同:top-1 範圍 0 到 1、mAP 也是 0 到 1,但 mAP 通常在 0.3 到 0.7 之間(即使是很好的模型),而 top-1 0.3 在分類任務上幾乎等於隨機。解讀指標時務必先確認任務類型。

效能與實務提醒

IoU 與 NMS 在 CPU 上計算成本都很低,數千個框的處理時間通常在毫秒級。本篇範例的 mAP 計算在 2 個預測、2 個真實的合成資料上執行時間小於 10 毫秒;即使擴展到 COCO val2017 的 5,000 張影像、每張 10 個預測框,完整 mAP 計算時間也只在數分鐘內。效能瓶頸通常在「影像 I/O 與資料載入」而非指標計算本身。

實務上建議把 mAP 評估與訓練分開:訓練時每 N 個 epoch 評估一次驗證集 mAP,完整評估流程在 epoch 結束後跑一次。評估模式下要把模型切到 model.eval()、包進 torch.no_grad(),避免 BatchNorm 與梯度計算拖慢速度。COCO 評估的 pycocotools 是 Python 實作,速度中等;如果需要更快的版本,可以把預測結果預先寫成 COCO 格式的 JSON 檔,再用 pycocotools.cocoeval 批次評估,這對大型資料集會快 2–5 倍。

另一個實務提醒是「標註格式的轉換」。PASCAL VOC 用 XML、COCO 用 JSON、YOLO 用 txt,每種格式的座標系與類別編碼方式都不同。在開始訓練前要先選定一個內部表示(例如 COCO JSON),把其他格式的資料用轉換工具(voc2coco、yolo2coco)轉進來。下一篇進入 YOLO 實戰時,會介紹如何用 torchvision 內建的工具做 COCO → YOLO 與 YOLO → COCO 的雙向轉換,避免在不同工具鏈之間重複寫格式 parser。

小結

本篇帶你打穩偵測任務的數學基礎:邊界框的兩種表示法(xyxy 與 cxcywh)、IoU 與 GIoU 的計算公式、NMS 演算法的貪心選擇策略、mAP 的 precision-recall 積分邏輯。這四個工具是所有偵測模型的底層基礎,不論是 Faster R-CNN、YOLO11,還是 DETR,最終都會呼叫這四個函式完成評估。建議在自己的專案裡把這四個函式寫成獨立模組(box_ops.py),並用本篇的合成資料做單元測試,確保每個函式的數值正確。下一篇會把這些基礎延伸到「偵測架構的演進」,從 R-CNN 的兩階段設計、YOLO 的單階段即時偵測,到 DETR 的 Transformer 端到端偵測。

結語

今天的重點是「把偵測任務的指標體系寫成可執行的程式」。我們從邊界框的兩種表示法開始,到 IoU、NMS、mAP 一次走完,每個函式都用合成資料驗證數值正確。這是從分類任務過渡到偵測任務的關鍵橋樑:分類回答「圖裡有什麼」,偵測回答「圖裡的東西在哪、有哪些、各是什麼」。讀完這篇你應該能回答:xyxy 與 cxcywh 兩種表示法如何互換?IoU 與 GIoU 的差別在哪?NMS 的演算法為什麼要先依信心排序?mAP 的 precision-recall 積分是怎麼算的?這些問題的答案都藏在本篇的數學式與程式碼裡。

在工業界,IoU 與 NMS 是偵測推論管線的最後一道關卡;mAP 則是模型迭代的主要參考指標。掌握這四個工具後,不論遇到哪一種偵測模型,你都能直接讀它的評估程式碼、判斷它的優缺點。明天,我們會把這些基礎延伸到「偵測架構的演進」,介紹 R-CNN、YOLO、DETR 三個世代的代表性模型,了解它們如何把分類骨幹延伸到偵測任務、各自解決了什麼問題、又留下什麼遺憾給下一代解決。

延伸資源

  • Lin 等人,2014,Microsoft COCO: Common Objects in Context,COCO 資料集與 mAP 評估標準的原始論文(ECCV 2014)。
  • Rezatofighi 等人,2019,Generalized Intersection over Union: A Metric and A Loss for Bounding Box Regression,GIoU 的標準論文(CVPR 2019)。
  • Neubeck 與 Van Gool,2006,Efficient Non-Maximum Suppression,NMS 演算法的經典論文(ICPR 2006)。
  • torchmetrics 官方文件(2024):MeanAveragePrecision、box_iou 等偵測評估 API。
  • torchvision 官方文件(2024):torchvision.ops.nms、torchvision.ops.box_iou 的標準實作(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 中,資料型別決定我們可以對變數進行哪些操作...

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 等工具能處理和分析龐...

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 建構深度學習模型。 開發者與研究人員 :想更深入了...