在搜索推荐等算法场景中,端侧基于用户行为进行实时推理,可以有效提升算法效果,但这需要在端侧进行高效的用户行为采集计算和模型推理。而且,端侧计算环境复杂,计算任务研发成本高,数据离散,数据理解缺失,这就要求我们需要进行端侧环境抽象隔离,才能实现低研发成本。
淘宝端计算平台基于自研的 MNN 推理引擎和深度优化的 Python 虚拟机,在端侧构建了完善的模型推理和特征计算环境,并构建了完善的发布监控等基础设施。目前淘宝端计算平台已服务了包含手机淘宝在内的 20+ App,支撑了百余个业务场景,并取得较好的算法和业务效果。
本文整理自阿里巴巴前端平台架构团队负责人黄丛宇(旁通)在 ArchSummit 2022 北京站演讲分享,主题为“淘宝端计算平台介绍”。
本次分享将从计算、数据、质量等多个维度,介绍淘宝端计算平台是如何搭建的以及端计算平台在淘宝业务中的实践,以期为你提供构建端计算平台的可复用经验。
以下是分享实录。
⼀、整体架构
端计算平台面临的问题和挑战
首先介绍淘宝端计算平台的整体架构。
端计算是相对于云计算的一个概念,相比云计算它有几个优势:第一,如果大家是基于云计算去做业务,在端上的交互要经过网络的传输,尤其是在移动端,2G 可能会是几十毫秒,甚至几百毫秒,但如果放到端上去做,只需要调用一个函数就能响应,是实时的。第二,在端侧做各种逻辑、计算的时候,数据存储不需要上云,数据存在用户的手机上,这样可以解决云计算中用户数据上云带来的隐私问题。如果数据上云存储,那么要传输的数据量会非常大,尤其对于手淘这种体量的 App 来说,它的传输量可能是 TB 级或者 PB 级的。第三,随着这两年手机的算力不断提高,移动端的计算能力是过剩的,而云端的计算资源捉襟见肘,所以把一些逻辑推到端上去做是有效利用资源的手段,可以节省云端的资源消耗和成本。
什么是端计算平台?在淘宝的环境下的定义,就是面向端侧、算法任务的一站式研发平台,包括基于 Python 的多端一致的计算容器和计算框架;一个端云数据通道,用来解决数据传输问题;以及一个统一的管理控制台,用于支撑整个任务的发布和管理。淘宝的端计算平台要解决以下几个问题:
统一高效的计算环境。因为我们有 iOS 端、安卓端,一个统一高效的计算环境可以屏蔽各种细节,这样我们只需要关注自己的算法逻辑,不用解决端侧功能上的细节问题。
灵活的发布实验策略。这个策划也是跟算法强相关的,因为算法的研发思路或模式跟工程不太一样,算法需要做大量的实验,要在一个业务场景里面做很多 AB 实验,这对于整个发布的灵活性,以及发布策略的兼容性的要求非常高,而 App 端的发布次数远远不能满足要求,所以端计算平台一定要能解决灵活发布的问题。
便捷的研发调试能⼒。因为算法要开发 Python 脚本,开发 Python 脚本需要研发调试的能力。
以上是整个端计算平台要解决的三大问题,后面会针对这三个问题详细讲解端计算平台各个部分的设计。
淘宝端计算平台的整体架构
上图是整个端计算平台逻辑,图左边是端侧相关的逻辑,最底下是基础能力,包括数据存储,是基于阿里内部一个优化版的 Sqlite 而不是源码做的。然后是 AliNNPython、MNN 引擎,MNN 引擎是我们端侧团队非常核心的深度学习推理引擎,它是在端上去做深度学习推理,基于 AliNNPython 做了一些优化和拆解,后面会详细讲解。
基础能力的封装层面向算法同学,提供更高层的能力的透出,方便他们去写脚本。这里面封装了各种 API,包括数据读写 API、模型推理 API、各种监控 API 等。端侧用户行为采集是整个端计算平台非常核心的一点,端计算平台是要能够基于用户的行为去做分析的。因为端上运行的脚本需要跟业务场景或上下文有关联,而用户行为是非常核心的关联点,我们这里会对用户行为做非常深入的处理。再上面一层我们封装成一个简单的框架,这个框架就是 PaaS 层,算法同学或者业务同学可以基于这个框架更方便地写脚本。在这之上,我们会去做一些任务的管理调度,这个是比较通用的功能,用于端上任务资源的调度。
上图右侧最下面是数据通道,用于端计算的任务和云端的数据传输。MNN 工作台提供完善的调试和研发的能力,是一个全功能的 RDE,不是一个简单的小工具,后面还会介绍。再上面是端计算的控制台,它、是一个网页的控制台,可以做一些任务管控、特征管控、任务发布、异常告警情。再往上一层是云端特征接入和管控,算法或者业务上传数据到云端让业务方使用,需要管控层进行管理。
⼆、计算环境
计算环境构建:AliNNPython 与 MNN 引擎简介
计算环境的 MNN 引擎和 AliNNPython 构成了整个端上的运行基础,可以跑 Python 脚本,可以跑深度模型。针对这两个引擎,我们有一些算法或者功能上的需求。首先是算法需要跨端、动态,屏蔽工程上的烦琐细节。其次,从工程上看,工程人员都会追求高效,但在手淘这种非常复杂的 App 里面很多资源是有严格限制的,比如内存、CPU,或者整个存储空间,我们要在受限的环境下尽量满足算法跨端的动态需求。MNN 引擎最近发布了 2.0 版本,它是在端侧的轻量级、高性能推理引擎,大家可以直接去官网,或者 GitHub 主页了解,这里不再赘述。
AliNNPython 是基于标准的 Python 虚拟机 CPython,做了一个面向端侧受限环境的优化版本虚拟机,基于 2.7 版本。我们主要做了两个工作,第一是对大小做精简。手淘对整个 App 包大小有比较严格的限制,我们花了大量的精力去解决包大小的限制问题,把包从原来的十几个 MB 压缩到了现在的 1.3MB。我们把编译部分拆到了云端,在端侧执行脚本的时候,直接基于编译好的 Bitcode 执行,也就说把任务发到云端之前,先把它编译成 Bitcode,再把 Bitcode 发布到端上执行,把整个执行效率和包大小控制在合理的范围内。我们还删掉了很多对端测算法任务没用的一些 Python 自带的标准库,删掉之后整个包大小就逐步减小了。
第二是对 PythonVM 的架构做了比较大的升级,支持线程级的 VM。Python 有一个全局锁,全局锁会导致整个线程是串行的,在 Python 里跑多线程是没法跑满多核的。我们解决了这个问题,把“锁”想办法给去掉了,去掉后在端上可以实现在多个核上并行跑多个 VM,这样整个 VM 的执行效率就会有比较大的提升,这个对在端侧做任务调度也有非常大的帮助。
三、研发运维
任务组织
计算环境核心就是这两个引擎,它们可以支持在端上跑深度模型、算法的 Python 脚本。为了让脚本能顺畅地发布到端上,我们有一套任务组织逻辑。首先我们对算法任务有组织约束。每个业务场景、需求场景都有一个 Git 仓库来管理所有的端计算任务的代码。每个仓库里面有多个分支,每个分支对应不同的任务。区别是,我们的分支是完全独立的,任务对应的分支是不会合并的,因为可能他俩干的事情是完全不一样的,一合并可能就乱了。分支是用来区分任务,而不是用来做平时那种合并类需求的。每个任务有一个对应的标签,标记任务版本,就是 tag。有 tag 就可以知道当前代码对应的版本,这样会有个比较简单的版本的管理。
基于场景或基于团队这个角度去做基于 Git 的组织方式的管理,在每个端计算的内部也有一个相对比较固定的结构。每个任务里面包括以下三个部分:
脚本,就是 Python 算法脚本;
资源,资源包括共享资源和独享资源;资源最核心的就是模型,在端上跑的深度模型通常是放在资源里面;
配置,用来做变量的设置。
任务的内部结构我们做了简单的固定化,大家在组织结构的时候可以用来参考。要说明的是,资源我们是分共享资源和独享资源的,这是为了解决下发问题。因为资源通常是模型,模型经常是兆级的,在手淘这种体量的 App 里下发兆级内容到用户手机上是件很麻烦的事情,而且会消耗大量的带宽,所以我们把共享资源先单独做下发。比如,有些任务可能下发的是一些独享资源,某个任务在某个版本或者某个用户里面才用,就只给他下发到某个用户的手机里,而不是一波全部下发,避免大量的资源浪费,这是我们在系统层面做的资源优化。
研发流程
把任务组织起来后,我们有一个对应的端计算任务研发的流程。当新的业务来了之后,要怎么去一步步地将任务开发上线。首先要做问题定义,这是比较通用的事请,在做任何事情前要先定义要做什么。接下来是埋点开发。这点比较特殊,因为我们的端计算平台是基于用户行为触发的,而用户行为是基于埋点采集的。这里要确认埋点是不是能够解决问题,或者是不是需要开发新的埋点。开发埋点很麻烦,因为涉及到 App 发版,所以需要慎重考虑。埋点问题解决后,就开始创建任务,在这之后就是后面的开发任务流程了。
开发任务可以基于 MNN 工作台做脚本编辑、真机调试和数据预览。任务开发完后就走整个的发布流程。接下来是在端上去做任务的执行。任务的执行的执行点通常会有两个分支,一个分支是数据采集,因为我们是基于用户行为去做计算,很多算法业务会把采集到的行为做计算,生成用户行为特征。用户行为特征会上传云端,或者给端侧的其他任务作为输入。这个地方会有一个分支,如果需要上传云端,就基于数据通道,把数据传到云端,在云端去做推理训练。如果在端上还有另外一个推理,或者训练任务,就在端上完成训练。把采集到的数据作为端上训练的一个输入,去做训练或者推理。另一个分支是模型压缩,针对云端训练出来的模型进行转换或压缩。在云端,算法通常用 TensorFlow 去做模型训练,训练出来的模型经过转换工具转成 MNN 模型,再经过压缩后下发到端上。
任务发布
接下来我们讲任务发布的逻辑。首先是发布策略,因为这是一个非常强的算法需求。当算法任务下来后,发任务可能会涉及发布到 App 的不同版本里,而不同版本采集的用户行为是不一样的。比如埋点,可能有的埋点在旧版本里没有,这就需要它能够区分不同的版本。我们需要定制策略来去解决这个问题,比如在某一个人群里面、或某几台设备里面,甚至某个用户的某台手机里面跑某一个任务。这样还有额外的好处,就是一旦能够把某个任务发到特定手机上之后,就可以有一个非常好用的线上 Beta 发布功能,算法同学可以非常方便地测试这个版本。比如说上线之前,在自己的手机上测,或者在同事手机上测。还有就是,如果你突然收到某个 crash,或者某个异常,但只在某几台手机产生,就可以把带调试信息的任务发到那台手机上,或者那个版本的 App 上去做信息采集,从而方便地调试端计算上的任务。
上图 Relase 部分是我们整个发布的技术设计。针对发布流程我们内部有回滚机制、有 Beta 机制,还有灰度机制。任务发布不管是端计算任务,还是普通服务端任务都会有些机制。下面将任务发到手机上流程。因为用户的手机不是实时在线,发布是有发布率的,我们基于推送把最新的配置信息先推送到用户手机上,类似发一个通知告诉用户的 App 端计算任务有更新了,用户的 App 在启动后会去拉对应的更新的内容。推送是不可能做到 100% 覆盖的,我们是基于淘宝的底层的基础设施去做的,覆盖率、推送率是由他们来决定,我们就是在上层做了个封装,相当于我们借助淘宝的推送通道完成发布机制的。我们基于推拉结合的方式,先把更新的信息推出去,之后 App 再来拉取。这样推送通道的实时性会好很多,不需要推太多东西。
调试运维
调试运维就是 MNN 工作台,这个是对外公开的,大家可以去官网下载。这个工作台是一个全功能案例,支持调试、代码编辑、日志、打点、断点。写端计算任务的时候,你可以基于这个 MNN 工作台把整个流程全部完成,可以在这个工作台完成编辑、调试和发布等功能,它还支持实时监测手机内存和 CPU 使用量。
这个控制台可以比较全面地监控已经发布到线上的任务。比如任务的运行次数、成功率、失败率以及运行失败分析。如果有运行失败,它会显示总的失败次数,下面还会有对应的错误的日志,或者异常的堆栈。最右下角这张图展示了整个任务在端上的执行的情况。但我们不是全量展示,因为就手淘的体量全量展示对云端资源的消耗量太大。这个事情不是业务强相关的,所以我们做了采样,在你开发调试的过程这个功能还是比较方便的。任务上线之后,运维能够覆盖到各种场景,不会出现任务发到线上后就不知道它到底做了什么的问题了。
四、计算框架
前面三部分讲完之后,大家应该能感受到我们整个端计算平台已经能够完整地支持任务开发、调和发布。算法或业务可以基于端计算平台去做开发了。但是光有这个平台是不够的,这个东西业务方到底怎么用?怎么写脚本?怎么去拿用户行为?查到的数据怎么用?为了解决这些问题,我们就做了一个计算框架。这是搞技术的通用思路——再加一层,这个地方我们就加了一层。计算框架就是面向端侧用户行为计算能力的封装。为什么要做这个事情?因为端侧的用户行为非常复杂,同一个点击在不同的页面代表的含义是不一样的。用户的行为的关联很复杂,要根据业务去定义。算法拿到的是单点数据,这个单点的数据怎么跟业务场景做关联?也就是说,基于单点采集的数据缺少场景、关联性,我们要去解决这个问题。同时还有一个问题,算法在基于用户行为做解决方案的时候,需要知道用户在买了商品之前,到底干了什么事情,点了什么,滑动了什么,收藏了什么,这些东西要串成一条完整样本,这个样本非常复杂。这种现状和需求会导致三个问题:
现状和需求差异比较大,一个单点就能组合出非常复杂的样本。
计算逻辑非常复杂。算法在做方案时要不断查各种数据库、各种数据表,然后去算某个点击到底在哪,过程非常烦琐。
数据种类增多。比如用户的每次点击在端计算平台来看就是个点击,但在算法角度它是不同的点击,对应的是不同的属性。比如收藏和购买这两个都叫点击,但对算法来说肯定是不同的事情。整个的数据种类会随着算法不断更新和语义扩展,而不断增加。
为了解决这三个问题,我们做了下面三件事情。
数据标准。把端侧用户行为做定义(或者叫规范、范式),这样算法再去基于用户行为计算场景的时候,就不需要再去知道具体埋点的细节。
计算框架。用来解决算法在脚本中各种问题,就是要定义各种 API、各种组件,帮助用户解决各种复杂的问题。
管控平台。把面向复杂场景的管理平台做得更细化,更好地满足算法需求。
数据标准
我们把用户的行为基于时间、空间这两个概念构建成了一个流(或者树)。在空间上,比如用户在某个页面里面做了点击,之后到了另一个页面,这就形成了一个上下级的关系,这个上下级的关系是可以构建成一棵树的。这种关系就可以组合成一棵用户行为树。在时间维度,用户的所有行为都是基于时间单向递增的,这种单向递增就是一个非常完美的数据流。基于这两个概念可以推导出两件事,一个是任务触发,比如开发一个端计算任务,想基于用户的某个行为触发,但这个行为的触发是有上下文场景的,可能在详情页里触发也可能在首页触发,于是这棵行为树天生就带着场景信息。整个行为触发会有一个比较通用的模式,这样大家可以顺着通用模式去解决问题。第二个推导是,有了用户行为树 / 流之后,在端侧计算用户的行为时,它会把它转变成一个树 / 流的裁减 / 归并。把之前一些很复杂的数据库查询转变成非常规范的数据结构操作。如图右侧所示,用户在某个页面里面进去之后又出来到下级页面,会形成层级的关系。
计算框架:流数据处理框架
大家不要把这个框架和云端的流数据处理框架混淆,它是面向端侧行为数据流的,不是大家理解 Blink 那种数据流处理。这个框架做了两个核心的事情,一个是触发的配置,就是算法需要知道这个任务在什么地点触发,基于场景的上下游的关系去做配置。这个地方的配置就是长串的 Page_name。在这里面触发之后,用户行为的获取基于刚才的设计,把用户行为数据某一棵子树拿下来处理,这里我们做了比较简单的封装。先基于用户行为条件过滤一下,把整棵子树拿下来,拿下来之后再做一些转换。我这里边写的比较简单,就直接打印一条日志,最后得到的是一个用户的行为。这样再基于这些用户行为数据去进一步做算法处理,或者特征的计算等。
管控平台
算法在端计算平台上有两大类事情,一类是去做深度模型的推理,深度模型的推理就是发一个模型到端上去做推理。另一类事情是,把采集上来的用户行为作为特征,作为模型的输入。我们针对特征层面做了更细化的管控。特征市场是为了帮算法做业务推广。比如有个特征做得很好,想给其他业务方用,这个时候在特征市场里就能够直接看到特征具体样子。
特征监控是针对在数据层面,可以看到一共算了多少数据,每个数据有预览,可以对数据的正确性、成功率、失败率等特征方面进行监控。特征发布跟任务发布是放到一起的,特征发布跟发布 App 或服务端变更差不多,有预发、线上、灰度流程,把这个流程走完了再去发步,以免系统崩溃。
五、业务实践
最后讲下业务实践。我选取了一个比较核心的场景——端上信息流推荐。端上信息流推荐就是淘宝首页,大家打开淘宝首页会刷新很多商品,它是基于你的各种喜好推荐出来的。比如你下拉了 50 个商品,在逛的过程中,可能会去点某个商品,也可能会长按某个商品点不感兴趣按钮。或者说对某个商品图片,你的停留时间比较长,点了这个商品,都说明你对这个商品的兴趣非常的浓厚;如果你点了那个不感兴趣,就是不喜欢这个商品。这里可以看到,人们的兴趣是不断变化的,以前的方式,一次下拉出来的 50 个商品就在你的手机里存着,我们没法去根据你的兴趣动态调整,这对整个推荐效率是有影响的。信息流的作用就是在端上做重排。用户在去下滑的过程中,会去动态的调整没有上屏的商品顺序,把你最感兴趣的商品放到最前面。也就是说,下面蹦出来的商品不是按之前的顺序,而是根据你感兴趣的程度动态提上来的,让你以最快的速度看到最感兴趣的商品。
端上信息流推荐解决了两个问题。一个是端上对用户行为感知是有延迟的。用户行为要基于各种通道上传到云端,可能几十秒,一两分钟就过去了。二是对用户行为感知是不全面的。比如滑动的快慢是很难上传到云端的,因为数据量太大了。除了端上重排,我们还做了智能请求。如果重排之后,后面这几十个商品都不是你感兴趣的,那么再怎么重排也没什么意义。这个时候就重新从云端基于你之前的各种新的兴趣点拉数据,拉下来之后再做重排,更好地匹配你的兴趣爱好。
下图右侧就整个系统的架构图。带来的业务效果就是,在去年大促时大概就这个场景的 GMV 提升了 10% 以上,以上是整个端计算平台在信息流上的使用场景介绍。
整个端计算平台后面会朝更高效、更精细、更稳定的方向去发展,这是平台类产品共同的发展方向。我们后面也会进一步打磨整个平台的各种能力。
演讲嘉宾:
黄丛宇(旁通),阿里巴巴技术专家。清华大学软件工程硕士,毕业后进入阿里巴巴,目前在淘宝端智能团队负责相关算法工程系统和端计算平台研发工作,主要负责服务端业务和平台系统的设计。
评论