0. 模型訓練小知識

自從深度學習火了以後,大家就把傳統視覺算法看低了。

因為模型的訓練需要GPU或者CPU飛速運轉好久才能完成,而訓練出來的模型又每次都非常意外,所以大家又把模型訓練戲稱為煉丹,真是充滿了玄學意味。 

深度學習時代,什麼最重要?數據!像我們這次的任務,需要準備100~500張圖片,確保包含不同角度、光照和模糊程度。如果想做的很好,業界推薦是幾千張幾萬張圖片。可想而知,準備這些樣本就會付出很多成本。尤其是機器視覺領域,工業中的瑕疵檢測,就需要大量的瑕疵樣本,而這些樣本不是那麼容易收集的。所以就會出現一個職業,賣樣本

 好了,現在樣本也有了,可以開始煉丹了嗎?別急,只有樣本還不夠,需要告訴計算機這次我們要煉什麼。那就是標註。我們藉助標註工具,在圖片中選出目標並做一個分類標記,這個過程就是標註。工具有挺多種,我們常用的是X-AnyLabeling-CPU。只不過它的自動化標註有問題,暫時我沒有解決。 

手動標註300張圖片,我們三人分工,搞了幾個小時。好了,現在都準備好了。那就將標註後的圖片進行一個分類。調用YOLO工具,進行訓練。CPU速度慢很多,與GPU不是一個量級。如果只是做個試驗,筆記本CPU也是可以的。

1. 樣本準備

自己拍攝照片話,就需要在不同的時間,不同的距離,不同的光照,不同的角度,不同的物體進行採樣拍照。多多益善。

2. 標註

X-AnyLabeling是一個開源項目,維護的非常健康。我們直接下載exe,雙擊就可以運行。標註是非常簡單而且枯燥的。下面截圖中,我們是要給圓圈中有十字的做標註。採用矩形框的方式,快捷鍵R配合鼠標左鍵就可以做一個框,將目標選中,並填寫好的它的類別。如果圖片不多,人工去標註也還好。

OpenCV-python小玩意18 YOLO目標檢測之模型訓練與指標解讀_數據

全部標註完成,導出YOLO水平框標籤OK了。 導出的Labels文件夾中有圖片和txt文件,txt格式為:

<class_id> <center_x> <center_y> <width> <height>

比如:

0 0.899370 0.356576 0.098531 0.115827

OpenCV-python小玩意18 YOLO目標檢測之模型訓練與指標解讀_召回率_02

標註導出

3. 訓練前準備

我們現在已經有了數據了,那麼需要將數據分為三部分:訓練集(80%)、驗證集(10%)和測試集(10%)。 數據集結構類似:

custom_dataset/
├── images/
│   ├── train/  # 訓練圖片
│   |── val/    # 驗證圖片
|   |── test/   # 測試圖片
└── labels/
    ├── train/  # 訓練標籤
    |── val/    # 驗證標籤
    └── test/   # 測試標籤

接下來我們創建數據集配置文件dataset.yaml

# Datasets
path: D:/datasets/  # 數據集文件夾所在路徑
train: train.txt  # 數據集路徑下的 train.txt
val: val.txt  # 數據集路徑下的 val.txt
test: test.txt  # 數據集路徑下的 test.txt

# Number of Classes 
nc: 1

# Classes Name List
names:
  0: cross

4. 訓練開始

from ultralytics import YOLO

# 加載預訓練模型
model = YOLO('yolov8n.pt')  # 基礎模型
def train():
    # 開始訓練
    model.train(
        data='dataset.yaml',
        epochs=100,              # 訓練輪數
        batch=16,                # 批大小
        imgsz=640,               # 圖像尺寸
        device='0',           # 使用GPU
        name='None',  # 保存模型訓練結果的項目目錄名稱
        patience=20,             # 早停機制
        lr0=0.01,                # 初始學習率
        augment=True,            # 自動增強
        hsv_h=0.015,             # 色調增強
        copy_paste=0.2           # 複製粘貼增強
        plots=True,           # 生成並保存訓練和驗證指標圖和預測示例圖,可視化模型性能和訓練進度
    )


if __name__ == '__main__':
    train()

5060Ti果然很厲害:100 epochs completed in 0.026 hours.

5. 推理與轉換為onnx格式

訓練完成後會生成兩個pt模型文件,best.pt和last.pt。我們直接用它進行推理:

yolo predict model=C:\runs\detect\train\weights\best.pt, source=D:\yolo\cross6.jpg

效果還補錯,那接下來我們將其改為onnx格式,讓應用面更廣。比如我就準備在C#中使用這個模型推理。 可以很簡單的:

from ultralytics import YOLO

# 加載一個模型,路徑為 YOLO 模型的 .pt 文件
model = YOLO(r"C:\runs\detect\train\weights\best.pt")

# 導出模型,格式為 ONNX
model.export(format="onnx")

也可以很複雜:

from ultralytics import YOLO

# 加載一個模型,路徑為 YOLO 模型的 .pt 文件
model = YOLO(r"runs/detect/train3/weights/best.pt")

# 導出模型,設置多種參數
model.export(
    format="onnx",  # 導出格式為 ONNX
    imgsz=(640, 640),  # 設置輸入圖像的尺寸
    keras=False,  # 不導出為 Keras 格式
    optimize=False,  # 不進行優化 False, 移動設備優化的參數,用於在導出為TorchScript 格式時進行模型優化
    half=False,  # 不啓用 FP16 量化
    int8=False,  # 不啓用 INT8 量化
    dynamic=False,  # 不啓用動態輸入尺寸
    simplify=True,  # 簡化 ONNX 模型
    opset=None,  # 使用最新的 opset 版本
    workspace=4.0,  # 為 TensorRT 優化設置最大工作區大小(GiB)
    nms=False,  # 不添加 NMS(非極大值抑制)
    batch=1,  # 指定批處理大小
    device="cpu"  # 指定導出設備為CPU或GPU,對應參數為"cpu" , "0"
)

使用onnx做個簡單推理:

yolo predict model=C:\runs\detect\train\weights\best.onnx, source=D:\yolo\cross6.jpg

6. 評估指標解讀

今天的任務算是完成了,最後我想説一下訓練的指標。請參考本次訓練結果圖(result.png):

OpenCV-python小玩意18 YOLO目標檢測之模型訓練與指標解讀_數據集_03

可以發現左側有三種loss(損失函數),右側有四個指標:precision(精確率)、recall(召回率)、mAP50和mAP50-95.

6.1 損失函數

損失函數(Loss Function)是衡量模型“預測錯誤程度”的工具:

box_loss(邊界框損失):衡量模型預測的“框位置”與真實位置的差距。cls_loss(分類損失):衡量模型判斷“是不是目標”的錯誤程度,比如圖中明明是目標狗,模型卻認為“有30%概率是老鼠”,cls_loss 就會變大;如果模型100%確定是狗,cls_loss 就接近0。

dfi_loss(分佈焦點損失):專門優化“模糊/邊緣不清晰”的檢測,讓模型更關注難分辨的目標。對於邊緣模糊的目標狗,dfi_loss 會“強迫”模型多學習這類樣本,減少漏檢。 

在圖中,我們可以看到: train/box_loss、train/cls_loss、train/dfi_loss:這三條訓練損失曲線均從高位快速下降並趨於平穩(最終接近0),無反彈或波動,説明模型學習過程正常,未出現過擬合或欠擬合。 val/box_loss、val/cls_loss、val/dfi_loss:驗證集損失與訓練集損失趨勢一致且差距較小,表明模型泛化能力良好,沒有過擬合訓練數據。

6.2 精確率

  • 公式Precision = 正確檢測的目標數模型檢測出的所有目標數

它表示檢測結果中有多少是真實的目標(避免誤檢)。 比如:Precisinotallow=0.9 表示模型標註的10個目標中,9個是真的,1個是誤檢。 穩定在1.0附近,説明模型對訓練數據中的檢測幾乎沒有誤檢。

6.3 召回率

  • 公式Recall = 正確檢測的目標數數據中實際的目標總數

它表示有多少真實目標被檢測出來(避免漏檢)。 比如:Recall=0.8 表示數據中有10個印章,模型找到了其中8個。 穩定在1.0附近,説明模型對訓練數據中的檢測幾乎沒有漏檢。

6.4 mAP50和mAP50-95

首先我們還要温習下之前看過的概念:

  1. IoU(交併比):衡量模型預測框與真實框的重疊程度(0~1,值越大越準)。比如預測框與真實框重疊50% → IoU=0.5。
  2. AP(平均精度):針對單個類別(如“圓圈”),計算不同置信度下的“精確率-召回率”曲線下面積(值越大,模型對該類別的檢測能力越強)。

6.4.1 mAP50(B)

先來逐字拆解:mAP:mean Average Precision(所有類別的AP平均值)。50:IoU閾值=0.5(即預測框與真實框重疊≥50%就算“檢測正確”)。(B):B代表“Box”(檢測框任務,區別於分割或關鍵點)。 

我們來通俗解釋下:假設測試集中有100張圖片,每張都有1個目標(共100個真實印章)。模型對這100個目標的預測結果中,只要預測框與真實框重疊≥50%,就視為“找對了”。mAP50(B)=0.9 表示在IoU=50%的寬鬆標準下,模型對印章的平均檢測準確率為90%。這是檢測任務的 “基礎及格線”,值≥0.8即達到實用標準(我們的訓練結果中 mAP50=1.0,十分優秀)。這個指標適合快速判斷模型是否“能用”(不及格就別用了)。

6.4.2 mAP50-95

這個指標表示IoU閾值從0.5到0.95,每隔0.05取一個值(共10個閾值:0.5、0.55、0.6、...、0.95),對每個IoU閾值計算一次 mAP,再取這10個 mAP 的平均值。 也即是説,IoU=0.5是個寬鬆的指標,預測框只要蓋住真實框一半以上就算對(哪怕位置偏一點)。而IoU=0.95是嚴格的指標,預測框必須幾乎完美覆蓋真實框(位置、大小都要極準,邊緣模糊的很難達標)。 mAP50-95(B)=0.85 表示:在從“寬鬆”到“嚴格”的10個標準下,模型的平均準確率為85%。 我們圖表中mAP50-95(B)的值最終穩定在0.85~0.9,説明即使在嚴格的IoU閾值(50%~95%)下,模型仍保持較高精度,對邊緣模糊、形變等情況有較強魯棒性。

6.4.3 總結

看這個表格,我們可以優先看 mAP@0.5,這樣可以綜合判斷模型好壞。而結合Precision/Recall,能夠明確模型是誤檢多還是漏檢多。