3. 使用OpenVINO加速边缘服务器上的推理

任务目标

在Intel NUC(或任意x86_64 Linux边缘服务器)上,使用OpenVINO Toolkit将PyTorch预训练的ResNet-50模型转换为OpenVINO中间表示(IR),并通过OpenVINO推理引擎完成图片分类推理,最终测量并对比CPU原生PyTorch推理与OpenVINO优化后的延迟和吞吐量。

环境与依赖

  • 操作系统:Ubuntu 22.04 LTS(x86_64)
  • 硬件:Intel NUC(第8代酷睿或更新,需支持AVX2指令集)
  • Python:3.9~3.11
  • 依赖安装:
pip install openvino-dev[onnx,tensorflow,pytorch]==2023.3.0
pip install torch torchvision onnx

实现步骤与代码

完整流程分为三步:模型导出为ONNX → 转换为OpenVINO IR格式 → 编写推理与性能测试脚本。以下代码将三步整合为一个可独立运行的Python脚本,在Intel NUC上直接执行即可完成全流程。

import time
import torch
import torchvision
import numpy as np
from pathlib import Path
from openvino.runtime import Core, PartialShape

# ========== 1. 导出ResNet-50为ONNX并转换为OpenVINO IR ==========
model = torchvision.models.resnet50(weights=torchvision.models.ResNet50_Weights.IMAGENET1K_V1)
model.eval()

onnx_path = "resnet50.onnx"
ir_path = Path("resnet50_ir")

# 固定输入尺寸为OpenVINO推理时的实际输入
dummy_input = torch.randn(1, 3, 224, 224)
torch.onnx.export(model, dummy_input, onnx_path,
                  input_names=["input"], output_names=["output"],
                  opset_version=13, dynamic_axes=None)

# 使用OpenVINO的模型转换API(无需单独调用mo命令行)
from openvino.tools.mo import convert_model
ov_model = convert_model(onnx_path, input_shape=[1, 3, 224, 224])
ov_model.reshape({0: PartialShape([1, 3, 224, 224])})
ov_model.save(ir_path)
print(f"IR模型已保存至: {ir_path}")

# ========== 2. 准备测试数据(随机噪声图,仅用于性能测量) ==========
# 注意:本案例聚焦性能测量,不涉及真实图片分类精度验证
input_tensor = torch.randn(1, 3, 224, 224)
input_numpy = input_tensor.numpy()

# ========== 3. PyTorch CPU推理性能基线 ==========
def benchmark_torch(model, input_tensor, num_iter=100):
    # 预热10次
    for _ in range(10):
        with torch.no_grad():
            model(input_tensor)
    torch.cuda.synchronize() if torch.cuda.is_available() else None

    latencies = []
    start_total = time.perf_counter()
    for _ in range(num_iter):
        t0 = time.perf_counter()
        with torch.no_grad():
            model(input_tensor)
        latencies.append((time.perf_counter() - t0) * 1000)  # ms
    total_time = time.perf_counter() - start_total

    avg_latency = np.mean(latencies)
    throughput = num_iter / total_time
    return avg_latency, throughput

# ========== 4. OpenVINO推理性能测量 ==========
def benchmark_openvino(compiled_model, input_data, num_iter=100):
    infer_request = compiled_model.create_infer_request()
    # 预热
    for _ in range(10):
        infer_request.infer(input_data)

    latencies = []
    start_total = time.perf_counter()
    for _ in range(num_iter):
        t0 = time.perf_counter()
        infer_request.infer(input_data)
        latencies.append((time.perf_counter() - t0) * 1000)
    total_time = time.perf_counter() - start_total

    avg_latency = np.mean(latencies)
    throughput = num_iter / total_time
    return avg_latency, throughput

# ========== 5. 执行测量并输出结果 ==========
NUM_ITER = 200  # 测量迭代次数

print("--- PyTorch CPU 推理基线 ---")
torch.set_num_threads(8)  # 与NUC物理核数对齐,可按实际调整
pt_latency, pt_throughput = benchmark_torch(model, input_tensor, NUM_ITER)
print(f"平均延迟: {pt_latency:.2f} ms | 吞吐量: {pt_throughput:.2f} FPS")

print("\n--- OpenVINO 推理 ---")
core = Core()
compiled_model = core.compile_model(str(ir_path / "resnet50.xml"), "CPU")
ov_latency, ov_throughput = benchmark_openvino(compiled_model, input_numpy, NUM_ITER)
print(f"平均延迟: {ov_latency:.2f} ms | 吞吐量: {ov_throughput:.2f} FPS")

print("\n--- 加速比 ---")
print(f"延迟加速比: {pt_latency / ov_latency:.2f}x")
print(f"吞吐量加速比: {ov_throughput / pt_throughput:.2f}x")

运行方式

# 在Intel NUC上执行(需已安装上述依赖)
python3 resnet50_openvino_benchmark.py

验证方法与判定标准

  • 模型转换验证:脚本执行后,当前目录下应生成resnet50_ir/文件夹,内含resnet50.xml和resnet50.bin两个文件,且无报错输出。
  • 推理正确性验证:OpenVINO推理结果与PyTorch推理结果的argmax类别索引应一致(因输入为随机噪声,类别无实际意义,仅验证推理链路正确)。可在脚本末尾添加如下断言:
# 在脚本末尾追加(验证两种框架输出一致性)
with torch.no_grad():
    pt_output = model(input_tensor).numpy()
ov_output = compiled_model([input_numpy])[0]
assert np.argmax(pt_output) == np.argmax(ov_output), "输出不一致,推理链路错误"
print("推理输出一致性验证通过")
  • 性能判定标准:在Intel NUC(8代i5或更高)上,OpenVINO的延迟加速比通常应达到1.5x~3x,吞吐量提升2x~4x。若加速比低于1.2x,请检查:
    ① BIOS中是否开启Intel Turbo Boost;
    ② torch.set_num_threads()是否与NUC物理核心数匹配;
    ③ 是否使用了openvino-dev而非旧版openvino包。
  • 结果判定:脚本输出的延迟和吞吐量为实际测量值,不同NUC配置结果会有差异。只要OpenVINO推理延迟低于PyTorch基线且输出一致性断言通过,即视为案例部署成功。

常见问题

  • 模型转换报错:若convert_model提示缺少依赖,执行pip install openvino-dev[onnx]补装ONNX前端。
  • AVX2指令集不支持:OpenVINO 2023.3要求CPU支持AVX2。若NUC较旧(如6代以前),需改用OpenVINO 2022.3 LTS版本。
  • 内存不足:ResNet-50推理约需500MB内存,若NUC内存小于4GB,建议将NUM_ITER降至50。