新年将至,年味渐浓。虽然疫情的阴霾还没彻底消散,但是相信很多人都对2022年的春天充满了期待。期待春暖花开,期待国泰民安,期待早一天在阳光下自由地呼吸。
老规矩,一年一度的美团技术年货如期而至。
在2022年春节到来之际,我们精选过去一年公众号50多篇技术文章以及20多篇国际顶会论文,整理制作成一本厚达1200多页的电子书,作为新年礼物赠送给大家。
这本电子书内容覆盖前端、后端、算法、数据、安全、测试等多个领域, 希望能对同学们的工作和学习有所帮助。
大家如果觉得有价值,也欢迎转给更多有相同兴趣、积极上进的同事和朋友们,一起切磋,共同成长。
最后,祝大家阖家欢乐,健康平安。新的一年,「虎」力全开,走出个「龙行虎步」,闯出个「虎虎生威」!
关注「美团技术团队」微信公众号,回复 【2021年货】,即可获取电子书的下载链接,大家可免费在线阅读、下载。
温馨提示:今年,我们不仅为大家准备了2021年的年度合集,同时我们将2019年到2021年美团技术团队前端、后端、算法的文章进行了分类整理,同时将安全、运维、测试相关的文章做了综合,大家可以选择性下载或者阅读。
因文件较大,可能需要一点耐心。因部分文章中的动态图片无法在电子书中进行完全的展示,大家可以移步美团技术团队官方博客 tech.meituan.com 或在美团技术团队公众号历史文章中进行查阅,感谢您的理解。
微信扫一扫,识别文末的二维码,关注「美团技术团队」微信公众号,回复【2020年货】、【2019年货】、 【2018年货】、【2017年货】,即可获取往期年货下载链接。
对话系统技术挑战赛DSTC(The Dialog System Technology Challenge)由微软、卡内基梅隆大学的科学家于2013年发起,旨在带动学术与工业界在对话技术上的提升,在对话领域具有极高的权威性和知名度。对话系统挑战赛今年已举办至第十届(DSTC10),吸引了微软、亚马逊、卡内基梅隆大学、Facebook、三菱电子研究实验室、美团、百度等全球知名企业、顶尖大学和机构同台竞技。
DSTC10共包含5个Track,每个Track包含某一对话领域的数个子任务。其中Track5 Task1 Automatic Open-domain Dialogue Evaluation较为系统全面地将开放领域对话的自动评估任务引入DSTC10比赛中。开放领域对话自动评估是对话系统的重要组成部分,致力于自动化地给出符合人类直觉的对话质量评估结果。相比于速度慢、成本高的人工标注,自动化评估方法可以高效率、低成本地对不同对话系统进行打分,有力促进了对话系统的发展。
不同于任务型对话有一个固定的优化目标,开放领域对话更接近人类真实的对话,评估难度更大,因而吸引了广泛的关注。DSTC10 Track5 Task1比赛共包含14个验证数据集(共包含37种不同的对话评估维度)和5个测试数据集(共包含11个评估维度)。美团语音团队最终以平均0.3104的相关性取得了该比赛的第一名,该部分工作已完成一篇论文MME-CRS: Multi-Metric Evaluation based on Correlation Re-Scaling for Evaluating Open-Domain Dialogue,并收录在AAAI2022 Workshop。
开放领域对话评估比赛收集了对话领域论文中的经典数据集,包括14个验证数据集(12个Turn-Level级别数据集和2个Dialog-Level级别数据集)和5个测试数据集。
数据集中的每个对话主要包含以下信息:
每个对话包含多个评估维度,如Context和Response的相关性,Response本身的流畅度等。每个数据集的评估维度不同,14个验证集总共包含37种不同的评估维度,具体包含Overall、Grammar、Relevance、Appropriateness、Interesting等。每个评估维度都有人工标注的打分,打分从1到5,分数越高表示当前评估维度的质量越高。
验证集和测试集的统计信息如图2和图3所示:
其中Turns表示对应数据集中的对话轮数;Qualities表示数据集中每个对话的评估维度,每个评估维度都有对应的人工标注打分;Annos表示每个数据集的标注量。
在该比赛中,每个数据集每个对话每个评估维度都有人工标注的打分,打分范围一般为1到5,一般求均值用于相关性计算。参赛队伍需要设计评估指标用于预测每个对话不同评估维度的打分。每个数据集的每个评估维度的预测打分会和人工标注的打分计算Spearman相关性,最后的比赛结果基于全部测试数据集的评估维度求均值。
开放领域对话的自动评估方法主要分为三类。
Overlap-based方法
早期研究人员将对话系统中Reference和Response类比于机器翻译中的原句和翻译句,借鉴机器翻译的评价指标来评估对话质量。Overlap-based方法计算对话中Response和Reference之间的词重叠情况,词重叠越高打分越高。经典方法包括BLEU[1]和ROUGE[2]等,其中BLEU根据精确率衡量评估质量,而ROUGE根据召回率衡量质量。Response的评估依赖于给定的Reference,而开放领域下合适的Response是无限的,因此,Overlap-based方法并不适用于开放领域对话评估。
Embedding-based方法
随着词向量和预训练语言模型的快速发展,Embedding-based评估方法取得了不错的性能。基于深度模型分别编码Response和Reference,并基于二者的编码计算相关性打分。主要方法包括Greedy Matching[3]、Embedding Averaging[4]和BERTScore[5-6]等。Embedding-based方法相比Overlap-based方法有较大的提升,但是同样依赖于Reference,仍然存在较大的优化空间。
Learning-based方法
基于Reference的开放领域对话评估存在一个One-To-Many[7]困境:即开放领域对话合适的Response是无限的,但人为设计的Reference是有限的(一般为5条左右)。因此,基于对比Reference和Response的相似性(字面重叠或者语义相似)设计开放领域评估方法存在较大局限性。相比已有的Overlap-based方法和Embedding-based方法,ADEM方法[8]首次使用层次化的编码器来编码Context和Reference,并对输入的Response进行打分。ADEM方法基于模型打分和人工打分的均方误差来优化模型参数,期望逼近人类的打分。ADEM模型相比Overlap-based方法和Embedding-based方法取得了很大的成功,Learning-based方法也逐渐成为了开放领域自动化评估的主流方法。
为了不断提高对话评估的准确和全面性,各种不同的评估维度层出不穷。为了应对越来越多评估维度带来的挑战,USL-H[9]将评估维度分为Understandability、Sensibleness和Likeability三类,如图4所示。USL-H针对性提出了VUP(Valid Utterance Prediction)、NUP(Next Utterance Prediction)和MLM(Mask Language Model)3种指标,分别衡量对话中:
现有的评估方法主要有以下问题:
设计的对话指标不够全面,难以综合衡量对话的质量
现有的自动评估方法主要聚焦在个别数据集的部分评估维度上。以当前较为全面的USL-H为例,该方法考虑了Response的流畅度、丰富度以及Context-Response句子对的相关性,但是USL-H忽略了:
实验证明,这些指标的遗漏严重影响了评估方法的性能。为了更全面稳定地评估多个对话数据集,设计考虑更多评估维度的指标势在必行。
缺乏有效的指标集成方法
现有方法大多倾向于为每种评估维度设计一种评估指标,这种思路面对越来越多的评估维度显得力不从心(考虑下比赛测试集共包含37种不同的评估维度)。每种对话维度的评估可能依赖数种评估指标,如Logical评估维度需要对话:1)Response流畅;2)Response和Context是相关的。设计基本的评估子指标,再通过合适的集成方法集成多个子指标打分,可以更全面有效表示不同的对话评估维度。
针对评估指标不够全面,本文设计了5类共7种评估指标(Multi-Metric Evaluation,MME)用于全面衡量对话的质量。基于设计的5类7种基础指标,我们进一步提出了相关性重归一化方法(Correlation Re-Scaling Method,CRS)来集成不同评估指标的打分。我们将提出的模型称为MME-CRS,模型整体架构图5所示:
为了解决现有方法的第一个问题,即设计的对话指标不够全面,我们在比赛中设计了5类共7种评估子指标。
目的:分析Response本身是否足够流畅可理解。
内容:首先基于Dailydialog数据集[10]构建response流畅度数据集,流程如下:
基于上述规则构建流畅度数据集后,在预训练模型SimCSE模型[11]上微调。微调后的模型可以计算任一对话的Response流畅度打分,记为FM打分。
目的:分析Context和Response的相关程度。
内容:基于Dailydialog数据集构建Context-Response句子对形式的相关性数据集,其中句子对相关为正样本,不相关则为负样本。负样本的通常构建思路是将Response随机替换成其他对话的Response。PONE方法[12]指出随机挑选的Respose和Context基本不相关,模型训练收益很小。因此,这里的做法是随机选择10条Response,并计算和真实Response的语义相关度,并选择排名居中的句子作为伪样本。构造数据集后再在SimCSE模型上微调,微调后的模型可用于计算对话中Context和Response的相关度打分,记为RM打分。
目的:分析Context和Response的主题一致性。
内容:GRADE方法[13]构建了Context和Response的主题词级别的图表示,并计算了Context和Response的主题词级别的相关度。相比粗粒度的相关性指标,GRADE更加关注细粒度级别的主题相关程度,是相关性指标的有效补充。TCM指标借鉴GRADE方法。
具体流程如下:首先提取Context和Response中的关键词构建图,其中每个关键词都是一个节点,只有Context和Response的关键词之间存在边。基于ConceptNet获取每个节点的表示,再使用图注意力网络(GATs)聚集关键词邻居节点的信息并迭代每个节点的表示,最后综合全部节点的表示得到对话的图表示。在主题词级别的图表示上连接全连接层用于分类,微调后的模型即可用于计算对话的TCM打分。
目的:分析生成Response的人或对话模型有多大的意愿参与当前对话。
内容:前面提到的指标都是从Context和Response视角评估对话质量,而用户参与度则是基于用户的视角来评估。用户参与度打分一般是0~5,分数越大,表示用户参与当前对话的兴趣越大。我们将ConvAI数据集[10]的参与度打分从1~5缩放到0~1,作为参与度打分数据集。预训练模型仍然使用SimCSE,用于预测对话的参与度打分。预训练后的模型可用于预测对话的用户参与度打分,记为EM。
目的:分析Response本身是否足够细节。
内容:SM指标用于避免Response模棱两可,缺乏信息量。
具体做法如下:序列Mask掉Response中的每一个Token,并基于SimCSE模型的MLM任务计算Negative Log-Likelihood损失,得到的打分称为SM-NLL。替换损失函数为Negative Cross-Entropy和Perplexity可以分别得到SM-NCE和SM-PPL打分,共3个SM指标打分。3个SM指标打分都需要分别归一化到0和1之间。
集成不同评估指标的打分是提高自动化对话评估效果的有效手段。
对每一个待评估的对话,基于上述5类7种基础指标可以得到7种不同的打分。对于待评估数据集的某个评估维度,需要综合7种指标打分得到一个综合打分,用于和人类打分计算相关性。我们的集成方法分为以下两步。
首先,计算验证集上每个数据集每个评估维度7种评估指标的相关性打分,相关性打分越大,认为该指标对该评估维度越重要。对越重要的评估指标赋予一个更大的权重,并将得到的权重在指标维度重新归一化,这样则得到了每个数据集每个评估维度上不同评估指标的权重分布:
其中$S{ijk}$是第$i$个数据集第$j$个评估维度上第$k$个评估指标的相关性打分,$d{ij}$是相关性打分的幂数,$d{ij}$越大则相关性打分越高的指标的权重就越大。一般当max($S{ijk}^{d{ij} }$)在1/3到1/2之间时集成效果最好,这是计算$d{ij}$的一种简单有效手段。实验中,将$d{ij}$设置为常数可以获得更好的泛化效果,我们将$d{ij}$设置为2,并在验证集上计算权重分布,再迁移到测试集上,取得了比赛最优性能。
在数据集维度,将不同数据集中相同评估维度的权重求均值,得到每个评估维度在不同评估指标上的权重分布:
注意这里得到的权重分布已经和具体数据集无关,可以将权重分布迁移到测试集上。
对每个测试集的每个评估维度,计算7种指标打分并基于第一步的权重求加权和,得到综合打分:
加权得到的综合打分和人工打分计算相关性,得到每种评估维度上的模型打分和人工打分的相关性打分。
我们的集成方法基于指标的相关性打分赋予权重并重新归一化,所以将该集成方法称为相关性重归一化方法。在得到的MME指标上使用CRS集成方法,可得MME-CRS评估算法。
我们的方法主要基于Dailydialog数据集预训练(除了EM子指标是使用ConvAI2数据集),在比赛验证集上计算集成方法的权重分布,最终在测试集上取得了0.3104的Spearman相关性打分。
图6展示了比赛基准模型Deep AM-FM[14]以及比赛Top5队伍在测试集上不同数据集评估维度的性能。本文的方法以0.3104的平均Spearman相关性系数取得了第一,且在5个数据集全部11个评估维度中的6个取得了第一,证明了本文方法的优越性能。
为了方便展示,图中方法采用了数据集-评估维度的展示方式。其中J、E、N、DT、DP分别表示JSALT、ESL、NCM、DST10-Topical、DSTC10-Persona数据集,而A、C、G、R分别表示Appropriateness、Content、Grammar、Relevance评估维度。我们对每个评估维度上最好的性能进行了加粗。
在消融实验部分,我们以本文方法MME-CRS评估为基准,在集成阶段分别去除FM、RM、TCM、EM、SM、RM+TCM指标,对比不同指标在集成过程中的重要性。实验性能如图7所示:
相关性指标RM和主题一致性指标TCM都使用了对话中的Context和Response信息,因此在实验中同时去除这两个指标,观察对性能的影响。从图7中的实验结果可以看出:
为了分析集成算法CRS的作用,本文对比了MME-CRS和MME-Avg(将MME多个指标打分简单平均)两个评估方法的性能,如图8所示:
从图中可以看出,MME-CRS方法相比于MME-Avg高了3.49%,证明了CRS算法在集成子指标打分方面的优越性能。
在本次比赛中,我们总结了开放领域对话自动评估存在的两个主要问题,即评估指标不够全面和缺乏有效的指标集成方法。针对评估指标不够全面的问题,本文设计了5类7种评估指标用于全面衡量对话的质量;基于7种基础指标,提出了相关性重归一化方法来计算每种对话评估维度的集成打分。
虽然本文方法在DSTC10比赛中取得了较好的成绩,但后续我们将继续探索其他更有效的评估指标和指标集成方法。我们正在尝试将比赛中的技术应用到美团具体业务中,如语音交互中心的智能外呼机器人、智能营销和智能客服中,在多个不同维度评估机器、人工客服与用户的对话质量,不断优化对话效果,提升用户的满意度。
鹏飞,晓慧,凯东,汪建,春阳等,均为美团平台/语音交互部工程师。
美团语音交互部负责美团语音和智能交互技术及产品研发,面向美团业务和生态伙伴,提供语音和口语数据的大规模处理及智能响应能力。团队在语音识别、合成、口语理解、智能问答和多轮交互等技术上已建成大规模的技术平台服务,研发包括外呼机器人、智能客服、语音交互平台等解决方案和产品并广泛落地。我们长期招聘志同道合的伙伴,感兴趣的同学可以将简历发送至:yuanchunyang@meituan.com(邮件主题:美团平台语音交互部)
作为业务研发,你是否遇到过因为 CDN 问题导致的业务图片加载失败,页面打开缓慢,页面布局错乱或者页面白屏?你是否又遇到过某些区域 CDN 域名异常导致业务停摆,客诉不断,此时的你一脸茫然,不知所措?作为 CDN 运维,你是否常常被业务方反馈的各种 CDN 问题搞得焦头烂额,一边顶着各种催促和压力寻求解决方案,一边抱怨着服务商的不靠谱?今天,我们主要介绍一下美团外卖技术团队端侧 CDN 的容灾方案,经过实践,我们发现该产品能有效减少运维及业务开发同学的焦虑,希望我们的这些经验也能够帮助到更多的技术团队。
CDN 因能够有效解决因分布、带宽、服务器性能带来的网络访问延迟等问题,已经成为互联网不可或缺的一部分,也是前端业务严重依赖的服务之一。在实际业务生产中,我们通常会将大量的静态资源如 JS 脚本、CSS 资源、图片、视频、音频等托管至 CDN 服务,以享受其边缘节点缓存对静态资源的加速。但是在享用 CDN 服务带来更好体验的同时,也经常会被 CDN 故障所影响。比如因 CDN 边缘节点异常,CDN 域名封禁等导致页面白屏、排版错乱、图片加载失败。
每一次的 CDN 故障,业务方往往束手无策,只能寄希望于 CDN 团队。而 CDN 的监控与问题排查,对 SRE 也是巨大的难题和挑战。一方面,由于 CDN 节点的分布广泛,边缘节点的监控就异常困难。另一方面,各业务汇聚得到的 CDN 监控大盘,极大程度上隐匿了细节。小流量业务、定点区域的 CDN 异常往往会被淹没。SRE 团队也做了很多努力,设计了多种方案来降低 CDN 异常对业务的影响,也取得了一定的效果,但始终有几个问题无法很好解决:
当前,美团外卖业务每天服务上亿人次,即使再小的问题在巨大的流量面前,也会被放大成大问题。外卖的动态化架构,70%的业务资源都依赖于 CDN,所以 CDN 的可用性严重影响着外卖业务。如何更有效的进行 CDN 容灾,降低 CDN 异常对业务的影响,是我们不断思考的问题。
既然以上问题 SRE 侧无法完美地解决,端侧是不是可以进行一些尝试呢?比如将 CDN 容灾前置到终端侧。不死鸟(Phoenix) 就是在这样的设想下,通过前端能力建设,不断实践和完善的一套端侧 CDN 容灾方案。该方案不仅能够有效降低 CDN 异常对业务的影响,还能提高 CDN 资源加载成功率,现已服务整个美团多个业务和 App。
为降低 CDN 异常对业务的影响,提高业务可用性,同时降低 SRE 同学在 CDN 运维方面的压力,在方案设计之初,我们确定了以下目标:
适用所有依赖 CDN ,希望降低 CDN 异常对业务影响的端侧场景,包括 Web、SSR Web、Native 等技术场景。
一直以来,CDN 的稳定性是由 SRE 来保障,容灾措施也一直在 SRE 侧进行,但仅仅依靠链路层面上的保障,很难处理局部问题和实现快速止损。用户终端作为业务的最终投放载体,对资源加载有着天然的独立性和敏感性。如果将 CDN 容灾前置到终端侧,无论从时效性,精准性,都是 SRE 侧无法比拟的。在端侧进行容灾,就需要感知 CDN 的可用性,然后实现端侧自动切换的能力。我们调研整个前端领域,并未发现业内在端侧 CDN 容灾方面有所实践和输出,所以整个方案的实现是从无到有的一个过程。
Phoenix 端侧 CDN 容灾方案主要由五部分组成:
为保证各个端侧容灾效果和监控指标的一致性,我们设计了统一的容灾流程,整体流程如下:
Web 端的 CDN 资源主要是 JS、CSS 和图片,所以我们的容灾目标也聚焦于这些。在 Web 侧的容灾,我们主要实现了对静态资源,异步资源和图片资源的容灾。
实现思路
要实现资源的容灾,最主要的问题是感知资源加载结果。通常我们是在资源标签上面添加错误回调来捕获,图片容灾可以这样实现,但这并不适合 JS,因为它有严格的执行顺序。为了解决这一问题,我们将传统的标签加载资源的方式,换成XHR来实现。通过Webpack在工程构建阶段把同步资源进行抽离,然后通过PhoenixLoader来加载资源。这样就能通过网络请求返回的状态码,来感知资源加载结果。
在方案的实现上,我们将 SDK 设计成了 Webpack Plugin,主要基于以下四点考虑:
通过调研发现,前端有 70%的工程构建都离不开 Webpack,而 Webpack Plugin 独立配置,即插即用的特性,是实现方案的最好选择。整体方案设计如下:
当然,很多团队在做性能优化时,会采取代码分割,按需引入的方式。这部分资源在同步资源生成的过程中无法感知,但这部分资源的加载结果,也关系到业务的可用性。在对异步资源的容灾方面,我们主要是通过对 Webpack 的异步资源处理方式进行重写,使用Phoenix Loader接管资源加载,从而实现异步资源的容灾。整体分析过程如下图所示:
CSS 资源的处理与 JS 有所差别,但原理相似,只需要重写 mini-css-extract-plugin 的异步加载实现即可。
Web 端方案资源加载示意:
容灾效果
客户端的 CDN 资源主要是图片,音视频以及各种动态化方案的 bundle 资源。Native 端的容灾建设也主要围绕上述资源展开。
实现思路
重新请求是 Native 端 CDN 容灾方案的基本原理,根据互备 CDN 域名,由 Native 容灾基建容灾域名重新进行请求资源,整个过程发生在原始请求失败后。Native 容灾基建不会在原始请求过程中进行任何操作,避免对原始请求产生影响。原始请求失败后,Native 容灾基建代理处理失败返回,业务方仍处于等待结果状态,重请新求结束后向业务方返回最终结果。整个过程中从业务方角度来看仍只发出一次请求,收到一次结果,从而达到业务方不感知的目的。为将重新请求效率提升至最佳,必须尽可能的保证重新请求次数趋向于最小。
调研业务的关注点和技术层面使用的网络框架,结合 Phoenix 容灾方案的基本流程,在方案设计方面,我们主要考虑以下几点:
基于以上设计要点,我们将 Phoenix 划分为以下结构图,图中将整体的容灾 SDK 拆分为两部分 Phoenix-Adaptor 部分与 Phoenix-Base 部分。
Phoenix-Base
Phoenix-Base 是整个 Phoenix 容灾的核心部分,其包括容灾数据缓存,域名更换组件,容灾请求执行器(区别于原始请求执行器),监控器四个对外不可见的内部功能模块,并包含外部接入模块,提供外部接入功能。
Phoenix-Adaptor
Phoenix-Adaptor 是 Phoenix 容灾的扩展适配部分,用于兼容各种网络框架。
容灾效果
① 业务成功率
以外卖图片业务为例,Android 业务成功率对比(同版本 7512,2021.01.17 未开启 Phoenix 容灾,2021.01.19 晚开启 Phoenix 容灾)。
iOS 业务成功率对比(同版本 7511,2021.01.17 未开启 Phoenix 容灾,2021.01.19 晚开启 Phoenix 容灾)。
② 风险应对
以外卖与美团图片做为对比 ,在 CDN 服务出现异常时,接入 Phoenix 的外卖 App 和未接入的美团 App 在图片成功率方面的对比。
端侧的域名重试,会在某一域名加载资源失败后,根据容灾列表依次进行重试,直至成功或者失败。如下图所示:
如果域名 A 大范围异常,端侧依然会首先进行域名 A 的重试加载,这样就导致不必要的重试成本。如何让资源的首次加载更加稳定有效,如何为不同业务和地区动态提供最优的 CDN 域名列表,这就是动态计算服务的要解决的问题。
计算原理
动态计算服务通过域名池和项目的 Appkey 进行关联,按照不同省份、不同地级市、不同项目、不同资源等维度进行策略管理。通过获取 5 分钟内对应项目上报的资源加载结果进行定时轮询计算,对域名池中的域名按照地区(城市&&省份)的可用性监控。计算服务会根据域名可用性动态调整域名顺序并对结果进行输出。下图是一次完整的计算过程:
假设有 A、B、C 三个域名,成功率分别是 99%、98%、97.8%,流量占比分别是 90%、6%、4%。基于转移基准,进行流量转移,比如,A 和 B 成功率差值是 1,B 需要把自己 1⁄2 的流量转移给 A,同时 A 和 C 的成功率差值大于 1,C 也需要把自己 1⁄2 的流量转移给 A,同时 B 和 C 的差值是 0.2,所以 C 还需要把自己 1⁄4 的流量转移给 B。最终,经过计算,A 的流量占比是 95%,B 是 3.5%,C 是 1.5%。最后,经过排序和随机计算后将最终结果输出。
因为 A 的占比最大,所以 A 优先被选择;通过随机,B 和 C 也会有一定的流量;基于转移基准,可以实现流量的平稳切换。
异常唤起
当某个 CDN 无法正常访问的时候,该 CDN 访问流量会由计算过程切换至等效的 CDN B。如果 SRE 发现切换过慢可以进行手动干预分配流量。当少量的 A 域名成功率上升后,会重复计算过程将 A 的流量加大。直至恢复初始态。
服务效果
动态计算服务使得资源的首次加载成功率由原来的99.7%提升至99.9%。下图为接入动态计算后资源加载成功率与未接入加载成功率对比。
在监控层面,SRE 团队往往只关注域名、大区域、运营商等复合维度的监控指标,监控流量巨大,对于小流量业务或者小范围区域的 CDN 波动,可能就无法被监控分析识别,进而也就无法感知 CDN 边缘节点异常。容灾监控建设,主要是为了解决 SRE 团队的 CDN 监控告警滞后和监控粒度问题。监控整体设计如下:
流程设计
端侧容灾数据的上报,分别按照项目、App、资源、域名等维度建立监控指标,将 CDN 可用性变成项目可用性的一部分。通过计算平台对数据进行分析聚合,形成 CDN 可用性大盘,按照域名、区域、项目、时间等维度进行输出,与天网监控互通,建立分钟级别的监控告警机制,大大提升了 CDN 异常感知的灵敏性。同时,SRE 侧的天网监控,也会对动态计算服务结果产生干预。监控整体流程如下:
监控效果
CDN 监控不仅从项目维度更加细粒度的监测 CDN 可用性,还为 CDN 异常排查提供了区域、运营商、网络状况、返回码等更丰富的信息。在监控告警方面,实现了分钟级异常告警,灵敏度也高于美团内部的监控系统。
端侧域名切换的有效性,离不开 CDN 服务的支持。在 CDN 服务方面,在原有 SRE 侧容灾的基础上,对 CDN 服务整体做了升级,实现域名隔离,解决了单域名对应多 CDN 和多域名对应单 CDN 重试无效的弊端。
经过一年的建设与发展,Phoenix CDN 容灾方案日趋成熟,现已成为美团在 CDN 容灾方面唯一的公共服务,在多次 CDN 异常中发挥了巨大的作用。在端侧,当前该方案日均容灾资源3000万+,挽回用户35万+,覆盖外卖,酒旅,餐饮,优选,买菜等业务部门,服务200+个工程,外卖 App、美团 App、大众点评 App均已接入。
在 SRE 侧,实现了项目维度的分钟级精准告警,同时丰富了异常信息,大大提高了 SRE 问题排查效率。自从方案大规模落地以来,CDN 异常时鲜有手动切换操作,极大减轻了 SRE 同学的运维压力。
由于前端技术的多样性和复杂性,我们的 SDK 无法覆盖所有的技术方案,所以在接下来的建设中,我们会积极推广我们的容灾原理,公开动态计算服务,希望更多的框架和服务在我们的容灾思想上,贴合自身业务实现端侧的 CDN 容灾。另外,针对方案本身,我们会不断优化资源加载性能,完善资源验签,智能切换等能力,也欢迎对 Phoenix CDN 容灾方案有兴趣的同学,跟我们一起探讨交流。同时更欢迎加入我们,文末附招聘信息,期待你的邮件。
魏磊、陈彤、张群、粤俊等,均来自美团外卖平台-大前端团队,丁磊、心澎,来自美团餐饮 SaaS 团队。
美团外卖平台-大前端团队是一个开放、创新、无边界的团队,鼓励每一位同学追求自己的技术梦想。团队长期招聘Android、iOS、FE 高级/资深工程师和技术专家。欢迎感兴趣的同学投递简历至:wangxiaofei03@meituan.com(邮件标题请注明:美团外卖大前端)。
反馈快速、竞争激烈的算法比赛是算法从业者提升技术水平的重要方式。从若干行业核心问题中抽象出的算法比赛题目具有很强的实际意义,而比赛的实时积分榜促使参加者不断改进,以试图超越当前的最佳实践,而且获胜方案对于工业界与学术界也有很强的推动作用,例如KDD Cup比赛产出的Field-Aware Factorization Machine(FFM)算法[1]、ImageNet比赛产出的ResNet模型[2]在业界都有着广泛的应用。
美团到店广告质量预估团队在美团内部算法大赛MDD Cup中获得了第一名,受大赛组委会的邀请,希望分享一些比较通用的比赛经验。本文是笔者7次Kaggle/KDD Cup冠军经验(如下图1所示)的分享,希望能帮助到更多的同学。
大家都知道,Kaggle/KDD Cup的比赛均为国际顶级赛事,在比赛圈与工业界有着很大的影响力。具体而言,Kaggle是国际上最大的顶级数据挖掘平台,拥有全球几十万用户,通过高额奖金与分享氛围产出了大量优秀算法方案,例如Heritage Health奖金高达三百万美元。目前,Kaggle比赛在艾滋病研究、棋牌评级和交通预测等方面均取得了突出成果,得益于此,Kaggle平台后来被Google公司收购。
ACM SIGKDD (国际数据挖掘与知识发现大会,简称 KDD)是数据挖掘领域的国际顶级会议。KDD Cup比赛是由SIGKDD主办的数据挖掘研究领域的国际顶级赛事。从1997年开始,每年举办一次,是目前数据挖掘领域最具影响力的赛事。该比赛同时面向企业界和学术界,云集了世界数据挖掘界的顶尖专家、学者、工程师、学生等参加,为数据挖掘从业者们提供了一个学术交流和研究成果展示的平台。
通过分析不难发现,KDD Cup举办20年来,一直紧密结合工业界前沿与热点问题,演进主要分为三个阶段。第一阶段从2002年左右开始,专注于互联网的热点推荐系统方面问题,包括推荐、广告,行为预测等;第二阶段聚焦在传统行业问题,比较关注教育、环境、医疗等领域;而在第三阶段,自2019年以来,重点关注非监督问题,例如AutoML、Debiasing、强化学习等问题,这类比赛的共同特点是通过以前方法难以解决现有的新问题。这三个阶段趋势也一定程度反应着当前工业界与学术界的难点与重点,无论从方式、方法,还是从问题维度,都呈现出从窄到宽,从标准向非标准演进的趋势。
本文会先介绍笔者的7次KDD Cup/Kaggle比赛冠军的方案与理解,问题涉及推荐、广告、交通、环境、人工智能公平性等多个领域问题。接着会介绍在以上比赛中发挥关键作用的AutoML技术框架,包括自动化特征工程,自动化模型优化,自动化模型融合等,以及如何通过该技术框架系统性建模不同的问题。最后再介绍以上比赛形成的通用方法,即面对一个新问题,如何进行分析、理解、建模、与挑战解决、从而实现问题的深度优化。
本文主要面向以下两类读者,其他感兴趣的同学也欢迎了解。
本部分将我们将以上比赛分为三个部分进行方案介绍,第一部分为推荐系统问题;第二部分为时间序列问题,跟第一部分的重要差别在于预测的是未来的多点序列,而非推荐系统的单点预估;第三部分为自动化机器学习问题,该问题比赛输入不为单一数据集,而是多问题的多数据集,并且在最终评估的b榜数据集问题也是未知的。因此,对于方案的鲁棒性要求非常高。如表1所示,后续将具体介绍七个比赛赛道的获胜方案,但会合并为五个核心解决方案进行具体的介绍。
本节主要介绍Kaggle Outbrain Ads Click Prediction和KDD Cup 2020 Debiasing比赛。二者任务都是面向用户下一次点击预估问题,但因为应用场景与背景的不同,存在着不同的挑战:前者的数据规模庞大,涉及到数亿个用户在千级别数量异构站点上的数十亿条浏览记录,对模型优化、融合有着严格的要求;后者则尤为关注推荐系统中的偏差问题,要求参赛选手提出有效的解决方案,来缓解选择性偏差以及流行度偏差,从而提高推荐系统的公平性。本节将分别介绍这两场比赛。
竞赛问题与挑战:竞赛要求在Outbrain网页内容发现平台上,预估用户下一次点击网页广告,具体参考:Kaggle Outbrain比赛介绍详情[26]。参赛选手会面对以下两个重要挑战:
基于多层级多因子的模型融合方案:针对本次赛题的挑战,我们队采用了基于多层级多因子的模型融合方案来进行建模。一方面对于异构站点行为,单一模型不易于全面刻画,另一方面,亿级别的数据规模给多模型的分别优化带来了较大的空间。由于FFM具有强大的特征交叉能力以及较强的泛化能力,能更好地处理高维稀疏特征。因此,我们选择该模型作为融合基模型的主模型。模型融合通过不同模型学习到有差异性的内容,从而有效挖掘用户在不同站点上的异质行为。模型融合的关键是产生并结合“好而不同”的模型[3][4]。基于多层级多因子的模型融合方案首先通过模型差异性、特征差异性多个角度来构造模型之间的差异性,然后通过多层级以及使用基学习器的多特征因子(模型pCTR预估值、隐层表征)进行融合:
具体地,如上图3所示。第一层级的目的是构建出有差异性的单个模型,主要通过不同类型的模型在用户最近行为、全部行为数据以及不同特征集合上分别进行训练,来产生差异性。第二层级则通过不同单个模型的组合进一步产生差异性,差异性的提升来源于两个方面,分别是模型组合方式的不同(用不同模型,根据单模型特征进行打分)以及用于模型组合的特征因子的不同,这里特征因子包括模型的打分以及模型中的隐层参数。第三层级则是考虑如何将不同融合结果组合在一起。由于划分出来的验证数据集较小,如果使用复杂非线性模型往往容易过拟合。所以这里使用了一个基于约束的线性模型来获得第二层级模型的融合权重。
上述方案同我们业务中模型相比,采用更多的模型融合,在取得高精度的同时产生了更高的开销,而在实际业务中要更加注重效果与效率的平衡。
竞赛问题与挑战:竞赛是以电子商务平台为背景,预估用户下一次点击的商品。并围绕着如何缓解推荐系统中的选择性偏差以及流行度偏差进行展开,具体参考:KDD Cup 2020 Debiasing比赛介绍详情[27]。推荐系统中的偏差问题有很多,除了上述两种偏差,还有曝光偏差、位次偏差等等[5][6]。我们团队之前也对位次偏差进行了相关研究[7]。而本次竞赛为了更好地衡量推荐系统对历史低热度商品的推荐效果,选手的成绩主要采用NDCG@50_half指标进行排名。该指标是从整个评测数据集中取出一半历史曝光少的点击商品,由于是低热度且有被点击的商品,可以跟更好的评估偏差问题。本次比赛包含了以下挑战:
基于i2i游走的Debiasing排序方案:我们的方案为基于i2i建模的排序框架。如图所示,整体流程包含四个阶段:i2i构图与多跳游走、i2i样本构建、i2i建模以及u2i排序。前两个阶段解决了选择性偏差问题,后两个阶段则侧重于解决流行度偏差问题。
第一个阶段是基于用户行为数据和商品多模态数据构建i2i图,并在该图上多跳游走生成候选样本。这种方式扩大了商品候选集,更好地近似系统真实候选集,缓解了选择性偏差。
第二个阶段是根据不同i2i关系计算i2i候选样本的相似度,从而决定每种i2i关系下候选样本的数量,最终形成候选集。通过不同候选的构造方法,探索出更多有差异的候选商品,可以进一步缓解选择性偏差问题。
第三个阶段包括基于i2i样本集的自动化特征工程,以及使用流行度加权的损失函数进行消除流行度偏差的建模。自动化特征工程中包含了商品多模态信息的刻画,这类信息能够反应商品在热度信息以外的竞争关系,能够一定程度上缓解流行度偏差问题。而流行度加权的损失函数定义如下:
其中,参数α与流行度成反比,来削弱流行商品的权重,从而消除流行度偏差。参数β是正样本权重,用于解决样本不平衡问题。
第四个阶段首先将i2i打分通过Max操作进行聚合,突出打分集合中低热度商品的高分信号,从而缓解流行度偏差问题。然后对商品列表的打分结合商品热度进行调整处理,进而缓解流行度偏差问题。
关于该比赛的更多细节,大家可以参考《KDD Cup 2020 Debiasing比赛冠军技术方案及在美团的实践》一文。
时序系列问题:时间序列问题相比于推荐系统问题的有较大差异。在任务上,推荐系统预测的是未来单个点,而时间序列预测未来多个点;在数据上,推荐系统通常包含用户、商品、上下文等多维信息,时间序列通常包含时间空间上变化的数值序列信息。
时间序列竞赛:在本文中,时间序列竞赛主要介绍KDD Cup 2018 Fresh Air和KDD Cup 2017 HighWay Tollgates Traffic Flow Prediction。它们都是时间序列问题,前者是预测未来两天的污染物浓度以及变化,后者是预测未来几个小时高速交通情况和变化。它们的共同点一是传统行业问题,实际意义强;二是存在各种突变性、稳定性低;三是都涉及到多地域、多空间问题,需结合时空进行建模。它们的异同点是污染物浓度突变需要一个短期时间才能发生,数据在突变时存在一定规律性,但交通突变具有强偶发性,交通道路容易受到偶发性车祸、偶发性地质灾害等影响,数据不会呈现出明显的规律性。
竞赛问题及挑战:竞赛目标是预测北京和伦敦48个站点在未来48小时里PM2.5/PM10/O3的浓度变化,具体参考: KDD Cup 2018比赛介绍详情[28]。参赛选手需要解决以下两个挑战:
基于Spatial-temporal Gated DNN与Seq2Seq的模型融合方案[9]:为了强化时间序列和空间拓扑的建模,我们引入了Spatial-temporal Gated DNN与Seq2Seq两个模型,并与LightGBM一起构建模型融合方案,具体如下。
(1)Spatial-temporal Gated DNN:对于时序问题而言,由于未来预测临近时间点的统计特征值差异较小,直接使用DNN模型会使得不同小时和站点的预测值差异性小,因此我们在DNN中引入Spatial-temporal Gate来突出时空信息。如下图6所示,Spatial-temporal Gated DNN采用了双塔结构,拆分了时空信息和其他信息,并且通过门函数来控制和强调时空信息,最终能够提高模型对时空的敏感度,实验中发现引入swish激活函数f(x) = x · sigmoid(x)能提升模型精度。
(2)Seq2Seq:尽管Spatial-temporal Gated DNN相比DNN对时空信息进行了强化,但是它们的数据建模方式都是将样本的历史数据复制48份,分别打上未来48小时的标签,相当于分别预测48小时的污染浓度值。这种方式其实和时间序列预测任务有所脱离,失去了时间连续性。而Seq2Seq建模方式可以很自然地解决这一问题,并且取得了不错的效果。下图7是本次比赛中,我们采用的Seq2Seq模型结构。针对时序性挑战,历史天气特征通过时间前后组织成序列输入到编码器当中,解码器依赖于编码结果以及未来天气预报特征进行解码,得到48小时的污染物浓度序列。未来天气预报信息对齐到解码器每个小时的解码过程中,解码器可以通过天气预报中的天气信息(比如风级、气压等)来有效预估出突变值。针对空间性挑战,方案在模型中加入站点嵌入以及空间拓扑结构特征来刻画空间信息,在模型中和天气信息进行拼接以及归一化,从而实现时空联合建模。
(3)模型融合:我们队采用了Stacking融合的方式,单个学习器通过不同模型、数据、建模方式来构建差异性。LightGBM模型使用了天气质量、历史统计、空间拓扑等特征,Spatial-temporal Gate则是引入了门结构,强化了时空信息。Seq2Seq利用序列到序列的建模方式,刻画了序列的连续性、波动性。最后使用了基于约束的线性模型将不同的单个学习器进行融合。
更多详情,大家可参考SIGKDD会议论文:AccuAir: Winning Solution to Air Quality Prediction for KDD Cup 2018。
竞赛问题及挑战:竞赛目标是以20分钟为时间窗口,给定前2小时高速公路入口到关卡的行驶状况,预测未来2小时的行驶状况,具体可参考:KDD Cup 2017比赛介绍详情[29]。竞赛根据行驶状况的不同,分为了行驶时间预测和交通流量预测两个赛道。参赛选手需要解决以下两个挑战:
基于交叉验证降噪的极值点优化模型融合方案:
(1)基于交叉验证的降噪,由于在线仅能进行一天一次的提交,并且最终的评测会由A榜测试集切到B榜测试集,并且由于A榜数据集小在线评测指标存在不稳定性,故而离线迭代验证的方式就显得尤为重要。为了能使离线迭代置信,我们采用两种验证方式进行辅助,第一种是下一天同时间段验证,我们在训练集最后M天上对每一天都取在线同一时间段的数据集,得到M个验证集。第二种是N-fold天级采样验证,类似N-fold交叉验证,我们取最后N天的每一天数据作为验证集,得到N个验证集。这两种方法共同辅助模型离线效果的迭代,保证了我们在B榜上的鲁棒性。
(2)极值点问题优化和模型融合:由于MAPE对于极值较敏感,我们在标签、损失、样本权重等不同方面分别进行多种不同处理,例如标签上进行Log变换和Box-Cox变换,Log变换是对标签进行Log转换,模型拟合后对预估值进行还原,这样能帮助模型关注于小值同时更鲁棒,损失使用MAE、MSE等多种,样本权重上利用标签对样本进行加权等,我们在XGBoost、LightGBM、DNN上引入这些处理生成多个不同模型进行模型融合,优化极值点问题,达到鲁棒效果。
备注:特别感谢共同参加KDD Cup 2017的陈欢、燕鹏、黄攀等同学。
自动化机器学习问题[10]主要包括KDD Cup 2019 AutoML和KDD Cup 2020 AutoGraph比赛。该类问题,一般具有以下三个特性:
竞赛问题及挑战:自动化图表示学习挑战赛(AutoGraph)是第一个应用于图结构数据的AutoML挑战,详情请见KDD Cup 2020 AutoGraph 比赛介绍[30]。竞赛选择图结点多分类任务来评估表示学习的质量,参与者需设计自动化图表示学习[11-13]解决方案。该方案需要基于图的给定特征、邻域和结构信息,高效地学习每个结点的高质量表示。比赛数据从真实业务中收集,包含社交网络、论文网络、知识图谱等多种领域共15个,其中5个数据集可供下载,5个反馈数据集评估方案在公共排行榜的得分,剩余5个数据集在最后一次提交中评估最终排名。
每个数据集给予了图结点id和结点特征,图边和边权信息,以及该数据集的时间预算(100-200秒)和内存算力(30G)。每个训练集随机将划分40%结点为训练集,60%结点为测试集,参赛者设计自动化图学习解决方案,对测试集结点进行分类。 每个数据集会通过精度(Accuracy)来确定排名,最终排名将根据最后5个数据集的平均排名来评估。综上,本次比赛需要在未见过的5个数据集上直接执行自动化图学习方案,参赛者当时面临着以下挑战:
基于代理模型的自动化多层次模型优化[14]
多类别层次化图模型优化:
(1)候选图模型的生成:现实世界中的图通常是多种属性的组合,这些属性信息很难只用一种方法捕捉完全,因此,我们使用了基于谱域、空域、Attention机制等多种不同类型的模型来捕捉多种属性关系。不同模型在不同数据集上效果差异较大,为了防止后续模型融合时加入效果较差的模型,会对GCN、GAT、APPNP、TAGC、DNA、GraphSAGE、GraphMix、Grand、GCNII等候选模型进行快速筛选,得到模型池。
(2)层次模型集成:这部分共包含两个维度的集成。第一层为模型自集成,为了解决图模型对初始化特别敏感,同种模型精度波动可达±1%的问题,采用了同模型的自集成,同时生成多个同种模型,并取模型预测的平均值作为该种模型的输出结果,成功降低了同种模型方差,提高了模型在不同数据集上的稳定性。第二层为不同模型集成,为了有效地利用来自本地和全球邻域的信息,充分捕获图的不同性质,我们采用加权集成了不同种类的图模型,进一步提高性能。同时针对在参数搜索阶段,需要同时优化模型内参数α,以及多种模型加权集成参数β,使用模型集成参数和模型内参数通过互迭代的梯度下降进行求解,有效提升了速度。
基于代理模型与最终模型的两阶段优化:数据集采样,对子图根据Label进行层次采样,减少模型验证时间;代理模型与Bagging,计算多个较小隐层模型的平均结果,快速对该类模型进行评估。使用Kendall Rank和SpeedUp平衡准确度与加速倍率,得到合适的代理模型。最终通过代理模型得到了最优的超参数,然后再对最终的大模型在搜索好的参数上进行模型训练。
具体详情,大家可参考团队ICDE 2022论文,AutoHEnsGNN: Winning Solution to AutoGraph Challenge for KDD Cup 2020。
经过上述的多场比赛,团队在多领域建模中不断总结与优化,抽象出其中较为通用的模块,总结得到针对数据挖掘类问题时的一套较为通用的解决方案——AutoML框架。该框架包含数据预处理,自动化特征工程[15]和自动化模型优化[16-20]三个部分。其中数据预处理部分主要负责特征分类、数据编码、缺失值处理等常见的基础操作,不过多展开。主要针对AutoML框架的自动化特征工程和自动化模型优化两个部分进行详细介绍。
特征工程是机器学习中至关重要的工作,特征的好坏直接决定了模型精度的上限。目前常见的方式是人工手动对特征进行组合与变换,但人工特征挖掘存在速度较慢、无法挖掘全面等问题。因此,设计全面挖掘的自动化特征工程能够比较好地解决上述问题,自动化特征工程主要包含三个部分:
高阶特征算子按多实体结果是否完全匹配,分为Match方式——匹配全部实体,All方式——匹配部分实体,得到另一实体的全部值的计算结果,这样两种特征产出方式。下图中举例说明,Match方式匹配用户与时间段两个实体,得到用户在该时间段的平均订单价格;All方式则只匹配用户,得到用户在所有时间段的平均订单价格。
相较于DeepFM、DeepFFM等算法,自动化特征工程具有三个方面的优势。首先在存在多表信息的情况下,容易利用非训练数据的信息,如在广告场景中,通过特征可以利用自然数据的信息,相比直接使用自然数据训练,不容易产生分布不一致等问题;其次,只通过模型自动交叉学习,对于某些强特征交叉没有手动构造学习得充分,许多显示交叉特征如用户商品点击率等往往有较强的业务意义,让模型直接感知组合好的特征往往比自动学习特征间的关系更为简单;第三方面对于许多高维度稀疏ID特征,如亿级别以上的推荐或广告场景中,DeepFM、DeepFFM对于这些特征的学习很难充分,自动化特征工程能给这些稀疏ID构造很强的特征表示。
基于重要度的网格搜索:在我们框架中采用的是全局基于重要度按照贪心的方式进行搜索,加快速度;得到的最优结果再进行小领域更详细网格搜索,缓解贪心策略导致的局部最优。根据以往比赛经验,总结不同模型的超参重要性排序如下:
在2021年8-9月美团举行的内部算法比赛MDD Cup 2021中,美团到店广告平台质量预估团队应用了AutoML框架并获得了冠军。下面结合这场比赛,介绍框架在具体问题中的应用。
MDD Cup 2021需要参赛者根据用户、商家在图谱中的属性、用户的历史点击、实时点击以及下单行为,预测下次购买的商家。包含四周的135万个订单行为,涉及20万个用户,2.9万个商家,17.9万个菜品,订单关联菜品数据共438万条,构成知识图谱。使用Hitrate@5作为评价指标。
数据预处理阶段:进行特征分类、异常值处理、统一编码等操作。主要涉及用户(用户画像特征等)、商家(品类、评分、品牌等)、菜品(口味、价格、食材等)三种实体数据及点击、购买(LBS、价格、时间等)两类交互数据,对原始数据进行特征分类、数据编码、缺失值处理等常见预处理操作。
自动化特征工程:一、二阶特征算子,首先对于类别、数据、时序、标签四类原始特征,按照可抽象的三种实体及两类交互数据进行一、二阶特征交叉,运用频数编码、目标编码与时序差分算子操作,在多时段上统计得到一、二阶统计特征。举例说明,如频数编码可计算用户点击某商家的次数、用户购买商家品类的nunique值,用户在某场景的下单数量等。目标编码可计算用户的平均订单价格,用户点击次数最多的商家品类等。时序差分可计算如用户购买某口味菜品的平均时间差等。多时段统计则意味着上述特征均可在不同时段上计算得到。
快速特征选择,上述自动产出的一、二阶统计特征数量共有1000+,其中存在大量无效特征,故使用LightGBM模型,从指标提升与重要性角度进行特征筛选与重要标识。如用户 x 菜品口味的特征没什么效果,进行筛除;用户最常购买的价格区间则很有效果,标识为重要特征进行高阶组合。
高阶特征算子,基于一、二阶特征算子组合构建的新特征,可以作为输入进行高阶特征组合。这里值得一提的是,高阶特征组合存在两种形式,第一种原始特征的更高阶组合,如用户在某个商家中最喜欢的菜品口味,结合三个实体,并不需要额外的运算,第二种需使用一、二阶新特征,其中频数编码的结果可以直接使用,目标编码与时序差分需要先进行数值分桶操作转换为离散值后才可使用,如用户订单价格区间的众数 x 商家订单价格平均值的分桶的联合count。循环进行特征组合与筛选后就得到了最终的特征集。
自动化模型优化:模型部分使用了LightGBM和DIN的融合方案,迭代过程中多次进行了自动超参搜索,通过自动化行、列采样及最优参数局部扰动构造了具有差异性的多个模型,融合得到最终的结果。
本节会就比赛的通用建模方法进行介绍,即面对一个新问题,如何进行快速高效的整体方案设计。
在面对新问题时,我们主要将技术框架分为以下三个阶段,即探索性建模、关键性建模、自动化建模。三个阶段具有逐渐深化,进一步补充的作用。
探索性建模:比赛前期,首先进行问题理解,包括评估指标与数据表理解,然后进行基础的模型搭建,并线上提交验证一致性。在一致性验证过程中往往需要多次提交,找到同线上指标一致的评估方式。探索性建模的核心目标是要找到迭代思路与方法,所以需要对问题做多方面探索,在探索中找到正确的方向。
一般在非时序问题,采用N-fold方法构造多个验证集,并可以灵活变换生成种子,得到不同的集合。而在时序问题,一般会采用滑窗方式,构造同线上提交时间一致的验证集,并可以向前滑动k天,来构造k个验证集。在多个验证集评估中,可以参考均值,方差,极值等参考指标综合评估,得到同线上一致的结果。
关键性建模:比赛中期,会就关键问题进行深挖,达成方案在榜单Top行列,在问题理解方面,会尽可能就评估方式进行损失函数自定义设计。
分类问题优化,可以结合Logloss、AUC Loss[21]、NDCG Loss等不同损失函数进行Mix Loss设计。而回归问题的损失函数设计要更复杂,一方面可以结合平方误差,绝对值误差等进行损失函数设计,另一方面可以结合Log变换,Box-cox变换等解决回归异常值等问题。
自动化建模:比赛后期,由于基于人的理解一方面在细节与角度有盲区,另一方面较难进行抽象关系的建模,所以我们会采用自动化建模进行补充。如下图18所示,先基于关系型多表输入,进行自动化关联,然后通过生成式自动化特征工程构建大量特征,再进行特征选择与迭代,然后基于模型输入进行自动化超参搜索与模型选择,最终基于多模型进行自动化融合构建,将生成的多元化模型关系进行选择与赋权。
自动化建模一般采用如图18的框架,先进行多表关联,然后基于先扩展后过滤的逻辑进行特征选择,下一步基于精选特征与多个超参范围进行超参搜索,最后采用XGBoost[22]、LightGBM、DNN、RNN、FFM等不同模型进行自动化模型融合。
算法比赛相对于工业界实际情况而言,一个重要区别是工业界涉及线上系统,在工程方面性能的挑战更大,在算法方面涉及更多的线上线下效果一致性问题。因此算法比赛会在模型复杂度、模型精度更进一步,在算法比赛中也产出了ResNet、Field-aware Factorization Machine(FFM)、XGBoost等算法模型,广泛应用于工业界实际系统。
在空气质量预测中,我们采用了时空结合的Spatial-temporal Gated DNN网络进行有效建模,同空气质量问题相接近,在美团的实际业务中也面临着时空相结合的建模问题,以用户行为序列建模为例。我们对用户的历史时空信息和当前时空信息进行了充分的建模和交互[24]。我们分辨出用户行为的三重时空信息,即:用户点击发生时的时间、用户请求发出的地理位置、用户所点击的商户的地理位置。
基于上述三重时空信息,我们提出Spatio-temporal Activator Layer(如图19):三边时空注意力机制神经网络来对用户历史行为进行建模,具体通过对请求经纬度信息、商户经纬度信息和请求时间的交互进行学习。针对空间信息交叉,我们进一步采用地理位置哈希编码和球面距离相结合的方式;针对时间信息交叉,我们也采用绝对与相对时间相结合的方式,有效实现用户行为序列在不同时空条件下的三边表达。最后,经上述网络编码后的时空信息经过注意力机制网络融合,得到LBS场景下用户超长行为序列对不同请求候选的个性化表达。
相比较而言,比赛中的Spatial-temporal Gated DNN更注重时空融合信息对于预测值的影响,由于需要预测的时间序列问题,更侧重于不同的时间、空间信息有能够将差异性建模充分。而在美团业务中的时空网络注重于细粒度刻画空间信息,源于不同的球面距离,不同的区块位置影响大,需要多重信息深度建模。更多详情,大家可参考团队的CIKM论文:Trilateral Spatiotemporal Attention Network for User Behavior Modeling in Location-based Search[23]。
在实际建模中,相对于比赛涉及到更多线上部分,而比赛主要专注于离线数据集的精度极值。同Debiasing比赛相比,在实际线上系统中,涉及到Bias等更多的问题,以Position Bias为例,实际的展示数据高位点击率天然高于低位,然而一部分是源于用户高低位之间的浏览习惯差异,因此对于数据的直接建模不足以表征对于高低位广告点击率与质量的评估。我们在美团实际广告系统中,设计了位置组合预估框架进行建模,取得不错的效果,这里不再详述。具体详情,大家可参考团队SIGIR论文:Deep Position-wise Interaction Network for CTR Prediction[7]。
一致的评估方式是决定模型泛化能力的关键
在比赛的机制中,通常最终评测的Private Data和此前一直榜单的Public Data并不是一份数据,有时切换数据会有几十名的名次抖动,影响最终排名。因此避免过拟合到常规迭代的Public Data是最终取胜的关键。那么在此问题上,如何构造同线上分布一致的验证集呢?从一致性角度,一般会构造时间间隔一致的验证集。而部分问题数据噪音较重,可以用动态滑窗等方式构造多个验证集相结合。一致的验证集决定着后面的迭代方向。
大数据注重模型的深化,小数据注重模型的鲁棒
不同数据集注重的内容不一样,在数据充分的场景下,核心问题是模型深化,以解决特征之间交叉,组合等复杂问题。而在小数据下,因为噪音多,不稳定性强,核心问题是模型的鲁棒。高数据敏感性是方案设计的关键。
方差与偏差的平衡是后期指导优化的关键
从误差分解角度去理解,平方误差可以分解为偏差(Bias)与方差(Variance)[25],在中前期模型复杂度较低时,通过提升模型复杂度,能够有效减低偏差。而在偏差已经被高度优化的后期,方差的优化是关键,因此在后期会通过Emsemble等方式,在单模型复杂度不变的基础上,通过模型融合优化结果。
AutoML的关键是人为先验的不断减少
在运用AutoML框架的同时,会有一些超参数等隐蔽的人为先验,把AutoML技术也以模型视角来理解,同样存在模型复杂度越高越容易过拟合的问题,迭代中的一个关键问题不是评估效果的好坏,而是方案是否存在不必要的超参数等信息,能否不断地简化AutoML的建模,不断地自动化,自适应适配各类问题。
最后,也特别感谢Convolution Team、Nomo Team、Getmax Team、Aister Team等队伍的队友们。
本文基于笔者7次算法比赛的冠军经历,分享推荐系统、时间序列及自动化机器学习等不同领域比赛中的算法经验,接着结合具体问题介绍AutoML技术框架,最后总结比赛中通用的建模方案,结合工业界方案介绍其与比赛的联系。希望文章中的一些算法比赛相关经验能够帮助算法爱好者更好地参与竞赛,能为大家提供一些思路,启迪更多的工程师与研究员在实际工作中取得更优结果。未来,我们团队将持续关注国际算法竞赛,积极进行比赛思路与工业方案结合的尝试,同时也欢迎大家加入我们团队,文末附有招聘信息,期待你的邮件。
胡可、兴元、明健、坚强,均来自美团广告平台质量预估团队。
美团到店广告平台算法团队立足广告场景,探索深度学习、强化学习、人工智能、大数据、知识图谱、NLP和计算机视觉前沿的技术发展,探索本地生活服务电商的价值。主要工作方向包括:
岗位要求:
具备以下条件优先:
感兴趣的同学可投递简历至:chengxiuying@meituan.com(邮件标题请注明:广平算法团队)。
再见2021,你好2022!
「美团技术团队」微信公众号祝大家新年快乐!温故而知新,我们根据文章的「阅读量」和「在看」数,以及所覆盖的技术领域,精选了22篇技术文章作为新年礼物送给大家。希望在2022年,继续陪大家一起,静心苦练,砥砺向前。
为了做出更好的内容,从2022年开始,我们在选题层面想多听听大家的意见和建议。我们准备了一份调研问卷,欢迎大家帮忙填写。我们会评选出5位小伙伴,送上来自美团礼品店精美的键盘手托(本次活动的截止日期为2022年1月6日)。
操作日志几乎存在于每个系统中,而这些系统都有记录操作日志的一套API。操作日志和系统日志不一样,操作日志必须要做到简单易懂。所以如何让操作日志不跟业务逻辑耦合,如何让操作日志的内容易于理解,如何让操作日志的接入更加简单?上面这些都是本文要回答的问题。本文主要围绕着如何「优雅」地记录操作日志展开描述。
剧本杀作为爆发式增长的新兴业务,在商家上单、用户选购、供需匹配等方面存在不足,供给标准化能为用户、商家、平台三方创造价值,助力业务增长。
本文介绍了美团到店综合业务数据团队从0到1快速建设剧本杀供给标准化的过程及算法方案。我们将美团到店综合知识图谱(GENE,GEneral NEeds net)覆盖至剧本杀行业,构建剧本杀知识图谱,实现供给标准化建设,包括剧本杀供给挖掘、标准剧本库构建、供给与标准剧本关联等环节,并在多个场景进行应用落地。
商品知识图谱作为新零售行业数字化的基石,提供了围绕商品的精准结构化理解,对业务应用起到了至关重要的作用。相比于美团大脑中原有的围绕商户的图谱而言,商品图谱需应对更加分散、复杂、海量的数据和业务场景,且面临着信息来源质量低、数据维度多、依赖常识以及专业知识等挑战。本文将围绕零售商品知识图谱,介绍美团在商品层级建设、属性体系建设、图谱建设人效提升等方向的探索。
GraphQL是Facebook提出的一种数据查询语言,核心特性是数据聚合和按需索取,目前被广泛应用于前后端之间,解决客户端灵活使用数据问题。本文介绍的是GraphQL的另一种实践,我们将GraphQL下沉至后端BFF(Backend For Frontend)层之下,结合元数据技术,实现数据和加工逻辑的按需查询和执行。这样不仅解决了后端BFF层灵活使用数据的问题,这些字段加工逻辑还可以直接复用,大幅度提升了研发的效率。
本文主要介绍一种通用的实时数仓构建的方法与实践。实时数仓以端到端低延迟、SQL标准化、快速响应变化、数据统一为目标。美团外卖数据智能组总结的最佳实践是:一个通用的实时生产平台跟一个通用交互式实时分析引擎相互配合,同时满足实时和准实时业务场景。两者合理分工,互相补充,形成易开发、易维护且效率高的流水线,兼顾开发效率与生产成本,以较好的投入产出比满足业务的多样性需求。
数据已成为很多公司的核心资产,而在数据开发的过程中会引入各种质量、效率、安全等方面的问题,而数据治理就是要不断消除引入的这些问题,保障数据准确、全面和完整,为业务创造价值,同时严格管理数据的权限,避免数据泄露带来的业务风险。数据治理是数字时代很多公司一项非常重要的核心能力,本文介绍了美团酒旅平台在数据治理方面的实践。
美团外卖商家端基于 FlutterWeb 的技术探索已久,目前在多个业务中落地了App、PC、H5的多端复用,有效提升了产研的整体效率。在这过程中,性能问题是我们面临的最大挑战,本文结合实际业务场景进行思考,介绍美团外卖商家端在FlutterWeb性能优化上所进行的探索和实践。
在微服务架构下,服务拆分会让API的规模成倍增长,使用API网关来管理API逐渐成为一种趋势。美团统一API网关服务Shepherd就是在这种背景下应运而生,适用于美团业务且完全自研,用于替换传统的Web层网关应用,业务研发人员通过配置的方式即可对外开放功能和数据。本文将介绍美团统一API网关诞生的背景、关键的技术设计和实现,以及API网关未来的规划。
2021年5月,美团NLP中心开源了迄今规模最大的基于真实场景的中文属性级情感分析数据集ASAP,该数据集相关论文被自然语言处理顶会NAACL2021录用,同时该数据集加入中文开源数据计划千言,将与其他开源数据集一起推动中文信息处理技术的进步。
本文回顾了美团情感分析技术的演进和在典型业务场景中的应用,包括篇章/句子级情感分析、属性级情感分析和观点三元组分析。在业务应用上,依托情感分析技术能力构建了在线实时预测服务和离线批量预测服务。截至目前,情感分析服务已经为美团内部十多个业务场景提供了服务。
本文分享了美团搜索中的多业务排序建模优化工作,我们主要聚焦在到店商家多业务场景,后续的内容会分为以下四个部分:第一部分是对美团搜索排序分层架构进行简单介绍;第二部分会介绍多路融合层上的多业务融合建模;第三部分会介绍精排模型的多业务排序建模;最后一部分是总结和展望。
Spock是国外一款优秀的测试框架,基于BDD(行为驱动开发)思想实现,功能非常强大。Spock结合Groovy动态语言的特点,提供了各种标签,并采用简单、通用、结构化的描述语言,让编写测试代码更加简洁、高效。目前,美团优选物流绝大部分后端服务已经采用了Spock作为测试框架,在开发效率、可读性和维护性方面均取得了不错的收益。
压缩在数据传输和存储过程中经常扮演着十分重要的角色,因此提高压缩的效率可以帮助我们节省时间和降低存储成本。本文介绍了压缩算法的优化在构建部署平台的应用,能够帮助研发团队提高研发和交付效率。
Kafka在美团数据平台承担着统一的数据缓存和分发的角色,针对因PageCache互相污染,进而引发PageCache竞争导致实时作业被延迟作业影响的痛点,美团基于SSD自研了Kafka的应用层缓存架构。本文主要介绍了该架构的设计与实现,主要包括方案选型,与其他备选方案的比较以及方案的核心思考点等,最后介绍该方案与其他备选方案的性能对比。
鸿蒙系统HarmonyOS是华为推出的一款分布式操作系统,那么如何在保证开发迭代效率的前提下,以相对低的成本将移动应用快速移植到鸿蒙平台上呢?美团外卖MTFlutter团队近期做了一次技术探索,成功地实现了Flutter对于鸿蒙系统的原生支持。
美团无人车配送中心团队获得NeurIPS 2020 INTERPRET轨迹预测挑战赛Generalizability赛道冠军、Regular赛道亚军,本文对算法层面进行了介绍。
本文介绍了美团平台自研的一款cocoapods插件,该插件可以大幅提升iOS工程代码的编译速度。文章前半部分主要介绍相关的原理,后面主要阐述在工程层面的实践。
随着美团外卖业务的发展,算法模型也在不断演进迭代中。本文从特征框架演进、特征生产、特征获取计算以及训练样本生成四个方面介绍了美团外卖特征平台在建设与实践中的思考和优化思路。
OCTO 2.0是美团下一代分布式服务治理系统,它基于美团现有服务治理系统OCTO 1.0与Service Mesh通信基础设施层的结合,是命名服务、配置管理、性能监控、限流鉴权等服务治理功能的全新演进版本。本文主要讲述OCTO 2.0的重要功能及实现思路。
在多形态业务场景下,如何保障多端体验的一致性,是前端技术领域一个比较受关注的方向。美团外卖前端技术团队基于FlutterWeb探索跨端(App\PC\H5)的解决方案,真正实现「Write Once & Run AnyWhere」,本文系该团队的实践经验总结。
设计稿(UI视图)转代码是前端工程师日常不断重复的工作,这部分工作复杂度较低但工作占比较高,所以提升设计稿转代码的效率一直是前端工程师追求的方向之一。
此前,前端工程师尝试过将业务组件模块化构建成通用视图库,并通过拖拽、拼接等形式搭建业务模块,从而实现视图复用,降低设计稿转代码的研发成本。但随着业务的发展和个性化的驱动,通用视图库无法覆盖所有应用场景,本文提出了一种设计稿自动生成代码的方案。
图数据结构,能够更好地表征现实世界。美团业务相对较复杂,存在比较多的图数据存储及多跳查询需求,亟需一种组件来对千亿量级图数据进行管理,海量图数据的高效存储和查询是图数据库研究的核心课题。本文介绍了美团在图数据库选型及平台建设方面的一些工作。
对美团安全团队来说,引入领先的安全技术设计能力,构建全方位、多维度智能防御体系,是我们不懈追求的目标。美团有众多基础设施,核心业务系统也需要以成熟的方法论进行威胁评审。本文将着重分享威胁建模是如何帮助美团安全团队评估、发现大量安全设计的风险,以及互联网企业应该如何大范围地实施威胁建模并完整地进行落地。
备注:数据统计于2021年12月30日16:00,同时入围两个榜单的文章已经做了去重处理。
再次邀请大家帮忙填一下美团技术团队公众号的调研问卷,欢迎大家留下宝贵的意见或者建议,期待未来我们能共同成长。
2013年12月4日,美团技术团队博客诞生,发表了第1篇文章。8年多的时间,2948天,目前我们已经发布了483篇技术文章,美团技术团队博客/公众号,感谢大家的一路相伴!
最后,祝大家在2022年,健康平安,「虎」力冲天。
时间回拨到 2018 年,Google 首次公开 FlutterWeb Beta 版,表露出要实现一份代码、多端运行的愿景。经过无数工程师两年多的努力,在今年年初(2021 年 3 月份),Flutter 2.0 正式对外发布,它将 FlutterWeb 功能并入了 Stable Channel,意味着 Google 更加坚定了多端复用的决心。
当然 Google 的“野心”不是没有底气的,主要体现在它强大的跨端能力上,我们看一下 Flutter 的跨端能力在 Web 侧是如何体现的:
上图分别是 FlutterNative 和 FlutterWeb 的架构图。通过对比可以看出,应用层 Framework 是公用的,意味着在 FlutterWeb 中我们也可以直接使用 Widgets、Gestures 等组件来实现逻辑跨端。而关于渲染跨端,FlutterWeb 提供了两种模式来对齐 Engine 层的渲染能力:Canvaskit Render 和 HTML Render,下方表格对两者的区别进行了对比:
Canvaskit Render 模式:底层基于 Skia 的 WebAssembly 版本,而上层使用 WebGL 进行渲染,因此能较好地保证一致性和滚动性能,但糟糕的兼容性(WebAssembly 从 Chrome 57 版本才开始支持)是我们需要面对的问题。此外 Skia 的 WebAssembly 文件大小达到了 2.5M,且 Skia 自绘引擎需要字体库支持,这意味着需要依赖超大的中文字体文件,对页面加载性能影响较大,因此目前并不推荐在 Web 中直接使用 Canvaskit Render(官方也建议将 Canvaskit Render 模式用于桌面应用)。
HTML Render 模式:利用 HTML + Canvas 对齐了 Engine 层的渲染能力,因此兼容性表现优秀。另外,MTFlutterWeb 对滚动性能已有过探索和实践,目前能够应对大部分业务场景。而关于加载性能,此模式下的初始包为 1.2M,是 Canvaskit Render 模式产物体积的 1/2,且我们可对编译流程进行干预,控制输出产物,因此优化空间较大。
基于以上原因,美团外卖技术团队选择在 HTML Render 模式下对 FlutterWeb 页面的性能进行优化探索。
美团外卖商家端以 App、PC 等多元化的形态为商家提供了订单管理、商品维护、顾客评价、外卖课堂等一系列服务,且 App、PC 双端业务功能基本对齐。此外,我们还在 PC 上特供了针对连锁商家的多店管理功能。同时,为满足平台运营诉求,部分业务具有外投 H5 场景,例如美团外卖商家课堂,它是一个以文章、视频等形式帮助商家学习外卖运营知识、了解行业发展和跟进经营策略的内容平台,具有较强的传播属性,因此我们提供了站外分享的能力。
为了实现多端(App、PC、H5)复用,提升研发效率,我们于 2021 年年初开始着手 MTFlutterWeb 研发体系的建设。目前,我们基于 MTFlutterWeb 完成提效的业务超过了 9 个,在 App 中,能够基于 FlutterNative 提供高性能的服务;在 PC 端和 Mobile 浏览器中,利用 FlutterWeb 做到了低成本适配,提升了产研的整体效率。
然而,加载性能问题是 MTFlutterWeb 应用推广的最大障碍。这里依然以美团外卖商家课堂业务为例,在项目之初页面完全加载时间 TP90 线达到了 6s 左右,距离我们的指标基线值(页面完全加载时间 TP90 线不高于 3s,基线值主要依据美团外卖商家端的业务场景、用户画像等来确定)有些差距,用户访问体验有很大的提升空间,因此 FlutterWeb 页面加载性能优化,是我们亟需解决的问题。
不过,想要突破 FlutterWeb 页面加载的性能瓶颈,我们面临的挑战也是巨大的。这主要体现在 FlutterWeb 缺失静态资源的优化策略,以及复杂的架构设计和编译流程。下图展示了 Flutter 业务代码被转换成 Web 平台产物的流程,我们来具体进行分析:
可以看出,要完成对 FlutterWeb 编译产物的优化,就需要干预 FlutterWeb 的众多编译模块。而为了提升整体的编译效率,大部分模块都被提前编译成了 snapshot 文件( 一种 Dart 的编译产物,可被 Dart VM 所运行,用于提升执行效率),例如:flutter_tools.snapshot、frontend_server.snapshot、dart2js.snapshot 等,这又加大了对 FlutterWeb 编译流程进行干预的难度。
如前文所述,为了实现逻辑、渲染跨平台,Flutter 的架构设计及编译流程都具有一定的复杂性。但由于各平台(Android、iOS、Web)的具体实现是解耦的,因此我们的思路是定位各模块(Dart-SDK、Framework、Flutter_Web_SDK、flutter_tools)的 Web 平台实现并寻求优化,整体设计图如下所示:
下面,我们分别对各项优化进行详细的说明。
工欲善其事,必先利其器,在开始做体积裁剪之前,我们需要一套类似于 webpack-bundle-analyzer 的包体积分析工具,便于直观地比较各个模块的体积占比,为优化性能提供帮助。
Dart2JS 官方提供了 –dump-info 命令选项来分析 JS 产物,但其表现差强人意,它并不能很好地分析各个模块的体积占比。这里更推荐使用 source-map-explorer ,它的原理是通过 sourcemap 文件进行反解,能清晰地反映出每个模块的占用大小,为 SDK 的精简提供了指引。下图展示了 FlutterWeb JS 产物的反解信息(截图仅包含 Framework 和 Flutter_Web_SDK):
FlutterWeb 依赖的 SDK 主要包括 Dart-SDK、Framework 和 Flutter_Web_SDK,这些 SDK 对包体积的影响是巨大的,几乎贡献了初始化包的所有大小。虽然在 Release 模式下的编译流程中,Dart Compiler 会利用 Tree-Shaking 来剔除那些引入但未使用的 packages、classes、functions 等,很大程度上减少了包体积。但这些 SDK 中仍然存在一些能被进一步优化的代码。
以 Flutter Framework 为例,由于它是全平台公用的模块,因此不可避免地存在各平台的兼容逻辑(通常以 if-else、switch 等条件判断形式出现),而这部分代码是不能被 Tree-Shaking 剔除的,我们观察如下的代码:
// FileName: flutter/lib/src/rendering/editable.dart
void _handleKeyEvent(RawKeyEvent keyEvent) {
if (kIsWeb) {
// On web platform, we should ignore the key.
return;
}
// Other codes ...
}
上述代码选自 Framework 中的 RenderEditable 类,当 kIsWeb 变量为真,表示当前应用运行在 Web 平台。受限于 Tree-Shaking 的机制原理,上述代码中,其它平台的兼容逻辑即注释 Other codes 的部分是无法被剔除的,但这部分代码,对 Web 平台来说却是 Dead Code(永远不可能被执行到的代码),是可以被进一步优化的。
上图展示了 SDK 的一部分功能构成,从图中可以看出,FlutterWeb 依赖的这些 SDK 中包含了一些使用频率较低的功能,例如:蓝牙、USB、WebRTC、陀螺仪等功能的支持。为此,我们提供了对这些长尾功能的定制能力(这些功能默认不开启,但业务可配置),将未被启用长尾的功能进行裁剪。
通过上述分析可得,我们的思路就是对 Dead Code 进行二次剔除,以及对这些长尾功能做裁剪。基于这样的思路,我们深入 Dart-SDK、Framework 和 Flutter_Web_SDK 各个击破,最终将 JS Bundle 产物体积由 1.2M 精简至 0.7M,为 FlutterWeb 页面性能优化打下了坚实的基础。
为了提升构建效率,我们将 FlutterWeb 依赖的环境定制为 Docker 镜像,集成入 CI/CD(持续集成与部署)系统。SDK 裁剪后,我们需要更新 Docker 镜像,整个过程耗时较长且不够灵活。因此,我们将 Dart-SDK、Framework、Flutter_Web_SDK 按版本打包传至云端,在编译开始前读取 CI/CD 环境变量:sdk_version(SDK 版本号),远程拉取相应版本的 SDK 包,并替换当前 Docker 环境中的对应模块,基于以此方案实现 SDK 的灵活发布,具体流程图如下图所示:
FlutterWeb 编译之后默认会生成 main.dart.js 文件,它囊括了 SDK 代码以及业务逻辑,这样会引起以下问题:
针对文件 Hash 化和 CDN 加载的支持,我们在 flutter_tools 编译流程中对静态资源进行二次处理:遍历静态资源产物,增加文件 Hash(文件内容 MD5 值),并更新资源的引用;同时通过定制 Dart-SDK,修改了 main.dart.js、字体等静态资源的加载逻辑,使其支持 CDN 资源加载。
更详细的方案设计请参考《Flutter Web在美团外卖的实践》一文。下面我们重点介绍 main.dart.js 分片相关的一些优化策略。
Flutter 官方提供 deferred as
关键字来实现 Widget 的懒加载,而 dart2js 在编译过程中可以将懒加载的 Widget 进行按需打包,这样的拆包机制叫做 Lazy Loading。借助 Lazy Loading,我们可以在路由表中使用 deferred 引入各个路由(页面),以此来达到业务代码拆离的目的,具体使用方法和效果如下所示:
// 使用方式
import 'pages/index/index.dart' deferred as IndexPageDefer;
{
'/index': (context) => FutureBuilder(
future: IndexPageDefer.loadLibrary(),
builder: (context, snapshot) => IndexPageDefer.Demo(),
)
... ...
}
使用 Lazy Loading 后,业务页面的代码会被拆分到了多个 PartJS(对应图中 xxx.part.js 文件) 中。这样看似解决了业务代码与 SDK 耦合的问题,但在实际操作过程中,我们发现每次业务代码的变动,仍然会导致编译后的 main.dart.js 随之发生变化(文件 Hash 值变化)。经过定位与跟踪,我们发现这个变化的部分是 PartJS 的加载逻辑和映射关系,我们称之为 Runtime Manifest。因此,需要设计一套方案对 Runtime Manifest 进行抽离,来保证业务代码的修改对 main.dart.js 的影响达到最低。
通过对业务代码的抽离,此时 main.dart.js 文件的构成主要包含 SDK 和 Runtime Manifest:
那如何能将 Runtime Manifest 进行抽离呢?对比常规 Web 项目,我们的处理方式是把 SDK、Utils、三方包等基础依赖,利用 Webpack、Rollup 等打包工具进行抽离并赋予一个稳定的 Hash 值。同时,将 Runtime Manifest (分片文件的加载逻辑和映射关系)注入到 HTML 文件中,这样保证了业务代码的变动不会影响到公共包。借助常规 Web 项目的编译思路,我们深入分析了 FlutterWeb 中 Runtime Manifest 的生成逻辑和 PartJS 的加载逻辑,定制出如下的解决方案:
在上图中,Runtime Manifest 的生成逻辑位于 Dart2JS Compiler 模块,在该生成逻辑中,我们对 Runtime Manifest 代码块进行了标记,之后在 flutter_tools 中将标记的 Runtime Manifest 代码块抽离并写入 HTML 文件中(以 JS 常量形式存在)。而在 PartJS 的加载流程中,我们将 manifest 信息的读取方式改为了 JS 常量的获取。按照这样的拆分方式,业务代码的变更只会改变 Runtime Manifest 信息 ,而不会影响到 main.dart.js 公共包。
经过以上引入 Lazy Loading、Runtime Manifest 抽离,main.dart.js 文件的体积稳定在 0.7M 左右,浏览器对大体积单文件的加载,会有很沉重的网络负担,所以我们设计了切片方案,充分地利用浏览器对多文件并行加载的特性,提升文件的加载效率。
具体实现方案为:将 main.dart.js 在 flutter_tools 编译过程拆分成多份纯文本文件,前端通过 XHR 的方式并行加载并按顺序拼接成 JavaScript 代码置于 < script > 标签中,从而实现切片文件的并行加载。
如上一节所述,虽然我们做了很多工作来稳定 main.dart.js 的内容,但在 Flutter Tree-Shaking 的运行机制下,各个项目引用不同的 Framework Widget,就会导致每个项目生成的 main.dart.js 内容不一致。随着接入 FlutterWeb 的项目越来越多,每个业务的页面互访概率也越来越高,我们的期望是当访问 A 业务时,可以预先缓存 B 业务引用的 main.dart.js,这样当用户真正进入 B 业务时就可以节省加载资源的时间,下面为详细的技术方案。
我们把整体的技术方案分为编译、监听、运行三个阶段。
下图为预缓存的整体方案设计:
编译阶段
编译阶段会扩展现有的发布流水线,在 flutter build 之后增加 prefetch build 作业,这样 build 之后就可以对产物目录进行遍历和筛选,得到我们所需资源进而生成云端 JSON,为运行阶段提供数据基础。下面的流程图为编译阶段的详细方案设计:
编译阶段分为三部分:
通过对流水线编译期的整合,我们可以生成新的云端 JSON 并上传到云端,为运行阶段的下发提供数据基础。
监听阶段
我们知道,浏览器对文件请求的并发数量是有限制的,为了保证浏览器对当前页面的渲染处于高优先级,同时还能完成预缓存的功能,我们设计了一套对缓存文件的加载策略,在不影响当前页面加载的情况下,实现对缓存文件的加载操作。以下为详细的技术方案:
在页面 DOMContentLoaded 之后,我们会监听三部分的的变化。
通过上述步骤,我们就可以得到一个首屏渲染完成的时机,之后就可以实现预缓存功能了。以下为预缓存功能的实现。
运行阶段
预缓存的整体流程为:下载编译阶段生成的云端 JSON,解析出需要进行预缓存资源的 CDN 路径,最后通过 HTTP XHR 进行缓存资源进行请求,利用浏览器本身的缓存策略,把其他业务的资源文件写入。当用户访问已命中缓存的页面时,资源已被提前加载,这样可以有效地减少首屏的加载时间。下图为运行阶段的详细方案设计:
在监听阶段,我们可以获取到页面的首屏渲染完成的时机,会获取到云端 JSON,首先判断该项目的缓存是否为启用状态。当该项目可用时,会根据全局变量 PROJECT_ID 进行资源数组的匹配,再以 HTTP XHR 方式进行预访问,把缓存文件写入浏览器缓存池中。至此,资源预缓存已执行完毕。
当有页面间互访问命中预缓存时,浏览器会以 200(Disk Cache)的方式返回数据,这样就节省了大量资源加载的时间,下图为命中缓存后资源加载情况:
目前,美团外卖商家端业务已有 10+ 个页面接入了预缓存功能,资源加载 90 线平均值由 400ms 下降到 350ms,降低了 12.5%;50 线平均值由 114ms 下降到 100ms,降低了 12%。随着项目接入接入越来越多,预缓存的效果也会越发的明显。
如前文所述,美团外卖商家业务大部分都是双端对齐的。为了实现提效的最大化,我们对 FlutterWeb 的多平台适配能力进行加强,实现了 FlutterWeb 在 PC 侧的复用。
在 PC 适配过程中,我们不可避免地需要书写双端的兼容代码,如:为了实现在列表页面中对卡片组件的复用。为此我们开发了一个适配工具 ResponsiveSystem,分别传入 PC 和 App 的各端实现,内部会区分平台完成适配:
// ResponsiveSystem 使用举例
Container(
child: ResponsiveSystem(
app: AppWidget(),
pc: PCWidget(),
),
)
上述代码能较方便的实现 PC 和 App 适配,但 AppWidget 或 PCWidget 在编译过程中都将无法被 Tree-Shaking 去除,因此会影响包体积大小。对此,我们将编译流程进行优化,设计分平台打包方案:
通过这样的方式,我们去除了各自平台的无用代码,避免了 PC 适配过程中引起的包体积问题。依然以美团外卖商家课堂业务(6 个页面)为例,接入分平台打包后,单平台代码体积减小 100KB 左右。
当访问 FlutterWeb 页面时,即使在业务代码中并未使用 Icon 图标,也会加载一个 920KB 的图标字体文件:MaterialIcons-Regular.woff。通过探究,我们发现是 Flutter Framework 中一些系统 UI 组件(如:CalendarDatePicker、PaginatedDataTable、PopupMenuButton 等)使用到了 Icon 图标导致,且 Flutter 为了便于开发者使用,提供了全量的 Icon 图标字体文件。
Flutter 官方提供的 --tree-shake-icons
命令选项是将业务使用到的 Icon 与 Flutter 内部维护的一个缩小版字体文件(大约 690KB)进行合并,能一定程度上减小字体文件大小。而我们需要的是只打包业务使用的 Icon,所以我们对官方 tree-shake-icons
进行了优化,设计了 Icon 的按需打包方案:
通过以上的方案,我们解决了字体文件过大带来的包体积问题,以美团外卖课堂业务(业务代码中使用了 5 个 Icon)为例,字体文件从 920KB 精简为 11.6kB。
综上所述,我们基于 HTML Render 模式对 FlutterWeb 性能优化进行了探索和实践,主要包括 SDK(Dart-SDK、Framework、Flutter_Web_SDK)的精简,静态资源产物优化(例如:JS 分片、文件 Hash、字体图标文件精简、分平台打包等)和前端资源加载优化(预加载与按需请求)。最终使得 JS 产物由 1.2M 减少至 0.7M(非业务代码),页面完全加载时间 TP90 线由 6s 降到了 3s,这样的结果已能满足美团外卖商家端的大部分业务要求。而未来的规划将聚焦于以下3个方向:
美团外卖技术团队正在基于 FlutterWeb 做更多的探索和尝试。如果你对这方面的技术也比较感兴趣,可以在文末留言,跟我们一起讨论。也欢迎大家给提出一些建议,非常感谢。
TensorFlow(下文简称TF)是谷歌推出的一个开源深度学习框架,在美团推荐系统场景中得到了广泛的使用。但TensorFlow官方版本对工业级场景的支持,目前做得并不是特别的完善。美团在大规模生产落地的过程中,遇到了以下几方面的挑战:
以上这些问题,并不是TensorFlow设计的问题,更多是底层实现的问题。考虑到美团大量业务的使用习惯以及社区的兼容性,我们基于原生TensorFlow 1.x架构与接口,从大规模稀疏参数的支持、训练模式、分布式通信优化、流水线优化、算子优化融合等多维度进行了深度定制,从而解决了该场景的核心痛点问题。
首先新系统在支持能力层面,目前可以做到千亿参数模型,上千Worker分布式训练的近线性加速,全年样本数据能够1天内完成训练,并支持Online Learning的能力。同时,新系统的各种架构和接口更加友好,美团内部包括美团外卖、美团优选、美团搜索、广告平台、大众点评Feeds等业务部门都在使用。本文将重点介绍大规模分布式训练优化的工作,希望对大家能够有所帮助或启发。
随着美团业务的发展,推荐系统模型的规模和复杂度也在快速增长,具体表现如下:
对于大流量业务,一次训练实验,从几个小时增长到了几天,而此场景一次实验保持在1天之内是基本的需求。
TensorFlow是一个非常庞大的开源项目,代码有几百万行之多,原生系统的监控指标太粗,且不支持全局的监控,如果要定位一些复杂的性能瓶颈点,就比较困难。我们基于美团已经开源的监控系统CAT[2],构建了TensorFlow的细粒度监控链路(如下图1所示),可以精准定位到性能的瓶颈问题。
同时,在性能优化的过程中,会涉及到大量的性能测试和结果分析,这也是一个非常耗费人力的工作。我们抽象了一套自动化的实验框架(如下图2所示),可以自动化、多轮次地进行实验,并自动采集各类监控指标,然后生成报告。
在推荐系统场景中,我们使用了TensorFlow Parameter Server[3](简称PS)异步训练模式来支持业务分布式训练需求。对于这套架构,上述的业务变化会带来什么样的负载变化?如下图3所示:
总结来看,主要包括通信压力、PS并发压力、Worker计算压力。对于分布式系统来说,通常是通过横向扩展来解决负载问题。虽然看来起可以解决问题,但从实验结果来看,当PS扩展到一定数量后,单步训练时间反而会增加,如下图4所示:
导致这种结果的核心原因是:Worker单步训练需要和所有的PS通信同步完成,每增加1个PS要增加N条通信链路,这大大增加了链路延迟(如下图5所示)。而一次训练要执行上百万、上千万步训练。最终导致链路延迟超过了加PS算力并发的收益。
而对于这个系统,优化的核心难点在于:如何在有限的PS实例下,进行分布式计算的优化。
对于推荐系统模型,绝大多数参数都是稀疏参数,而对稀疏参数来说有一个非常重要的操作是Embedding,这个操作通常也是负载最重的,也是后续优化的重点。由于我们对稀疏参数进行了重新定义,后续的优化也基于此之上,所以我们先介绍一下这部分的工作。
在原生的TensorFlow中构建Embedding模块,用户需要首先创建一个足够装得下所有稀疏参数的Variable,然后在这个Variable上进行Embedding的学习。然而,使用Variable来进行Embedding训练存在很多弊端:
我们首先解决了有无的问题,使用HashTable来替代Variable,将稀疏特征ID作为Key,Embedding向量作为Value。相比原生使用Variable进行Embedding的方式,具备以下的优势:
简化版的基于PS架构的实现示意如下图6所示:
核心流程大致可以分为以下几步:
这部分优化,是分布式计算的经典优化方向。PS架构是一个典型的“水桶模型”,为了完成一步训练,Worker端需要和所有PS完成交互,因此PS之间的平衡就显得非常重要。但是在实践中,我们发现多个PS的耗时并不均衡,其中的原因,既包括TensorFlow PS架构简单的切图逻辑(Round-Robin)带来的负载不均衡,也有异构机器导致的不均衡。
对于推荐模型来说,我们的主要优化策略是,把所有稀疏参数和大的稠密参数自动、均匀的切分到每个PS上,可以解决大多数这类问题。而在实践过程中,我们也发现一个比较难排查的问题:原生Adam优化器,实现导致PS负载不均衡。下面会详细介绍一下。
在Adam优化器中,它的参数优化过程需要两个β参与计算,在原生TensorFlow的实现中,这两个β是所有需要此优化器进行优化的Variabl(或HashTable)所共享的,并且会与第一个Variable(名字字典序)落在同一个PS上面,这会带来一个问题:每个优化器只拥有一个β_1和一个β_2,且仅位于某个PS上。因此,在参数优化的过程中,该PS会承受远高于其他PS的请求,从而导致该PS成为性能瓶颈。
但是通过观察Adam的优化算法,我们可以看到β_1和β_2都是常量,且蓝色高亮的部分都是相对独立的计算过程,各个PS之间可以独立完成。基于这样的发现,优化的方法也就非常直观了,我们为每一个PS上的Adam优化器冗余创建了β参数,并在本地计算t和alpha值,去除了因此负载不均导致的PS热点问题。
该优化所带来的提升具备普适性且效果明显,在美团内部某业务模型上,通过β热点去除可以带来9%左右的性能提升。此外,由于摆脱了对β的全局依赖,该优化还能提高PS架构的可扩展性,在扩增Worker数量的时候相比之前会带来更好的加速比。
通过2.2章节的分析可知,系统的通信压力也非常大,我们主要基于RDMA做了通信优化的工作。首先简单介绍一下RDMA,相比较于传统基于套接字TCP/IP协议栈的通信过程,RDMA具有零拷贝、内核旁路的优势,不仅降低了网络的延迟,同时也降低了CPU的占用率,RDMA更适合深度学习模型的相关通信过程。
RDMA主要包括三种协议Infiniband、RoCE(V1, V2)、iWARP。在美团内部的深度学习场景中,RDMA通信协议使用的是RoCE V2协议。目前在深度学习训练领域,尤其是在稠密模型训练场景(NLP、CV等),RDMA已经是大规模分布式训练的标配。然而,在大规模稀疏模型的训练中,开源系统对于RDMA的支持非常有限,TensorFlow Verbs[4]通信模块已经很长时间没有更新了,通信效果也并不理想,我们基于此之上进行了很多的改进工作。
经过优化后的版本,在1TB Click Logs[5]公开数据集、DLRM[6]模型、100个Worker以上的训练,性能提升了20%~40%。在美团的多个业务模型上,对比TensorFlow Seastar[7]改造的通信层实现也有10%~60%的速度提升。同时也把我们的工作回馈给了社区。
RDMA有三种数据传输的方式SEND/RECV、WRITE、READ,其中WRITE、READ类似于数据发送方直接在远程Memory进行读写,Receiver无法感知,WRITE和READ适用于批量数据传输。在TensorFlow内部,基于RDMA的数据传输方式使用的是WRITE单边通信模式。
在RDMA传输数据时,需要提前开辟内存空间并将其注册到网卡设备上(Memory Registration过程,下称MR),使得这片空间可以被网卡直接操作。开辟新的内存并注册到设备上,整个过程是比较耗时的。下图9展示了不同大小的内存绑定到网卡设备上的耗时,可以看到随着注册内存的增大,绑定MR的耗时迅速增加。
社区版Tensorflow RDMA实现,Tensor创建依旧沿用了统一的BFC Allocator,并将所有创建的Tensor都注册到MR上。正如上面所提到的,MR的注册绑定具有性能开销,高频、大空间的MR注册会带来显著的性能下降。而训练过程中的Tensor,只有那些涉及到跨节点通信的Tensor有必要进行MR,其余Tensor并不需要注册到MR。因此,优化的方法也就比较直接了,我们识别并管理那些通信Tensor,仅对这些跨节点通信的Tensor进行MR注册就好了。
RDMA静态分配器是上一个MR注册优化的延伸。通过Memory Registration优化,去除非传输Tensor的MR注册,我们降低了MR注册数量。但是在稀疏场景大规模的训练下,并行训练的Worker常有几百上千个,这会带来新的问题:
针对上面的问题,我们引入了MR静态分配器的策略。
这里核心的设计思路为:
具体到实现中,我们引入了Allocation Analysis模块,在训练开始的一段时间,我们会对分配的历史数据进行分析,以得到一个实际预开辟MR大小以及各个Tensor的预留空间大小。然后我们会暂停训练的进程,启动Allocator的构造过程,包括MR的创建以及通信双端的信息同步。利用相关信息构造MR Info Map,这个Map的Key是传输Tensor的唯一标记(ParsedKey,计算图切图时确定),Info结构体中包含了本地地址指针、offset大小、ibv_send_wr相关信息等。然后恢复训练,后续Tensor的传输就可以使用静态开辟好的MR进行收发,也免去了因Shape变化而产生的多次协商过程。
TensorFlow社区版的RDMA通信过程,不仅仅包含上面Tensor数据的发送和接收过程,还包括传输相关的控制消息的发送和接收过程,控制消息的发送和接收过程同样是使用了ibv_post_send和ibv_post_recv原语。原生的控制流实现存在一些瓶颈,在大规模训练时会限制控制流的吞吐,进而影响数据收发的效率。具体体现在:
针对上面的问题,我们采用了Multi RequestBuffer与CQ负载均衡优化,破除了在请求发送和请求应答环节可能存在的吞吐瓶颈。
对于Tensorflow PS架构熟悉的同学会了解,一整张计算图被切割为Worker端和PS端后,为了使两张计算图能够彼此交换数据,建立了基于Rendezvous(汇合点)机制的异步数据交换模式。如下图12所示:
基于上图的切图逻辑,Recv算子代表着这一侧计算图有Tensor的需求,而Tensor的生产者则位于与之配对的另一设备上的Send算子背后。
在具体实现上,Tensorflow实现了Recv-Driven的数据交换模式,如上图所示,位于DeviceA和DeviceB的两张计算图会异步并发的执行,位于DeviceB的Recv执行时会发起一条RPC请求发往DeviceA,DeviceA收到请求后,会将请求路由到Rendezvous中,如果在当中发现所需要的数据已经生产好,并被Send算子注册了进来,那么就地获取数据,返回给DeviceB;如果此时数据还没有生产好,则将来自于DeviceB的Recv请求注册在Rendezvous中,等待后续DeviceA生产好后,由Send算子发送过来,找到注册的Recv,触发回调,返回数据给DeviceB。
我们看到,汇合点机制优雅地解决了生产者消费者节奏不同情况下数据交换的问题。不过Recv-Driven的模式也引入了两个潜在的问题:
针对上面提到的问题,我们在RDMA上实现了另外一种数据交换的模式,叫做Send-Driven模式。与Recv-Driven模式相对,顾名思义就是有Send算子直接将数据写到Recv端,Recv端接收数据并注册到本地Rendezvous中,Recv算子直接从本地的Rendezvous中获取数据。具体流程如下图13所示:
从图中可以看到,相较于Recv-Driven模式,Send-Driven模式的通信流程得到了比较大的简化,另外在数据ready后立即发送的特性,跳过了一侧的Rendezvous,并且对于生产者先于消费者的情况,可以加快消费端数据获取的速度。
这部分优化,也是分布式计算的经典优化方向。整个流程链路上那些可以精简、合并、重叠需要不断去挖掘。对于机器学习系统来说,相比其它的系统,还可以用一些近似的算法来做这部分工作,从而获得较大的性能提升。下面介绍我们在两个这方面做的一些优化实践。
在启用HashTable存储稀疏参数后,对应的,一些配套参数也需要替换为HashTable实现,这样整个计算图中会出现多张HashTable以及大量的相关算子。在实践中,我们发现需要尽量降低Lookup/Insert等算子的个数,一方面降低PS的负载,一方面降低RPC QPS。因此,针对稀疏模型的常见用法,我们进行了相关的聚合工作。
以Adam优化器为例,需要创建m、v两个slot,以保存优化中的动量信息,它的Shape与Embedding相同。在原生优化器中,这两个Variable是单独创建的,并在反向梯度更新的时候会去读写。同理,使用HashTable方案时,我们需要同时创建两张单独的HashTable用来训练m、v参数。那么在前向,反向中需要分别对Embedding、m、v进行一次Lookup和一次Insert,总共需要三次Lookup和三次Insert。
这里一个优化点就是将Embedding、 m、v,以及低频过滤的计数器(见下图14的Counting HashTable)聚合到一起,作为HashTable的Value,这样对稀疏参数的相关操作就可以聚合执行,大大减少了稀疏参数操作频次,降低了PS的压力。
该特性属于一个普适型优化,开启聚合功能后,训练速度有了显著的提高,性能提升幅度随着模型和Worker规模的变化,效果总是正向的。在美团内部真实业务模型上,聚合之后性能相比非聚合方式能提升了45%左右。
流水线,在工业生产中,指每一个生产单位只专注处理某个片段的工作,以提高工作效率及产量的一种生产方式。在计算机领域内,更为大家熟知的是,流水线代表一种多任务之间Overlap执行的并行化技术。例如在典型的RISC处理器中,用户的程序由大量指令构成,而一条指令的执行又可以大致分为:取指、译码、执行、访存、写回等环节。这些环节会利用到指令Cache、数据Cache、寄存器、ALU等多种不同的硬件单元,在每一个指令周期内,这5个环节的硬件单元会并行执行,得以更加充分的利用硬件能力,以此提高整个处理器的指令吞吐性能。处理器的指令流水线是一套复杂而系统的底层技术,但其中的思想在分布式深度学习框架中也被大量的使用,例如:
我们看到,在深度学习框架设计上,通过分析场景,可以从不同的视角发掘可并行的阶段,来提高整体的训练吞吐。
对于大规模稀疏模型训练时,核心模型流程是:先执行稀疏参数的Embedding,然后执行稠密部分子网络。其中稀疏参数Embedding在远端PS上执行,主要耗费网络资源,而稠密部分子网络在本地Worker执行,主要耗费计算资源。这两部分占了整个流程的大部分时间,在美团某实际业务模型上分别耗时占比:40+%、50+%。
那我们是否可以提前执行稀疏参数的Embedding,来做到通信和计算的Overlap,隐藏掉这部分时间呢?从系统实现上肯定是可行的,但从算法上讲,这样做会引入参数Staleness的问题,可能会导致模型精度受到影响。但在实际的生产场景中,大规模异步训练时本身就会带来几十到几百个步的滞后性问题。经过我们测试,提前获取一两步的稀疏参数,模型精度并未受到影响。
在具体实现上,我们把整个计算图拆分为Embedding Graph(EG)和Main Graph(MG)两张子图,两者异步独立执行,做到拆分流程的Overlap(整个拆分过程,可以做到对用户透明)。EG主要覆盖从样本中抽取Embedding Key,查询组装Embedding向量,Embedding向量更新等环节。MG主要包含稠密部分子网络计算、梯度计算、稠密参数部分更新等环节。
两张子图的交互关系为:EG向MG传递Embeding向量(从MG的视角看,是从一个稠密Variable读取数值);MG向EG传递Embedding参数对应的梯度。上述两个过程的表达都是TensorFlow的计算图,我们利用两个线程,两个Session并发的执行两张计算图,使得两个阶段Overlap起来,以此到达了更大的训练吞吐。
上图是Embedding流水线的架构流程图。直观来看分为左侧的样本分发模块,顶部的跨Session数据交换模块,以及自动图切分得到的Embedding Graph和Main Graph,蓝色的圆圈代表新增算子,橙色箭头代表EG重点流程,蓝色箭头代表MG重点流程,红色箭头代表样本数据重点流程。
通过上面的设计,我们就搭建起了一套可控的EG/MG并发流水线训练模式。总体来看,Embedding流水线训练模式的收益来源有:
另外,在API设计上,我们做到了对用户透明,仅需一行代码即可开启Embedding流水线功能,对用户隐藏了EG/MG的切割过程。目前,在美团某业务训练中,Embedding流水线功能在CPU PS架构下可以带来20%~60%的性能提升(而且Worker并发规模越大,性能越好)。
经过2.2章节的分析可知,我们不能通过持续扩PS来提升分布式任务的吞吐,单实例PS的并发优化,也是非常重要的优化方向。我们主要的优化工作如下。
PS架构下,大规模稀疏模型训练对于HashTable的并发读写要求很高,因为每个PS都要承担成百乃至上千个Worker的Embedding压力,这里我们综合速度和稳定性考虑,选用了tbb::concurrent_hash_map[10]作为底层HashTable表实现,并将其包装成一个新的TBBConcurrentHashTable算子。经过测试,在千亿规模下TBBConcurrentHashTable比原生MutableDenseHashTable训练速度上快了3倍。
对于大规模稀疏模型训练来说,Embedding HashTable会面对大量的并发操作,通过Profiling我们发现,频繁动态的内存申请会带来了较大性能开销(即使TensorFlow的Tensor有专门的内存分配器)。我们基于内存池化的思路优化了HashTable的内存管理。
我们在HashTable初始化时,会先为Key和Value分别创造两个BucketPool,每个池子都会先Malloc较大一块内存备用,考虑到可能会有对HashTable进行中的Key和Value进行Remove的场景(如Online Learning训练时),需要对从HashTable中删除的Key和Value所使用的内存进行回收,因此每个BucketPool还有一个ReuseQueue来负责维护回收的内存。每次向内部的哈希表数据结构中Insert Key和Value的时候,Key和Value内存和释放分配都进行池化管理。用这种方式降低了大规模稀疏训练中遇到稀疏内存分配开销,整体端到端训练性能提升了5%左右。
经过2.2章节的分析,Worker的计算压力也非常大,如果不优化Worker,同时要保持吞吐,需要横向扩展更多的Worker,给PS带来更大的压力。而对于用户来说,如果能在有限的计算资源下带来性能提升,对业务价值更高。我们通过CAT统计出了一些高频算子,并进行了专项优化。这里选取Unique&DynamicPartition算子融合案例进行分享。
在TensorFlow PS架构中,包括Embedding向量在内的共享参数都存储在PS上,并通过网络与Worker交互,在进行Embedding查询过程中,往往会涉及如下两个环节:
通常这两个过程会利用TensorFlow既有的算子进行搭建,但在实际使用中,我们发现它并不是很高效,主要问题在于:
总结来说,HashTable开辟过大会导致大量的minor_page_fault,导致访存的时间增加,HashTable过小又可能会导致扩容。我们采用了基于启发式算法的内存自适应Unique算子实现,通过对训练历史重复率的统计,我们可以得到一个相对合理的HashTable大小,来提高访存的性能;另外Unique算子内HashTable的具体选择上,经过我们的多种测试,选择了Robin HashTable替换了原生TF中的实现。
进一步,我们对围绕Embedding ID的Unique和Partition环节进行了算子合并,简化了逻辑实现。经过上述的优化,Unique单算子可以取得51%的加速,在真实模型端到端上可以获得10%左右的性能提升,算子总数量降低了4%。
在整个关键算子优化的过程中,Intel公司的林立凡、张向泽、高明进行大量的技术支持,我们也复用了他们的部分优化工作,在此深表感谢!
大规模稀疏能力在业务落地的过程中,算法层面还需要从特征和模型结构上进行对应升级,才能拿到非常好的效果。其中外卖广告从业务特点出发,引入大规模稀疏特征完成外卖场景下特征体系的升级,提供了更高维的特征空间和参数空间,增强了模型的拟合能力。重新设计了面向高维稀疏场景的特征编码方案,解决了特征编码过程中的特征冲突问题,同时编码过程去掉了部分冗余的特征哈希操作,一定程度上简化了特征处理逻辑,并降低了特征计算的耗时。
在系统层面,面对百亿参数、百亿样本以上量级的大规模稀疏模型的训练,会带来训练迭代效率的大大降低,单次实验从一天以内,增长到一周左右。美团机器学习平台训练引擎团队,除了上述TensorFlow框架层面的优化、还针对业务模型进行了专项优化,整体吞吐优化了8到10倍(如果投入更多计算资源,可以进一步加速),大大提升业务的迭代效率,助力外卖广告业务取得了较为明显的提升。
TensorFlow在大规模推荐系统中被广泛使用,但由于缺乏大规模稀疏的大规模分布式训练能力,阻碍了业务的发展。美团基于TensorFlow原生架构,支持了大规模稀疏能力,并从多个角度进行了深度优化,做到千亿参数、千亿样本高效的分布式训练,并在美团内部进行了大规模的使用。对于这类关键能力的缺失,TensorFlow社区也引起了共鸣,社区官方在2020年创建了SIG Recommenders[11],通过社区共建的方式来解决此类问题,美团后续也会积极的参与到社区的贡献当中去。
美团推荐系统场景的模型训练,目前主要运行在CPU上,但随着业务的发展,有些模型变得越来越复杂,CPU上已经很难有优化空间(优化后的Worker CPU使用率在90%以上)。而近几年,GPU的计算能力突飞猛进,新一代的NVIDIA A100 GPU,算力达到了156TFLOPS(TF32 Tensor Cores)、80G显存、卡间带宽600GB/s。对于这类复杂模型的Workload,我们基于A100 GPU架构,设计了下一代的分布式训练架构,经过初步优化,在美团某大流量业务推荐模型上也拿到了较好的效果,目前还在进一步优化当中,后续我们会进行分享,敬请期待。
美团机器学习平台大量岗位持续招聘中,社招/校招均可(欢迎投递我们的校招北斗岗位:美团机器学习平台基础架构),坐标北京/上海,构建多领域的公司级机器学习平台,帮大家吃得更好,生活更好。简历可投递至:huangjun03@meituan.com。
作为一家生活服务在线电子商务平台,美团致力于通过科技链接消费者和商户,努力为消费者提供品质生活。到店餐饮(简称到餐)作为美团的核心业务之一,是满足用户堂食消费需求、赋能餐饮商户在线运营的重要平台,在服务百万级别的餐饮商户和亿级别C端用户的过程中,积累了海量的用户评论信息(User Generated Content, UGC),包含了用户到店消费体验之后的真情实感,如果能够有效提取其中的关键的情感极性、观点表达,不仅可以辅助更多用户做出消费决策,同时也可以帮助商户收集经营状况的用户反馈信息。
近年来,大规模预训练模型(BERT)、提示学习(Prompt)等NLP技术飞速发展。文本分类、序列标注、文本生成各类自然语言处理任务的应用效果得到显著提升,情感分析便是其中最常见的应用形式之一。它的任务目标在于通过NLP技术手段对输入文本进行分析、处理、归纳、推理,给出文本情感极性判定的结果。
按照情感极性判定粒度,可以细分为篇章/整句粒度情感分析、细粒度情感分析(ABSA, Aspect-based Sentiment Analysis)[1]。一般而言,细粒度情感分析的任务目标主要围绕属性(Aspect Term)、观点(Opinion Term)、情感(Sentiment Polarity)三要素展开,可以拆分为属性抽取、观点抽取以及属性-观点对的情感倾向判定三个级联任务[2-5]。例如,对于给定的用户评论“这家店环境不错,但服务很糟糕”,预期的输出结果为(环境,不错,正向)、(服务,糟糕,负向)。
到餐算法团队结合到餐业务供给侧、平台侧、需求侧的业务场景,为核心业务链路的智能化提供高效、优质的算法解决方案,通过算法能力辅助业务降本提效。本文结合到餐B/C端业务场景,探索细粒度情感分析技术在用户评价挖掘方向的应用实践。
秉承“帮大家吃得更好,生活更好”的使命,到餐面向消费者提供包括套餐、代金券、买单、预订等在内的丰富产品和服务,并通过黑珍珠餐厅指南、大众点评必吃榜等榜单,以及搜索、查询、评价等,帮助消费者更好地作出消费决策。同时,为商家提供一站式的营销服务,帮助餐饮商户沉淀口碑、获取用户、增加复购等,进而轻松管理餐厅。
随着餐饮连锁化加速、行业竞争格局激烈,商户管理宽幅和难度逐步加大,商户的经营要求更加精细,数据管理意识更加迫切。用户历史评论中蕴含着大量用户消费后的反馈,是情感分析的重要组成部分,不仅能够描述消费感受,同时也能反映出就餐环境的好坏。因此,做好情感分析有利于帮助餐饮门店提升服务质量,也能够更好地促进消费体验。
UGC评价分析,主要是从评论文本中挖掘出菜品、服务、食品安全(简称食安)等方面相关信息,获取用户在各个维度的细粒度情感,细致刻画商家的服务现状,如上图2所示。对于餐饮商户,菜品、服务、食安评价分析问题可以拆解如下:
其中问题2和3是典型的三元组抽取任务,即识别服务或食安方面的(属性,观点,情感)。对于问题1,在服务、食安评价问题的基础上,菜品评价需要识别评论中提及的菜品,相比业界四元组(属性,观点,属性类别,情感)[6]抽取任务,到餐场景下主要为 (菜品,属性,观点,情感)四元组的识别。
在美团内部,我们针对UGC评价分析问题,调研了相关工作成果,主要为基于MT-BERT预训练模型开发了多任务模型,试图解决情感分析中的ACSA (Aspect-Category Setiment Analysis) 问题以及(属性,观点,情感)三元组抽取问题,并实现了句子粒度的情感分类工具开发,同时开源了基于真实场景的中文属性级情感分析数据集ASAP[7-9]。但对于美团到餐业务来说,我们需要基于具体场景提出针对性的解决方案,如四元组抽取任务,不能直接复用其他团队的相关技术和工具,因此有必要建设服务于到餐业务场景的细粒度情感分析技术。
在业界,我们也调研了行业其他团队如腾讯、阿里在细粒度情感分析方面的相关研究。2019年腾讯AI Lab和阿里达摩院合作[3],提出了基于两个堆叠的LSTM和三个组件(边界引导、情感一致性和意见增强)的模型,将“BIOES”标注体系与情感正向(Positive)、中性(Neutral)、负向(Negative)结合形成统一标签,可以同时识别属性和情感。同年,阿里达摩院提出了BERT+E2E-ABSA模型结构,进一步解决属性和情感的联合抽取问题[10],同时提出(属性,观点,情感)[2]三元组抽取任务,并给出了两阶段解决框架,首先分别识别出属性(情感融合为统一标签)和观点,然后判断属性-观点是否配对。
自此,业界后续研究开始向三元组联合抽取展开[11-14]。2021年2月,华为云[6]提出(属性,观点,属性类别,情感)四元组抽取多任务模型,其中一个任务识别属性和观点,另一个任务识别属性类别和情感。2021年4月,腾讯[15]引入Aspect-Sentiment-Opinion Triplet Extraction (ASOTE)任务,提出了一个位置感知的BERT三阶段模型,解决了(属性,观点,情感)三元组抽取问题。
调研机构 | 行业 | 预训练模型 | 细粒度情感分析问题 | 阅读理解问题 | 三元组问题 | 四元组问题 | 联合抽取问题 |
---|---|---|---|---|---|---|---|
阿里达摩院[2,10] | 电子商务 | ✓ | ✓ | ✗ | ✓ | ✗ | ✓ |
华为云[6] | 云服务 | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ |
腾讯[15] | 社交 | ✓ | ✓ | ✗ | ✓ | ✗ | ✗ |
美团到餐 | 本地生活 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
从学术界来看,更关注于如何更好地进行实体抽取、情感分类以及多任务的联合抽取,可能会忽略工业界落地更关注的计算时效性(如多维度标注与情感维度整合,增加计算、存储资源消耗,在有限资源下时长延迟)、效果准确性(如任务模块端到端开发,忽略业务的个性化,直接复用导致准确性降低)等方面要求,导致相关技术方法并不能直接应用于业务场景,需要进一步开发完善才能实现业务的落地。
如上表所示,针对以上调研,我们借鉴了美团搜索与NLP部在三元组细粒度情感分析方面的经验,拆解到餐四元组抽取问题,并结合学界最先进的阅读理解、注意力机制等方面的实体抽取、情感分类经验,设计开发了应用于到餐业务的细粒度情感分析解决方案。
如上文所述,菜品评价主要关注菜品、评价属性、菜品观点和观点情感,而服务、食安评价问题,主要关注服务或食安方面的评价属性、观点和情感。就细粒度情感分析任务而言,可以看出,前一个问题涉及四元组信息,而后两个问题仅涉及三元组信息。
由于三元组问题可以看作是四元组问题的子问题,不失一般性,下文将重点阐述四元组相关技术挑战。
问题3:如何同时对四元组抽取、识别,减少pipeline方法的错误累计影响?
减少pipeline方法的错误累计影响,典型的解决方案是提出同时处理信息抽取和分类任务,即多任务学习。传统的方法是直接尝试多任务学习的思路,但过程中忽略了实体间依赖的关系,甚至远程关联关系[2]。当前也在尝试直接将四元组转化成多任务学习过程,将来期望通过建立实体间pair或triplet关系,进行联合抽取、识别。
综上,对于问题1和问题2,我们会按照pipeline识别的结果,再利用策略进行抽取结果的优化;对于问题3,整合实体、关系及分类任务,进行联合学习,将有助于减少pipeline方法的错误累计影响。
如上文2.3的问题2所述,我们采用pipeline的方法,将四元组抽取问题拆解为三个任务,分为实体识别、观点抽取、观点类别和情感分类,如下图4所示:
自2018年BERT[16]出现以后,NER模型由传统的LSTM+CRF替换为BERT+CRF(或者BERT+LSTM+CRF),一度是业界NER任务的SOTA模型,近两年来NER任务主要从以下两个方面进行改进:
考虑到引入额外特征需要构建人工词典,以及转化问答任务形式依赖于人工模板,成本较高,因此采用BERT+CRF模型。
学习率调整,模型策略调优。在实验过程中,我们发现BERT+CRF相比简单的BERT+Softmax效果提升甚微,究其原因,由于预训练模型经过微调之后可以学习到具有明显区分度的特征,导致增加CRF层对实体识别结果几乎没有影响。然而,一个好的CRF转移矩阵显然对预测是有帮助的,可以为最后预测的标签添加约束来保证预测结果的合理性。进一步实验后发现,通过调整BERT和CRF层的学习率,如BERT使用较小的学习率而CRF层使用100倍于BERT的学习率 (即$e2/e1>100$,如图5所示),最终BERT+CRF的效果相比BERT+Softmax有了较明显的提升。此外,在传统NER模型LSTM+CRF基础上,我们也实验了BERT+LSTM+CRF,但效果居然有些许下降,而且预测时间也增加了,因此最终没有引入LSTM层。
观点抽取任务在业界也称为Target-oriented Opinion Words Extraction(TOWE),旨在从评论句子中抽取出给定目标对应的观点词。观点抽取也可以看作是一种NER任务,但若评论涉及多个实体和观点,如何准确抽取所有“实体-观点”关系是一个技术挑战。借鉴MRC(Machine Reading Comprehension)任务的思想,通过构建合理的Query引入先验知识,辅助观点抽取。
QA任务形式,观点抽取建模。如图6所示,模型整体由预训练层和输出层两部分组成。输出层我们使用了常规QA任务输出,包括开始标签(Start Label)和结束标签(End Label),但需要人工设计Quey。参考论文[20]经验,以图3为例,实验发现Query设计为“找出鲜虾馅饺子口味、口感、分量、食材、卖相、价格、卫生以及整体评价”效果最好,可能融入了观点描述信息,更加有助于观点抽取。考虑到QA任务天然有类别不平衡的问题,因此损失函数引入针对类别不平衡的Focal Loss,用于提升观点抽取模型的效果。由于观点抽取也可以看作是NER任务,故我们尝试将输出层设计为CRF层,但实验效果并不理想,可能由于观点语句长度不一且比较个性化,影响模型识别。另一方面,考虑到Google中文预训练模型BERT是以字粒度为切分,没有考虑到传统NLP中的中文分词,在预训练层我们将BERT模型替换为哈工大开源的中文预训练模型,如BERT-wwm-ext、RoBERTa-wwm等,最终模型效果取得进一步提升。
观点类别和情感分类可以看作两个分类任务,其中菜品评价四元组任务的观点类别包含口感、口味、分量、食材、卖相、价格、卫生、菜品整体等8个标签,而情感包含正向、中性、负向、未提及等4个标签,都是业务预定义好的。考虑到用户评论提及某个菜品的观点可能涉及多个维度,若每个维度单独建模,需要构建多个模型,较复杂且维护困难。结合ATAE-LSTM[22]和NLP中心[7-9]情感分析的经验和到餐业务特点,模型整体结构设计为多任务多分类学习框架。
多任务多分类模型,联合建模观点类别和情感。如图7所示,模型整体分为两个部分,分别为BERT共享层和Attention独享层,其中BERT共享层学习观点Embedding表示,Attention独享层学习观点在各个观点类别的情感倾向。考虑到评论中各部分会聚焦不同的观点维度,通过引入Attention结构,使得模型更加关注特定维度相关的文本信息,进而提升整体效果。
pipeline方法的优点是将目标问题拆分为多个子模块问题,对子模块分别优化,通过后处理能在一定程度上解决实体间多对多关系的问题。然而,pipeline方法也会存在一些致命缺陷,主要包括:
参考业界情感分析联合学习现状,主要为(属性,观点,情感)三元组联合抽取。结合到餐业务场景特点(如挑战2.3的问题2所述),整体设计为两阶段模型,第一阶段为对菜品实体、观点和情感联合训练,第二阶段为对观点进行分类,进而得到四元组识别的结果。
目前在学术界,三元组(属性,观点,情感)联合抽取的方法主要包括序列标注方法[11]、QA方法[5,12]、生成式方法[13,14]等。结合菜品分析场景和pipeline方法中观点抽取模块的经验,我们采取了QA式的联合抽取方法,主要参考模型Dual-MRC[5]。
Dual-MRC模型的改进,三元组联合抽取建模。在模型设计过程中,由于Dual-MRC模型分类情感倾向是对某个属性的整体评价,即一个属性只对应一个情感。然而,在到餐业务场景中,新增了菜品实体的识别,同时UGC评论中存在对同一个菜品实体包含不同观点及情感倾向。如图3所示,“味道特别好”表达了对“鲜虾饺子”正向情感,而“有点贵”显然表达了负面情感。因此,我们对Dual-MRC模型进行了改造,将观点和情感标签整合成统一标签。如图8所示,到餐Dual-MRC整体结构基于双塔BERT模型,通过引入两个Query,左边负责抽取菜品实体,右边负责抽取观点和观点情感,从而实现三元组联合抽取。
模型结构说明:
在此基础上,我们也探索了四元组联合抽取的可能,具体操作为对右边Query2进行改造,如“找出鲜虾馅饺子口味评价”,对于每个观点类别都需要构建Query进行预测,从而实现四元组联合抽取。但考虑计算量级较大且耗时较长,最终将观点类别另做预测。
观点类别分类,显然是一个文本分类问题,通常做法是基于BERT分类,取[CLS]位置的Embedding,接一个全连接层和Softmax层即可。在到餐业务场景中,主要面临少样本问题,参考业界NLP少样本解决方法,以基于对比学习的R-drop[23]方法和基于Prompt[24]的第四范式为代表。我们在BERT模型结构基础上,分别实验了Prompt模板方法(如图9所示)和R-drop数据增强(如图10所示)。其中,Prompt模板主要借鉴P-tuning[25]的思想,采取自动化构建模板的方式,基于MLM任务解决问题。
图9中[u1]~[u6]代表BERT词表里边的[unused1]~[unused6],即使用未出现的Token构建模板,Token数目为超参数。实验结果发现,基于BERT的预训练模型,结合P-tuning或R-drop结构,分类效果都能得到一定的提升,且P-tuning的效果略优于R-drop,后续还会持续探索少样本解决方法。
利用到餐的UGC标注数据,对于四元组识别进行了整体效果测评,最终以整体四元组的精确率和召回率计算F1值作为性能评估指标。如图11所示,采用经典的BERT+CRF模型进行实体抽取,在到餐评论标注数据仅达到0.61的F1,经过学习率等调参(Baseline Tuning)优化之后,F1值提升2.61%。如上文所述,在观点抽取模块中,将序列标注问题转化成问答(QA)问题后,采用BERT+MRC模型,F1显著提升至0.64,提升了5.9%,表明问题转化获得较大收益。此外,采用哈工大中文预训练BERT仍取得一定幅度的提升,F1提升至0.65。注意,图11中的模型迭代表示四元组问题中重点优化模块的模型,最终评测四元组整体效果来进行对比分析。
品牌仪表盘
品牌仪表盘作为旗舰店能力的重要环节,提供品牌层面的数据服务,助力生意增长。产品定位为头部餐饮品牌的数据中心,具备基础的数据披露能力,通过量化业务效果,指导商户经营决策。由于大客在平台沉淀了丰富的线上信息(大量的交易/流量/评论数据),可挖掘分析空间较大。应用细粒度情感分析技术从评论数据中挖掘菜品维度、服务维度、食品安全维度相关信息,量化商户经营表现,指导经营动作。关于菜品的用户反馈监控,品牌商户更关注菜品、口味、口感等维度的用户反馈。如上文所述模型迭代后,菜品情感、口味情感、口感情感识别准确率都得到一定的提升。
到餐商户菜品信息优化
随着到餐加强了菜品信息建设,主要包括在生产层面上,整合了商户各来源菜品数据,建设了商户菜品中心,并优化了C端菜品UGC上传功能,有效补充UGC菜品生产;在消费层面上,整合了商户通菜品和网友推荐菜菜品,并基于菜品信息的完善,优化了C端菜品信息的内容聚合及展示消费。同时配合到餐业务,持续通过评价信息生产建设赋能,更多的引导用户从评价生产层面进行商户菜品的描述介绍。主要针对到餐商户菜品关联的评价信息,进行信息联动与展示层面的优化,相比迭代前,有评价菜品覆盖率得到较大的提升。
开店宝评价管理
商家通过提供餐饮服务来获取用户,用户消费后通过评价给商家以反馈,促使商家去不断优化,提供更好的服务,从而获取更多的用户,达到正向循环。评价分析的意义在于建立起评价和餐饮服务之间的通道,实现评价对服务的正向促进循环。通过分析评价内容,帮助商家发现餐厅在菜品、服务、环境等方面,做得好和做得不好的地方,进而针对性的改善。相比迭代前,菜品、服务、环境维度关联评论数得到很大的提升。
经过近一年的建设,情感分析相关能力不但成功应用到到餐商户经营、供应链等业务,而且优化了多源菜品信息,辅助品牌商户进行用户反馈监控,提升商户服务能力。在联合学习探索上,目前主要将四元组问题转化为两阶段模型,如图11所示,F1值有所下降,仅达到0.63。究其原因,可能是在三元组联合抽取模型中,忽略了实体间的关系,尤其长程关系 (如上文2.4的问题3所述),导致性能不足预期。接下来,将进一步提升情感分析四元组抽取能力,挖掘UGC中用户的核心需求以及重要反馈。在技术方面,将持续进行模型迭代演进,主要涉及:
实验结果还有很大的改进空间,需要进一步探索模型优化方法,如优化预训练模型,使用MT-BERT等,以及在联合抽取中进一步引入实体间关系,来提升四元组抽取的性能。
主要通过改造Query实现四元组抽取,但是计算量级较大,需要探索模型结构优化,减少冗余的计算量,使其满足四元组联合抽取。
到餐场景涉及多个情感分析场景,需要建设灵活方便的通用框架,有助于快速支持业务,以及减少资源消耗。
未来,团队将持续优化应用技术,解决到餐业务场景中的情感分析需求。细粒度情感分析是具有挑战和前景的任务,到店餐饮算法团队将和各位读者共同持续探索和研究。
术语 | 解释 |
---|---|
ABSA | 细粒度情感分析,Aspect-based Sentiment Analysis |
NER | 命名实体识别,Named Entity Recognition |
TOWE | 面向目标的观点词抽取,Target-oriented Opinion Words Extraction |
MRC | 阅读理解,Machine Reading Comprehension |
MLM | 语言掩码模型,Masked Language Model |
BERT | 基于变换器的双向编码器表示,Bidirectional Encoder Representations from Transformers |
CRF | 条件随机场,Conditional Random Fields |
LSTM | 长短期记忆,Long Short-Term Memory |
R-drop | 基于dropout的正则策略,regularization strategy upon dropout |
储哲、王璐、润宇、马宁、建林、张琨、刘强,均来自美团到店事业群/平台技术部。
美团到店平台技术部的到餐业务数据策略组菜品知识图谱方向主要负责将菜品知识应用到到餐相关业务,使命是为到餐业务提供高效、优质、智能的应用算法解决方案。基于海量的到餐业务数据,应用前沿的实体抽取、关系挖掘、实体表征学习、细粒度情感分析、小样本学习、半监督学习等算法技术,为到餐业务提供算法能力支持。
业务数据策略组菜品知识图谱方向长期招聘自然语言处理算法专家/机器学习算法专家,感兴趣的同学可以将简历发送至hejianlin@meituan.com。
终端容器无关化(Containerless):与服务无关化(Serverless)的概念类似,即在保持顶层业务研发语言不变更的情况下,在下层可以兼容性地升级、替换终端容器的能力,让用户无需关心终端容器的运维,只要将精力聚焦到业务逻辑上的技术。
React2X是一款面向多终端、跨平台、容器无关化研发框架。在整个美团前端技术栈日益规范的趋势下,React技术栈在我们技术体系环节中的地位变得越来越重要。在广告、营销这些推广属性的业务上,在各个终端(包括美团App、美团外卖App、大众点评App,以及站外的微信小程序、百度小程序、头条&抖音小程序等其他终端)实现“一次开发,同步需求上线”的业务诉求也变得越来越多。在这样的背景下,我们定义了React2X应用的核心场景:
最终我们的核心痛点围绕在了美团系·小程序和美团系·App矩阵上的同一个需求的多次开发运维上,为了解决研发人力瓶颈问题,我们需要一款“一次研发,多终端容器复用”的研发框架来提升研发效率。
调研整个前端领域,我们找到了一些业界的解决方案,像是美团最早的mpvue、腾讯的Wepy、滴滴的Chameleon、京东的Taro等等。在经过比较与试用之后,我们最终基于投入产出比的价值判断,选择站在巨人的肩膀上研发定制一款满足美团技术、业务场景的研发框架——React2X(后面简称R2X)。从R2X第一个版本发布到现在,已经接受了来自于公司各个业务两年多的考验。所以我们希望通过本文帮助大家对R2X有一个大致的了解。
为了解决业务需求在多端容器需要重复开发的难题,通过代码复用实现开发提效,我们确定了以下的目标:
R2X开发框架主要期望能最终面向多终端应用的终端容器,用于场景化研发:
即:
针对上述核心目标和应用场景,我们对市面上的跨容器框架进行了调研。由于美团外卖的技术栈统一是React为主,所以我们的必备要求是:一款以React为DSL语言的复用框架,能快速融入美团的技术生态。
根据下表的对比,如果以React为DSL语言出发,当时就只有Taro一家能满足我们的业务诉求,但它的生态环境并不适合在美团体系内使用。基于多方面因素的考虑,我们决定结合各大主流框架之所长,然后开发出一款属于美团外卖的跨容器复用框架。
对比项 | mpvue | Taro 1.3 | Chameleon | WePY | UniApp |
---|---|---|---|---|---|
DSL | Vue | 类React(Nerv) | 类Vue | Vue | Vue |
是否支持 React Native | 否 | 是,但支持效果不佳 | Weex | 否 | 否 |
兼容 API | 无 | 有(API支持程度不一) | 自研多态协议 | 无 | 是 |
跨端组件库 | 无 | 有 | 有 | 无 | 无 |
美团生态 | 有 | 无 | 无 | 无 | 无 |
语法校验 | 无 | ESLint | 自研 | 无 | 有 |
TypeScript | 有 | 有 | 无 | 有 | 有 |
定制化扩展 | 无 | 可自研Plugin | 无 | 无 | 有 |
编译扩展 | 无 | 无 | 无 | 无 | 有 |
调研结论 | 不匹配 | 部分满足 | 部分满足 | 不匹配 | 不匹配 |
注:前期调研时间截止到2019年05月,可能与当前数据存在一定的出入。
当我们决定要打造一款属于美团外卖的跨容器复用框架之后,在实现的过程中主要遇到了以下挑战:
① 各个容器之间差异性适配成本 - 语法语义:MRN/小程序/Webview在DSL上就有着完全不同的语法语义。 - 端能力:同一容器在不同端上表现也存在不少差异,比如外卖App中MRN容器和美团App中MRN容器分别有定制的Native模块以及各类桥协议等。
② 业务接入的使用成本 - 首次成本:作为一个新定义的框架如何让新业务方快速上手,如何从存量业务线进行迁移。 - 边际成本:如何融合美团的基建生态,让业务方快速复用平台能力。
③ 顶层架构的合理设计 - 高可维护性、高度扩展性:如何快速升级、替换、新增一款底层容器。
注:以上问题我们会在下文“技术全景”章节中给予解答。
目前,业界以小程序作为跨端目标平台的框架较多,但大多都是使用Web前端技术栈作为基础,但同时兼顾React Native的技术栈就比较少。下表中列出的是支持以React、类React作为DSL的相关框架进行对比。
R2X | Taro 1.3 | Taro 3.0 | Rax | Remax | |
---|---|---|---|---|---|
原理 | R2X 1.0重编译时,R2X 2.0重运行时 | 重编译时 | 重运行时 | 重运行时 | 重运行时 |
容器重点 | 以MRN,小程序,WebView为主,同时支持MTFlutter、Mach、游戏、PC浏览器 | 以小程序、Web为主,React Native支持不多 | 以小程序、Web为主,React Native交给58团队支持 | 小程序、Web、Flutter | 小程序、Web |
API | 支持KNB桥&对多平台API进行了统一 | 对多平台API进行了统一 | 对多平台API进行了统一 | 多平台不统一 | 多平台不统一 |
跨端组件库 | 有 | 有TaroUI,但不支持React Native | 有TaroUI,但不支持React Native | 无 | 无 |
业务组件扩展 | 提供扩展方案 | 参考TaroUI | 参考TaroUI | 提供扩展方案 | 提供扩展方案 |
美团内部生态支持 | 已支持埋点监控等 | 无 | 无 | 无 | 无 |
模块化能力 | 支持 | 不支持 | 不支持 | 不支持 | 不支持 |
编译插件扩展 | 支持 | 不支持 | 支持 | 支持 | 支持Webpack配置 |
综上所述,从目前的业务形态来看,R2X在容器的匹配程度以及美团生态支持程度上看,是现阶段的最佳方案。R2X相较于业界其他框架来说,拥有更加完善的适用于美团外卖团队的本地化实现。
基于业界跨平台框架和美团内部的跨平台框架,我们针对性能也进行了Benchmark测试,最终对比结果如下。
小程序性能对比
框架 | 创建 | 更新 | 添加 | 交换 | 删除 |
---|---|---|---|---|---|
R2X-MP | 947.6 | 586.8 | 1467.2 | 1355.2 | 82.2 |
Remax | 2798.2 | 1872.6 | 5162.2 | 4818.2 | 86.4 |
Taro-MP | 1653.4 | 976.4 | 2483.2 | 2256.6 | 65.2 |
结论:可以看到,在小程序Benchmark测试结果中,R2X-MP是领先于Remax和Taro-MP。
与React Native性能对比
框架 | 创建 | 更新 | 添加 | 交换 | 删除 |
---|---|---|---|---|---|
R2X-MRN | 309.875 | 83.75 | 384 | 191.875 | 82.125 |
MRN | 297.625 | 105.25 | 400.125 | 231.625 | 65.875 |
Taro-RN | 209.5 | 77.5 | 246.25 | 85.125 | 17.125 |
结论:在React Native的Benchmark测试结果中,R2X-MRN和MRN基本持平,但都低于纯React Native的性能表现。
除了支持了基本的React Native、小程序和Webview容器同构场景之外,R2X还实现了在MTFlutter、Mach、小游戏(Webview游戏、微信小游戏&小程序、美团小游戏)、PC浏览器等容器上的同构能力扩展,相比于业内的其他跨容器开发框架的生态也更加丰富和健全。
React Native | 小程序 | Webview | Flutter | 模块级容器 | 小游戏容器 | PC浏览器(PC/App同构) | |
---|---|---|---|---|---|---|---|
R2X | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
Taro | 支持 | 支持 | 支持 | 不支持 | 不支持 | 不支持 | 不支持 |
Rax | 支持 | 支持 | 支持 | 不支持 | 不支持 | 不支持 | 不支持 |
Remax | 支持 | 支持 | 支持 | 不支持 | 不支持 | 不支持 | 不支持 |
上图为R2X的架构全景图,整体架构可以按照从下到上,从左到右的视角进行解读:
因为R2X覆盖了美团内部大部分的主流容器场景,所以技术体系较为复杂和庞大,大家可以根据自身的业务形态,选择性地去了解对应场景的同构方案。
CLI作为R2X项目驱动器,它不仅包含了命令行的启动,更重要的,它是各个编译构建的核心。
在早期,CLI执行build命令时,我们通过–type来区分不同的构建容器,从而加载不同的编译逻辑。通过指定构建容器的形式来实现同一套代码能够构建出不同的容器产物。但经过长时间的业务迭代,我们发现了这种结构存在的问题:
针对以上问题,我们考虑对CLI进行了一次全新的重构,引入插件化能力(关于插件化能力的具体实现会在下文详细描述)。CLI整体结构变成如下图所示:
整个CLI模块只需要关心参数的解析以及插件的加载执行,不需要再实现各个容器的具体编译逻辑。通过Hooks的形式,将编译的各个时机暴露给插件,插件基于这些Hook进行编译能力的实现,最终输出产物给CLI模块。这种形式带来了以下几个好处:
R2X的目的是希望通过一套代码能够在多端上运行,但是由于多端差异的存在,我们需要设计一套统一的标准规范来进行对齐。在运行时部分,主要分为组件/接口的对齐。
多端差异
在开始讲述实现之前,我们先来看看各端之间的差异到底在哪些地方。
组件(标签)差异
API差异
样式差异
小程序的WXSS和Webview的CSS在参数属性上其实是几乎一致的,但是在层级关系上有着很大的差别,小程序分为全局样式与局部样式,各个组件之间的样式也是不会相互影响(默认配置下)。而对比React Native采用的StyleSheet,是用Inline Style的方式,不支持全局样式,不支持标签样式,并且属性有诸多限制,如只能使用Flex布局等等。
如何适配
从以上章节我们已经了解到了各个端之间有着非常大的差异点,那我们应该如何克服这些困难呢?
由于各端对组件和API的支持程度不同,我们选定了一端为基础标准,定义好各个组件的属性以及接口参数,通过TypeScript的Interface进行实现。然后在各个端分别基于以上的接口进行功能对齐实现,对于端能力限制的功能进行了一定取舍,对高优功能进行了SDK底层实现适配。最终,我们基于已有的功能封装实现了一套完整的基础组件@r2x/components
和基础API@r2x/r2x
。
随着R2X的在美团内部的应用越来越多,大家对于R2X模式的认可度也在不断提高,我们从业务方中经常听到以下这些问题:“是否可以增加支持某某功能/容器”,“我们业务架构比较特殊,能否做出一些调整”。业务方对R2X会有更多功能/容器的诉求,也会有更多定制化的需求出现。
所以,我们决定实现一套完整的开放式插件能力,提供一种相对比较简单的方式,让大家能够自己来定制这些特殊需求。在最新的版本中,我们将R2X的编译时进行了重构,在新的编译时架构中引入了基于Tapable的插件系统。开发者可以通过编写插件的方式为R2X拓展更多功能,或者为自身业务线定制更多的个性化功能。
在插件类型分为两类:
插件能力的整体架构如下:
借助开发式插件能力,我们将之前编写了若干个平台容器插件,开发者安装后即可使用:
除了扩展新的容器平台,我们还可以通过继承现有的容器插件,来编写一些特殊的定制化功能插件。
1. 对代码进行预处理
基于开放式插件能力,我们可以像Babel插件一样,通过对AST语法的修改对代码源文件进行编译前后的修改。比如:修改文件引用路径、插入代码片段、处理本地图片等等。
2. 对文件产物进行修改
在编译产出生成时,我们可以对编译文件的内容、文件路径、文件结构进行修改。结合自身业务的定制化,CLI可以将R2X项目和现有的原生项目进行结合改造。
除了以上功能,插件化能力为用户在编译时提供了极大的自由度。如果你想体验的话,欢迎加入美团外卖技术团队。
为什么需要多态能力?
多态能力是用于提供跨端时各端组件及API的统一解决方案。基于多态能力,开发者可以定制自己的跨端组件。而R2X具备了完善的跨端能力,能够覆盖多终端和容器,为什么还需要多态?
业务研发为了满足各自场景的要求需要一定的灵活性。同时,Webview/小程序/React Native容器存在端上的差异,需要开发者人为进行环境判断。逻辑一复杂、跨端数量一多,代码可读性变低、维护成本起飞,这不是我们的本意。
基于这样的背景,R2X提供了扩展性良好的多态能力。
R2X多态能力介绍
对于多态能力的支持,我们分为两类:
通过差异化代码可轻松满足端差异诉求。
页面级容器场景同构我们借鉴了业内Taro1.x、Rax等优秀跨端框架的做法,结合美团内部容器、基建特点做了很多本地化实现和定制,在Webview、MRN、小程序三种容器上通过不同的编译时方案进行预处理,并且引入了React运行时,保证了对React DSL支持的完整程度和代码同构率,编译时转化流程方案和运行时结构如下图所示:
R2X2.0相比于R2X1.0,运行时方案能够解决编译时方案带来的语法限制问题:
同时也支持大部分React第三方生态库,目前已支持使用原生react-redux。彻底抹平各端的差异,无论是MRN、Flutter、小程序、Webview的自定义组件都可以直接当成React组件引入,小程序原生自定义组件也无需配置usingComponents。
在模块级同构方案上,我们在App上依赖Mach容器;在小程序容器中,我们克服了Mach容器渲染机制的约束(运行时与虚拟DOM的使用限制),单独在小程序上设计了Mach容器渲染方案,实现了R2X-模块化(R2X-Module)在客户端和小程序上99%以上的代码同构率。
整体方案
模板驱动方案
目前,R2X-Module在客户端和小程序容器的同构率在99.3%以上,在性能方面首次渲染时长和模板渲染时长的TP50时间分别是185ms和144ms,比较优秀但还存在优化空间。同时提供R2X-Module SDK供业务方选择。R2X-Module SDK初始化以及模板加载渲染流程如下图所示:
在移动互联网发展已经高度成熟的今天,移动端的PV流量占比绝大数,以外卖广告商家端为例,PC端仅仅占有很少比例,其中PC流量占比在我们部分业务上已经不及5%。因此在某些场景下实现PC/App的同构方案能够解放一部分人力,对提高开发效率来说是十分必要的。目前,外卖广告商家端的一些轻量布局差异的页面,已经完成了PC/App同构的方案设计和落地。
样式同构适配
端能力扩展
R2X的基础能力支持Webview/MRN/小程序三端,缺少对PC微前端子项目的支持。要实现PC/APP多端同构需要对R2X的端能力进行扩展。PC端本质上也属于Web端,因此PC微前端的端能力扩展可以复用大部分的Webview的端能力。整体架构图、技术设计要点、扩展流程图如下所示:
平台代码处理
在项目同构开发中,不可避免地会出现跟平台强相关的代码或者业务逻辑,比如某些API调用的是App的底层能力,只能在React Native中使用,在Web端肯定是不支持的。或者由于产品需求的原因,某些交互或者展示差异较大等等。而项目针对某一端进行编译、打包时,其他不相关的端代码是无用、多余的,如果保留的话,不仅会增加代码体积,甚至会出现编译报错,因此我们需要借助平台代码处理的能力来进行优化。平台代码的处理主要包含三部分:模块导入、组件展示、业务逻辑。
主要思路是使用注释和指定平台的方式,让特定的平台代码只在特定平台生效,注释关键字%%platform%%
,比如%%RN%%
表示React Native端独有,%%MICRO%%
表示PC微前端独有,%%MICROWebview%%
表示PC微前端、Webview 两端生效。示例代码如下:
import A from '@r2x/r2x-a'; // %%RN%%只在React Native端保留。
import B from '@r2x/r2x-b'; // %%MICRO%% 只在MICRO端保留。
import C from '@/utils/c'; // 这是所有端生效的公共模块。
import D from '@r2x/r2x-d'; // %%MICROWebview%%在MICRO、Webview多端生效的模块。
实现react2x-game同构方案主要做的两点:渲染层的兼容、业务层的兼容。
渲染层兼容
在上文,我们提到过“无论是Webview游戏、小程序、小游戏、美团小游戏都为我们提供了Canvas、WebGL控件”,很大程度地降低了我们兼容渲染层的复杂度。下面表单,是各端对于语法以及Canvas、WebGL、Document、Window等基础功能的支持情况:
对象 | Webview | 微信小游戏 | 微信小程序 | 美团小游戏 |
---|---|---|---|---|
语法 | JavaScript | JavaScript | JavaScript | JavaScript |
Canvas | 支持 | 支持 | 支持 | 支持 |
Canvas(离屏) | 支持 | 支持 | 不支持 | 支持 |
WebGL | 支持 | 支持 | >2.11.0 | 支持 |
Ducument | 支持 | 不支持 | 不支持 | 不支持 |
Window | 支持 | 不支持 | 不支持 | 不支持 |
可以看出,在语法层面各端都支持了JavaScript语法,但是在执行环境以及基础功能上的差异比较大,总结来说:
执行环境:小游戏、小程序不具备DOM、BOM的能力(渲染引擎中会大量使用)。 基础功能:小程序不支持离屏Canvas,在2.11.0版本以后才开始支持WebGL。
为了解决这些问题,我们设计开发了adaptor层,用来模拟document、window的能力。使游戏引擎可以在非Webview的环境下正常的执行和调用BOM、DOM的基础功能。同时,制定离屏canvas的适配方案,用来解决小程序无法支持离屏canvas的问题。为了获取到有效离屏canvas,我们制作了 “r2x-add-wxml-loader” ,在.wxml文件的loader阶段自动注入额外的< canvas/>控件,并隐藏于手机屏幕之外,用于模拟游戏引擎中的离屏canvas。
多端兼容构建
在构建层面,我们通过集成的多种个性化插件工具,对多端代码进行差异处理。如:环境变量注入、各端适配代码的混入、规范检测、代码解析和转化等。针对小游戏、小程序代码和执行环境的特殊性,制作wx-build-plugin、lwxapp-build-plugin等用于处理小游戏和小程序的打包工作。结合上文中提到的各类差异的处理方案,制作add-wxml-loader、transfrom-loader、wxss-loader等工具协助完成项目构建。如下图14所示,构建之初会注入本次构建的环境变量,读取和分析配置文件,集成和初始化构建工具集合,为项目构建做准备。然后在构建环节,针对各端的差异进行差别处理,分析层针对不同文件进行解析,并在转换层进行转换和构建,最终生成各端需要的最终产物。
效果收益
R2X在美团外卖业务中得到了广泛的应用。截止2021年10月,R2X累计在美团内部已有二十多个部门在使用或者在调研中,总计落地了上百个工程、页面,框架下载量达百万次,页面平均代码同构率达90%以上。R2X生态体系在容器代码复用与运维层面,累计为美团节省成本上千人/日,并提升动态化页面转化5%-8%的成功率。
综上所述,在美团外卖多元化业务形态和容器多样性的情况下,跨容器复用成为了发展的必经之路。而R2X在经历了两年的迭代下也取得了阶段性的成果,在美团各个业务场景都完成了业务的落地覆盖,针对公司的生态环境接入也做出了不少的基础建设。我们相信跨容器多端代码复用依旧是当前缩减项目交付周期,减少研发成本,提升研发效率的重要一环。但目前我们在很多复杂的业务场景下做的不够完美,因此还有许多工作待完善,例如:
最后,感谢各个相关研发团队对R2X建设过程中的鼎力支持,R2X的发展离不开所有参与者日以继夜的投入和贡献,我们会持续基于R2X在终端容器领域进行更多探索。如果大家觉得R2X还不错,或者对美团的R2X框架比较感兴趣,欢迎跟我们一起交流探讨。
正浩、宝石、彭震,均为美团外卖终端团队研发工程师。
美团外卖长期招聘Android、iOS、FE、Java高级/资深工程师和技术专家,欢迎有兴趣的同学投递简历到lizhenghao@meituan.com。
美团到店搜索广告负责美团、大众点评双平台站内搜索流量的商业变现,服务于到店餐饮、休娱亲子、丽人医美、酒店旅游等众多本地生活服务商家。在美团搜索场景中广告的展示样式非常原生,用户使用美团服务不会明显区分广告和自然结果,而广告用户体验损失则会显著影响搜索流量。因此,搜索广告除了优化流量变现效率等商业指标外,也需要重点优化用户体验,不断降低不相关广告对用户体验的损害,这样才能保证整个平台生态长期健康地发展。
在优化用户体验的目标下,如何正确的衡量用户体验,定义不相关广告是首要解决的问题。在搜索广告中,受结果列表页广告位置偏差、素材创意等因素影响,我们无法单一使用点击率(CTR)等客观性指标来衡量用户体验,尤其首位、首屏等排序靠前广告的相关性问题被认为是影响用户体验的主要因素。因此,我们首先建立了美团场景下的搜索广告相关性标准和评估体系,主要通过例行采样和人工评估的方式对搜索广告结果进行相关、一般和不相关的分档标注,进而驱动我们的广告相关性模型和策略迭代。然后,使用广告排序前五位的Badcase率(即Badcase@5)作为搜索广告的相关性评估指标。
在定义出广告相关性问题和评估指标后,使用相关性模型进行搜索关键词(Query)和候选广告(Doc)的相关性打分,类似于NLP文本匹配任务,但实际建模中也发现若干困难。美团搜索结果以商户门店(POI)粒度展示,即Doc除了POI文本外,还包含一系列的团单或商品描述,内容非常丰富但也带来较多冗余或歧义,且不同业务的文本表达差异较大,比如结婚类商品“朱颜【出门出阁红色秀禾】,南国【中式婚礼嫁衣红色秀禾】”,给广告内容编码带来挑战。
其次,美团广告商户大多没有选择竞价关键词,且POI和团单标题并未面向搜索优化,Doc内容与Query可能存在文本表达偏差。比如“满月酒”和“嗨派星球宝宝宴·游轮派对,生日宴个性气球拱门”,需要处理这类信息缺失的问题。我们最初采用ESIM[1]交互式模型,但实践中发现该模型对我们复杂广告内容的表征能力有限,正负样本区分能力不足,在过滤不相关广告的同时对相关广告的误伤率较高。自2018年底以来,以BERT[2]为代表的预训练模型在多项NLP任务上都取得了突破,我们也开始探索预训练技术在搜索广告相关性上的应用。
针对搜索语义匹配任务,Google[3]和Bing[4]的搜索团队已经基于BERT来编码Query和候选Doc,进而改善相关性的效果。预训练模型在美团内部的NLP场景中也有不少落地实践,美团搜索已经验证了预训练模型在文本相关性任务上的有效性[5]。
而针对预训练在语义匹配任务中的应用,业界也提出不少的解决方案。中科院计算所郭嘉丰等人提出PROP[6]和B-PROP[7]等针对搜索任务的预训练方法,主要思想是引入文档中代表词预测ROP(Representative wOrds Prediction)任务。纽约大学石溪分校曹庆庆等人提出DeFormer[8]分解预训练语言模型来做问答等语义匹配任务,在BERT的低层分别对问题和文档各自编码,再在高层部分拼接问题和文档的表征进行交互编码,让文档和问题在编码阶段尽可能地独立,从而提升模型的整体效率。百度刘璟等人提出RocketQA[9]和RocketQAv2[10]等面向端到端问答的检索模型训练方法,通过跨批次负采样、去噪的强负例采样以及数据增强技术大幅提升了双塔模型的效果。陈丹琦等人提出SimCSE[11],采用自监督来提升模型的句子表示能力,从而提升语义匹配的效果。
另一方面,2020年至今,预训练从“大炼模型”迈向了“炼大模型”的阶段,通过设计先进的算法,整合尽可能多的数据,汇聚大量算力,集约化地训练超大模型,持续提升模型效果。不论是公开论文结果还是美团内部实践,均已证明:更大规模的预训练模型能带来更好的下游任务效果。因此,美团广告平台与美团搜索与NLP部进行了合作,尝试利用预训练模型来优化搜索关键词和广告结果的相关性,进一步降低首屏广告Badcase,提升用户体验。
本文分为算法探索、应用实践和总结规划三个部分,对预训练技术在搜索广告相关性的落地方案进行介绍。在算法探索部分介绍了我们在训练样本上的数据增强、预训练(Pre-training)和微调(Fine-tuning)阶段的模型结构优化;在应用实践部分,本文介绍了以知识蒸馏为主的模型压缩方法、相关性服务链路优化方案,以及所取得的业务效果;最后,我们总结了相关性方面的优化方法,并对未来技术探索进行了展望。希望这些经验和思考能够给从事相关研究的同学带来一些帮助或启发。
在美团搜索广告场景中,相关性计算可以看做用户搜索意图(Query)和广告商户(POI/Doc)之间的匹配问题,实践中我们采用了能够从多角度衡量匹配程度的集成方法,具体方案为分别基于Query和POI的结构化信息匹配、文本匹配和语义匹配等方法进行打分并且进行分数融合。其中,结构化信息匹配主要是对Query分析结果与POI进行类目、属性等信息的匹配;文本匹配方面借鉴了搜索引擎中的传统相关性方法,包括Query和POI的term共现数、Query term覆盖率、TF-IDF、BM25打分等;语义匹配包括传统的隐语义匹配(如基于LDA或者Word2Vec计算相似度)和深度语义匹配方法。在广告相关性服务中,我们采用学习能力更强的深度语义匹配模型。
深度语义匹配通常分为表示型和交互型两类:表示型模型一般基于双塔结构分别得到两段输入文本的向量表示,最后计算两段文本表示的相似度;该方法的优点是Doc向量可提前离线计算缓存,且匹配阶段计算速度很快,适合线上应用;缺点是只在模型最后阶段进行交互,对文本之间匹配关系学习不足。而交互型模型在初期即对两段输入文本进行交互,匹配阶段可以采用更复杂的网络结构,以学习文本间细粒度匹配关系;这种方法往往可以达到更高的精度,主要挑战在于线上应用的性能瓶颈。
美团搜索广告相关性服务的基线模型采用Transformer+ESIM的交互式模型结构,在满足性能的前提下有效解决了部分相关性问题,但是实际应用中仍然存在一些不足,主要包括: 1. 训练数据中存在标签错误、正负样本分布不一致等问题; 2. Doc除了基础门店信息外还关联了大量商品和团单内容,如果直接将这些信息拼接成长文本作为Doc输入,由于模型结构限制往往需要对Doc文本进行截断,因而导致信息丢失; 3. 基线模型对于长文本的表征能力有限,相关性判别能力不足,很难在控制变现效率影响的同时解决更多的Badcase。
为了解决这些问题,我们基于BERT在训练数据、特征构造和模型方面进行若干探索和实践。下文将逐一展开介绍。
由于BERT模型微调阶段所需数据量相比ESIM模型更少,并且对数据覆盖全面度、标签准确度、数据分布合理性等因素更为敏感,在进行模型结构探索前,我们先按照如下思路产出一份可用性较高的数据。搜索广告涉及的业务众多且差异性大,包含的团单和商品种类多元,我们希望BERT的微调数据尽可能覆盖各个场景和主要服务。如果全部人工标注人力和时间成本较高,而用户点击转化行为能一定程度反映出广告是否相关,所以训练数据主要基于曝光点击日志构造,对于部分困难样本加以规则及人工校验。我们根据业务特性对训练数据的主要优化包括以下几点。
正样本主要通过点击数据得到,我们对4个月内的Query-POI点击数据进行统计,并且基于曝光频次和CTR进行数据清洗以减少噪声。实际采样流程中,假设对于某个Query需要取N个POI构造N条正样本,采样过程中令POI被采样的概率与其点击数成正比,这样做主要是基于点击越多相关性越高的认知,既可以进一步保证标签置信,又有利于模型学习到POI之间不同的相关程度。
在实验中我们也尝试了另外两个正样本采样方法:1) 对某个Query随机取N个POI,2) 对某个Query取点击最多的N个POI。实践发现方法1会采样到较多的弱相关样本,而方法2得到的大多为强相关样本,这两种方式均不利于模型拟合真实场景的数据分布。
我们按照模型学习的困难程度,从低到高设计了三种负样本采样方式:
美团搜索广告场景下,Query中可能包含地址词、品牌词、服务核心词等多种成分,Query文本一般较短,90%以上的Query长度小于10;POI的主要文本特征包括门店名称和商品信息,而广告主的商品数量普遍较多,直接拼接商品标题会导致POI文本过长,有26%的POI文本长度超过240。
由于相关性模型的主要目标是学习Query和POI之间的服务相关性,大量冗余文本信息会影响模型性能和学习效果,我们对Query和POI文本进行如下处理以提取关键文本信息:
最终,我们的微调样本包括约50万条数据,涵盖餐饮、休娱、亲子和丽人等20个主要类目,其中正负样本比例为1:5,三种负样本比例为2:2:1。
由于美团搜索广告涉及餐饮、休娱亲子、丽人医美等大量业务场景,并且不同场景之间差异较大。从过去的实践经验可知,对于某个业务场景下的相关性优化,利用该业务数据训练的子模型相比利用全业务数据训练的通用模型往往效果更佳,但这种方法存在几个问题:1) 多个子模型的维护和迭代成本更高;2) 某些小场景由于训练数据稀疏难以正确学习到文本表示。
受到多业务子模型优缺点的启发,我们尝试了区分业务场景的多任务学习,利用BERT作为共享层学习各个业务的通用特征表达,采用对应不同业务的多个分类器处理BERT输出的中间结果,实际应用中根据多个小场景的业务相似程度划分成N类,亦对应N个分类器,每个样本只经过其对应的分类器。多业务模型的主要优势在于,能够利用所有数据进行全场景联合训练,同时一定程度上保留每个场景的特性,从而解决多业务场景下的相关性问题,模型结构如下图1所示:
由于美团商户POI和商品标题可能缺乏有效信息表达,有时仅根据Query和POI商品文本很难准确判断两者之间的语义相关性。例如【租车公司,<上水超跑俱乐部;宝马,奥迪>】,Query和POI文本的相关性不高,而该商户的三级品类是“养车-用车租车-租车”,我们认为引入品类信息有助于提高模型效果。
为了更合理的引入品类信息,我们对BERT模型的输入编码部分进行改造,除了与原始BERT一致的Query、Doc两个片段外,还引入了品类文本作为第三个片段,将品类文本作为额外片段的作用是防止品类信息对Query、Doc产生交叉干扰,使模型对于POI文本和品类文本区别学习。
下图2为模型输入示意图,其中红色框内为品类片段的编码情况,Ec为品类片段的片段编码(Segment Embedding)。由于我们改变了BERT输入部分的结构,无法直接基于标准BERT进行相关性微调任务。我们对BERT重新进行预训练,并对预训练方式做了改进,将BERT预训练中用到的NSP(Next Sentence Prediction)任务替换为更适合搜索广告场景的点击预测任务,具体为“给定用户的搜索关键词、商户文本和商户品类信息,判断用户是否点击”。预训练数据采用自然及广告搜索曝光点击数据,大约6千万样本。
为了清晰准确地反映模型迭代的离线效果,我们通过人工标注的方法构建了一份广告相关性任务Benchmark。基线ESIM模型、BERT模型以及本文提到的优化后BERT模型在Benchmark上的评估指标如下表1所示:
Model | Accuracy | AUC | F1-Score |
---|---|---|---|
ESIM(基线,旧训练数据) | 67.73% | 76.94% | 72.62% |
MT-BERT-Base | 74.88% | 82.65% | 75.85% |
MT-BERT-Base-多业务 | 75.41% | 83.03% | 76.49% |
MT-BERT-Base-引入品类信息 | 77.33% | 83.85% | 77.93% |
MT-BERT-Large-引入品类信息 | 77.87% | 85.06% | 79.14% |
表1 广告相关性任务模型优化迭代指标
我们首先利用上文介绍的数据增强后的训练样本训练了MT-BERT-Base模型(12层768维),与ESIM模型相比,各项指标均显著提升,其中AUC提升6.6PP。在BERT模型优化方面,多任务学习和引入品类信息这两种方式均能进一步提升模型效果,其中引入品类信息的MT-BERT-Base模型效果更佳,相比标准的MT-BERT-Base模型AUC提升1.2PP。
在BERT模型规模方面,实验发现随着其规模增长,模型效果持续提升,但是预训练和部署成本也相应增长,最终我们选取了大约3亿参数量的MT-BERT-Large模型(24层1024维),在同样引入品类信息的条件下,相比MT-BERT-Base模型AUC增长1.21PP,相比ESIM模型AUC增长8.12PP。
在模型的实践落地过程中,我们也遇到若干挑战,并且针对性的设计了优化方案。第一个挑战是BERT模型的前向耗时无法满足线上性能要求,我们通过知识蒸馏和低精度量化方法对模型进行压缩,并且采用离线缓存与实时预测结合的方式进一步提升了服务性能。
另一个挑战是,在广告业务场景下,需要综合考虑平台变现效率、用户体验、商户供给及转化等因素,如何使相关性分数在广告整体链路中发挥出更好的作用。我们目前采用了低质量广告过滤、重排阶段考虑相关性因子以及TOP位次广告门槛控制等策略。下文对应用实践方面的具体方案进行介绍。
由于BERT模型的庞大参数量和前向预测耗时,直接部署上线会面临很大的性能挑战,通常需要将训练好的模型压缩为符合一定要求的小模型,业内常用模型压缩方案包括模型裁剪、低精度量化和知识蒸馏等。知识蒸馏[12]旨在有效地从大模型(教师模型)中迁移知识到小模型(学生模型)中,在业内得到了广泛的研究和应用,如HuggingFace提出的DistillBERT[13]和华为提出的TinyBERT[14]等蒸馏方法,均在保证效果的前提下大幅提升了模型性能。
经过在搜索等业务上的探索和迭代,美团NLP团队沉淀了一套基于两阶段知识蒸馏的模型压缩方案,包括通用型知识蒸馏和任务型知识蒸馏,具体过程如下图3所示。在通用型知识蒸馏阶段,使用规模更大的预训练BERT模型作为教师模型,对学生模型在无监督预训练语料上进行通用知识蒸馏,得到通用轻量模型,该模型可用于初始化任务型知识蒸馏里的学生模型或直接对下游任务进行微调。在任务型知识蒸馏阶段,使用在有监督业务语料上微调的BERT模型作为教师模型,对学生模型在业务语料上进行领域知识蒸馏,得到最终的任务轻量模型,用于下游任务。实验证明,这两个阶段对于模型最终效果的提升都至关重要。
在美团搜索广告场景下,首先我们基于MT-BERT-Large(24层1024维)在大规模无监督广告语料上进行第一阶段通用型知识蒸馏,得到MT-BERT-Medium(6层384维)通用轻量模型,在下游的广告相关性任务上进行微调。MT-BERT-Medium属于单塔交互结构,如图4(a)所示。
目前,每个Query请求会召回上百个POI候选,交互模型需要分别对上百个Query-POI对进行实时推理,复杂度较高,很难满足上线条件。常见解决方案是将交互模型改造成如图4(b)所示的双塔结构,即分别对Query和POI编码后计算相似度。由于大量候选POI编码可以离线完成,线上只需对Query短文本实时编码,使用双塔结构后模型效率大幅提升。我们使用通用型蒸馏得到的MT-BERT-Medium模型对双塔模型中Query和POI的编码网络进行初始化,并且在双塔在微调阶段始终共享参数,因此本文将双塔模型记为Siamese-MT-BERT-Medium(每个塔为6层384维)。双塔结构虽然带来效率的提升,但由于Query和POI的编码完全独立,缺少上下文交互,模型效果会有很大损失,如表2所示,Siamese-MT-BERT-Medium双塔模型相比MT-BERT-Medium交互模型在相关性Benchmark上各项指标都明显下降。
为了充分结合交互结构效果好和双塔结构效率高的优势,Facebook Poly-encoder[15]、斯坦福大学ColBERT[16]等工作在双塔结构的基础上引入不同复杂程度的后交互层(Late Interaction Layer)以提升模型效果,如图4©所示。后交互网络能提升双塔模型效果,但也引入了更多的计算量,在高QPS场景仍然很难满足上线要求。针对上述问题,在第二阶段任务型知识蒸馏过程中,我们提出了虚拟交互机制(Virtual InteRacTion mechanism, VIRT),如图4(d)所示,通过在双塔结构中引入虚拟交互信息,将交互模型中的知识迁移到双塔模型中,从而在保持双塔模型性能的同时提升模型效果。
任务型知识蒸馏及虚拟交互的具体过程如上图5所示。在任务型知识蒸馏阶段,我们首先基于MT-BERT-Large交互模型在业务语料上进行微调得到教师模型。由于学生模型Siamese-MT-BERT-Medium缺乏上下文交互,如图5(b)所示,注意力矩阵中的灰色部分代表了2块缺失的交互信息,我们通过虚拟交互机制对缺失部分进行模拟,计算公式如下为:
我们对蒸馏阶段各个模型进行了Benchmark上的效果评估以及线上QPS=50时的性能测试,结果如表2所示。通过虚拟交互进行任务型知识蒸馏得到的任务轻量模型Siamese-MT-BERT-Medium相较于直接对通用轻量模型进行微调得到的同结构的Siamese-MT-BERT-Medium(W/O任务型知识蒸馏)模型,各项效果指标明显提升,其中Accuracy提升1.18PP,AUC提升1.66PP,F1-Score提升1.54PP。最终我们对任务轻量模型Siamese-MT-BERT-Medium进行上线,相较于最初的MT-BERT-Large模型,线上推理速度提升56倍,完全满足线上服务的性能要求。
Model | 模型规模 / 模型结构 | Accuracy | AUC | F1-Score | 参数量 | 耗时 |
---|---|---|---|---|---|---|
MT-BERT-Large | 24层1024维 / 交互 | 77.87% | 85.06% | 79.14% | 340M | 227.5ms |
通用轻量模型:MT-BERT-Medium | 6层384维 / 交互 | 77.62% | 84.79% | 78.63% | 21M | 16.8ms |
Siamese-MT-BERT-Medium(w/o 任务型知识蒸馏) | 6层384维 / 双塔 | 74.23% | 81.65% | 75.37% | 21M | 4.0ms |
任务轻量模型: Siamese-MT-BERT-Medium | 6层384维 / 双塔 | 75.41% | 83.31% | 76.91% | 21M | 4.0ms |
表2 模型效果对比
为了更好地衡量广告召回结果的相关程度,除了基于模型得出的语义相关性之外,我们还计算了文本相关性、类目相关性等分数,并对所有分数进行融合得到最终的相关性分数。其中,文本相关性的计算借鉴了搜索引擎场景的检索相关性方法,例如Query和POI的字符串包含关系、短语匹配数和匹配率、以及BM25分等。
另外,文本匹配同时考虑了原串匹配、核心词匹配及同义词匹配等多维度指标;类目相关性主要基于Query意图分类和商户类目信息进行匹配。分数融合模型可以选择LR或者GBDT等复杂度比较低的模型,并基于高质量标注数据集训练得到。
通过模型结构和分数融合策略的迭代优化可以得到更加准确合理的相关性分数,但是在实际的相关性应用中,还需要紧密结合广告业务场景,综合考虑平台变现效率、用户体验、广告主供给及转化等多方面因素。基于“减少低质量的不相关广告”和“相关广告排序应该尽量靠前”两个基本要求,我们设计了几种相关性分数的具体应用方式:
为了进一步提升服务性能并且能有效利用计算资源,模型部署阶段我们采用高频流量缓存、长尾流量实时计算的方案。对高频Query-POI对进行离线相关性计算并写入缓存,每日对新增或商品信息变化的Query-POI对进行增量计算并更新缓存,线上相关性服务优先取缓存数据,如果失效则基于蒸馏后的任务轻量模型进行实时计算。对于输入相关性服务的Query-POI对,缓存数据的覆盖率达到90%以上,有效缓解了在线计算的性能压力。
线上实时计算的任务轻量模型使用TF-Serving进行部署,TF-Serving预测引擎支持使用美团机器学习平台的模型优化工具—ART框架(基于Faster-Transformer改进)进行加速,在将模型转为FP16精度后,最终加速比可达到5.5,数值平均误差仅为5e-4,在保证精度的同时极大地提高了模型预测的效率。
为了更加直接客观地反映线上广告相关性情况,我们建立了美团场景下的搜索广告相关性标准和评估体系,对搜索关键词和广告结果进行相关、一般和不相关的分档标注,采用排序前五位广告的Badcase率(即Badcase@5)作为搜索广告的相关性评估核心指标。
除此之外,由于CTR能够通过用户行为间接反映广告的相关程度,并且便于在线上进行AB实验评估,而NDCG可以反映相关性分数用于广告列表排序的准确性,所以我们选取CTR和NDCG作为间接指标来辅助验证相关性模型迭代的有效性。我们对本文的优化进行了线上小流量实验,结果显示,实验组CTR提升1.0%,覆盖率降低1.0%,变现效率基本没有损失。并且经过人工评测,Badcase@5降低2.2PP,NDCG提升2.0PP,说明优化后的相关性模型能够对召回广告列表进行更加准确的校验,有效提升了广告相关性,从而给用户带来更好的搜索体验。
下面列举了两个Badcase解决示例,图8(a)和8(b)分别包含了搜索“登记照”和“头皮SPA”时的基线返回结果(左侧截图)和实验组返回结果(右侧截图),截图第一位是广告结果。在这两个示例中,实验组相关性模型将不相关结果“麻朵新生儿摄影”和“莲琪科技美肤抗衰中心”检测出来并过滤掉,让相关广告得以首位展示曝光。
本文介绍了预训练技术在美团到店搜索广告相关性上的应用,主要包括样本数据增强、模型结构优化、模型轻量化及线上部署等优化方案。在数据增强方面,为了基于曝光点击数据构造出适合美团广告场景下相关性任务的训练数据,我们构造了多种类型负样本,在采样时考虑正样本置信度、关键词频率平滑、正负样本均衡等因素,另外也对POI和团单商品文本进行关键词抽取得到更加简短有效的文本特征。
在模型结构优化方面,我们尝试了对不同业务场景做多任务学习,以及在BERT输入中引入品类文本片段这两种方案使模型更好地拟合美团搜索广告业务数据,并利用规模更大的预训练模型进一步提升了模型的表达能力。
在实践应用中,为了同时满足模型效果和线上性能要求,我们对中高频流量进行离线打分和缓存,并且利用MT-BERT-Large蒸馏得到的双塔模型进行线上实时预测以覆盖长尾流量。最终,在保证广告平台收入的前提下,有效降低了搜索广告Badcase率,提升了用户在平台的搜索体验。
目前,广告相关性打分主要应用于阈值门槛,目的是端到端的过滤掉不相关广告,从而快速降低广告Badcase。在此基础上,我们期望相关性模型继续提升区分相关和一般相关广告的能力,从而在重排序中作为排序因子更好的平衡变现效率和用户体验指标,更准确的度量用户体验损失和变现效率提升的兑换关系。此外,在本地搜索类场景下,局部供给经常比较匮乏,实际召回效果对比全局供给的情况更依赖相关性打分的能力,所以我们依然需要在相关性模型上持续深入迭代,并支撑广告召回模型和策略的进一步优化。
在具体技术方向上,相关性门槛阈值设置、广告长文本表达和业务知识融合等方面依然存在优化和提升空间: