【YOLO 物件偵測教學】#05 部署與優化:將 YOLO 模型應用到實際場景

測驗:YOLO 部署與優化

共 5 題,點選答案後會立即顯示結果

1. 為什麼訓練完成的 PyTorch 模型(.pt 檔案)不適合直接部署到生產環境?

  • A. PyTorch 模型無法進行推論
  • B. 需要完整 PyTorch 環境、推論速度較慢、跨平台支援有限
  • C. PyTorch 模型只能在 Windows 上執行
  • D. PyTorch 模型檔案太小,缺少必要資訊

2. 如果要在 NVIDIA GPU 上獲得最佳推論速度,應該匯出為哪種格式?

  • A. ONNX
  • B. CoreML
  • C. TensorRT (engine)
  • D. TFLite

3. 量化技術中,FP16 半精度相較於 FP32 全精度,主要的特性是什麼?

  • A. 模型大小不變,精度完全相同
  • B. 模型大小減半,精度損失極小,速度提升 1.5-2 倍
  • C. 模型大小增加一倍,精度提升
  • D. 只能在 CPU 上使用

4. 在即時影片偵測中,如果 FPS 不足,以下哪個方法最有效?

  • A. 使用更大的模型(如 YOLOv8x)
  • B. 提高輸入解析度到 1280×1280
  • C. 增加 batch size
  • D. 降低輸入解析度(如 640 改為 320)或實作跳幀處理

5. 在 Raspberry Pi(無 GPU)上部署 YOLO 模型,應該優先選擇哪種匯出格式?

  • A. TensorRT
  • B. NCNN 或 TFLite
  • C. CoreML
  • D. 原始 PyTorch 格式

前言

經過前四篇的學習,你已經能夠訓練出自己的 YOLO 模型。但訓練完成只是第一步——如何將模型部署到實際應用中,才是真正的挑戰。

本篇將帶你了解:

  • 如何將 PyTorch 模型匯出為不同格式
  • 各種部署平台的選擇與考量
  • 如何優化模型以獲得更好的推論速度
  • 建立即時偵測應用的完整流程

模型匯出:從 .pt 到部署格式

為什麼需要匯出?

YOLO 訓練完成後,你會得到一個 .pt 檔案(PyTorch 格式)。這個格式在開發時很方便,但在實際部署時有幾個問題:

  1. 需要完整的 PyTorch 環境:部署環境需要安裝 PyTorch,佔用大量空間
  2. 推論速度較慢:PyTorch 格式未針對推論優化
  3. 跨平台支援有限:不是所有平台都能直接使用 PyTorch

因此,我們需要將模型「匯出」為更適合部署的格式。

基本匯出指令

# 匯出為 ONNX 格式(最通用)
yolo export model=best.pt format=onnx

# 匯出為 TensorRT 格式(NVIDIA GPU 最佳化)
yolo export model=best.pt format=engine

# 匯出為 CoreML 格式(Apple 裝置)
yolo export model=best.pt format=coreml

# 匯出為 TFLite 格式(行動裝置、邊緣運算)
yolo export model=best.pt format=tflite
Code language: PHP (php)

執行後,你會在同一目錄下看到對應格式的檔案:

runs/detect/train/weights/
├── best.pt          # 原始 PyTorch 模型
├── best.onnx        # ONNX 格式
├── best.engine      # TensorRT 格式
└── best.mlmodel     # CoreML 格式
Code language: PHP (php)

匯出參數詳解

yolo export model=best.pt format=onnx imgsz=640 half=True simplify=True
Code language: PHP (php)

讓我們看看這些參數的意義:

參數 說明 常用值
model 模型路徑 best.ptyolov8n.pt
format 匯出格式 onnx, engine, coreml, tflite
imgsz 輸入圖片尺寸 640, 320, 1280
half 使用 FP16 半精度 True, False
simplify 簡化 ONNX 模型 True, False
dynamic 動態輸入尺寸 True, False
batch 批次大小 1, 8, 16

各格式比較

格式比較表
┌─────────────┬──────────────┬──────────────┬─────────────┐
│    格式     │    適用場景   │     優點     │     缺點    │
├─────────────┼──────────────┼──────────────┼─────────────┤
│ PyTorch     │ 開發測試     │ 最靈活       │ 部署不便    │
│ ONNX        │ 跨平台部署   │ 通用性最高   │ 速度中等    │
│ TensorRT    │ NVIDIA GPU   │ 速度最快     │ 僅限 NVIDIA │
│ CoreML      │ Apple 裝置   │ 整合度高     │ 僅限 Apple  │
│ TFLite      │ 行動/邊緣    │ 檔案小       │ 精度可能降  │
└─────────────┴──────────────┴──────────────┴─────────────┘

量化技術:讓模型更小更快

什麼是量化?

量化(Quantization)是將模型的權重從高精度(FP32)轉換為低精度(FP16 或 INT8)的技術。想像一下:

  • FP32:用 32 位元儲存一個數字,精度高但佔空間
  • FP16:用 16 位元儲存,精度稍低但大小減半
  • INT8:用 8 位元儲存,大小再減半但精度損失更多
精度與大小關係
┌──────────┬──────────┬──────────┬────────────┐
│   精度   │ 模型大小 │ 推論速度 │  精度損失  │
├──────────┼──────────┼──────────┼────────────┤
│  FP32    │   100%   │  基準    │    無      │
│  FP16    │    50%   │  1.5-2x  │   極小     │
│  INT8    │    25%   │  2-4x    │   可接受   │
└──────────┴──────────┴──────────┴────────────┘
Code language: CSS (css)

FP16 半精度量化

最簡單的量化方式,幾乎不損失精度:

# 匯出時啟用 FP16
yolo export model=best.pt format=onnx half=True
Code language: PHP (php)

在 Python 中使用:

from ultralytics import YOLO

# 載入模型時指定半精度
model = YOLO('best.pt')
results = model.predict('image.jpg', half=True)
Code language: PHP (php)

INT8 量化

需要校準資料集來確保精度:

# TensorRT INT8 量化
yolo export model=best.pt format=engine int8=True data=coco.yaml
Code language: PHP (php)

這裡 data=coco.yaml 提供校準資料,讓模型知道如何最佳化數值範圍。

量化效果實測

以 YOLOv8n 為例,在 RTX 3080 上的表現:

實測數據(輸入 640x640)
┌──────────┬──────────┬──────────┬──────────┐
│   模式   │ 模型大小 │   FPSmAP    │
├──────────┼──────────┼──────────┼──────────┤
│ FP32     │  6.3 MB  │   180    │  37.3%   │
│ FP16     │  3.2 MB  │   280    │  37.2%   │
│ INT8     │  1.6 MB  │   420    │  36.8%   │
└──────────┴──────────┴──────────┴──────────┘
Code language: CSS (css)

FP16 幾乎不損失精度,但速度提升明顯,是最推薦的選擇。

部署場景與平台選擇

伺服器部署:最大效能

適合需要處理大量請求的場景,如雲端 API 服務。

# server_deploy.py - 使用 FastAPI 建立偵測 API
from fastapi import FastAPI, UploadFile
from ultralytics import YOLO
import cv2
import numpy as np

app = FastAPI()
model = YOLO('best.engine')  # 使用 TensorRT 格式

@app.post("/detect")
async def detect(file: UploadFile):
    # 讀取上傳的圖片
    contents = await file.read()
    nparr = np.frombuffer(contents, np.uint8)
    img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)

    # 執行偵測
    results = model(img)

    # 整理結果
    detections = []
    for r in results:
        for box in r.boxes:
            detections.append({
                'class': model.names[int(box.cls)],
                'confidence': float(box.conf),
                'bbox': box.xyxy[0].tolist()
            })

    return {'detections': detections}
Code language: PHP (php)

啟動伺服器:

uvicorn server_deploy:app --host 0.0.0.0 --port 8000
Code language: CSS (css)

邊緣裝置部署:Jetson 系列

NVIDIA Jetson(如 Jetson Nano、Orin)是邊緣 AI 的熱門選擇。

環境準備:

# 在 Jetson 上安裝 ultralytics
pip install ultralytics

# 確認 CUDA 可用
python -c "import torch; print(torch.cuda.is_available())"
Code language: PHP (php)

匯出 TensorRT 模型:

# 直接在 Jetson 上匯出,確保相容性
yolo export model=best.pt format=engine device=0
Code language: PHP (php)

注意事項:

  • 在目標裝置上匯出 TensorRT 模型,避免相容性問題
  • Jetson Nano 建議使用 YOLOv8n(最小模型)
  • 適當降低輸入尺寸(如 320×320)可大幅提升 FPS

邊緣裝置部署:Raspberry Pi

Raspberry Pi 沒有 GPU,需要使用 CPU 優化的格式。

# 匯出為 NCNN 格式(ARM CPU 優化)
yolo export model=best.pt format=ncnn

# 或使用 TFLite
yolo export model=best.pt format=tflite
Code language: PHP (php)

效能期望(Pi 4B):

  • YOLOv8n + NCNN:約 5-10 FPS
  • YOLOv8n + TFLite:約 3-5 FPS

如果需要更高 FPS,考慮使用 Coral USB Accelerator。

即時影片偵測應用

基本架構

# realtime_detect.py - 即時攝影機偵測
import cv2
from ultralytics import YOLO
import time

def main():
    # 載入模型
    model = YOLO('best.pt')  # 或 best.onnx, best.engine

    # 開啟攝影機
    cap = cv2.VideoCapture(0)  # 0 = 預設攝影機
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

    # FPS 計算
    prev_time = time.time()

    while True:
        ret, frame = cap.read()
        if not ret:
            break

        # 執行偵測
        results = model(frame, verbose=False)

        # 在畫面上繪製結果
        annotated_frame = results[0].plot()

        # 計算並顯示 FPS
        curr_time = time.time()
        fps = 1 / (curr_time - prev_time)
        prev_time = curr_time
        cv2.putText(annotated_frame, f'FPS: {fps:.1f}', (10, 30),
                    cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

        # 顯示結果
        cv2.imshow('YOLO Detection', annotated_frame)

        # 按 'q' 退出
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()
Code language: PHP (php)

優化即時偵測效能

1. 降低輸入解析度

# 使用較小的輸入尺寸
results = model(frame, imgsz=320)  # 預設是 640
Code language: PHP (php)

效果:FPS 可提升 2-4 倍,但小物件偵測效果會下降。

2. 跳幀處理

不需要每一幀都偵測:

frame_count = 0
skip_frames = 2  # 每 3 幀偵測 1 次

while True:
    ret, frame = cap.read()
    frame_count += 1

    if frame_count % (skip_frames + 1) == 0:
        results = model(frame)
        last_results = results

    # 使用上次的結果繪製
    if last_results:
        annotated_frame = last_results[0].plot()
Code language: PHP (php)

3. 使用多執行緒

將讀取影像和推論分開:

import threading
from queue import Queue

class VideoStream:
    def __init__(self, src=0):
        self.cap = cv2.VideoCapture(src)
        self.queue = Queue(maxsize=2)
        self.stopped = False

    def start(self):
        threading.Thread(target=self._update, daemon=True).start()
        return self

    def _update(self):
        while not self.stopped:
            ret, frame = self.cap.read()
            if not self.queue.full():
                self.queue.put(frame)

    def read(self):
        return self.queue.get()

    def stop(self):
        self.stopped = True

# 使用方式
stream = VideoStream(0).start()
while True:
    frame = stream.read()
    results = model(frame)

效能評估與監控

關鍵指標

部署後需要監控這些指標:

指標 說明 理想值
FPS 每秒處理幀數 依應用需求,通常 >15
Latency 單幀處理延遲 <100ms
GPU 使用率 GPU 負載 60-80%(有餘裕)
記憶體使用 顯存/記憶體佔用 低於上限

效能測試腳本

# benchmark.py - 效能測試
from ultralytics import YOLO
import time
import numpy as np

def benchmark(model_path, num_iterations=100):
    model = YOLO(model_path)

    # 使用假圖片測試
    dummy_input = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)

    # 暖機(讓 GPU 進入工作狀態)
    for _ in range(10):
        model(dummy_input, verbose=False)

    # 正式測試
    times = []
    for _ in range(num_iterations):
        start = time.time()
        model(dummy_input, verbose=False)
        times.append(time.time() - start)

    times = np.array(times) * 1000  # 轉換為毫秒

    print(f"模型: {model_path}")
    print(f"平均延遲: {times.mean():.2f} ms")
    print(f"標準差: {times.std():.2f} ms")
    print(f"FPS: {1000 / times.mean():.1f}")
    print(f"最快: {times.min():.2f} ms")
    print(f"最慢: {times.max():.2f} ms")

# 比較不同格式
benchmark('best.pt')
benchmark('best.onnx')
benchmark('best.engine')  # 需要 TensorRT
Code language: PHP (php)

典型輸出

模型: best.pt
平均延遲: 12.45 ms
標準差: 1.23 ms
FPS: 80.3
最快: 10.21 ms
最慢: 18.67 ms

模型: best.engine
平均延遲: 4.32 ms
標準差: 0.45 ms
FPS: 231.5
最快: 3.89 ms
最慢: 5.67 ms
Code language: CSS (css)

常見問題與解決方案

Q1: TensorRT 匯出失敗

錯誤訊息: TensorRT export requires TensorRT>=7.0.0
Code language: JavaScript (javascript)

解決方案:

# 安裝 TensorRT
pip install tensorrt

# 或者在 Jetson 上使用系統自帶的 TensorRT
Code language: PHP (php)

Q2: ONNX 模型推論結果不同

可能原因: opset 版本不相容

解決方案:

# 指定 opset 版本
yolo export model=best.pt format=onnx opset=12
Code language: PHP (php)

Q3: 記憶體不足

解決方案:

  1. 使用較小的模型(n < s < m < l < x)
  2. 降低輸入解析度
  3. 減少 batch size
  4. 使用 FP16 或 INT8 量化

Q4: 邊緣裝置 FPS 太低

優化順序:

  1. 確認使用正確的匯出格式(TensorRT for Jetson)
  2. 降低輸入尺寸(640 -> 320)
  3. 使用較小的模型
  4. 實作跳幀處理

部署檢查清單

在正式上線前,確認以下項目:

部署前檢查
[ ] 模型已匯出為目標格式
[ ] 在目標硬體上測試過效能
[ ] FPS 符合應用需求
[ ] 記憶體使用在合理範圍
[ ] 錯誤處理機制已實作
[ ] 有監控和日誌機制
[ ] 已準備回滾方案
Code language: CSS (css)

系列總結

恭喜你完成了 YOLO 物件偵測的完整學習旅程!讓我們回顧一下:

篇章 主題 你學會了
#01 基礎概念 YOLO 的原理與架構
#02 環境與快速開始 安裝環境與執行推論
#03 資料集準備 標註與格式轉換
#04 訓練與評估 訓練模型與分析指標
#05 部署與優化 匯出、量化與即時應用

下一步建議

  1. 嘗試不同的部署場景:從本地應用到雲端 API
  2. 探索進階功能:追蹤、分割、姿態估計
  3. 優化模型架構:針對特定場景客製化
  4. 學習 MLOps:模型版本控制、持續訓練

參考資源

進階測驗:YOLO 部署與優化 進階

共 5 題情境應用題,測試你對部署與優化的深入理解

情境:你正在為一家物流公司開發包裹偵測系統,需要部署在配備 NVIDIA RTX 3080 的伺服器上,每秒需處理至少 200 張圖片,同時 mAP 損失不能超過 1%。

1. 根據文章中的實測數據,哪種配置最符合需求?

  • A. YOLOv8n + FP32,FPS 180,mAP 37.3%
  • B. YOLOv8n + INT8,FPS 420,mAP 36.8%
  • C. YOLOv8n + FP16,FPS 280,mAP 37.2%
  • D. 使用原始 PyTorch 格式,保持最高精度
情境:你需要將 YOLO 模型部署到 Jetson Orin 邊緣裝置上。你已經在開發機(RTX 4090)上訓練好模型,現在要準備部署。

2. 根據文章建議,TensorRT 模型的匯出應該在哪裡進行?為什麼?

  • A. 在開發機上匯出,因為效能更好可以更快完成匯出
  • B. 在目標 Jetson 裝置上匯出,因為 TensorRT 針對特定硬體優化,避免相容性問題
  • C. 在雲端伺服器上匯出,然後下載到 Jetson
  • D. 不需要匯出,直接使用 .pt 檔案即可
情境:你開發的即時偵測應用在 Jetson Nano 上只能達到 8 FPS,但客戶要求至少 15 FPS。你已經使用 YOLOv8n 和 TensorRT 格式。

3. 根據文章的優化建議,下一步應該優先嘗試什麼?

  • A. 換用 YOLOv8s 模型以提高精度
  • B. 將模型匯出為 ONNX 格式
  • C. 增加輸入解析度到 1280×1280
  • D. 降低輸入尺寸(如 640 改為 320)並實作跳幀處理
情境:你要進行 INT8 量化以進一步壓縮模型大小,但發現匯出指令中需要額外的 data 參數。

4. 這個 data=coco.yaml 參數的作用是什麼?

yolo export model=best.pt format=engine int8=True data=coco.yaml
  • A. 指定輸出檔案的命名格式
  • B. 定義模型要偵測的類別數量
  • C. 提供校準資料集,讓模型知道如何最佳化數值範圍
  • D. 設定訓練時使用的超參數
情境:你正在使用多執行緒優化即時偵測應用,參考了文章中的 VideoStream 類別實作。

5. 這種多執行緒架構的主要優點是什麼?

class VideoStream: def __init__(self, src=0): self.cap = cv2.VideoCapture(src) self.queue = Queue(maxsize=2) self.stopped = False def start(self): threading.Thread(target=self._update, daemon=True).start() return self
  • A. 可以同時使用多個 GPU 進行推論
  • B. 將影像讀取和模型推論分離,避免 I/O 阻塞影響推論效能
  • C. 可以提高模型的偵測精度
  • D. 減少模型的記憶體使用量

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *