ONNX生态简介与实战:ONNX Runtime

概述

官网,ONNX是Open Neural Network Exchange的缩写,作为一种开放开源(GitHub,21.2K Star,4K Fork)的神经网络交换格式,能够实现不同深度学习框架间的模型转换与共享,使得基于ONNX的模型可以在多种环境下高效运行。

在LLM之前(机器学习和深度学习时代)就是非常流行的模型格式(模型通用语言),跨框架+模型的中间表示(Intermediate Representation,IR)格式。

核心特性:

  • 跨框架互操作性:在PyTorch中训练的模型可以导出为ONNX格式,再被TensorFlow、MXNet等其他框架读取
  • 标准化算子集:定义统一的数学运算和神经网络操作标准
  • Protocol Buffers序列化:使用Google的Protobuf作为底层序列化协议,.onnx文件包含完整的计算图结构和权重参数
  • 版本化管理:通过opset_version管理算子集的演进

支持各种不同格式的模型之间的互相转换:

  • TensorFlow
  • PyTorch
  • TensorRT
  • Keras
  • CNTK
  • Caffe
  • TVM
  • mxnet
  • ncnn

定位(三大目标):

  • 将模型从任何框架转换为ONNX格式;
  • 将ONNX格式转换为任何其他的框架;
  • 使用推理引擎运行ONNX模型,让推理更快速。

生态图

如上图,大模型推理系统三个组成部分:

  • 硬件:硬件并行化,CPU、GPU、TPU底层加速原理有很大差异,但都追求最大并行化;
  • 软件:模型本身不变化,优化计算图的两种方向:
    • Low-level库:如英伟达提供的cuDNN包可更好地在GPU上处理;
    • 图编译器:如AI编译器TVM、GLOW主要优化前向传播或后向传播。
  • 算法:算法类方法会改变模型或架构来加速推理,如剪枝、量化、蒸馏、压缩。

应用场景:

  • 移动端:很多手机APP后台就是通过ONNX把大模型从PyTorch转到CoreML或NNAPI;
  • 医学影像:用PyTorch训练CT/MRI的病灶检测模型,ONNX+OpenVINO可把模型部署到普通CPU服务器;
  • 智能家居和IoT:ONNX能把PyTorch模型转成适合ARM芯片的TensorRT引擎;
  • 云端推理服务:HuggingFace、Azure、AWS都支持直接加载ONNX模型,一键部署为云端推理API。

优点

  • 跨平台性强:PyTorch、TensorFlow、Scikit-learn、XGBoost甚至部分大模型都能导出到ONNX;
  • 部署灵活:支持CPU、GPU、ARM、FPGA、NPU,基本覆盖主流硬件;
  • 推理速度快:ONNX Runtime、TensorRT等配合下,能比原始框架提升2~5倍速度;
  • 生态丰富:微软、AWS、Intel、NVIDIA等巨头都在推,社区活跃。

缺点

  • 不支持所有算子:新模型里的自定义层可能需要手动写插件;
  • 调试成本高:模型一旦转换失败,错误信息可能晦涩难懂;
  • 大模型适配慢:像GPT-4这种超大模型,ONNX生态还在追赶;
  • 版本兼容问题:不同框架或opset可能导致兼容性问题。

趋势

  • 支持大模型:正在适配Transformer、LLM的常见算子;
  • 轻量化结合:支持量化、蒸馏,让大模型能跑在手机上;
  • 推理引擎一体化:未来可能直接一键导出+自动优化+多硬件部署,极大降低门槛;
  • 云边端协同:成为AI模型在云端与边缘之间的桥梁,打通全链路。

Converter

即ONNX Converter,转换器,用于在不同框架和ONNX之间转换的工具:

  • torch.onnx.export():PyTorch转ONNX
  • tf2onnx:TensorFlow转ONNX
  • onnx2tf:ONNX转TensorFlow
  • onnx2pytorch:ONNX转PyTorch
  • onnx2keras:ONNX转Keras

格式

ONNX使用Protocol Buffers格式存储模型,直接利用Protobuf的IR(onnx.proto)和编译器能力(protoc),将模型的计算图结构序列化为二进制文件。

可直接使用Protobuf将ONNX模型转换为可读的文本:

bash 复制代码
wget https://github.com/onnx/onnx/raw/main/onnx/onnx.proto
wget https://github.com/onnx/models/blob/main/validated/vision/classification/mnist/model/mnist-8.onnx
protoc --decode=onnx.ModelProto --proto_path=E:/ipynb onnx.proto < ./mnist-8.onnx > ./mnist.onnx.txt

得到mnist.onnx.txt文件,主要由三部分组成:

  • 可扩展的计算图模型的定义
  • 标准数据类型定义
  • 内部算子定义

onnx.proto 文件定义Model、Graph、Node、Tensor等核心数据结构,是理解和操作ONNX文件的底层基础。

复制代码
ModelProto
  ├── ir_version: 版本号
  ├── opset_import: 算子集版本
  ├── GraphProto
  │	├── node[]: 计算节点列表
  │	├── initializer[]: 权重张量
  │	├── input[]: 输入定义
  │	└── output[]: 输出定义
  └── metadata_props: 元数据

量化

ONNX支持多种量化方式,将模型从FP32压缩为INT8/FP16,减少模型体积并提升推理速度:

  • 动态量化:推理时动态计算量化参数
  • 静态量化:使用校准数据集预计算量化参数
  • 量化感知训练(QAT):在训练过程中模拟量化效果

参考大模型基础之量化

算子

参考官方文档

每个ONNX版本对应一个算子集版本号opset_version。随着版本演进,新算子被加入,老算子被废弃。导出模型时需指定opset_version,决定模型中可用的算子范围。

  • Opset 11:常见基础算子
  • Opset 15:支持更多Transformer相关算子
  • Opset 17-18:扩展更多高级算子
  • Opset 20+:最新算子集支持

前两者结合,即为IR;算子包含基本算子和函数算子Functions,后者表示复合操作,由其他算子组合而成的一个子图。如果一个运行时没有实现某个函数算子,就将函数算子替换为其子图的具体实现,称为内联。

IR定义计算图结构:

计算图本质上是一个有向无环图DGA:

  • 节点:表示具体操作,如卷积、Softmax算子;
  • 边:表示Tensor,张量;
  • 控制流:特殊节点;
  • 节点依赖关系:特殊边,表示节点之间的依赖关系

算子操作符组成:

  • 算子类型,如Conv2D、Softmax
  • 属性:算子参数,如卷积的kernel_size、stride、paddin
  • 输入/输出张量信息:如张量的Shape、DType

model,graph,node,tensor等数据结构

计算图

ONNX,定义一套标准化的计算图表示方式,用于描述神经网络模型的结构和权重。

PyTorch训练的模型会变成一个计算图,继而在对应的硬件上运行,如果计算图可以表示为一个中间表示IR,则图编译器(或AI编译器)就很容易在不同设备上优化,IR可以理解为一种通用表示。

AI框架和AI编译器的区别:

  • AI框架可能会使用一些新的算子,但AI编译器可能没有实现;
  • 一些算子的实现方式可能不一样;
  • 有些编译器只能和某些框架适配,如Intel的OpenVino编译器只能用于TensorFlow,不适合PyTorch。

ONNX是一种通用模型计算图格式,大部分AI框架/AI编译器都支持它。

AI编译器将不同AI框架中的高级计算图映射为特定硬件上的执行操作,会进行一系列优化:

  • 图重写:图结构决定操作OP的执行顺序,而作业调度(Job scheduling)考虑如何将OP执行顺序最佳化:
    • 删除一些节点或边
    • 算子融合
    • 子图替代
    • 删除无用的层
  • 算子融合:将多个连续的操作合并为一个复合操作,在计算图中,由很多细粒度的算子组成,每个操作都要独立的计算和读写内存,可以将多个连续的操作合并为一个复合操作,这样可以减少中间结果的存储和读取,也能充分利用硬件专有的Kernel(如专为矩阵乘操作的Tensor Core)。比如,convolution、ReLU、BatchNorm可以融合为计算图中的一个节点(算子)
  • 算子分配与调度优化
    上述两个优化基本上是和硬件无关的,对于图编译器来说,最终操作要运行在各种硬件上,它充当了硬件的抽象,选择不同的算子运行在特定硬件上(GPU、NPU、CPU),比如卷积操作适合 GPU运行,标量运算适合CPU运行;通过考虑跨硬件依赖关系,合理调度执行的顺序,最终提升运行效率。

目前大部分 AI 框架都有自己的计算图表示方法和优化目标,可以根据需求开发大模型,但大模型部署越来越多样化(比如边缘设备运行,一个框架部署到另外一个框架支持的环境),那么不同框架开发出来的模型跨平台部署或性能优化就非常困难了。

这时候计算图如果有一个统一的中间表示 IR,用于屏蔽不同框架之间的差异,从而让不同 AI 编译器去优化,ONNX 就是这样一个通用 IR。

实战

转换

PyTorch转换为ONNX:

py 复制代码
import torch
import torch.nn as nn

# 定义模型
class SimpleClassifier(nn.Module):
	def __init__(self):
		super(SimpleClassifier, self).__init__()
		self.fc = nn.Linear(768, 2)  # BERT输出768维
	def forward(self, x):
		return self.fc(x)

# 初始化模型
model = SimpleClassifier()
model.eval()
dummy_input = torch.randn(1, 768)
torch.onnx.export(
	model,
	dummy_input,
	"classifier.onnx",
	input_names=["input"],
	output_names=["output"],
	dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}},
	opset_version=17
)

解读:

  • dynamic_axes:保证输入输出支持动态Batch;
  • opset_version:版本号,建议选13+,兼容性更好。

推理

使用ONNX Runtime:

py 复制代码
import onnxruntime as ort
import numpy as np

# 加载模型
session = ort.InferenceSession("classifier.onnx")
input_data = np.random.randn(1, 768).astype(np.float32)
inputs = {"input": input_data}
# 推理
outputs = session.run(None, inputs)
print("推理结果:", outputs[0])

FastAPI

集成FastAPI,部署为服务:

py 复制代码
from fastapi import FastAPI
import uvicorn

app = FastAPI()
session = ort.InferenceSession("classifier.onnx")
@app.post("/predict")
def predict(data: list):
	input_data = np.array(data, dtype=np.float32).reshape(1, -1)
	result = session.run(None, {"input": input_data})
	return {"prediction": result[0].tolist()}
if __name__ == "__main__":
	uvicorn.run(app, host="0.0.0.0", port=8000)

ONNX Runtime

官网,简称ORT,微软官方开源(GitHub,21.2K Star,4.1K Fork)跨平台、高性能运行时推理引擎,支持CPU/GPU/NPU,专门用于执行ONNX模型,负责将ONNX格式的模型加载到内存中,并利用硬件加速能力高效运行推理。

核心特性:

  • 多语言API:支持Python、C++、C#、Java、JavaScript、Rust
  • 跨平台支持:Linux、Windows、macOS、Android、iOS,甚至Web浏览器
  • Execution Providers机制:通过插件式的执行提供者,支持不同硬件后端
  • 图优化技术:自动进行算子融合、常量折叠、内存优化等
  • 训练加速:除了推理,还支持大模型训练加速

核心概念

  • 运行时:ORT之所以可在不同的硬件设备上运行,抽象一个API,称为Execution Providers(EP),如CUDA EP。
  • EP:EP 是对某个硬件的一种抽象,每个 EP 向 ORT 说明其硬件支持的能力,比如支持的算子、GPU显存管理、异构调度执行。EP一般由ORT官方和厂商合作开发,如cuDNN调用。如果没有算子分配给 EP 执行,那 ORT 会有回退机制,如让CPU去执行,确保模型能够总是能够运行。两种类型:
    • Kernel EP,如CPUExecutionProvider、CUDAExecutionProvider,ORT官方实现的;
    • Runtime EP,一般是硬件厂商实现的,如TensorRTExecutionProvider,用于专门的优化。
  • Graph图优化:ONNX模型就是一个计算图,ORT就是对计算图进行优化。图优化分为离线和在线模式;也可分为三层优化:
    • 基础优化:保留语义的图重写,和具体后端硬件无关,在图分区之前运行,包括常量折叠、冗余节点删除(如Identity、Slice、Unsqueeze、Dropout)、算子融合。
    • 扩展优化:包括更复杂的节点集成,只适用于分配给CPU、CUDA和ROCm执行的节点,也就是说和硬件有关,或者和特定模型架构有关。
    • 张量内存布局优化:改变数据布局,让硬件更好优化
  • 图转换:图优化可从其他角度理解,如从模型的应用水平分析看,优化一般被称为图转换,图转换包含图优化。分为三个层次:
    • 无条件转换:计算图生成后可以立刻进行的图转换,如Cast、Memcpy等操作确保张量类型和设备一致
    • 通用转换:和设备无关的转换,比如在推理阶段移除计算图中的Dropout;
    • EP级别转换:图转换一种分类:
      • 全局模式:图转换会对整个计算图进行转换,在ORT中该接口称为Graph Transformer;
      • 局部模式:图转换对某个子图或一些节点进行转换,在ORT中该接口称为Rewriting Rule。
  • Graph Partitioning:图分区。和EP有关,ORT根据可用EP将模型图划分为多个子图(每个子图是某个EP连续能处理的节点),每个子图对应一个不同的EP,ORT提供一个默认EP(cpu),如果没有找到更高效的EP处理子图,就使用默认的EP处理。
    图分区的具体技术实际上也很简单,按照特定顺序考虑可用的EP,为每个EP分配其能够处理的最大子图,采用贪婪搜索的模式。
    最终图转换和图分区将原始模型图转换为由分配给默认EP或其他已注册EP的算子组成的新图,ORT执行引擎负责运行该图,也可以叫做图调度和执行。
  • 整体架构
    从组件的角度理解,如下图:

    整体比较简单:
    • 加载ONNX模型;
    • 转换成内存中的图结构;
    • 图分区,根据支持情况拆分子图;
    • 并行调度器运行各个子图,交由不同EP执行
      从图优化和图分区处理流程角度理解,更详细:

      涵盖核心流程:
    • ONNX模型加载成中间表示IR;
    • 进行平台无关的图优化(如节点融合)
    • 进行图分区;
    • 针对每个EP做定制优化;
    • 使用顺序或并行调度执行EP;
    • 各个EP负责具体执行子图
  • ORT主要目标:
    • 充分使用硬件能力:最大限度地自动利用不同硬件平台上的加速器(如GPU)和运行时;
    • 提供硬件工作的抽象接口:为自定义加速器和运行时提供正确的抽象和运行时支持,这种抽象为EP,它定义并向ORT公开功能;
    • 兼容性:不奢望模型在一个EP上完全运行,可在异构设备上协同工作,强调兼容性
    • 图转换:ORT的核心功能,为模型计算图转换提供优化功能:全局优化,具体算子优化。

EP

Execution Providers,执行提供者,通过插件化的架构,允许同一个ONNX模型在不同硬件上无缝切换运行:

执行提供者 硬件平台 使用场景
CPUExecutionProvider CPU 通用推理,无GPU环境
CUDAExecutionProvider NVIDIA GPU GPU加速推理
TensorrtExecutionProvider NVIDIA GPU 通过TensorRT进一步优化
DirectMLExecutionProvider Windows GPU 支持AMD/Intel/NVIDIA GPU
OpenVINOExecutionProvider Intel CPU/GPU/VPU Intel硬件优化
CoreMLExecutionProvider Apple Silicon macOS/iOS优化
XNNPACKExecutionProvider CPU/移动端 移动端和边缘设备
WebGPUExecutionProvider 浏览器 Web端推理
ACLExecutionProvider ARM CPU ARM架构优化
ROCmExecutionProvider AMD GPU AMD GPU加速

选择不同EP

py 复制代码
import onnxruntime as ort

# CPU推理
session_cpu = ort.InferenceSession('model.onnx')
# GPU推理(CUDA)
session_gpu = ort.InferenceSession('model.onnx', providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
# TensorRT推理(NVIDIA GPU上极致优化)
session_trt = ort.InferenceSession('model.onnx', providers=['TensorrtExecutionProvider', 'CUDAExecutionProvider'])

ORT图优化

图转换,分为四个层次,参考core/optimizer/graph_transformer_level.h代码:

c 复制代码
enum class TransformerLevel : int {
	Default = 0,  // required transformers only
	Level1,       // basic optimizations
	Level2,       // extended optimizations
	Level3,       // layout optimizations
	// The max level should always be same as the last level.
	MaxLevel = Level3
};

解读:

  • Default层次:不管如何配置,都会进行无条件转换,大部分情况是图转换前就优化,也有图转换后再做优化
  • Level1:基础优化,和后端EP无关,通用型优化,如常量折叠、算子融合
  • Level2:扩展优化,一般针对特定后端EP进行算子融合优化
  • Level3:Layout布局优化,目前就只有一个NhwcTransformer

拓展

Olive

GitHub

功能:微调、量化。

olive quantize子命令可支持各种量化方法,包括AWQ、GPTQ、BitsAndBytes、ORT静态方法。

量化示例:

bash 复制代码
olive quantize --model_name_or_path meta-llama/Llama-3.2-1B-Instruct --algorithm awq --output_path models/llama/awq --log_level 1
# 带校准数据
olive quantize --algorithm gptq \
	--model_name_or_path meta-llama/Llama-3.2-1B-Instruct  \
	--data_name wikitext --subset wikitext-2-raw-v1 \
	--split train --max_samples 128 --output_path models/gptq

优化示例:

bash 复制代码
olive auto-opt \
	--model_name_or_path models/llama/awq \
	--device cpu \
	--provider CPUExecutionProvider \
	--use_ort_genai \
	--output_path models/llama/onnx \
	--log_level 1

将量化模型优化为ONNX模型。

先量化再微调,量化带来的损失可能在微调过程中得以校准:

bash 复制代码
olive quantize --model_name_or_path meta-llama/Llama-3.2-1B-Instruct \
	--trust_remote_code --algorithm awq \
	--output_path models/llama/awq --log_level 1

olive finetune --method lora --model_name_or_path models/llama/awq \
	--data_name xxyyzzz/phrase_classification \
	--text_template "<|start_header_id|>user<|end_header_id|>\n{phrase}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n{tone}" \
	--max_steps 100 --output_path ./models/llama/ft --log_level 1

# 自动优化
olive auto-opt --model_name_or_path models/llama/ft/model \
	--adapter_path models/llama/ft/adapter --device cpu \
	--provider CPUExecutionProvider --use_ort_genai \
	--output_path models/llama/onnx --log_level 1

可视化

ONNX可视化工具有很多,参考模型可视化项目

相关推荐
大鹏的NLP博客11 天前
深度学习模型PyTorch与ONNX数值一致性校验通用技术报告
人工智能·pytorch·深度学习·onnx
阿钱真强道13 天前
13 YOLOv8的ONNX输出结构——9个张量如何变成检测框
目标检测·onnx·yolov8·nms·dfl
weiwei228441 个月前
神经网络模型导出及开放标准格式ONNX
pytorch·onnx
再一次等风来1 个月前
YOLO26 实测记录:从模型下载、预测验证到 ONNX Runtime 推理部署
yolo·计算机视觉·onnx·yolo26
慢慢向上的蜗牛1 个月前
Qwen3-0.6B ONNX(KV-Cache)模型部署
llm·onnx·文本生成·自回归·kv-cache
指尖在键盘上舞动2 个月前
RKNN 模型部署:onnx转rknn后精度下降 —— 精度调优与问题排查
python·ubuntu·rk3588·rknn·onnx·npu
vonlycn3 个月前
PaddleDetection转ONNX 填坑
python·onnx·paddledetection
antzou3 个月前
字幕视频合成
onnx·tts·asr·vad·paraformer
antzou3 个月前
语音识别 (ASR)
人工智能·语音识别·onnx·asr·paraformer