3. 数据与AI资产的版本控制
在Databricks的AI DataOps实践中,版本控制不是“把Notebook放进Git”这么简单。一个可发布的AI资产通常由五类对象共同构成:Notebook/代码、Delta表结构与数据契约、MLflow模型、特征表定义以及配置文件。它们的变化节奏不同、存储位置不同、可回滚程度也不同。如果只对代码做版本控制,而模型、特征和配置仍靠人工记录,发布时就无法回答“这次上线的模型对应哪份特征定义、哪套参数、哪版表结构”。因此,本节的核心目标是建立一套以Git为单一可信源、以不可变版本标识串联五类资产的管理方式。
一、五类资产分别“版本化什么”
首先要区分“文件版本”和“数据版本”。Git擅长管理文本,不擅长管理大规模数据文件。因此对Delta表,我们版本化的不是Parquet文件本身,而是表结构(Schema)、数据契约、分区与约束、以及指向具体数据快照的版本标识。对MLflow模型,版本化的是模型注册表中的模型版本号、Run ID、签名(Signature)与依赖环境,而不是模型权重文件本身。对特征表,版本化的是特征定义、实体主键、时间窗口、计算逻辑与物化表名。对配置文件,版本化的是YAML/JSON原文,包括集群规格、任务依赖、环境变量与阈值参数。
下表给出五类资产的版本化对象与Git中的承载形式,可作为团队约定的基础:
| 资产类型 | 版本化对象 | Git承载形式 | 不可变标识 |
|---|---|---|---|
| Notebook/代码 | 源码、函数、测试 | .py / .sql / .ipynb | commit SHA |
| Delta表 | Schema、契约、分区 | DDL / schema.yaml | 表版本号 + commit SHA |
| MLflow模型 | 模型版本、签名、依赖 | model_ref.yaml | 模型版本号 + Run ID |
| 特征表 | 特征定义、窗口、主键 | feature_def.yaml | 特征表版本 + commit SHA |
| 配置文件 | 参数、集群、依赖 | config/*.yaml | commit SHA |
二、分支模型:主干保护 + 短生命周期特性分支
推荐采用“main + release + feature”三层分支模型。main分支始终保持可发布状态,任何直接push都应被禁止,只能通过Pull Request合并。release分支对应具体发布批次,例如release/2024.11,用于冻结候选版本并打Tag。feature分支命名建议带工作项编号,如feature/AID-231-feature-store-v2,生命周期不超过3天,避免长期分叉导致合并冲突。
关键约束是:模型、特征、配置的变更必须与代码变更在同一个PR中提交。例如调整特征窗口从7天改为14天,不能只改Notebook,而应同时更新feature_def.yaml中的window_days字段,并在PR描述中说明对模型的影响。这样合并后的commit SHA就能唯一锚定“代码+特征+配置”的一致状态。
三、代码评审流程:从“看代码”到“看资产一致性”
AI项目的评审不能只审逻辑正确性,还要审资产一致性。建议在PR模板中固定以下检查项:
- Schema兼容性:Delta表字段是否新增/删除/改类型?删除字段属于破坏性变更,必须走单独评审并标注影响范围。
- 特征一致性:Notebook中引用的特征名是否都在
feature_def.yaml中声明?窗口、主键是否与定义一致? - 模型引用:
model_ref.yaml中的模型版本是否已注册?签名输入是否与当前特征表匹配? - 配置差异:
config/下是否有未说明的参数变更?阈值、集群规格变化是否附带影响评估? - 可回滚性:本次变更能否通过回退commit SHA恢复?是否涉及不可逆的数据迁移?
评审至少需要一名领域Owner和一名平台Owner。领域Owner判断业务逻辑与特征合理性,平台Owner判断Schema、模型注册与发布流程是否合规。对于破坏性变更,建议要求两名Owner同时批准。
四、实操示例:一次特征窗口调整的完整提交
假设我们要把用户活跃度特征的统计窗口从7天调整为14天。正确做法不是只改Notebook,而是同步修改三处文件并提交到同一分支:
# feature_def.yaml
feature_name: user_active_score
entity: user_id
window_days: 14 # 由 7 改为 14
aggregation: count
source_table: silver.user_events
materialized_table: gold.user_active_score_v2
# config/training.yaml
feature_table: gold.user_active_score_v2
model_name: user_churn_model
threshold: 0.62
# notebooks/train_churn.py
feature_df = spark.read.table("gold.user_active_score_v2")
提交后,PR描述中应写明:窗口变化会影响特征分布,需重新训练模型;materialized_table改为v2以避免覆盖旧特征表;训练配置中的feature_table同步指向v2。合并后,CI流水线会校验Schema、注册新模型版本,并把commit SHA写入模型Tag,例如git_commit=9f3c2a1。这样在发布时,只需查看模型版本上的Tag,就能反查到对应的特征定义与配置。
git_commit、feature_table_version、config_commit。这三个值构成发布时的“资产指纹”,任何一项对不上都不应进入生产。
五、Delta表与特征表的版本策略
Delta表本身提供时间旅行能力,可通过DESCRIBE HISTORY查看表版本。但表版本不等于代码版本,二者需要显式关联。推荐在schema.yaml中记录table_version字段,并在每次Schema变更时递增。特征表则建议采用“版本化表名”策略,例如gold.user_active_score_v1、_v2,避免直接修改原表导致下游模型输入漂移。旧版本保留至少一个发布周期,确认无引用后再归档。
DROP TABLE或OVERWRITE直接替换生产特征表。正确做法是新建版本表、切换配置引用、观察一个周期后再下线旧表。否则一旦模型效果异常,将无法快速回退到旧特征。
六、发布Tag与资产清单
当release分支通过全部评审与CI后,打上语义化Tag,例如v2024.11.0。Tag对应的commit中应包含一份自动生成的release_manifest.yaml,列出本次发布涉及的Notebook、Delta表版本、MLflow模型版本、特征表版本与配置commit。这份清单是发布审计和回滚的依据,也是AI DataOps中“可追溯”的具体落地形式。
总结来说,Git管理的不只是代码,而是代码、表结构、模型引用、特征定义与配置的联合版本。分支模型保证变更隔离,评审流程保证资产一致,Tag与清单保证发布可追溯。三者结合,才能让AI资产在持续交付中保持可控、可回滚、可审计。