共计 5184 个字符,预计需要花费 13 分钟才能阅读完成。
微博举荐团队:陈雨、韩楠、蔡小娟、高家华
1. 我的项目背景
热门微博是新浪微博的重要性能之一,蕴含热门流、热点流、频道流、小视频后举荐、视频社区等场景。
举荐首页 发现页举荐 沉迷视频
weidl 机器学习框架为热门微博在线学习提供模型训练和推理服务,举荐全链路中在线推理服务的性能始终是 weidl 框架优化迭代的重要指标。在线学习零碎依靠于 weidl 框架。其服务的吞吐量、均匀响应工夫、承接上游 QPS、机器资源占用等指标互相制衡,其中 weidl 框架推理计算的性能至关重要,与举荐服务全链路的整体性能指标及老本密切相关。摸索引擎中计算图运行时算子计算减速的各种个性及优化反对成为本我的项目次要方向。
DeepRec 是阿里巴巴团体提供的针对搜寻、举荐、广告场景模型的训练 / 预测引擎,在分布式、图优化、算子、Runtime 等方面对稠密模型进行了深度性能优化,同时提供了稠密场景下丰盛的 Embedding 相干性能。
本文次要介绍热门微博举荐的整体架构与 DeepRec 对热门举荐框架性能上的晋升,并具体分析的 weidl 平台中应用的 DeepRec 的重要优化点。
2. 热门微博举荐零碎与 weidl 在线学习平台
2.1 热门微博举荐零碎整体架构
热门微博举荐零碎可分为前台业务与 weidl 在线学习平台两个局部。前台业务为各个业务的接口,负责将举荐后果返回给业务方。在线学习平台集成了样本拼接、模型训练、参数服务器、模型服务等多个模块,为热门举荐的多个业务实现了残缺的举荐流程,可疾速为新业务搭建一套举荐零碎。
2.2 weidl 在线学习平台
在线学习平台是整个零碎最外围的局部,次要负责召回、粗排、精排等模块。热门举荐零碎为全链路大规模深度模型的在线学习零碎,其中召回模块有趣味召回、热点召回、策略召回、模型召回等多路召回,别离从千万级物料库中召回局部候选集,通过每路配额配置,将万级物料送入粗排模块。粗排阶段通过物料特色离线生成、用户特色实时拉取的形式,实现高性能的打分服务,通过粗排排序后,将千级候选集送入精排阶段。精排阶段模型最为简单,物料与用户特色实时拉取,多场景多指标交融,最终通过规定零碎的重排,选出一次曝光的博文,举荐给用户。
在线学习平台底层推理计算局部采纳 bridge 模式,反对多个 backend,包含 DeepRec、TensorFlow、Torch、TensorRT 等,同时反对基于 CPU 与 GPU 的模型训练与在线推理。
weidl 在线学习平台
热门微博举荐零碎从 2018 年开始,通过几年的降级,在实时性和规模上都有了实质的晋升。
2.2.1 实时性
实时性包含模型学习到用户行为的速度,模型参数的更新利用到线上模型服务的速度。举荐零碎的更新速度越快,越可能反馈用户最近的用户习惯,越可能给用户进行越有时效性的举荐;模型更容易发现最新风行的数据 pattern,越可能让模型反馈找到最新的风行趋势。工程上次要通过以下几个方面,实现举荐零碎的实时性。
a. 样本拼接作为模型训练的终点,一条残缺的样本拼接实现的速度决定了模型学习用户行为速度的下限,目前热门举荐样本拼接窗口为 30 分钟,即用户在客户端的互动行为在 30 分钟内必会生成一条样本,送入 kafka 队列。
b. 模型训练读取样本流 kafka,保障 kafka 无积压,所以该条样本会在毫秒级被模型学到,并通过 rpc 调用,更新到训练的参数服务器,并将新的模型参数推入 kafka 队列。
c. 参数同步服务从模型更新的 kafka 队列中读取数据,将模型最新的参数通过 rpc 调用,发送给在线服务所用的参数服务器中,此时从用户行为到模型更新实现。
d. 模型在线推理服务直连参数服务器,实时拉取模型最新参数进行打分。除去样本拼接所需的 30 分钟窗口,其余流程在 1 分钟内实现。
2.2.2 大规模深度简单模型
热门举荐业务从最后的 FM 模型,到当初召回阶段以双塔为主,粗排阶段以 cold dnn 为主,精排阶段以多场景、多指标的简单深度模型为主,模型在特色数量、指标个数、模型构造复杂度上都产生了质的变动,给业务带来了很大的收益。
精排模型从 snr 模型迭代到 mm 模型
粗排双塔模型迭代到 cold dnn 模型
模型复杂度的晋升给工程架构带来了不小的压力,一个 multitask 模型比一个单指标的 dnn 模型在算力上是成倍的减少。为了简单模型的落地,热门微博举荐团队摸索了多种开源框架,包含 TensorRT, XDL,TFRA 等,通过测试与源码剖析,这些框架都在原生 Tensorflow 根底上做了不同方向的优化,但性能始终无奈满足要求。同时,咱们也通过指令集优化、改良 TensorFlow 内存治理、算子交融等形式,优化 weidl kernel 局部性能。
在一直的优化与开源框架的尝试中,发现 DeepRec 框架在性能、易用性、与 weidl 的兼容性上都全面胜出,最终,热门举荐框架引擎采纳 DeepRec 引擎,晋升了训练与在线推理的新能,同时也给业务带来了成果上的晋升。
3.DeepRec 及相干模块优化点分析
3.1 OneDNN 库减速算子运算
DeepRec 集成了最新版本的开源的跨平台深度学习性能减速库 oneDNN(oneAPI Deep Neural Network Library),英特尔相干团队进一步优化将 oneDNN 原有的线程池对立成 DeepRec 的 Eigen 线程池,缩小了线程池切换开销,防止了不同线程池之间竞争而导致的性能降落问题。oneDNN 针对支流算子实现了性能优化,包含 MatMul、BiasAdd、LeakyReLU 等在稠密场景中的常见算子。针对热门微博的线上模型,性能晋升显著。
在 DeepRec 中英特尔 CESG 团队针对搜寻广告举荐模型中存在着大量稠密算子如 Select、DynamicStitch、Transpose、Tile、SparseSegmentMean、Unique、SparseSegmentSum、SparseFillEmptyRows 等一系列稠密算子进行了深度的优化,上面介绍 2 个罕用稠密算子的优化办法。
3.1.1 Select 算子优化
Select 算子实现原理是根据条件来做元素的抉择,此时可采纳向量化指令的 mask load 形式,如图所示,以缩小原先由 if 条件带来大量判断所导致的工夫开销,而后再通过批量抉择晋升数据读写效率,最终线上测试表明,性能晋升显著。
3.1.2 Transpose 算子优化
同样,能够应用向量化的 unpack 和 shuffle 指令对 transpose 算子进行优化,即通过小 Block 的形式对矩阵进行转置,最终经线上测试表明,性能晋升同样非常显著。
3.2 要害门路优先的调度引擎
DeepRec 通过对执行引擎以及底层线程池的从新设计,达到在不同的场景下,包含 trianing 和 inference,能做到更佳执行性能。保障不同线程之间的均衡性,尽量减少线程之间的 steal,防止加锁等问题。
Executor 的设计须要思考对内存的拜访及其并行实现之间的分割,进行多层次任务调度,缩小缓存缺失和近程内存拜访,充分发挥多核、多节点 CPU 的并行个性,晋升零碎的运行性能。在线程池层面,设计 Cost-aware 线程池,联合内存感知以及算子类型等信息,进行针对性优化;在计算图层面,对张量内存的地位进行调度,有利于线程池的调度;在算子生成层面,进行有利于线程池任务调度的算子工作划分。
DeepRec 提供的基于要害门路优化的执行引擎,通过动静采集 Session Run 状况,统计与计算多组指标,并构建 CostModel,计算出一个较优的调度策略。该性能中蕴含了基于要害门路的调度策略,依据 CostModel patching 执行细碎算子的调度策略以及线程池 Cost-aware 调度策略等。
在 graph 执行过程中,Collector 会监测所有算子执行以及线程池状况,包含算子执行工夫,线程池 pending 工作饱和度,以及算子的前后依赖关系。这些参数会通过 CostModel 来计算更佳的调度策略。对于一张 graph 来说,存在一条或者多条要害门路,即从输出到输入通过的延时最长的逻辑门路。graph 执行总的工夫肯定是大于等于要害门路工夫。为了让整个 graph 执行更快,并发更佳高效,在 graph 执行时该当优先执行要害门路上的节点。
在稠密模型图中,可能会存在大量细碎算子,会带来大量调度开销。有些能够通过算子交融来做优化,算子交融个别通过 graph pattern 匹配或者手动指定子图来确定须要交融的对象,难以笼罩全副算子。故而在 executor 层面,通过 trace 运行时数据来动静进行批量调度执行,这样能够缩小非必要的细碎算子调度开销。
在线程调度层面,目前的线程池调度策略比较简单,如果以后执行线程是 inter 线程,优先将 task 调度到以后线程执行,若不是,则调度到一个 random 线程上。线程的 balance 齐全由 steal 机制来保障。在咱们的察看中,发现 inter 线程之间存在大量的 steal,这会导致很多锁以及反复的线程调度等开销。CostModel executor 通过采集运行时数据,来确定更佳的线程来执行工作,缩小大量的 steal 行为。
在简单模型上,应用 DeepRec 的 CostModel 调度,可能生成更佳的调度策略,缩小调度开销。在测试的 snr 模型上均匀耗时稳固优化 2ms。
3.3 动静感知的内存 / 显存分配器
在张量内存治理方面,通常存在两点问题,一个是内存碎片过多,另一个是没有思考模型构造存在多分支的状况下算子并行带来的内存增长。其内存治理非常粗放,大体上都是运行时根据内存申请动静进行内存开释和调配,同时进行一些内存池治理。因为无奈感知下层利用的调配申请特点,这种内存治理存在着内存碎片过多的特点。例如在不分明后续内存申请的状况下,因为后期的屡次内存调配和开释,会导致起初的大内存申请因为内存碎片的问题而须要一块新的内存或者 OOM。
深度学习模型的内存调配因为其利用特点存在着显著的规律性,训练时都是以一个个 mini-batch 的模式训练,每个 mini-batch 的调配特色大体上保持一致,训练时前向过程始终分配内存,较少开释,而反向过程中会开释前向计算中的长期张量,开释大量内存,所以内存会周期性出现先增长后升高的特色。基于此学习到执行过程中内存调配 pattern,从而缩小内存的动态分配以及对内存块做到最佳的复用。同时自适应内存分配器也是 graph-aware 的,这样使得不同子图之间存在较小的互相烦扰,进步调配效率。自适应内存分配器根本架构如下图所示:
自适应内存分配器在训练过程对于后面的 K 轮进行一些统计,通过 Allocator 模块,对内存的调配,包含调配的工夫点、调配的大小,统计好调配的工夫点和大小后,在 K 轮完结之后会应用启发式的一些算法布局出一个较优的 tensor cache planner,planner 会创立 allocator,并且预调配一些 tensor 内存块,后续的调配会优先通过此 allocator 进行调配。
自适应内存分配器根本准则是应用尽量少内存,同时进步内存的复用率。整体来讲,自适应内存分配器解决了在稠密场景中内存调配上存在的一些问题,次要包含,第一,缩小了在稠密场景中,大量内存调配问题,包含小内存和大内存。譬如小内存调配呈现在特色的处理过程中,包含一些特色的拼接,或者在做一些穿插特色,这里会存在大量的小内存的调配。同样在模型训练也存在很多大的内存,包含 attention、RNN、或者全连贯层,会有一些大内存的调配。缩小大内存的调配,进而也缩小了 minor pagefault 数量。第二,对于 tensor 能做到更好的复用,缩小了总体的内存占用量。
4.DeepRec 在业务中获得的收益
4.1 服务性能晋升
热门微博已于 9 月将 weidl 的 backend 全量替换为 DeepRec,线上服务与训练都获得了很大的收益,最显著的是精排多任务模型,图计算局部 DeepRec 比原生 TensorFlow 耗时升高 50%,精排阶段整体耗时升高 20%,单机吞吐量晋升 30%。
对于双塔和 cold dnn 模型,图计算局部耗时升高 20%,粗排阶段整体耗时升高 10%,单机吞吐量晋升 20%,模型训练模块性能晋升 20%,晋升了训练速度并无效的改善了样本积压问题。
4.2 性能晋升所带来的其余收益
举荐引擎模块整体耗时缩小与吞吐量的晋升,缩小了举荐在训练与在线推理上所应用的机器资源,极大的升高了公司老本。
在线推理服务性能晋升,使举荐引擎各个模块能够计算更多的候选物料,粗排阶段能够计算更多的候选物料,晋升物料库总量与扩充召回条数,精排也由 1000 条扩到 2000 条,每个阶段候选物料数的减少,都会对整体指标有显著的晋升。
更多对于 DeepRec 训练 / 预测引擎相干信息: https://github.com/alibaba/Deep