本文介绍了Netflix为解决跨域ML资产碎片化问题而构建的元数据服务(MDS)与模型生命周期图。通过统一URI标识、事件驱动的实体丰富、Datomic图数据库存储与Elasticsearch索引,实现了模型、特征、管道、实验等实体间的自动关联发现。文章详细阐述了从事件摄取到知识丰富的五阶段流程,以及多跳推理连接模型与A/B测试的具体案例。
1 引言
1.1 Netflix的ML发展历程
随着Netflix的发展,机器学习持续支持我们向会员交付价值并在业务的多个领域推动卓越。当Netflix在十多年前开始投资机器学习时,它主要专注于单一领域:个性化。Scala是行业标准,我们的ML团队相对较小,优化会员参与度是我们的主要用例。快进到今天,机器学习已成为Netflix业务转型的支柱。我们现在将ML应用于各种业务领域,包括:
- 个性化:优化参与度并帮助会员发现他们喜欢的内容
- 工作室:前期和后期制作工作流
- 支付:欺诈检测、支付路由和周期性计费优化
- 广告:我们最新的领域,需要实时决策和定向
......以及公司中不断增长的额外用例
每个领域都使用不同的技术栈、不同的业务指标和不同的组织结构运作。虽然这种多样性证明了机器学习如何在Netflix的多个垂直领域演变以推动价值,但这种增长引入了一个新的挑战:实现跨域的模型和数据交叉传播。
2 挑战:碎片化的ML格局
2.1 模型成为黑盒
随着我们的ML投资在这些领域扩展,一个关键问题出现了:产生的模型在很大程度上变成了黑盒。没有任何发现基础设施,ML从业者无法轻松地在业务垂直领域之间协作或共享工作。
考虑一个具体的例子:内容嵌入。我们的工作室团队创建了复杂的嵌入,用于识别场景边界、检测视觉转换和理解内容结构。这些嵌入最初是为制作工作流构建的。
但这些相同的嵌入在其他地方可能非常有价值。广告可以假设使用内容嵌入进行上下文匹配(确保广告与当前播放的内容的语调和内容保持一致)。个性化可以利用它们进行剧集营销和推荐(将剧集的主题或情绪与用户的偏好观看偏好相匹配)。然而,实现这种交叉传播极其困难。
2.2 工具孤岛化
为什么?我们的ML工具存在于孤岛中,每个都有自己的后端服务和用户界面。模型注册表不知道哪些A/B测试正在使用其模型,管道编排器不知道下游模型依赖关系。ML从业者必须遍历多个系统来回答关于他们工作的基本问题。查找模型需要打开模型注册表,了解其血缘意味着切换到管道编排器,追踪哪些A/B测试使用该模型需要导航到实验平台。这种碎片化阻止从业者回答关键问题:
- 发现: 存在哪些特征?哪些数据源可用于为模型生成特征?
- 血缘: 哪个管道正在为特定模型生成数据?哪些数据源提供这些特征?
- 影响: 哪些A/B测试正在运行此模型?如果我更改此特征,哪些模型会崩溃?谁拥有这个链中的每个部分?
3 难题:连接一切
3.1 异构元数据的挑战
真正的挑战不仅仅是构建一个统一的UI。我们需要连接我们的ML从业者用于执行ML生命周期不同部分的基础设施的不同部分。
我们的ML生态系统从数十个来源生成元数据:
- 管道编排系统发出执行详情、阶段依赖关系和数据转换
- 部署模型注册表跟踪模型版本、工件、陈旧性和部署历史
- 实验平台管理A/B测试及其配置
- 特征存储目录特征定义和使用
- AI数据集平台跟踪数据集的创建、管理、发现和加载
- 身份平台维护用户、团队和组织元数据
每个系统采用不同的格式、标识符和心智模型。我们必须解决的硬技术问题是:我们如何收集这些异构元数据,将其转换为统一的实体模型,并构建一个连接的图,实现跨业务领域的真正探索和协作?
3.2 解决方案:元数据服务与模型生命周期图
我们的答案是元数据服务(MDS),它构建了一个模型生命周期图,用于索引和连接Netflix跨域的ML相关实体。MDS针对ML元数据(例如模型、特征、管道、实验、数据集)的实时摄取进行了优化,并回答跨域问题,例如"哪些实验正在运行此模型?"或"哪些模型共享这些特征?"它是实现发现的基础,从各种来源摄取事件,用上下文丰富它们,并具体化跨实体的关系。
我们的愿景:让每个Netflix的ML资产对每个ML从业者都可发现、可理解和可重用,无论他们的团队或领域如何。
4 核心抽象:系统的词汇表
4.1 关键概念定义
在深入技术实现之前,了解支撑MDS的概念模型是有帮助的。这个词汇表实现了跨团队和系统的一致通信:
组件(Component): 任何可以使用AI平台(AIP)统一资源标识符(URI)唯一寻址的对象。AIP URI遵循格式aip://<componentType>/<platformId>/<resourceId>,确保全局唯一性。例如:
- 模型:
aip://model/registry/ranking-v5 - 用户:
aip://user/identity/alice - 管道:
aip://pipeline/orchestrator/weekly-training
实体(Entity): ML生态系统中的组件,具有额外的属性,如名称、描述、创建日期和所有者。实体代表ML特定的资产,如模型、特征和管道。
实体类型(Entity Type): 共享相同数据形状的一组实体。数据形状是一组属性约束,指定实体必须具有的属性和关系。
领域(Domain): 相关实体类型的功能分组,定义ML资产类别的抽象接口。例如,模型领域定义模型和模型实例的外观,而管道领域定义计划、请求和执行。
提供者(Provider): 领域的具体实现,由特定源系统支持。例如,模型领域目前由我们的内部模型注册表支持。这种分离允许MDS为同一领域支持多个提供者。如果引入新的模型注册表,它可以作为额外的提供者添加,而无需更改领域接口。
4.2 概念示例
我们可以用一个具体的例子来总结这些概念:

这种基于URI的寻址方案至关重要,因为它允许任何服务用单个字符串引用任何ML资产,并且MDS可以将该引用解析回丰富的、连接的元数据。
5 从事件到实体到图
5.1 整体流程概述
从原始系统事件到可查询的图的旅程分阶段发生。让我们通过每个阶段用一个具体的例子来走一遍:通过关系推断将模型连接到其A/B测试。
5.2 事件摄取
MDS通过Kafka和AWS SNS/SQS与各种源系统集成,实时消费事件。源系统发出包含标识符和事件类型的轻量事件。
示例事件:
json
{
"event_type": "model_instance_created",
"instance_id": "ranking-model-v5-20XX0101",
...
}
这种设计保持生产者简单。源系统只需要宣布发生了更改,而无需构建完整的负载或理解下游需求。
每个源系统在MDS中都有专用的事件处理器:
- 管道编排:摄取管道执行事件,包括节点定义、计划、请求和作业尝试
- 模型注册表:捕获模型部署、配置和版本更新
- 特征存储:跟踪特征定义及其版本
- 实验平台:监控A/B测试配置和分配
- 数据集:跟踪ML数据集及其版本
- 身份平台:维护所有权和团队成员信息
5.3 实体丰富
MDS为每种事件类型实现了一个水合(hydration)契约。当事件到达时,MDS:
- 验证事件模式
- 调用源系统的API以获取完整、当前的状态
- 将响应转换为规范化实体
这种设计有一个关键属性:事件的顺序无关紧要。MDS总是从事实来源获取最新的事实。这种模式将事件流与状态一致性解耦。如果事件总线丢弃消息或无序传递,下一个事件会纠正状态。事件流成为变更的通知,而不是变更的日志。
这种变更通知模式有一些重要的权衡。从好的方面来说,它保持生产者简单,使我们对无序或丢弃的事件具有鲁棒性,并确保MDS可以通过从事实来源读取来始终协调到最新状态。权衡是,我们在水合期间对源系统施加了额外的读取负载,并且需要在我们丰富的工作器中有意地考虑速率限制、缓存和退避,以免使它们过载。
对于我们的排序模型示例,当model_instance_created事件到达时,MDS调用模型注册表API:GET /api/v1/instances/ranking-model-v5-20XX0101
注册表响应一个完整的描述符。示例响应(仅关键字段):
json
{
"id": "ranking-model-v5-20XX0101",
"pipeline_run_id": "train-weekly-ranking-20XX0101",
"owner_emails": ["alice@netflix.com"],
"labels": [{"key": "team", "value": "personalization"}],
...
}
5.4 数据转换与规范化
原始事件是异构的,每个源系统都有自己的模式和语义。MDS工作器将这些事件转换为具有标准化字段的统一实体模型。
如果没有规范化,下游消费者将需要理解每个源系统的模式。规范化创建了一致的接口,允许查询和关系跨所有实体类型工作。这里是一个例子。
规范化MDS实体:
json
{
"id": "aip://model/registry/ranking-model-v5-20XX0101",
"pipeline_run": "aip://pipeline-run/orchestrator/train-weekly-ranking-20XX0101",
"entity_type": "ModelInstance",
"owners": ["aip://user/identity/alice"],
"tags": [{"tag": "team", "value": "personalization"}],
...
}
规范化过程标准化字段名称和格式。例如,平台特定的ID变为全局AIP URI,owner_emails变为具有已解析用户URI的owners,标签变为tags。像pipeline_run_id这样的外键被转换为实体引用。然而,仍然没有引用哪些A/B测试正在使用此模型。模型注册表不跟踪实验,实验平台不跟踪哪个管道产生了给定模型。这就是知识丰富变得关键的地方。
5.5 存储与索引
一旦规范化,实体就被持久化到Datomic,并立即在Elasticsearch中索引。这在事件处理流中同步发生。
Datomic用于缓存和关系
规范化实体首先被写入Datomic,它既作为本地缓存又作为图数据库。
为什么选择Datomic?Datomic既作为MDS的记录系统,又作为丰富过程的工作数据集。其不可变事实模型意味着我们可以不断添加关系而不会丢失原始实体状态。
我们存储的内容:
- 所有实体属性作为事实
- 实体引用(可能指向尚未完全解析的实体的外键)
- 所有关系作为具体化的边(由丰富过程添加)
- 实体生命周期状态(跟踪哪些实体已完全丰富 vs 等待水合)
这实现了:
- 复杂的图遍历: 在单个查询中从模型导航到其特征再到它们的数据源
- 实体关系: 跨多个领域连接而不会产生N+1查询问题
- 灵活的模式演变: 随着目录增长,轻松添加新的实体类型和属性
- 渐进式丰富: 后台作业有效识别和处理需要额外水合的实体,无需重新处理完全丰富的实体即可实现渐进式图完成
在实践中,我们使用Datomic进行关系繁重的导航查询,例如:
- 从这个模型实例开始,向我展示所有上游数据集和下游实验。
- 给定这个特征,列出所有消费模型及其拥有的团队。
这些查询通常跨越图中的多个跳数,并受益于Datomic的不可变事实模型和跨实体关系的高效连接。
Elasticsearch用于发现
在写入Datomic之后,实体立即在Elasticsearch中索引,以支持跨目录的快速、全文搜索。
我们索引的内容:
- 主要字段:实体名称、描述、实体类型、所有者名称
- 关系元数据:相关实体的名称(例如模型的特征、管道、A/B测试)存储在相关字段中
- 标签:存储为键值对的领域特定元数据(例如_team::personalization, env::production, model.state::released_)
索引结构:
- 单一实体索引:所有实体类型(模型、特征、管道等)都索引在一个统一的索引中,由entityType字段区分
- 单独的所有者索引:用于用户和组的专用索引,以支持跨实体所有者搜索
- 相关性提升:精确名称匹配的得分高于其他相关匹配
这实现了:
- 跨实体名称、描述、标签和相关元数据的多字段文本搜索
- 相关性排名与提升(精确名称匹配的得分显著更高)
- 按实体类型、所有权、标签和领域特定属性的复杂过滤(存储为标签)
- 模糊匹配以处理拼写错误和部分查询
Elasticsearch为系统的入口点提供支持:用户通常从AIP门户中的自由文本搜索开始(搜索模型名称、团队或领域术语),然后在落地到实体页面后切换到图导航。索引作为摄取和丰富工作流的一部分近实时发生,因此更改通常在门户中可见,延迟对于交互使用是可以接受的。
5.6 知识丰富与图形成
一旦实体元数据在Datomic中持久化,计划的后台进程就会接管以发现和具体化关系。这些丰富作业定期运行,扫描未缓存或部分解析的实体(仅作为引用存在而没有完整元数据的实体)。
丰富工作流:
- 识别候选: 查找标记为未缓存或具有未解析引用的实体
- 水合关系: 查询事实来源系统以获取相关实体详情
- 具体化边: 将发现的关系写回Datomic
- 重新索引: 触发Elasticsearch索引以更新实体
- 标记为丰富: 更新实体状态以防止冗余处理
这种异步方法允许MDS处理图形成的计算成本而不会阻塞实时事件摄取。它还支持重试逻辑和渐进式丰富,因为新实体变得可用。
因为丰富是异步的,新发现的关系可能在基础实体创建后出现短暂延迟(通常是分钟而不是秒)。我们跟踪每个实体上次丰富的时间,并在AIP门户中显示此时间戳,以便从业者可以推断陈旧性,并知道何时可以安全地依赖特定关系进行调试或影响分析。
为什么丰富? 源系统是专为特定目的构建的,不知道其他领域的实体。丰富发现并具体化了跨系统关系,这些关系支持强大的血缘和影响查询。
示例:将模型连接到A/B测试
当MDS处理新的模型实例时,后台丰富作业通过多跳推理发现关系:
步骤1:直接链接到管道
模型引用一个pipeline_run_id。丰富作业水合管道并发现其A/B测试关联:GET /api/v1/pipeline-runs/train-weekly-ranking-20XX0101
响应:
json
{
"run_id": "train-weekly-ranking-20XX0101", "pipeline": "weekly-ranking-trainer",
"ab_test_cells": [
{"test_id": "12345","cell_number": 2,"cell_name": "treatment_ranking_v5"}
]
...
}
步骤2:发现A/B测试上下文
丰富作业发现管道为A/B测试单元#2运行,并查询实验平台以获取测试详情:GET /api/v1/tests/12345
json
{
"test_id": "12345",
"name": "Ranking Model v5 vs v4",
"status": "ACTIVE",
"cells": [{"cell_number": 1, "name": "control_ranking_v4"}],
...
}
步骤3:推断传递关系
丰富作业现在有了完整的链:
- 模型实例由管道运行产生
- 管道运行为A/B测试单元#2执行
- A/B测试单元#2属于A/B测试"Ranking Model v5 vs v4"
- 模型实例现在与此A/B测试关联
作业将推断的关系写回Datomic并触发重新索引,并在图中具体化这些边。MDS不仅仅存储被告知的内容;它通过在后台遍历图来推导新知识。
为什么这很重要: 没有MDS,回答"哪些A/B测试正在使用此模型?"需要:
- 在模型注册表中查找模型
- 找到哪个管道产生了它
- 检查管道编排器中的A/B测试标签
- 查询实验平台以获取测试详情
使用模型生命周期图,它是一个单一查询:
graphql
query {
model(id: "aip://model/registry/ranking-model-v5-20XX0101") {
name
owners { name }
currentInstance {
version
pipeline {
name
owners { name }
}
features {
edges {
node {
name
data { edges { node { name } } }
}
}
}
associatedAbTests {
name
cells { number name }
}
}
}
}
反向查询也有效:"实验12345中正在测试哪些模型?"
6 启用探索,而不仅仅是搜索

有了模型生命周期图,我们从实体搜索转向实体探索。发现不仅仅是找到一个模型;它是关于遍历关系:
- 从一个模型开始,探索其特征
- 从特征,导航到驱动它们的核心数据
- 从数据,追溯到生成它的管道
- 从管道,查看哪些团队拥有和依赖它们
- 从实验,了解哪些模型正在被测试
例如,想象一位工程师调查个性化模型的参与度指标下降。他们可能:
- 从AIP门户中为受影响推荐提供支持的模型实例开始。
- 检查模型的特征并跟随一个可疑的特征到其上游数据集。
- 从数据集页面,看到其管道最近有失败的运行并识别拥有的团队。
- 确认哪些A/B测试当前正在运行此模型实例以了解哪些会员和界面受到影响。
在MDS和模型生命周期图之前,这需要跨多个工具(模型注册表、管道编排器、实验平台)进行手动检查。现在,它是在单一界面中的连续旅程。
这种基于图的探索回答了以前不可能的问题:
- 血缘查询: 此模型的完整血缘是什么,从训练数据到生产实验?
- 影响分析: 如果我更改此特征,哪些模型将受到影响?
- 使用发现: 哪些A/B测试正在使用此模型?
- 依赖映射: 我的管道传递依赖哪些数据源?
- 弃用计划: 哪些实体不再使用并可以退役?
每个实体都有深层上下文:其创建时间、所有权、更新历史,最重要的是,它与其他实体的关系。
模型生命周期图通过AIP门户向从业者展示,这是一个统一的界面,提供跨所有实体类型的全文搜索、具有可导航关系的详细实体页面,以及团队和个人个性化视图。
AIP门户中的典型交互如下:
- 搜索: 在由Elasticsearch支持的单一搜索框中输入模型、特征、数据集或团队名称。
- 检查: 落在一个实体页面上,显示关键元数据(描述、所有者、领域、标签)以及关系面板。
- 探索: 点击进入相关实体(上游数据集、下游实验和兄弟模型版本)以在门户中导航模型生命周期图而无需离开。
当新实体类型引入MDS时,门户自动提供基线搜索、实体页面和关系导航,然后我们可以随着时间推移在其上叠加领域特定的可视化(如模型部署历史或数据集版本时间线)。
7 未来之路:开放挑战
7.1 工具扩展
构建ML生命周期图是一个持续的旅程。重大挑战仍然存在,这些代表了未来的机会:
- 工具扩展: 随着新ML工具的出现,我们需要可扩展的稳健集成模式。我们如何设计插件架构,使添加新来源变得无缝?如果我们跟不上新工具,从业者将被迫回到碎片化的视图,模型生命周期图将失去覆盖范围和信任。
7.2 领域特定可视化
- 领域特定可视化: 不同实体类型需要不同的可视化体验。模型页面应显示部署历史、A/B测试关联和性能指标。特征页面应突出数据血缘和消费模型。管道页面必须显示执行历史、依赖关系和计划。数据集页面需要版本时间线和下游消费者。我们如何设计一个灵活的UI框架,允许每个实体类型有自己定制的体验,同时在门户中保持一致的导航和交互模式?没有丰富的、领域特定的体验,门户有成为通用目录的风险,而不是ML从业者在日常工作流中依赖的工具。
7.3 元数据质量与高级关系推断
-
元数据质量: 今天,MDS通过摄取时的事实来源水合和模式验证确保数据一致性。后台丰富作业不断推断关系并从源系统具体化实体。然而,在规模上确保完整性和及时性仍然存在挑战。当源系统未能发出事件时,当所有权信息变得陈旧时,或当实体缺乏描述和上下文元数据时,图的效用会下降。我们如何构建自动验证和丰富系统来检测元数据异常、建议缺失的关系,并在数百万实体中维护质量基准?糟糕或陈旧的元数据会侵蚀从业者的信任:如果图不完整或不正确,团队将恢复临时知识和一次性集成,而不是将MDS作为事实来源使用。
-
高级关系推断: 超越源系统中声明的显式关系,我们如何推断隐式连接?我们能否基于共享特征检测两个模型服务于相似的目的?我们能否基于类似管道的使用模式推荐特征?我们处于探索这些想法的早期阶段。做得好,它们会将MDS从被动目录转变为ML资产的主动推荐引擎,加速重用并减少跨域的重复工作。
8 致谢
这项工作代表了AI平台组织中出色同事的集体努力:Emma Carney、Megan Ren、Nadeem Ahmad、Pat Oleniuk、Prateek Agarwal、Tigran Hakobyan、Yinglao Liu