2. 评分模型基础:模型文件格式(ONNX/PMML)、模型加载与单次推理流程

在生产环境中部署评分模型,首先需要理解模型的持久化格式以及加载推理的基本原理。本章将深入剖析两种主流开放标准格式——ONNX 与 PMML,并演示从模型加载到单次推理的完整链路。

2.1 模型文件格式对比:ONNX vs PMML

ONNX(Open Neural Network Exchange)与 PMML(Predictive Model Markup Language)分别代表了深度学习与传统机器学习领域的标准化方向。下表从核心定位、适用场景、推理性能与生态支持四个维度进行对比:

对比维度 ONNX PMML
核心定位 深度学习模型交换格式,聚焦算子图与张量计算 传统机器学习模型交换格式,聚焦数据变换与统计模型
适用模型 CNN、RNN、Transformer、GNN 等深度神经网络 决策树、随机森林、逻辑回归、GBDT、朴素贝叶斯等
推理性能 支持 GPU/CUDA/TensorRT 加速,算子融合优化 通常运行在 CPU 上,优化依赖实现引擎
生态支持 PyTorch、TensorFlow、ONNX Runtime、OpenVINO R、Python(sklearn2pmml)、Java(JPMML)
文件结构 Protobuf 序列化的二进制图结构 XML 文本格式,包含数据字典、转换字典、模型节点
选型建议:若评分通道以深度学习模型为主(如 CTR 预估中的 DNN、NLP 评分),优先选择 ONNX;若通道以传统树模型或集成模型为主,且需要跨语言部署,PMML 是更成熟的选择。

2.2 ONNX 模型结构解析

ONNX 模型文件本质上是一个序列化的 ModelProto 对象,其核心组件包括:

  • GraphProto:计算图定义,包含所有节点(NodeProto)、输入输出(ValueInfoProto)与初始化张量(TensorProto)。
  • NodeProto:单个算子节点,记录算子类型(如 Conv、Relu、MatMul)、输入输出名称与属性。
  • TensorProto:存储权重、偏置等常量数据,支持多种数据类型(FLOAT、INT64、UINT8 等)。
  • ModelMetadata:模型版本、生产者信息、文档描述等元数据。

下图展示了 ONNX 模型文件的内部层级结构:

ONNX 模型文件内部结构 ModelProto GraphProto ModelMetadata OperatorSetId NodeProto[] ValueInfoProto[] TensorProto[] 关键说明 • NodeProto[]:算子节点列表,定义计算流程(Conv → Relu → Pool → ...) • ValueInfoProto[]:输入/输出张量的名称、形状与数据类型 • TensorProto[]:模型权重与偏置常量,以 Protobuf 二进制存储

2.3 PMML 模型结构解析

PMML 采用 XML 层级结构,核心元素包括:

  • DataDictionary:定义输入字段的名称、数据类型(double、string、integer)与有效值范围。
  • TransformationDictionary:定义数据预处理逻辑,如归一化、离散化、缺失值填充。
  • MiningModel / RegressionModel / TreeModel:具体的模型定义,包含分段函数、回归表、决策树节点等。
  • Output:定义输出字段的名称与后处理逻辑(如概率转换、类别映射)。
PMML 优势:完整记录了从原始特征到最终评分的全链路变换,使得模型部署不依赖训练框架的预处理代码,真正实现“一次训练,到处评分”。

2.4 模型加载与单次推理流程

无论使用 ONNX 还是 PMML,评分通道的推理流程均可抽象为以下四个阶段:

  1. 模型加载:将模型文件从磁盘读入内存,解析为运行时对象。
  2. 输入预处理:将原始请求数据转换为模型期望的张量形状与数据类型。
  3. 推理执行:调用推理引擎执行前向计算,得到原始输出。
  4. 输出后处理:将原始输出转换为业务评分(如概率、分数、等级)。

下图以 ONNX Runtime 为例,展示单次推理的完整数据流:

ONNX Runtime 单次推理流程 1. 模型加载 2. 输入预处理 3. 推理执行 4. 输出后处理 各阶段核心操作 ① 模型加载:Ort::SessionOptions 配置会话,Ort::Session::Load(model_path) 加载 ONNX 文件 ② 输入预处理:将 JSON/CSV 特征转换为 Ort::Value,shape 对齐模型输入要求 ③ 推理执行:session.Run(run_options, input_names, input_values, output_names, output_values) ④ 输出后处理:从 Ort::Value 提取 float 数组,应用 sigmoid/softmax 或业务映射

2.5 代码示例:ONNX Runtime 单次推理

以下 Python 代码演示了加载 ONNX 模型并执行单次推理的完整过程:

import numpy as np
import onnxruntime as ort

# 1. 创建推理会话
session = ort.InferenceSession(
    "credit_scoring_model.onnx",
    providers=["CUDAExecutionProvider", "CPUExecutionProvider"]
)

# 2. 获取输入输出信息
input_name = session.get_inputs()[0].name
input_shape = session.get_inputs()[0].shape   # 例如 [1, 128]
output_name = session.get_outputs()[0].name

# 3. 构造输入数据(模拟单条样本)
input_data = np.random.randn(1, 128).astype(np.float32)

# 4. 执行推理
raw_output = session.run([output_name], {input_name: input_data})[0]

# 5. 后处理:sigmoid 转换为概率
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

score = sigmoid(raw_output[0][0])
print(f"评分结果: {score:.4f}")
性能注意:在高并发场景下,应避免每次请求都重新加载模型。模型应作为全局单例在服务启动时加载,推理会话复用。ONNX Runtime 的 Session 对象是线程安全的,可被多个请求共享。

2.6 模型加载的常见陷阱

  • 动态轴问题:ONNX 模型输入 shape 可能包含动态维度(如 batch_size = -1),推理时需要显式绑定实际 shape。
  • 算子兼容性:不同 ONNX opset 版本支持的算子不同,导出时需确认目标推理引擎支持的 opset 版本。
  • PMML 缺失值处理:PMML 模型在 TransformationDictionary 中定义了缺失值策略,但不同实现引擎(如 JPMML、sklearn2pmml)的默认行为可能不一致,需显式验证。
  • 内存泄漏:频繁创建/销毁 Ort::Session 会导致 GPU 显存泄漏,务必使用连接池或单例模式管理会话生命周期。
最佳实践:在模型加载阶段执行一次“预热推理”(warm-up inference),触发算子编译与显存分配,避免首次请求延迟过高。