OpenVINO教程(四):benchmark_app 实战详解及 FP32 与 INT8 模型性能对比

本文会介绍OpenVINO基准测试工具benchmark_app,并且用它来测试和对比FP32和INT8模型的推理性能。

benchmark_app详解

benchmark_app 是 OpenVINO™ Toolkit 提供的一个非常实用的命令行工具,用于评估神经网络模型在指定硬件上的推理性能(如吞吐率和延迟)。这个工具特别适合在部署前快速测试模型在不同设备(CPU、GPU、VPU 等)上的表现

主要功能

  • 测试模型的吞吐率(Throughput)和延迟(Latency)
  • 支持同步(sync)和异步(async)两种推理方式
  • 可自定义输入 shape、batch size、运行时间等参数
  • 支持大部分 IR 模型 (.xml + .bin)、ONNX 模型、Paddle 模型等

常用参数说明

参数含义示例
-m模型文件路径(.xml)-m model.xml
-d推理设备-d CPU、GPU、MYRIAD
-api使用的推理 API-api sync 或 -api async
-shape设置输入的 shape(适用于 dynamic shape)-shape “[1,3,640,640]”
-t运行时间(单位:秒)-t 15 表示运行 15 秒
-bBatch size-b 4
-niter指定推理迭代次数-niter 100
-nireq并发请求数(async 模式下使用)-nireq 2
-report_type生成报告类型(no_counters, average_counters, detailed_counters)-report_type average_counters
-report_folder指定报告生成的文件夹-report_folder ./results
-progress是否显示进度条-progress true

输出信息

执行完成后,通常会看到类似以下内容的统计信息:

Count:           1062 iterations
Duration:        15001.52 ms
Latency:
    Median:      9.34 ms
    Average:     9.45 ms
    Min:         8.92 ms
    Max:         10.57 ms
Throughput:      70.74 FPS
  • Count: 1062 iterations 在规定的测试时间内完成的推理次数

    • 含义:在规定的测试时间内(由 -t 参数指定),成功完成了 1062 次推理(inference)。
    • 注意:这与 batch size 和设备性能密切相关。更高的 count 通常意味着吞吐率也更高。
  • Duration: 15001.52 ms

    • 含义:整个测试持续的时间,单位是毫秒(ms),此处为约 15 秒。
    • 与参数对应:通常与你设置的 -t(测试时长)相符,比如 -t 15 ≈ 15000 毫秒。
  • Latency(延迟):每一次推理耗时

    • 单位:全部为毫秒(ms)
    • 延迟越低越好,尤其适用于实时应用(如视频分析、边缘计算等)
    • Median 通常比 Average 更稳健,不易受极端值影响
类型含义
Median所有推理时间的中位数(去掉极端值的干扰)
Average所有推理时间的平均值
Min单次推理最快耗时
Max单次推理最慢耗时
  • Throughput: 70.74 FPS 单位时间内处理的帧数(FPS)
    • 含义:每秒钟处理的 图像帧数(即推理次数/秒),单位是 FPS(frames per second)
    • 适用于:批量处理任务,例如视频流分析或服务器端推理任务
    • 影响因素:
      • 推理设备(如 CPU vs GPU)
      • 模型类型与复杂度
      • API 模式(sync vs async)
      • 并发请求数(-nireq)

用法示例

如执行如下命令:

benchmark_app -m model.xml -d CPU -api async -shape "[1,3,640,640]" -t 15 \
  -report_type detailed_counters -report_folder ./results

-m model.xml:指定你要测试的模型。
-d CPU:用 CPU 跑模型。
-api async:用异步方式推理(效率高)。
-shape “[1,3,640,640]”:模拟输入一个 640×640 的图像。
-t 15:测试 15 秒。
-report_type detailed_counters:输出每一层的耗时报告。
-report_folder ./results:会在 ./results 文件夹中生成 .csv 报告文件,包含每层网络的执行耗时(可以用于瓶颈分析)

FP32,INT8模型性能比较

基于上篇博客,我们增加性能测试函数,如下:

def run_openvino_benchmark(model_path: Path):
    """
    使用 OpenVINO 的 benchmark_app 工具对指定模型进行性能测试。
    """
    if model_path.exists():
        print(f"Running OpenVINO benchmark_app...  {model_path}")

        cmd = [
            "benchmark_app",
            "-m", str(model_path),
            "-d", "CPU",
            "-api", "async",
            "-shape", "[1,3,640,640]",
            "-t", "15"
        ]

        try:
            result = subprocess.run(cmd, check=True, capture_output=True, text=True)
            print(result.stdout)
        except subprocess.CalledProcessError as e:
            print("benchmark_app failed:")
            print(e.stderr)
    else:
        print(f"Model not found: {model_path}")

当我们测试未量化前的模型时做如下调用

run_openvino_benchmark(IR_MODEL_PATH)

得到结果
benchmark_app fp32

当我们测试量化后的模型时做如下调用

run_openvino_benchmark(INT8_MODEL_PATH)

得到结果
benchmark_app int8
可以比较INT8模型与FP32模型,推理延迟和推理速度明显是INT8模型更优

完整代码

下面是本教程的完整代码

from pathlib import Path
from zipfile import ZipFile
from typing import Dict

import urllib.request
import tkinter as tk
from PIL import Image, ImageTk

from ultralytics import YOLO
from ultralytics.utils import DEFAULT_CFG
from ultralytics.cfg import get_cfg
from ultralytics.data.converter import coco80_to_coco91_class
from ultralytics.data.utils import check_det_dataset

import openvino as ov
import nncf
import subprocess


# ----------------------------- #
# 全局配置和路径定义
# ----------------------------- #

MODEL_VARIANTS = ["yolo11n", "yolo11s", "yolo11m", "yolo11l", "yolo11x"]
MODEL_NAME = MODEL_VARIANTS[0]  # 默认使用最轻量的 yolo11n 模型
PT_MODEL_PATH = f"{MODEL_NAME}.pt"
IR_MODEL_DIR = Path(f"{MODEL_NAME}_openvino_model")
IR_MODEL_PATH = IR_MODEL_DIR / f"{MODEL_NAME}.xml"
INT8_MODEL_PATH = Path(f"{MODEL_NAME}_openvino_int8_model/{MODEL_NAME}.xml")

IMAGE_PATH = Path("./coco_bike.jpg")
OUT_DIR = Path("./")

# COCO 数据集资源路径
DATA_URL = "http://images.cocodataset.org/zips/val2017.zip"
LABELS_URL = "https://github.com/ultralytics/yolov5/releases/download/v1.0/coco2017labels-segments.zip"
CFG_URL = "https://raw.githubusercontent.com/ultralytics/ultralytics/v8.1.0/ultralytics/cfg/datasets/coco.yaml"
DATA_PATH = OUT_DIR / "val2017.zip"
LABELS_PATH = OUT_DIR / "coco2017labels-segments.zip"
CFG_PATH = OUT_DIR / "coco.yaml"

# ----------------------------- #
# 工具函数模块
# ----------------------------- #

def download_file_if_needed(url: str, filename: str, dest_dir: Path) -> Path:
    """
    下载文件(若文件已存在则跳过)
    """
    dest_dir.mkdir(parents=True, exist_ok=True)
    file_path = dest_dir / filename
    if not file_path.exists():
        print(f"Downloading: {filename}")
        urllib.request.urlretrieve(url, file_path)
    else:
        print(f"File already exists: {file_path}")
    return file_path

def prepare_test_image():
    """
    确保测试图片存在,如无则从官方地址下载
    """
    if not IMAGE_PATH.exists():
        download_file_if_needed(
            "https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/image/coco_bike.jpg",
            IMAGE_PATH.name, IMAGE_PATH.parent
        )

def load_or_export_openvino_model() -> ov.CompiledModel:
    """
    加载或导出 YOLOv11 OpenVINO IR 模型,并编译为 CPU 运行时模型
    """
    model = YOLO(PT_MODEL_PATH).to("cpu")
    if not IR_MODEL_PATH.exists():
        model.export(format="openvino", dynamic=True, half=True)
    core = ov.Core()
    ir_model = core.read_model(IR_MODEL_PATH)
    return core.compile_model(ir_model, "CPU")

def build_ultralytics_model() -> YOLO:
    """
    创建 Ultralytics 的 YOLO 模型接口,用于调用预测器
    """
    return YOLO(IR_MODEL_DIR, task="detect")

def prepare_dataset() -> 'tuple[nncf.Dataset, object]':
    """
    下载并解压 COCO 数据集,构造验证器和 NNCF 所需数据集格式
    """
    if not (OUT_DIR / "coco/labels").exists():
        download_file_if_needed(DATA_URL, DATA_PATH.name, DATA_PATH.parent)
        download_file_if_needed(LABELS_URL, LABELS_PATH.name, LABELS_PATH.parent)
        download_file_if_needed(CFG_URL, CFG_PATH.name, CFG_PATH.parent)

        with ZipFile(LABELS_PATH, "r") as z:
            z.extractall(OUT_DIR)
        with ZipFile(DATA_PATH, "r") as z:
            z.extractall(OUT_DIR / "coco/images")

    args = get_cfg(cfg=DEFAULT_CFG)
    args.data = str(CFG_PATH)

   # 用 ultralytics 的 validator 构建 dataset
    det_model = build_ultralytics_model();
    validator_cls = det_model.task_map[det_model.task]["validator"]
    validator = validator_cls(args=args)

    validator.data = check_det_dataset(args.data)
    validator.stride = 32
    dataloader = validator.get_dataloader(OUT_DIR / "coco", 1)
    validator.class_map = coco80_to_coco91_class()
    validator.names = YOLO(PT_MODEL_PATH).to("cpu").model.names
    validator.nc = 80


    def transform_fn(data: Dict):
        return validator.preprocess(data)['img'].numpy()

    return nncf.Dataset(dataloader, transform_fn), validator

def quantize_model(original_model: ov.Model, quant_dataset: nncf.Dataset) -> ov.Model:
    """
    使用 NNCF 对 OpenVINO 模型进行混合精度量化(混合 INT8/F32)
    """
    ignored_scope = nncf.IgnoredScope(
        subgraphs=[
            nncf.Subgraph(
                inputs=[f"__module.model.{22 if 'v8' in MODEL_NAME else 23}/aten::cat/Concat",
                        f"__module.model.{22 if 'v8' in MODEL_NAME else 23}/aten::cat/Concat_1",
                        f"__module.model.{22 if 'v8' in MODEL_NAME else 23}/aten::cat/Concat_2"],
                outputs=[f"__module.model.{22 if 'v8' in MODEL_NAME else 23}/aten::cat/Concat_7"]
            )
        ]
    )

    quant_model = nncf.quantize(
        original_model,
        quant_dataset,
        preset=nncf.QuantizationPreset.MIXED,
        ignored_scope=ignored_scope
    )
    ov.save_model(quant_model, str(INT8_MODEL_PATH))
    print(f"Quantized model saved to: {INT8_MODEL_PATH}")
    return quant_model

def predict_and_show_image(det_model: YOLO, compiled_model: ov.CompiledModel):
    """
    使用模型对图像进行目标检测,并通过 Tkinter GUI 显示检测结果
    """
    if det_model.predictor is None:
        config = {"conf": 0.25, "batch": 1, "save": False, "mode": "predict"}
        args = {**det_model.overrides, **config}
        det_model.predictor = det_model._smart_load("predictor")(overrides=args, _callbacks=det_model.callbacks)
        det_model.predictor.setup_model(model=det_model.model)

    det_model.predictor.model.ov_compiled_model = compiled_model
    results = det_model(IMAGE_PATH)
    result_img = Image.fromarray(results[0].plot()[:, :, ::-1])

    root = tk.Tk()
    root.title("YOLOv11 (OpenVINO INT8) Detection Result")
    tk_img = ImageTk.PhotoImage(result_img)
    label = tk.Label(root, image=tk_img)
    label.pack()
    root.mainloop()


def run_openvino_benchmark(model_path: Path):
    """
    使用 OpenVINO 的 benchmark_app 工具对指定模型进行性能测试。
    """
    if model_path.exists():
        print(f"Running OpenVINO benchmark_app...  {model_path}")

        cmd = [
            "benchmark_app",
            "-m", str(model_path),
            "-d", "CPU",
            "-api", "async",
            "-shape", "[1,3,640,640]",
            "-t", "15"
        ]

        try:
            result = subprocess.run(cmd, check=True, capture_output=True, text=True)
            print(result.stdout)
        except subprocess.CalledProcessError as e:
            print("benchmark_app failed:")
            print(e.stderr)
    else:
        print(f"Model not found: {model_path}")

# ----------------------------- #
# 主执行流程
# ----------------------------- #

def main():
    # 1. 准备测试图像(如无则下载)
    prepare_test_image()

    # 2. 加载或导出 OpenVINO IR 模型,并编译运行(用于量化或预测)
    compiled_fp_model = load_or_export_openvino_model()

    # 3. 构造 Ultralytics YOLO 接口,用于推理/验证
    det_model = build_ultralytics_model()

    # 4. 若 INT8 模型已存在,则直接加载;否则进行量化生成
    core = ov.Core()
    if INT8_MODEL_PATH.exists():
        quantized_model = core.read_model(INT8_MODEL_PATH)
        compiled_int8_model = core.compile_model(quantized_model, "CPU")
    else:
        quant_dataset, _ = prepare_dataset()
        quantized_model = quantize_model(core.read_model(IR_MODEL_PATH), quant_dataset)
        compiled_int8_model = core.compile_model(quantized_model, "CPU")

    # 5. 使用benchmark_app测试未量化前的模型的性能
    run_openvino_benchmark(IR_MODEL_PATH)

    # 6. 使用benchmark_app测试INT8量化后的模型的性能
    run_openvino_benchmark(INT8_MODEL_PATH)

if __name__ == "__main__":
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值