背景
PART.1
在本系列的前几篇文章中,我们分别探讨了模型生命周期管理和推理性能优化。当一个模型经过训练、性能测试和调优后,终于部署到线上服务于我们的业务,是否就可以高枕无忧了呢?其实不然。模型上线后,我们需要持续监控其输入数据分布是否发生变化,以及预测结果是否仍然准确,并在适当时候采取措施保证预测的正确性。这就需要机器学习平台具备强大的监控和可观测性功能。然而,机器学习模型的监控具有其独特的特点和挑战。本文旨在介绍模型可观测性的概念和重要性,并分享eBay推理平台在模型可观测性实施工作当中的一些工程实践经验。
“亿”想天开AI系列推文目录:
模型推理平台核心功能和整体架构,及主流框架支持
模型生命周期管理
模型端到端服务优化及资源优化
模型特征监控(本篇)
大模型落地实践
什么是模型可观测性
PART.2
模型可观测性是指对机器学习模型在生产环境中的行为进行监控和分析,以确保模型预测结果的准确性和稳定性。通过可观测性,我们能够实时监控系统的内部状态,并保障其服务的可用性。例如,监控系统CPU和内存的使用率、服务响应的状态码和响应时间等。然而,模型的可观测性,情况更加复杂。
推理服务的可观测性
首先,系统的可观测性具有分层特性,下图展示了不同层级关注的不同指标。我们将所有系统可观测性指标按层次分类,每一层对应其特定的观测指标,故障类型及解决方案。
基础设施层(Infra Layer):主要集中于基础设施的稳定性和性能指标,例如硬件健康状况、网络延迟、资源利用率等。这一层的监控确保底层基础设施的正常运行。
平台层(Platform Layer):主要关注推理平台服务的可靠性和效率,例如服务的响应时间、请求处理成功率、负载均衡等。这一层的监控确保平台服务的持续可用性和高效运行。
模型层(Model Layer):主要关注模型和特征的数据质量与分布,例如特征的异常比例、模型的预测准确性、数据漂移(Data Drift)等。这一层的监控确保模型的输入分布稳定以及预测结果的可靠且符合预期。
通过分层监控,我们可以更精准地定位问题并采取针对性的措施,确保整个系统的稳定和高效运行。在接下来的部分,我们将深入探讨模型可观测性的重要性及其实现方法。
模型可观测性
与其他软件系统一样,我们通过可观测性实时监控推理服务系统的内部状态,保障其服务的可用性。例如,我们监控系统CPU和内存的使用率,过高或过低可能都表明系统处于异常状态。我们还监控服务响应的状态码和响应时间,当错误状态码比例或响应时间超过阈值时,需要报警进行人工干预。然而,对于模型的监控,情况更加复杂,更难以判断系统是否处于异常状态。比如在以下场景中,我们如何判断当前服务是处于正常还是异常状态?
个性化推荐模型返回正确的状态码,但推荐的商品都是最热门的商品。
圣诞节过后,广告排序模型仍然将节日礼品类商品排在前面。
为了监控模型服务的状态,我们定义了一系列模型可观测性指标用于监测和报警,如下表所示。
Category | Description |
QPS | 以时间区间和模型为条件聚合统计特征的QPS,可根据配置与1天/周前相同时间区间进行比较,低于或超过界限自动报警。 |
Error/Null/ Invalid Count | 以时间区间和模型为条件聚合统计特征的错误数/null值/异常数,可根据配置与1天/周前相同时间区间进行比较,低于或超过界限自动报警。 |
Error/Null/ Invalid Rate | 以时间区间和模型为条件聚合统计特征的错误比例/null值比例/异常比例,可根据配置与1天/周前相同时间区间进行比较,低于或超过界限自动报警。 |
Percentile | 以时间区间和模型为条件聚合统计数值类型特征的的P99, P95,P90等,可根据配置与1天/周前相同时间区间进行比较,低于或超过界限自动报警。 |
TopK | 统计Category类型特征TopK分布。 |
PSI | 计算当前小时/天与7天前的PSI值,PSI技术主要用于数据漂移的检测,关于PSI技术后面会详细介绍。 |
模型可观测性挑战
模型异构:在企业中,推理平台上通常会有多个模型部署服务于不同的业务,这些模型使用不同的算法、框架和数据schema。这种异构性增加了监控的复杂性,因为每个模型的特征提取和观测指标各不相同。
监测指标复杂:模型监控需要跟踪的指标种类繁多,计算复杂,包括但不限于特征QPS、特征区间、特征分布等。这些指标不仅要监控当前状态,还需要进行历史对比和趋势分析。特别是在检测数据漂移时,需要多维度的指标和复杂计算来捕捉数据的变化。
海量数据:实时监控大量数据不仅需要强大的计算和存储能力,还需要高效的数据处理和分析方法。就机器学习平台而言,每秒钟都会接收到成千上万的预测请求,存在近实时的监控需求,像监控数据漂移这类情况,无疑给监控系统架构带来极为巨大的挑战。另外,我们还需要保存一段时间内的历史数据,为问题查找、异常检测和模型重训练等提供数据支持。
异常检测:及时检测并响应异常是模型监控的核心任务之一。然而,异常情况的定义和检测并非易事,模型及特征的异常具有隐藏性。对此,我们需要设计高效的异常检测算法,并结合业务场景设定合理的阈值和警报机制,确保异常能够被准确识别和处理。
模型可观测性实践
PART.3
总体架构
基于模型可观测性的特点和难点, 我们在架构设计上核心考虑以下几个原则:
元数据驱动的配置管理:通过元数据配置管理,使模型所有者能够灵活地自主定义和调整所监控的特征和指标。
非侵入式模型日志接入:使用非侵入式的日志接入方式,直接通过配置便可简单开启模型监控,减少模型所有者开发成本。
高性能和可扩展性:模型日志高效实时收集、处理,并支持业务层的可扩展性。
海量数据的高效存储:采用结构化的离线存储方案,确保高效的数据存储和检索。
基于这些原则,我们设计的模型监控架构如下:
支持用户自主定义特征监控列表和提取规则,并根据元数据自动生成Logging Schema, 将异构的模型特征表达成统一,平铺式的特征列表。其主要功能和优点如下:
精简资源使用:不需要保存所有特征的快照,比如常量特征。还可以通过特征提取规则,仅保存所需特征。 高效传输和存储:观测的数据量巨大,使用二进制类型进行序列化以替代文本类型,提高传输和存储效率。 版本控制:用户可以随时变更需要快照的特征,每次变更生成一个新的版本,推理服务和特征ETL作业使用同一个版本的Logging Schema进行数据的序列化和反序列化,保证不出错。
我们使用Fluentd sidecar作为模型日志数据收集层,实现简单便捷的模型监控接入,模型所有者无需关心特征的写入和序列化实现,隔绝实现细节。
在Logging Service归集接收所有模型的日志,根据Logging Schema反序列化模型日志,并将其写入ClickHouse和Kafka。为什么不将model logging直接发送至Kafka,而添加Logging service这一层?
连接压力:模型Pod数量众多且生命周期较短,更新频繁。这么多的连接数量和变动,对Kafka broker造成很大压力。 性能优化:单个pod的QPS数量不高,直接写入Kafka不能很好地发挥kafka性能。 业务需求:我们有模型日志后处理的业务需求, 比如数据漂移检测就是在Logging service中将特征展开,将数据记录至ClickHouse。
我们使用Hive存储结构化历史数据用于数据备份和模型重训练,而ClickHouse存储模型特征数据并建立索引用于模型监控,异常检测及报警。
核心功能
模型日志服务
在一个典型的模型推理流程中,业务方首先会请求推理服务的API接口。推理服务接收到请求后,将其转发至相应的GPU或CPU模型服务进行处理。模型服务返回结果后,推理服务再对结果进行处理并返回给业务方。
为了方便模型所有者进行监控和日志收集,我们在推理服务上使用了Fluentd sidecar来收集模型日志。Fluentd 是一个开源的数据收集器,旨在将数据日志进行统一转换和传输。通过使用Fluentd sidecar容器,我们可以无缝地将模型服务产生的日志收集并推送至Logging Service。Fluentd的插件体系允许我们灵活配置日志的收集、处理和转发,确保日志数据在整个传输过程中的完整性和可靠性。
Fluentd sidecar将模型日志推送至Logging Service后。在Logging Service内,模型日志按特征维度展开,并借助Sherlock的Event功能将其写入ClickHouse。Sherlock是eBay内部基于开源工具构建的可观测性平台,为开发人员提供统一视图,以监控应用程序和基础设施的性能,集成了监控、报警和故障排除功能,并支持Metrics、Events、Logs和Tracing四种类型的可观测性数据。
此外,Logging Service还会对模型日志进行过滤和分组,分别写入各业务方独立的Kafka topic中。随后,离线模型日志dump脚本将数据从Kafka写入离线存储(Hive),方便业务方查询和解决问题。对于有模型重训练需求的情况,这些离线数据表与业务数据表可以通过join生成训练数据集,用于模型的重新训练。
当前,Logging Service从数十万个客户端收集数据,每日数据量达到几十TB,日写入特征Event记录数最大约700亿条,生成训练数据集约300亿条。
观测数据模型
为了实现基于特征的监控,如下图所示,我们在Logging Service中将模型日志按特征展开,并写入ClickHouse的特征表中。每个模型请求包含数百到上千个特征,每个特征都将在ClickHouse中生成一行数据,并添加isNull、isValid等标识字段。
此外,特征还可以与其他特征联合记录。例如,记录特征A时,同时记录特征B、C、D作为标签, 用于联合查询,目前我们最多支持5个标签。因此,ClickHouse中的模型特征数据表模式如下:
Name | Type | Index |
featureName | String | True |
modelName | Array[String] | True |
value | Number | |
isNull | boolean | |
isError | boolean | |
isValidValue | boolean | |
execTimeMil | long | |
label1Name | String | |
label1Value | String | |
label2Name | String | |
label2Value | String | |
label3Name | String | |
label3Value | String | |
label4Name | String | |
label4Value | String | |
label5Name | String | |
label5Value | String |
异常检测
对于写入ClickHouse后的特征数据,我们使用ClickHouse的查询语句查询数据。某些简单异常可通过查询结果直接查出并作为监控指标,如特征null值比率,特征value值异常比率等。而复杂的检测指标则需要进一步计算,为了提升检测能力,我们引入了Population Stability Index(简称PSI)技术作为数据漂移检测的重要手段。
PSI技术
作为数据漂移检测的重要技术手段,在数据写ClickHouse之后,会有一个定期的Job从Sherlock读取特征数据并计算PSI值,用于模型监控。
PSI是衡量模型输入数据分布变化的一种常用技术,用于检测数据漂移。它通过比较两个不同时间段的变量分布,评估变量的稳定性和变化程度。
PSI计算步骤:
分 段
将变量的取值范围分成多个区间,通常将变量的取值按百分位分段。
计算比例
分别计算参考时间段(baseline)和当前时间段(current)中,每个区间内数据点所占的比例P和Q。
计算PSI值
使用公式计算每个区间的PSI值,再将所有区间的PSI值相加得到总体PSI值。公式为:
模型所有者通过监控PSI值,在PSI大于阈值时选择重新训练模型或调整参数。
数据备份以及模型重训练
模型日志被写入Kafka后,会有一个Flink作业从Kafka中读取数据并转存入Hive表中。Hive表字段包括tracking id、时间、业务数据等,方便业务方进行数据分析。此外,对于有重训练需求的模型,还有Feature ETL作业从Hive表中读取数据,并与业务方的Hive表进行联接以生成训练数据集。
下图演示了基于特征快照功能的新训练集生成过程。推理请求通过快照记录到离线Hive表中,特征ETL Job会定时(每日)分析离线表的数据,然后将特征打平存入特征表中。当用户有需要时,可以根据准备好的request ID以及label信息,与特征表做join生成新的训练集。
总结展望
PART.4
模型可观测性在机器学习生命周期中扮演着至关重要的角色。它不仅能够帮助我们实时监控模型的健康状态,还能有效检测数据漂移和异常情况,从而保障模型的预测准确性和业务价值。
通过分层监控的架构设计,我们可以精准地定位问题并采取针对性的措施,确保系统的稳定和高效运行。eBay推理平台的工程实践经验展示了如何通过元数据驱动的配置管理、非侵入式日志接入、高性能和可扩展的日志处理系统,以及有效的异常检测算法,构建一个全面的模型可观测性系统。
随着机器学习技术的不断进步和应用场景的日益丰富,尤其是在大模型广泛应用的背景下,模型可观测性将面临全新的机遇与挑战。同时,随着数据隐私和安全问题愈发受到关注,对数据安全与合规性的要求也日趋严格,需要在保证模型可观测性的同时,确保数据的安全和合规性。这些领域将成为未来的重点方向。