此外,这一套配置文件也是离线样本生产时使用的特征配置文件,结合统一的OP&Transformer代码逻辑,进一步保证了离线/在线处理的一致性,也简化了上线的过程。因为只需要在离线状态下配置一次样本生成文件,即可在离线样本生产、在线模型预估两个场景通用。
4.2 完善预估系统:性能、接口与周边设施
4.2.1 高效的模型预估过程
OP和Transformer构建了框架处理特征的基本能力。实际开发中,为了实现高性能的预估能力,我们采用了分片纯异步的线程结构,层层Call Back,最大程度将线程资源留给实际计算。因此,预估服务对机器的要求并不高。
为了描述清楚整个过程,这里需要明确特征的两种类型:
- ContextLevel Feature:全局维度特征,一次模型预估请求中,此类特征是通用的。比如时间、地理位置、距离、用户信息等等。这些信息只需计算一次。
- DocLevel Feature:文档维度特征,一次模型预估请求中每个文档的特征不同,需要分别计算。
一个典型的模型预估请求,如下图所示:
Augur启动时会加载所有特征的表达式和模型,一个模型预估请求ModelScoreRequest会带来对应的模型名、要打分的文档id(docid)以及一些必要的全局信息Context。 Augur在请求命中模型之后,将模型所用特征构建成一颗树,并区分ContextLevel特征和DocLevel特征。由于DocLevel特征会依赖ContextLevel特征,故先将ContextLevel特征计算完毕。对于Doc维度,由于对每一个Doc都要加载和计算对应的特征,所以在Doc加载阶段会对Doc列表进行分片,并发完成特征的加载,并且各分片在完成特征加载之后就进行打分阶段。也就是说,打分阶段本身也是分片并发进行的,各分片在最后打分完成后汇总数据,返回给调用方。 期间还会通过异步接口将特征日志上报,方便算法同学进一步迭代。
在这个过程中,为了使整个流程异步非阻塞,我们要求引用的服务提供异步接口。若部分服务未提供异步接口,可以将其包装成伪异步。这一套异步流程使得单机(16c16g)的服务容量提升超过100%,提高了资源的利用率。
4.2.2 预估的性能及表达式的开销
框架的优势:得益于分布式,纯异步流程,以及在特征OP内部做的各类优化(公用特征 、RPC聚合等),从老框架迁移到Augur后,上千份文档的深度模型预估性能提升了一倍。
至于大家关心的表达式解析对对于性能的影响其实可以忽略。因为这个模型预估的耗时瓶颈主要在于原始特征的抽取性能(也就是特征存储的性能)以及预估服务的性能(也就是Serving的性能)。而 Augur 提供了表达式解析的Benchmark测试用例,可以进行解析性能的验证。
_I(_I('xxx'))
Benchmark Mode Cnt Score Error Units
AbsBenchmarkTest.test avgt 25 1.644 ± 0.009 ms/op
一个两层嵌套的表达式解析10W次的性能是1.6ms左右。相比于整个预估的时间,以及语言化表达式对于特征迭代效率的提升,这一耗时在当前业务场景下,基本可以忽略不计。
4.2.3 系统的其他组成部分
一个完善可靠的预估系统,除了“看得见”的高性能预估能力,还需要做好以下几个常被忽略的点:
- 特征日志处理流程: 预估时产出的特征日志,需要通过框架上传到公司日志中心或者以用户希望的方式进行存储,方便模型的迭代。当然,必要的时候可以落入本地,方便问题的定位。
- 监控,系统&特征:系统监控不用多说,美团内部的Cat&天网,可以构建出完善的监控体系。另一方面,特征的监控也很重要,因为特征获取的稳定性决定了模型预估的质量,所以我们构建了实时的特征分布监控系统,可以分钟级发现特征分布的异常,最大限度上保证模型预估的可靠性。
- 丰富的接口:除了预估接口,还需要有特征抽取接口、模型打分Debug 接口、特征表达式测试接口、模型单独测试接口、特征模型刷新接口、特征依赖检等等一系列接口,这样才可以保证整个系统的可用性,并为后面管理平台的建设打下基础。
Augur在完成了以上多种能力的建设之后,就可以当做一个功能相对完善且易扩展的在线预估系统。由于我们在构建 Augur的时候,设立了明确的边界,故以上能力是独立于业务的,可以方便地进行复用。当然,Augur的功能管理,更多的业务接入,都需要管理平台的承载。于是,我们就构建了Poker平台,其中的在线预估管理模块是服务于Augur,可以进行模型特征以及业务配置的高效管理。我们将在下一小节进行介绍。
4.3 建设预估平台:快速复用与高效管理
4.3.1 能力的快速复用
Augur在设计之初,就将所有业务逻辑通过OP和Transformer承载,所以跟业务无关。考虑到美团搜索与NLP部模型预估场景需求的多样性,我们还为Augur赋予多种业务调用的方式。
- Java服务化调用:即基于Augur构建一个完整的Service,可以实现无状态分布式的弹性预估能力。
- Thrift调用:Java服务化版本中内置了对Thrift 的支持,使不同语言的业务都可以方便地拥有模型预估能力。
- 双框架:Augur支持同一个服务同时提供Pigeon(美团内部的RPC框架)以及Thrift 服务,从而满足不同业务的不同需求。
- Java SDK:Augur同样支持以SDK的方式将能力嵌入到已有的集群当中。但如此一来,分布式能力就无法发挥了。所以,我们一般应用在性能要求高、模型比较小、特征基本可以存在本地的场景下。
其中服务化是被应用最多的方式,为了方便业务方的使用,除了完善的文档外,我们还构建了标准的服务模板,任何一个业务方基本上都可以在30分钟内构建出自己的Augur服务。服务模板内置了60多个常用逻辑和计算OP , 并提供了最佳实践文档与配置逻辑,使得业务方在没有指导的情况下可以自行解决95%以上的问题。整个流程如下图所示:
当然,无论使用哪一种方式去构建预估服务,都可以在美团内部的Poker平台上进行服务、模型与特征的管理。
4.3.2 Augur管理平台Poker的构建
实现一个框架价值的最大化,需要一个完整的体系去支撑。而一个合格的在线预估平台,需要一个产品级别的管理平台辅助。于是我们构建了Poker(搜索实验平台),其中的在线预估服务管理模块,也是Augur的最佳拍档。Augur是一个可用性较高的在线预估框架,而Poker+Augur则构成了一个好用的在线预估平台。下图是在线预估服务管理平台的功能架构:
首先是预估核心特征的管理,上面说到我们构建了语言化的特征表达式,这其实是个较为常见的思路。Poker利用Augur提供的丰富接口,结合算法的使用习惯,构建了一套较为流畅的特征管理工具。可以在平台上完成新增、测试、上线、卸载、历史回滚等一系列操作。同时,还可以查询特征被服务中的哪些模型直接或者间接引用,在修改和操作时还有风险提示,兼顾了便捷性与安全性。
模型管理也是一样,我们在平台上实现了模型的配置化上线、卸载、上线前的验证、灰度、独立的打分测试、Debug信息的返回等等。同时支持在平台上直接修改模型配置文件,平台可以实现模型多版本控制,一键回滚等。配置皆为实时生效,避免了手动上线遇到问题后因处理时间过长而导致损失的情况。
4.3.3 Poker + Augur的应用与效果
随着Augur和Poker的成熟,美团搜索与NLP部门内部已经有超过30个业务方已经全面接入了预估平台,整体的概况如下图所示:
预估框架使用迁移Augur后,性能和模型预估稳定性上均获得了较大幅度的提升。更加重要的是,Poker平台的在线预估服务管理和Augur预估框架,还将算法同学从繁复且危险的上线操作中解放出来,更加专注于算法迭代,从而取得更好的效果。以点评搜索为例,在Poker+Augur稳定上线之后,经过短短半年的时间,点评搜索核心KPI在高位基础上仍然实现了大幅提升,是过去一年半涨幅的六倍之多,提前半年完成全年的目标。
4.4 进阶预估操作:模型也是特征
4.4.1 Model as a Feature,同构or异构?
在算法的迭代中,有时会将一个模型的预估的结果当做另外一个模型输入特征,进而取得更好的效果。如美团搜索与NLP中心的算法同学使用BERT来解决长尾请求商户的展示顺序问题,此时需要BERT as a Feature。一般的做法是离线进行BERT批量计算,灌入特征存储供线上使用。但这种方式存在时效性较低(T+1)、覆盖度差等缺点。最好的方式自然是可以在线实时去做BERT模型预估,并将预估输出值作为特征,用于最终的模型打分。这就需要Augur提供Model as a Feature的能力。
得益于Augur抽象的流程框架,我们很快超额完成了任务。Model as a feature,虽然要对一个Model做预估操作,但从更上层的模型角度看,它就是一个特征。既然是特征,模型预估也就是一个计算OP而已。 所以我们只需要在内部实现一个特殊的OP,ModelFeatureOpreator就可以干净地解决这些问题了。
我们在充分调研后,发现Model as a Feature有两个维度的需求:同构的特征和异构的特征。同构指的是这个模型特征与模型的其他特征一样,是与要预估的文档统一维度的特征,那这个模型就可以配置在同一个服务下,也就是本机可以加载这个Stacking模型;而异构指的是Model Feature与当前预估的文档不是统一维度的,比如商户下挂的商品,商户打分需要用到商品打分的结果,这两个模型非统一维度,属于两个业务。正常逻辑下需要串行处理,但是Augur可以做得更高效。为此我们设计了两个OP来解决问题:
- LocalModelFeature: 解决同构Model Feature的需求,用户只需像配置普通特征表达式一样即可实现在线的Model Stacking;当然,内部自然有优化逻辑,比如外部模型和特征模型所需的特征做统一整合,尽可能的减少资源消耗,提升性能。 该特征所配置的模型特征,将在本机执行,以减少RPC。
- RemoteModelFeature:解决异构Model Feature的需求,用户还是只需配置一个表达式,但是此表达式会去调用相应维度的Augur服务,获取相应的模型和特征数据供主维度的Augur服务处理。虽然多了一层 RPC,但是相对于纯线性的处理流程,分片异步后,还是有不少的性能提升。
美团搜索内部,已经通过LocalModelFeature的方式,实现了BERT as a Feature。在几乎没有新的使用学习成本的前提下,同时在线上取得了明显的指标提升。
4.4.2 Online Model Ensemble
Augur支持有单独抽取特征的接口,结合Model as a Feature,若需要同时为一个文档进行两个或者多个模型的打分,再将分数做加权后使用,非常方便地实现离线Ensemble出来模型的实时在线预估。我们可以配置一个简单的LR、Empty 类型模型(仅用于特征抽取),或者其他任何 Augur 支持的模型,再通过LocalModelFeature配置若干的Model Feature,就可以通过特征抽取接口得到一个文档多个模型的线性加权分数了。而这一切都被包含在一个统一的抽象逻辑中,使用户的体验是连续统一的,几乎没有增加学习成本。
除了上面的操作外,Augur还提供了打分的同时带回部分特征的接口,供后续的业务规则处理使用。
5. 更多思考
当然,肯定没有完美的框架和平台。Augur和Poker还有很大的进步空间,也有一些不可回避的问题。主要包括以下几个方面。
被迫“消失”的Listwise特征
前面说到,系统架构设计中没有“银弹”。在采用了无状态分布式的设计后,请求会分片。所以ListWise类型的特征就必须在打分前算好,再通过接口传递给Augur使用。在权衡性能和效果之后,算法同学放弃了这一类型的特征。
当然,不是说Augur不能实现,只是成本有些高,所以暂时Hold 。我们也有设计过方案,在可量化的收益高于成本的时候,我们会在Augur中开放协作的接口。
单机多层打分的缺失
Augur一次可以进行多个模型的打分,模型相互依赖(下一层模型用到上一层模型的结果)也可以通过Stacking技术来解决。但如果模型相互依赖又逐层减少预估文档(比如,第一轮预估1000个,第二轮预估 500),则只能通过多次RPC的方式去解决问题,这是一个现实问题的权衡。分片打分的性能提升,能否Cover多次RPC的开销?在实际开发中,为了保持框架的清晰简单,我们选择了放弃多层打分的特性。
离线能力缺失?
Poker是搜索实验平台的名字。我们设计它的初衷,是解决搜索模型实验中,从离线到在线所有繁复的手工操作,使搜索拥有一键训练、一键Fork、一键上线的能力。与公司其他的训练平台不同,我们通过完善的在线预估框架倒推离线训练的需求,进而构建了与在线无缝结合的搜索实验平台,极大地提升了算法同学的工作效。
未来,我们也会向大家介绍产品级别的一站式搜索实验平台,敬请期待。
6.未来展望
在统一了搜索的在线预估框架后,我们会进一步对Augur的性能&能力进行扩展。未来,我们将会在检索粗排以及性能要求更高的预估场景中去发挥它的能力与价值。同时 ,我们正在将在线预估框架进一步融合到我们的搜索实验平台Poker中,与离线训练和AB实验平台做了深度的打通,为业务构建高效完整的模型实验基础设施。
如果你想近距离感受一下Augur的魅力,欢迎加入美团技术团队!
7. 作者简介
朱敏,紫顺,乐钦,洪晨,乔宇,武进,孝峰,俊浩等,均来自美团搜索与NLP部。