CV Day 13 YOLO11 實戰(一):訓練自己的資料集
執行需求:Colab T4 可跑。本篇在 Colab 免費 T4(16 GB VRAM)上從下載資料集到訓練 50 epoch 跑完整流程,yolo11n 約 10 分鐘、yolo11s 約 18 分鐘;CPU 不建議執行,會跑很久。如果你的資料量更大或要嘗試 yolo11m/l/x,請考慮 Colab Pro 或自架 GPU。
引言
昨天的內容中,我們把 VOC XML、COCO JSON 與 YOLO txt 三種標註格式來回轉換了一遍。標註準備好之後,下一步就是「真的把模型訓起來」。今天我們用 Ultralytics 8.3.x 提供的 YOLO11 模型,從下載公開資料集、篩選與轉換格式、準備 data.yaml、載入預訓練權重、啟動訓練、收尾評估,一條龍跑完一個物件偵測專案的核心訓練流程。讀完之後你會了解 Ultralytics 8.3 的 YOLO 物件、model.train()、model.val() 三個方法的真實寫法,並能把這套流程搬到自己的資料集上。
本篇選擇的示範資料是經典的 PASCAL VOC 2007(物件偵測的標準 benchmark,20 類、trainval 共 9,963 張影像),官方網站提供直接下載,不需要註冊或 API Key。為了讓 Colab 免費版能在合理時間內跑完,我們只取其中兩個類別(person 與 car)、每個類別各抽 300 張,並用昨天學過的格式轉換把它變成 YOLO 格式。這樣安排有兩個好處:第一,類別數少、資料量小,Colab T4 跑得起來;第二,整個「下載 → 篩選 → 轉格式 → 寫 data.yaml」的流程,正好把 Day 12 的內容接到真實訓練上,而不是拿一份已經整理好的資料集跳過準備工作。之後你要換成自己的資料時,只要替換影像與 labels 即可,後續指令都不需要改。
YOLO11 是 Ultralytics 在 2024 年 9 月發布的版本(官方名稱是 YOLO11,不是 YOLOv11),提供五個預訓練權重:yolo11n(nano,2.6M 參數)、yolo11s(small,9.4M)、yolo11m(medium,20.1M)、yolo11l(large,25.3M)、yolo11x(xlarge,56.9M)。本篇預設用 yolo11n,理由是它能在 Colab T4 的 16 GB VRAM 內用 batch=16 跑得動;如果你的資料集類別數 ≤ 5、影像尺寸 ≤ 1280,yolo11n 是最快的 baseline。當你想追求更高的 mAP,再換 yolo11s 或 yolo11m 即可,後續流程完全相同。
Ultralytics 8.3 的訓練介面
Ultralytics 8.3 把整個訓練流程包成三個方法:model.train()、model.val()、model.predict()(後兩個會在 Day 14 詳細展開)。model.train() 接受 100 多個參數,但實際上只需要傳 data、epochs、imgsz、batch、device、project 這幾個就能啟動訓練;其餘參數都用預設值即可。預設值是 Ultralytics 與 YOLO11 論文共同驗證過的最佳配方,跟著走通常能得到不錯的 baseline。
訓練過程中,Ultralytics 會自動完成以下工作:下載預訓練權重(如果本地沒有)、把資料切分成 train/val(如果還沒切過)、啟動資料增強(mosaic、mixup、hsv 抖動等)、把模型搬到指定裝置、寫訓練 log 到 project/name 目錄、每個 epoch 結束時在驗證集上計算 mAP@0.5 與 mAP@0.5:0.95、儲存 last.pt 與 best.pt 兩個 checkpoint。這套自動化是 Ultralytics 最大的賣點:比起自己寫 torchvision 訓練迴圈,省下的程式碼量非常可觀;對於剛入門的使用者,這也意味著「只要資料格式正確,就能跑出合理的結果」。
8.3 版的 model.train() 還引入了一個值得注意的設計:當 data.yaml 內 path 欄位寫的是絕對路徑時,Ultralytics 會直接把整個資料夾視為訓練根目錄,不再做相對路徑推算。這對 Colab 環境特別方便:你只要把資料放在 /content/datasets/voc-subset/,data.yaml 的 path 就寫 /content/datasets/voc-subset,訓練指令就能穩定找到 train/val 兩個子目錄。另一個細節是 model.train() 的 cache 參數:設成 "disk" 會把第一輪讀取的影像快取到硬碟,再次執行時 I/O 時間壓到接近零;對 Colab 來說特別有用,因為重複實驗時不必每次都重讀原始影像。
在著手訓練之前,還有一個觀念上的選擇要先決定:用「遷移學習」還是「從頭訓練」。Ultralytics 預設呼叫 YOLO("yolo11n.pt") 就是遷移學習,會載入在 COCO 資料集(80 類、約 118K 影像)上預訓練的權重;這對幾乎所有自訂資料集都是更好的起點,因為 backbone 已經學會了「邊緣、紋理、形狀」等通用特徵,你只需要 fine-tune 到自家類別即可。從頭訓練(YOLO("yolo11n.yaml"),注意是 .yaml 不是 .pt)只有在你的資料集是「特殊領域」(例如衛星圖、顯微鏡影像、合成數據)且資料量極大(10K+ 張)時才值得嘗試,否則通常達不到遷移學習的效果。我們這個 VOC 子集只有 600 張訓練影像,更是遷移學習的典型場景,直接用 YOLO("yolo11n.pt") 即可。
另一個常被忽略的設計是「類別順序」。Ultralytics 的 model.train() 不會檢查你的 data.yaml 與 labels/*.txt 的索引順序是否一致,而是直接信任 names 的順序。所以如果你的訓練資料來自兩個不同來源,第一份的標註把 person=0、car=1,第二份把 car=0、person=1,Ultralytics 不會幫你偵測這個錯誤,而是直接把模型搞混。對應的最佳實務:把所有訓練資料統一由一個 Python 腳本重新編碼,把類別字串先映射到全域一致的整數索引,再生成 YOLO txt。這種「集中式類別字典」是避免 bug 最便宜的方法,也是大型團隊協作時常被低估的工程紀律。
完整實作:從 VOC 2007 到 YOLO 格式再到訓練
以下範例在 Colab T4 上從下載資料到訓練完成約 15 分鐘。流程分三步:先用 shell 指令下載並解開 VOC 2007,再用 Python 篩選兩個類別並轉成 YOLO 格式,最後呼叫 Ultralytics 8.3 啟動訓練。執行前需要:pip install ultralytics==8.3.0(Colab 預裝的版本可能不同,請鎖定 8.3.x)。
# 1. 下載並解開 PASCAL VOC 2007 trainval(約 450 MB,無需註冊)
mkdir -p /content/datasets
cd /content/datasets
wget -q http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar
tar -xf VOCtrainval_06-Nov-2007.tar
ls VOCdevkit/VOC2007
# 輸出:Annotations ImageSets JPEGImages SegmentationClass SegmentationObject
# 2. 只取 person 與 car 兩類、每個類別各 300 張,轉成 YOLO 格式
from pathlib import Path
import shutil
import xml.etree.ElementTree as ET
VOC_ROOT = Path("/content/datasets/VOCdevkit/VOC2007")
DATA_ROOT = Path("/content/datasets/voc-subset")
CLASSES = ["person", "car"]
PER_CLASS = 300
for split in ["train", "val"]:
(DATA_ROOT / "images" / split).mkdir(parents=True, exist_ok=True)
(DATA_ROOT / "labels" / split).mkdir(parents=True, exist_ok=True)
# ImageSets/Main/train.txt 是官方訓練清單,val.txt 是驗證清單
for split, split_file in [("train", "train.txt"), ("val", "val.txt")]:
ids = (VOC_ROOT / "ImageSets" / "Main" / split_file).read_text().split()
counts = {c: 0 for c in CLASSES}
for image_id in ids:
root = ET.parse(VOC_ROOT / "Annotations" / f"{image_id}.xml").getroot()
W = int(root.findtext("size/width"))
H = int(root.findtext("size/height"))
lines = []
for obj in root.findall("object"):
name = obj.findtext("name")
if name not in CLASSES or counts[name] >= PER_CLASS:
continue
bb = obj.find("bndbox")
xmin = float(bb.findtext("xmin"))
ymin = float(bb.findtext("ymin"))
xmax = float(bb.findtext("xmax"))
ymax = float(bb.findtext("ymax"))
# VOC 是絕對像素座標;YOLO 要正規化成 0-1 的 cx cy w h
cx = (xmin + xmax) / 2 / W
cy = (ymin + ymax) / 2 / H
bw = (xmax - xmin) / W
bh = (ymax - ymin) / H
lines.append(f"{CLASSES.index(name)} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}")
counts[name] += 1
if not lines:
continue
shutil.copy(
VOC_ROOT / "JPEGImages" / f"{image_id}.jpg",
DATA_ROOT / "images" / split / f"{image_id}.jpg",
)
(DATA_ROOT / "labels" / split / f"{image_id}.txt").write_text(
"\n".join(lines) + "\n", encoding="utf-8"
)
print(f"{split}:{sum(counts.values())} 個標註,{counts}")
# 輸出:train:600 個標註,{'person': 300, 'car': 300}
# 輸出:val:600 個標註,{'person': 300, 'car': 300}
這段程式碼是 Day 12 格式轉換的實戰版:VOC 的 <bndbox> 是絕對像素座標,要除以影像寬高正規化成 0–1;類別字串則透過固定的 CLASSES 清單映射成索引(person=0、car=1),這正是上一節提醒的「集中式類別字典」。每張影像只在有標註時才複製,避免產生空 label 的檔案。最後的 print 是很好的自我檢查:如果兩個類別的數量差距很大,通常代表篩選條件寫錯了。
# 3. 寫出 Ultralytics 需要的 data.yaml
import yaml
data_cfg = {
"path": str(DATA_ROOT),
"train": "images/train",
"val": "images/val",
"nc": 2,
"names": CLASSES,
}
with open(DATA_ROOT / "data.yaml", "w", encoding="utf-8") as f:
yaml.safe_dump(data_cfg, f, allow_unicode=True, sort_keys=False)
print((DATA_ROOT / "data.yaml").read_text(encoding="utf-8"))
# 輸出:
# path: /content/datasets/voc-subset
# train: images/train
# val: images/val
# nc: 2
# names:
# - person
# - car
data.yaml 是 Ultralytics 訓練的「契約檔」:path 是資料根目錄,train/val 是相對於 path 的影像目錄,nc 是類別數,names 是「索引到名稱」的對應(順序就是類別索引)。我們把 train 與 val 都寫成 images/...,因為標註檔就在對應的 labels/... 目錄——Ultralytics 會自動把 images 換成 labels 去尋找同名 txt,這個慣例一定要遵守。本系列在 Day 12 已經示範過 YOLO txt 格式,現在看到的就是它的實際應用:每張影像的 labels/*.txt 第一欄就是 names 的索引。
# 4. 載入 YOLO11 nano 預訓練權重(Ultralytics 8.3 會自動從官方 hub 下載)
from ultralytics import YOLO
model = YOLO("yolo11n.pt") # 首次執行會自動下載約 5.5 MB 的權重
print(f"模型參數量:{sum(p.numel() for p in model.model.parameters()) / 1e6:.2f} M")
print(f"骨幹層數:{len(list(model.model.model))}")
# 輸出:模型參數量:2.58 M
# 輸出:骨幹層數:23
這行程式碼是 Ultralytics 8.3 的「一句話載入模型」範本:YOLO("yolo11n.pt") 會去檢查當前目錄有沒有 yolo11n.pt,沒有的話自動從 https://github.com/ultralytics/assets/releases 下載。下載完成後會被快取到 ~/.config/Ultralytics/ 與工作目錄,第二次執行時直接從磁碟讀取,整個載入過程不到 0.3 秒。model.model 是內部的 nn.Module,可以用來檢查參數量與層數;若要換成 yolo11s,改寫成 YOLO("yolo11s.pt") 即可。
# 5. 啟動訓練(Ultralytics 8.3 的標準 train 呼叫)
results = model.train(
data=str(DATA_ROOT / "data.yaml"),
epochs=50,
imgsz=640,
batch=16,
device=0, # Colab T4 在 device 0
project="runs/detect",
name="voc_yolo11n",
cache="disk", # 把訓練影像快取到 /content
patience=10, # 10 個 epoch 沒進步就早停
workers=2, # Colab 提供 2 個 CPU 核心
)
print("訓練完成,最佳權重路徑:")
print(f" {results.save_dir}/weights/best.pt")
# 輸出:訓練完成,最佳權重路徑:
# runs/detect/voc_yolo11n/weights/best.pt
這段就是「一鍵訓練」的核心。Ultralytics 8.3 把所有訓練細節藏在 model.train() 內部:你只需要告訴它資料位置、訓練輪數、影像尺寸、batch size 與裝置,它會自動處理預訓練權重繼承、optimizer 設定(SGD with momentum=0.937)、學習率排程(線性 warmup 後 cosine decay)、資料增強(mosaic、mixup、HSV 抖動、random flip)、checkpoint 儲存、log 寫入 TensorBoard 等。實務上有幾個參數值得特別注意:imgsz=640 是 YOLO11 的標準訓練尺寸,視你的影像而定可調到 320(更快)到 1280(更精準);batch=16 在 T4 的 16 GB VRAM 內對 yolo11n 是安全的,yolo11s/m 要降到 8 或 4;cache="disk" 讓 Colab 把第一輪讀取的影像快取到硬碟,再訓練時 I/O 時間壓到接近零。
訓練過程中你會看到類似以下的 log(實際數字會略有不同):
# 6. 訓練 log(Ultralytics 8.3 自動寫到 runs/detect/voc_yolo11n/results.csv)
# 重要欄位:epoch、train/box_loss、train/cls_loss、metrics/mAP50(B)、metrics/mAP50-95(B)
import pandas as pd
log_path = Path("runs/detect/voc_yolo11n/results.csv")
if log_path.exists():
df = pd.read_csv(log_path)
df.columns = [c.strip() for c in df.columns]
print(df[["epoch", "train/box_loss", "metrics/mAP50(B)", "metrics/mAP50-95(B)"]].tail(5))
# 輸出(實際數字會略有不同):
# epoch train/box_loss metrics/mAP50(B) metrics/mAP50-95(B)
# 0 46 0.782 0.868 0.551
# 1 47 0.771 0.872 0.556
# 2 48 0.764 0.875 0.559
# 3 49 0.758 0.878 0.562
# 4 50 0.753 0.881 0.565
這段讀取 Ultralytics 訓練後寫出的 CSV,印出最後 5 個 epoch 的關鍵指標。train/box_loss 是邊界框回歸損失,越低代表框預測越準;metrics/mAP50(B) 是在 IoU=0.5 下的平均精度(mean Average Precision),越高代表整體偵測表現越好;metrics/mAP50-95(B) 是更嚴格的指標(IoU 從 0.5 到 0.95 平均),也是 COCO 官方排名用的指標。對「框出人與車」這種邊界清楚的任務,mAP50 達到 0.85 以上就算合格;mAP50-95 達到 0.55 以上代表框的位置也相當準確。這份資料集很小(每類 300 張),數字漂亮不代表模型泛化能力強,換到真實場景前一定要用自己的驗證資料再測一次。
# 7. 在驗證集上做一次完整評估(model.val())
metrics = model.val(data=str(DATA_ROOT / "data.yaml"), imgsz=640, batch=16)
print(f"mAP50 = {metrics.box.map50:.4f}")
print(f"mAP50-95 = {metrics.box.map:.4f}")
print(f"precision = {metrics.box.mp:.4f}")
print(f"recall = {metrics.box.mr:.4f}")
# 輸出(實際數字會略有不同):
# mAP50 = 0.8813
# mAP50-95 = 0.5648
# precision = 0.8931
# recall = 0.8402
model.val() 會載入「當前模型」(訓練後的最後一輪權重)對驗證集跑一次完整評估,回傳 DetMetrics 物件。metrics.box.map50 與 metrics.box.map 就是 mAP@0.5 與 mAP@0.5:0.95;metrics.box.mp 與 metrics.box.mr 是 precision 與 recall。實務上如果你想用「最佳權重」評估,可以重新載入 runs/detect/voc_yolo11n/weights/best.pt:best = YOLO("runs/detect/voc_yolo11n/weights/best.pt"); best.val(...),這會得到訓練過程中最高的 mAP。Day 14 我們會把這份 best.pt 拿去推論與匯出,演示部署前的標準工作流。
常見錯誤與踩雷
錯誤一:data.yaml 的 path 寫成相對路徑導致找不到資料。Ultralytics 8.3 預期 path 是絕對路徑或相對於當前工作目錄;Colab 的工作目錄預設是 /content/,如果你寫 path: ./datasets/voc-subset 但工作目錄被切換到別處,訓練會直接報 No such file or directory。對應排查方向:用 pwd 確認當前工作目錄、或在 data.yaml 直接寫絕對路徑 path: /content/datasets/voc-subset。
錯誤二:labels/*.txt 第一欄的類別索引超出範圍。如果你的資料來自 LabelImg 或別的工具匯出,類別索引可能從 1 開始(而非 0),或是對應到 data.yaml 的 names 之外的索引。Ultralytics 訓練時若遇到索引超範圍,會直接報 IndexError: list index out of range,且不會告訴你哪張影像出問題。對應排查方向:用 find . -name '*.txt' -exec awk '{print $1}' {} + | sort -u 把所有 YOLO txt 的第一欄抓出來排序,確認落在 0 到 nc-1 之間。
錯誤三:Colab VRAM 不足導致訓練中斷。如果你的 batch 太大或 imgsz 太高,T4 的 16 GB 可能不夠用,常見錯誤訊息是 CUDA out of memory. Tried to allocate 1.50 GiB。對應排查方向:把 batch 從 16 降到 8 再降到 4,或把 imgsz 從 640 降到 320;如果還是不行,換成 yolo11n(不要用 yolo11m/l/x)。另一個解法是啟用 amp=True(預設就會開),它會自動把 forward/backward 換成 float16,省下約 30% VRAM。
錯誤四:訓練到一半 Colab 中斷。Colab 免費版在閒置超過 90 分鐘或執行超過 12 小時會自動中斷 session,導致訓練從頭來過。對應排查方向:把訓練輸出掛載到 Google Drive(project="/content/drive/MyDrive/runs/detect");或是把 epoch 數拆小(先跑 10 epoch 看 loss 走向,再決定要不要續跑)。Ultralytics 8.3 也支援 resume=True 從中斷點續訓,只要把 last.pt 路徑傳給 YOLO(...) 即可。
錯誤五:把 YOLO("yolo11n.pt") 寫成 YOLO("yolo11n")。Ultralytics 8.3 從字串判斷模型類型時,若沒寫副檔名,會把它當成「本地檔案路徑」而非「官方預訓練權重」處理,導致 FileNotFoundError。對應排查方向:永遠寫 YOLO("yolo11n.pt"),讓 Ultralytics 自動去下載;如果要指定本地路徑,也要把副檔名寫出來。
效能與實務提醒
在 Colab T4 上跑 yolo11n + VOC 子集(600 張訓練影像、640×640、batch=16),50 epoch 約 10 分鐘,平均每 epoch 12 秒左右。換成 yolo11s 約 18 分鐘,換成 yolo11m 約 35 分鐘。這個時間讓你能在一個 session 內反覆做實驗,非常適合學習階段。如果你的資料集是 10K+ 張影像,建議先在子集(1K–2K)上跑 10 epoch 看收斂趨勢,再決定 epoch 數與學習率,避免一次跑 50 epoch 後才發現設定錯誤。
另一個工程上的小建議:model.train() 的 save_period 參數預設是 -1(只在最後存 best/last),如果你的訓練時間長或想中途評估,可以設成 10 或 20,每 N 個 epoch 存一份 checkpoint,避免最後 5 個 epoch 出現 overfitting 把最佳權重洗掉的情況。patience=10 是「10 個 epoch 沒進步就早停」,對於大多數任務是合理的預設;如果你發現模型還沒收斂就停了,可以調到 20 或 30。資料增強方面,Ultralytics 8.3 預設開啟 mosaic(每 4 張影像拼成一張)與 mixup(兩張影像線性混合),對於小資料集特別有幫助;如果你的影像都是高解析度、且不適合拼接(例如醫療影像),可以把 mosaic=0.0 與 mixup=0.0 關閉。
最後一個提醒:訓練完成後請把 runs/detect/voc_yolo11n/weights/best.pt 與 last.pt 都下載到本機(或上傳到 Google Drive),不要只留在 Colab 暫存空間。Day 14 我們會把 best.pt 拿來推論、匯出 ONNX、並用驗證集的影像做視覺化檢查,那一步會用到今天的訓練結果。如果你想跳過 VOC、用自己的資料做實驗,只要把 data.yaml 與 labels/*.txt 換成自己的即可,model.train() 的呼叫完全不需改動。
小結
今天用 Ultralytics 8.3 的 YOLO11 nano 模型在 PASCAL VOC 2007 的兩類子集上跑完一次完整訓練,從下載與格式轉換、YOLO("yolo11n.pt") 載入預訓練權重、model.train(data=..., epochs=50, imgsz=640) 啟動訓練、到 model.val() 收尾評估,整段約 15 分鐘並達到 mAP@0.5 約 0.88。Ultralytics 8.3 的訓練介面把資料載入、預訓練繼承、增強、checkpoint、log 都包進單一方法呼叫,對於剛入門的使用者非常友善。記住訓練的三個關鍵參數:data(指向 data.yaml)、epochs(訓練輪數)、imgsz(影像尺寸),其餘都用預設即可。下一篇文章會把今天的 best.pt 拿來做推論、匯出 ONNX,並用驗證集影像做視覺化檢查,演示部署前的最後一哩路。
結語
今天的重點是「把昨天的標註格式接到 Ultralytics 8.3 的訓練管線上」。我們用 PASCAL VOC 2007(公開、免註冊)作為示範資料,自己寫了篩選與轉換腳本,再從載入 yolo11n、啟動訓練、到讀取 results.csv 與 metrics,整個流程在 Colab T4 上約 15 分鐘。讀完這篇你應該能回答:Ultralytics 8.3 的 model.train() 最少要傳哪些參數?data.yaml 的 path 與 names 各代表什麼?metrics.box.map50 與 metrics.box.map 差別在哪?明天,我們會把今天訓練出來的 best.pt 拿來推論、視覺化預測框、匯出 ONNX,並用驗證集的影像做一輪完整的「部署前最後檢查」。
延伸資源
- Ultralytics YOLO11 官方文件(8.3.x,2024):
https://docs.ultralytics.com/,YOLO類別、model.train()、model.val()、model.predict()的完整 API 與參數清單。 - PASCAL VOC 2007 官方網站(自訂學術用途授權):
http://host.robots.ox.ac.uk/pascal/VOC/voc2007/,資料集下載、20 類定義與標註格式說明。 - Ultralytics YOLO11 釋出說明(2024 年 9 月):
https://github.com/ultralytics/ultralytics/releases,YOLO11 與 YOLOv8 在架構、改進點、預訓練權重的對照表。 - Ultralytics 資料集格式文件(8.3.x,2024):
https://docs.ultralytics.com/datasets/detect/,data.yaml欄位與目錄結構的官方要求。 - Ultralytics GitHub Issues 區(2024):
https://github.com/ultralytics/ultralytics/issues,搜尋你的錯誤訊息通常能找到對應的修復方式。
留言
張貼留言