PostgreSQL 的强大数据管理能力与 ONNX Runtime 的高性能机器学习推理能力相结合,实现了“库内机器学习”,从而避免了繁琐的数据移动,显著降低了系统复杂性和推理延迟。

这个架构的六个部分,并阐述其完整的技术流程。


PG + ONNX Runtime 架构详解

该架构的核心思想是:将机器学习推理作为数据库的一个内置函数,就像 SUM()AVG() 一样,允许用户使用标准的 SQL 语句直接在数据库内对数据进行模型推理。

1. ML 训练框架 → 导出 ONNX

目标: 获得一个与训练框架无关的、标准化的模型文件。

  • 技术过程:
    1. 模型开发: 数据科学家使用他们熟悉的 ML 框架进行模型训练和验证,例如 PyTorch, TensorFlow, Scikit-Learn, XGBoost 等。
    2. 模型转换: 训练完成后,利用各框架提供的转换工具或 onnx 生态库(如 torch.onnx.export, tf2onnx)将模型导出为 .onnx 格式文件。
    3. ONNX 优势:
      • 互操作性: 打破了框架锁定的问题。一个 PyTorch 训练的模型可以在 ONNX Runtime 中部署,无需关心后端。
      • 优化: ONNX 定义了一套通用的计算图表示,为后续的图优化(如算子融合、常量折叠)提供了基础。
      • 标准化: 统一的模型格式简化了部署流程。
2. ONNX 模型库 (.onnx)

目标: 集中化管理部署所需的模型。

  • 技术过程:
    • 导出的 .onnx 文件被存储在一个中心化的位置。这个位置可以是:
      • 数据库服务器的本地文件系统: 最简单的方式,PostgreSQL 扩展需要有文件读取权限。
      • 云存储(S3, GCS, Azure Blob): 更适用于云环境,扩展需要集成相应的 SDK 来下载模型。
      • 数据库表本身(作为 BLOB 存储): 将模型二进制数据存入专门的表。这种方式管理起来最“数据库原生”,扩展启动时从表中读取模型。
    • 通常需要一个版本管理机制,例如使用 model_nameversion 来唯一标识一个模型,以便在 SQL 函数中指定使用哪个模型。
3. PostgreSQL 扩展 (C/C++)

目标: 作为桥梁,将 PostgreSQL 与 ONNX Runtime 连接起来。

  • 技术过程:
    • 这是一个用 C 或 C++ 编写的 PostgreSQL 扩展(例如 pg_onnx)。它通过 PostgreSQL 的扩展 API 加载到数据库进程中。
    • 核心组件:
      • 模型加载器:
        • 提供 SQL 函数(如 onnx_load_model('model_name', '/path/to/model.onnx')),让数据库在启动时或按需将 ONNX 模型从模型库加载到内存。
        • 在内部,它调用 ONNX Runtime C API 来创建并初始化一个 Inference Session。
      • Session 管理器:
        • 维护一个 Session 池。为每个加载的模型创建多个 Session,以避免在高并发场景下频繁创建和销毁 Session 的开销。
        • Session 包含了模型的优化后计算图、已分配的缓冲区等,是执行推理的上下文环境。
      • SQL 函数 onnx_run()
        • 这是暴露给最终用户的接口。其函数签名可能类似于:
          onnx_run(model_name TEXT, input_data JSONB) -> JSONB
          -- 或者,为了更好的性能,使用特定的数组或自定义类型
          onnx_run(model_name TEXT, input_array FLOAT8[]) -> FLOAT8[]
          
        • 处理过程:
          1. 解析输入: 函数接收 SQL 传入的参数(如一行数据的某些列)。
          2. 数据转换: 将 PostgreSQL 的数据类型(如 FLOAT8[], TEXT)转换为 ONNX Runtime 所需的 C++ 数据结构(如 std::vector<float>, Ort::Value)。这是最关键也是最复杂的步骤之一,需要处理类型对齐和内存布局。
          3. 调用推理: 从 Session 池中获取一个空闲的 Session,传入转换好的输入数据,调用 session.Run()
          4. 接收输出: 获取 ONNX Runtime 返回的推理结果。
          5. 结果转换: 将输出数据从 ONNX Runtime 格式转换回 PostgreSQL 可以识别的数据类型(如 JSONB 或数组),并作为函数返回值。
4. ONNX Runtime 推理引擎

目标: 高效执行模型推理。

  • 技术过程:
    • PostgreSQL 扩展在初始化时动态链接 ONNX Runtime 库。
    • onnx_run() 被调用时,扩展将计算任务委托给 ONNX Runtime。
    • ONNX Runtime 内部工作流:
      1. 图优化: 在模型加载阶段,ONNX Runtime 会对原始的计算图进行一系列优化,例如:
        • 算子融合: 将多个小算子(如 Conv + BatchNorm + ReLU)合并为一个更高效的大算子。
        • 常量折叠: 将计算图中可以预先计算出的常量节点替换为结果。
        • 节点消除: 删除无用的节点(如 Identity)。
      2. 提供器系统: ONNX Runtime 支持不同的 Execution Providers
      3. 内核执行: 根据选定的 EP,调用相应的算子实现来执行计算图。
5. CPU/GPU 加速

目标: 利用硬件资源最大化推理性能。

  • 技术过程:
    • 这是在 第4点 中通过选择不同的 Execution Provider 来实现的。
    • CPU:
      • 默认使用 ONNX Runtime CPU EP。它可以利用现代 CPU 的 SIMD 指令集(如 AVX2, AVX-512)进行加速。
      • 配置简单,兼容性最好。
    • GPU:
      • 使用 CUDA EPTensorRT EP
      • CUDA EP: 提供通用的 GPU 加速支持。
      • TensorRT EP: NVIDIA 的深度学习推理优化器,能对模型进行更深层次的图优化和内核调优,通常能获得极致的性能,但转换过程可能需要额外时间。
      • PostgreSQL 服务器需要安装相应的 GPU 驱动和 CUDA 工具包。扩展在初始化时加载 CUDA EP,后续的模型计算将自动在 GPU 上执行。
6. 推理结果写回数据库

目标: 完成闭环,将推理结果持久化或供后续查询使用。

  • 技术过程:
    • onnx_run() 函数的返回值可以直接在 SQL 语句中使用。
    • 典型应用模式:
      • 批量推理与更新:
        -- 对一张表中的所有行进行批量推理,并将结果更新到新列中
        UPDATE my_table
        SET prediction = onnx_run('my_model', features_column);
        
      • 实时筛选:
        -- 在查询时实时推理,并筛选出符合条件的数据
        SELECT *
        FROM sensor_data
        WHERE onnx_run('anomaly_detection', sensor_readings) ->> 'is_anomaly' = 'true';
        
      • 聚合分析:
        -- 将推理结果与其他数据聚合
        SELECT 
            onnx_run('sentiment_analysis', review_text) ->> 'sentiment' as sentiment,
            COUNT(*)
        FROM customer_reviews
        GROUP BY sentiment;
        
    • 通过这种方式,机器学习推理无缝地融入了数据处理的工作流,无需将数据导出到外部 Python 脚本或 API 服务。

完整处理流程示例

假设我们有一个 products 表,包含 product_description 文本列,我们需要用 NLP 模型判断其情感倾向。

  1. 准备模型: 数据科学家在 PyTorch 中训练一个 BERT 情感分析模型,并使用 torch.onnx.export 导出为 sentiment_model.onnx
  2. 部署模型: DBA 将 sentiment_model.onnx 上传到数据库服务器的 /models/ 目录。
  3. 加载模型: 连接至 PostgreSQL,执行 SELECT onnx_load_model('sentiment', '/models/sentiment_model.onnx');。扩展加载模型并创建 CUDA Session 池。
  4. 执行推理: 分析师运行一条 SQL:
    SELECT 
        product_id,
        product_description,
        onnx_run('sentiment', product_description) -> 'sentiment' as sentiment_label
    FROM products;
    
  5. 内部执行:
    • 对于每一行,onnx_run 函数被调用。
    • 扩展将 product_description 文本进行预处理(分词、转换为 ID),并封装成 Ort::Value
    • 从池中获取一个 sentiment 模型的 Session,调用 session.Run()
    • ONNX Runtime 使用 CUDA EP 在 GPU 上高效执行模型计算。
    • 扩展获取输出 logits,转换为 positivenegative 标签,并封装成 JSONB。
    • 结果返回给 SQL 查询引擎。
  6. 获取结果: 查询最终返回一个包含产品 ID、描述和情感标签的结果集。这个结果可以直接被应用程序使用,或者通过 INSERT ... SELECT 写入另一张报表表中。

总结

PG + ONNX Runtime 架构 的核心价值在于 “将计算带给数据”。它通过一个精心设计的 PostgreSQL 扩展,将专业的 ML 推理引擎集成到数据库内核中,使得执行 SQL 和 ML 推理成为同一个事务。这带来了以下显著优势:

  • 极低的延迟: 避免了网络传输和数据序列化/反序列化的开销。
  • 简化架构: 无需维护独立的 ML 推理服务和相关的数据管道。
  • 强大的 SQL 集成: 能够利用 SQL 的全部能力(JOIN, WINDOW, AGGREGATION)与 ML 推理相结合,实现非常复杂的数据处理逻辑。
  • 高性能与灵活性: 得益于 ONNX Runtime 和其对多种硬件加速的支持。

这种架构特别适合于需要对海量数据进行实时或批量机器学习推理的场景,如金融风控、推荐系统、物联网异常检测和自然语言处理等。

更多推荐