跳转至

Vertica 机器学习模型版本管理

原文:Machine Learning Model Versioning 适用版本:Vertica 12.0SP4+

概述

OpenText Vertica 数据库被全球众多企业信赖,用于大规模存储和分析大数据。在数据库内部运行机器学习具有诸多优势——包括增强的性能、安全收益以及简化维护。Vertica-ML 已在 Vertica 数据库中提供分布式机器学习能力多年,且不断添加新的 ML 算法和工具,赋能用户在数据库内运行高性能 ML 算法。

Vertica 的愿景是成为 ML 生态系统中的模型存储和管理系统。为此,Vertica 已开始支持导入由外部库训练的 ML 模型。无论是内部训练还是外部训练后导入的模型——一旦归档到 Vertica 数据库中,都享有与其他数据库对象同等的安全管理能力。通过引入数据库内模型版本管理(In-DB Model Versioning),Vertica 旨在为归档在数据库中的模型提供 MLOps 支撑。

模型版本管理为 Vertica 数据库中的 ML 模型提供了生产级管理基础设施:

  • 将候选模型注册到特定应用(registered_name
  • 跟踪模型状态流转(审核 -> 预发布 -> 生产 -> 归档/拒绝)
  • 多用户协同提交候选模型
  • 生产模型到历史版本的自动归档与回滚

所有模型(无论 Vertica 内生训练还是外部训练后导入的 PMML/TensorFlow 模型)均享受同等的安全和版本管理能力。

什么是模型版本管理

在生产流水线中,通常有一个计算机程序使用训练好的 ML 模型进行预测。由于许多应用场景中数据具有非平稳性(non-stationary),生产中的模型需要定期被新训练的模型替换,有时甚至一天数次。此外,出于技术、业务和合规目的,之前投入生产的模型应被归档,并在必要时能够重回生产。模型版本管理提供了满足这些需求的基础设施,降低了模型管理的复杂性。

大多数情况下,数据科学家在提出一个候选生产模型之前会训练大量模型。Vertica 12.0SP4 引入的模型版本管理提供了一个基础设施,用于注册候选生产模型,并在数据库中跟踪和管理其状态。该版本管理基础设施支持协作环境,多个用户可以针对同一应用提交候选模型,这些模型通过 registered_name 标识。在模型版本管理环境中,同一 registered_name 下的所有注册模型——无论其创建者、算法类型或生产状态如何——都被视为同一模型的不同版本。

MLSUPERVISOR 角色(12.0SP3+)

MLSUPERVISOR 是预定义角色,继承了 DBADMIN 的所有 ML 模型管理权限。换言之,被授予 MLSUPERVISOR 角色的用户可以代表 dbadmin 管理 V_CATALOG.MODELS 表中的所有模型。通过将这一角色授予受信任的用户,dbadmin 可以将模型管理任务的职责委派出去,解放自己。

模型版本管理操作说明

为了理解数据库内模型版本管理的工作方式,假设 Vertica 的一个客户有两位数据科学家(DS1 和 DS2)以及两个 ML 应用(Application-I 和 Application-II)。

Application-I

第一位数据科学家 DS1 在训练和评估大量模型后,决定将使用 DB 内 rf_classifier 算法训练的模型 RF_model 提交为 Application-I 的候选。她通过调用 register_model 函数注册模型:

DS1 注册 schema1.RF_model

-- user DS1 registers the schema1.RF_model model to model_app_I
=> SELECT register_model('schema1.RF_model', 'model_app_I');
REGISTER_MODEL
-------------------------------------------------------------------------
Model [schema1.RF_model] is registered as [model_app_I], version [1]
(1 row)

register_model 函数接受两个参数:第一个是要注册的模型名称,第二个是 REGISTERED_NAME(注册名称)。REGISTERED_NAME 可以代表一个更高层次应用的一组模型,组内每个模型拥有唯一的版本号。注册模型后,该模型会出现在 v_catalog.registered_models 表中。

假设此前没有模型以 registered_namemodel_app_I 注册过,因此 schema1.RF_model 的注册版本号设为 1。注册模型意味着将其提名为生产候选,因此需要提升该模型的安全性。为此,模型的所有者变为 dbadmin,原所有者仅保留 USAGE 权限。拥有 USAGE 权限的用户可以:

  • v_catalog.modelsv_catalog.registered_models 表中查看该模型
  • 使用 get_model_summaryget_model_attribute 函数检索模型信息
  • 使用相应的转换函数为数据库内评分(in-DB scoring)应用该模型

模型刚注册完毕时的状态为 under_reviewregistered_models 表中模型的状态有五个有效值:under_reviewstagingdeclinedproductionarchived。下图展示了模型的不同状态及它们之间的有效转换:

Howto Mlver 0

只有拥有 MLSUPERVISOR 角色(或 DBADMIN)的用户才能更改注册模型的状态。假设在我们的例子中有一位 ML 管理员(supervisor),他批准了最近注册的模型并将其状态改为 staging。处于此状态的模型可能会进入 A/B 测试或进行其他验证。在此阶段之后,ML 管理员可以将模型状态改为 production。使用 change_model_status 函数来更改注册模型的状态:

Supervisor 变更 (model_app_I, 1) 的状态

-- supervisor changes the status of (registered_name, registered_version)=(model_app_I, 1) to staging
=> SELECT change_model_status('model_app_I', 1, 'staging');
CHANGE_MODEL_STATUS
-----------------------------------------------------------------------------
The status of model [model_app_I] - version [1] is changed to [staging]
(1 row)

-- supervisor changes the status of (model_app_I, 1) to production
=> SELECT change_model_status('model_app_I', 1, 'production');
CHANGE_MODEL_STATUS
-----------------------------------------------------------------------------
The status of model [model_app_I] - version [1] is changed to [production]
(1 row)

=> SELECT * FROM REGISTERED_MODELS;
registered_name | registered_version |   status   |        registered_time        |      model_id     | schema_name | model_name |   model_type  |   category
-----------------+--------------------+------------+-------------------------------+-------------------+-------------+------------+---------------+----------------
model_app_I     |                  1 | PRODUCTION | 2023-03-23 09:52:00.082166-04 | 45035996273714020 | schema1     | RF_model   | RF_CLASSIFIER | VERTICA_MODELS
(1 row)

此时,第二位数据科学家 DS2 为 Application-I 训练了一个类型为 xgb_classifier 的 DB 内模型,并将其注册到 model_app_I。该模型的初始状态为 under_review,版本号为 2:

DS2 注册 schema2.XGB_model

-- user DS2 registers the schema2.XGB_model model to model_app_I
=> SELECT register_model('schema2.XGB_model', 'model_app_I');
REGISTER_MODEL
-------------------------------------------------------------------------
Model [schema2.XGB_model] is registered as [model_app_I], version [2]
(1 row)

管理员可以再次将该模型的状态改为 staging,然后改为 production。在同一 registered_name 的模型中,只能有一个模型处于 production 状态。因此,将新模型的状态改为 production 会自动将前一个 production 模型的状态改为 archived:

Supervisor 变更 (model_app_I, 2) 的状态

-- supervisor changes the status of (model_app_I, 2) to staging
=> SELECT change_model_status('model_app_I', 2, 'staging');
CHANGE_MODEL_STATUS
-----------------------------------------------------------------------------
The status of model [model_app_I] - version [2] is changed to [staging]
(1 row)

-- supervisor changes the status of (model_app_I, 2) to production
=> SELECT change_model_status('model_app_I', 2, 'production');
CHANGE_MODEL_STATUS
-----------------------------------------------------------------------------
The status of model [model_app_I] - version [2] is changed to [production]
(1 row)

=> SELECT * FROM REGISTERED_MODELS;
registered_name | registered_version |   status   |        registered_time        |      model_id     | schema_name | model_name |   model_type   |   category
-----------------+--------------------+------------+-------------------------------+-------------------+-------------+------------+----------------+----------------
model_app_I     |                  2 | PRODUCTION | 2023-03-24 05:29:25.990626-02 | 45035996273714020 | schema2     | XGB_model  | XGB_CLASSIFIER | VERTICA_MODELS
model_app_I     |                  1 | ARCHIVED   | 2023-03-23 09:52:00.082166-04 | 45035996273850350 | schema1     | RF_model   | RF_CLASSIFIER  | VERTICA_MODELS
(2 rows)

Application-II

两位数据科学家可以异步地为多个应用注册模型。所有注册模型都会被自动分配一个版本号。版本号是严格递增的整数,(registered_name, registered_version) 组合保证全局唯一。假设 Application-II 是一个回归问题。首先,DS2 提交一个在 Python 中训练、转换为 PMML、然后导入到 Vertica 的模型:

DS2 注册 schema2.py_model

-- user DS2 registers the schema2.py_model model to model_app_II
=> SELECT register_model('schema2.py_model', 'model_app_II');
REGISTER_MODEL
-------------------------------------------------------------------------
Model [schema2.py_model] is registered as [model_app_II], version [1]
(1 row)

然后,Supervisor 将该注册模型投入 model_app_II 的生产状态:

Supervisor 变更 (model_app_II, 1) 的状态

-- supervisor changes the status of (model_app_II, 1) to staging
=> SELECT change_model_status('model_app_II', 1, 'staging');
CHANGE_MODEL_STATUS
-----------------------------------------------------------------------------
The status of model [model_app_II] - version [1] is changed to [staging]
(1 row)

-- supervisor changes the status of (model_app_II, 1) to production
=> SELECT change_model_status('model_app_II', 1, 'production');
CHANGE_MODEL_STATUS
-----------------------------------------------------------------------------
The status of model [model_app_II] - version [1] is changed to [production]
(1 row)

与此同时,DS1 将一个在 Vertica 中训练的模型注册到同一应用:

DS1 注册 schema1.linreg

-- user DS1 registers the schema1.linreg model to model_app_II
=> SELECT register_model('schema1.linreg', 'model_app_II');
REGISTER_MODEL
-------------------------------------------------------------------------
Model [schema1.linreg] is registered as [model_app_II], version [2]
(1 row)

随后,DS2 将一个在 Vertica 外部使用 TensorFlow 训练、然后导入到 Vertica 的模型注册到 model_app_II

DS2 注册 schema2.tf_model

-- user DS2 registers the schema2.tf_model model to model_app_II
=> SELECT register_model('schema2.tf_model', 'model_app_II');
REGISTER_MODEL
-------------------------------------------------------------------------
Model [schema2.tf_model] is registered as [model_app_II], version [3]
(1 row)

假设 Supervisor 先只变更 DS1 的模型状态:

Supervisor 变更 (model_app_II, 2) 的状态

-- supervisor changes the status of (model_app_II, 2) to staging
=> SELECT change_model_status('model_app_II', 2, 'staging');
CHANGE_MODEL_STATUS
-----------------------------------------------------------------------------
The status of model [model_app_II] - version [2] is changed to [staging]
(1 row)

=> SELECT * FROM REGISTERED_MODELS WHERE registered_name = 'model_app_II';
registered_name | registered_version |    status    |        registered_time        |      model_id     | schema_name | model_name |      model_type       |   category
-----------------+--------------------+--------------+-------------------------------+-------------------+-------------+------------+-----------------------+----------------
model_app_II    |                  3 | UNDER_REVIEW | 2023-03-25 10:29:25.970567-02 | 45035996273853740 | schema2     | tf_model   | TF_MODEL              | TENSORFLOW
model_app_II    |                  2 | STAGING      | 2023-03-25 08:19:41.190625-03 | 45035996273853815 | schema1     | linreg     | LINEAR_REGRESSION     | VERTICA_MODELS
model_app_II    |                  1 | PRODUCTION   | 2023-03-21 09:42:10.082135-01 | 45035996273853890 | schema2     | py_model   | PMML_REGRESSION_MODEL | PMML
(3 rows)

上述示例结束后,数据库内模型版本管理基础设施各要素及 registered_models 表的状态如下图所示:

Howto Mlver 1

DBADMIN 或任何拥有 MLSUPERVISOR 角色的用户可以使用 DROP MODEL 语句删除任何注册模型,无论其当前状态如何。在我们的示例中,假设 Supervisor 删除了 (model_app_II, 2) 模型(其对应的 schema_namemodel_name 可在 REGISTERED_MODELS 表中查到):

Supervisor 删除 (model_app_II, 2)

-- supervisor drops (model_app_II, 2)
=> DROP MODEL schema1.linreg;
DROP MODEL

=> SELECT * FROM REGISTERED_MODELS WHERE registered_name = 'model_app_II';
registered_name | registered_version |    status    |        registered_time        |      model_id     | schema_name | model_name |      model_type       |   category
-----------------+--------------------+--------------+-------------------------------+-------------------+-------------+------------+-----------------------+----------------
model_app_II    |                  3 | UNDER_REVIEW | 2023-03-25 10:29:25.970567-02 | 45035996273853740 | schema2     | tf_model   | TF_MODEL              | TENSORFLOW
model_app_II    |                  1 | PRODUCTION   | 2023-03-21 09:42:10.082135-01 | 45035996273853890 | schema2     | py_model   | PMML_REGRESSION_MODEL | PMML
(2 rows)

模型状态与流转

                        ┌──────────────┐
                        │ UNREGISTERED │  (尚未注册)
                        └──────┬───────┘
                               │ register_model()
                        ┌──────────────┐
                   ┌───│ UNDER_REVIEW  │  (待审核)
                   │   └──────┬───────┘
                   │          │ change_model_status(..., 'staging')
                   │          ▼
                   │   ┌──────────────┐
                   │   │   STAGING     │  (A/B 测试/验证)
                   │   └──────┬───────┘
                   │          │ change_model_status(..., 'production')
                   │          ▼
                   │   ┌──────────────┐     自动归档旧版本
                   │   │  PRODUCTION   │ ──────────────────► ┌──────────┐
                   │   └──────┬───────┘                      │ ARCHIVED │
                   │          │                              └──────────┘
                   │          │ change_model_status(..., 'declined')
                   │          ▼
                   │   ┌──────────────┐
                   └───│   DECLINED   │  (审核拒绝)
                       └──────────────┘

五种状态说明:

状态 说明 设置者
under_review 刚注册,等待审核 自动(注册后初始状态)
staging 审核通过,可进入 A/B 测试或预发布验证 MLSUPERVISOR / DBADMIN
production 正式上线。同一 registered_name 下唯一 MLSUPERVISOR / DBADMIN
archived 被新生产版本替换后自动归档 自动(新版本上线时)
declined 审核被拒 MLSUPERVISOR / DBADMIN

状态转换约束:

  • 只有 MLSUPERVISOR 角色(或 DBADMIN)可执行状态变更
  • 同一 registered_name 下同时只能有一个 production 模型——将新模型设为 production 时,旧 production 模型自动变为 archived
  • 状态标签不强制约束模型的使用,它只是一个标记。应用程序需自行利用这些信息来实施期望的使用约束

模型版本历史追溯

为便于监控和调试,访问注册历史非常有用。例如,了解谁在何时更改了注册模型的状态。这些信息可以从 v_monitor.model_status_history 表中获取。出于安全考虑,只有 superuser 或被授予足够权限的用户才能查询此表。

上述示例中 model_app_II 的表内容如下:

-- dbadmin queries the model_status_history table
=> SELECT * FROM model_status_history WHERE registered_name = 'model_app_II' ORDER BY registered_version;
registered_name     | registered_version |  new_status  |  old_status  |       status_change_time      |    operator_id    | operator_name |      model_id     | schema_name | model_name
---------------------+--------------------+--------------+------------- +-------------------------------+-------------------+---------------+-------------------+-------------+-------------
model_app_II         |                  1 | UNDER_REVIEW | UNREGISTERED | 2023-03-21 09:42:10.082135-01 | 45035996273964824 | DS2           | 45035996273853890 | schema2     | py_model
model_app_II         |                  1 | STAGING      | UNDER_REVIEW | 2023-03-21 10:05:23.012172-05 | 45035996273704962 | supervisor    | 45035996273853890 | schema2     | py_model
model_app_II         |                  1 | PRODUCTION   | STAGING      | 2023-03-21 10:35:41.185485-02 | 45035996273704962 | supervisor    | 45035996273853890 | schema2     | py_model
model_app_II         |                  2 | UNDER_REVIEW | UNREGISTERED | 2023-03-25 08:19:56.190625-03 | 45035996273931856 | DS1           | 45035996273853815 |             |
model_app_II         |                  2 | STAGING      | UNDER_REVIEW | 2023-03-25 11:15:13.746265-05 | 45035996273704962 | supervisor    | 45035996273853815 |             |
model_app_II         |                  3 | UNDER_REVIEW | UNREGISTERED | 2023-03-25 10:29:25.970567-02 | 45035996273964824 | DS2           | 45035996273853740 | schema1     | tf_model
(6 rows)

注意,(model_app_II, 2)schema_namemodel_name 列为空,因为该模型先前已被删除。对于任何已被删除的注册模型,这两列都将为空。可以在已删除模型相同的 schema 上创建同名的新模型并注册到相同的 registered_name,但其 model_idregistered_version 将与之前的模型不同。

值得一提,v_monitor.model_status_history 表的内容基于名为 v_internal.dc_registered_model_status_history 的 Data-Collector 构建。如有需要,dbadmin 可以调整该 Data-Collector 的保留策略属性。

关键规则

  1. 未注册的模型不会出现在 registered_models 表中。

  2. 自动归档:如果某 registered_name 下已有 production 模型,将新模型的状态改为 production 时,原 production 模型自动变为 archived

  3. 权限:只有 dbadmin 和 MLSUPERVISOR 角色对注册模型拥有完整权限(USAGE/ALTER/DROP)。当然,所需的任何权限都可以后续委派给其他用户。

  4. 删除后消失:模型被删除后,无论其之前处于何种状态,都不会再出现在 registered_models 表中。

  5. 历史记录完整性model_status_history 表中的历史记录包括已注销(unregistered)和已删除(dropped)的模型。但已删除模型的 schema_namemodel_name 列显示为空。

  6. 唯一标识:每个已注册模型可以通过 [schema_name.]model_name(REGISTERED_NAME, REGISTERED_VERSION) 组合来唯一标识。

  7. 状态仅为标记:注册模型的状态只是一个标记,不强制约束模型的使用。应用程序有责任利用提供的数据库内模型版本管理工具,并强制实施任何额外的期望约束。

总结

Vertica 12.0SP4 提供了数据库内模型版本管理(In-DB Model Versioning),建立了一套用于注册候选模型并在数据库中跟踪和管理其状态的机制。模型版本管理工具应能降低生产环境中模型管理的复杂性。更多信息请参考官方文档:Model Versioning

扩展阅读