导语 | 本文分享了微信游戏推荐系统从调研、设计、搭建到运维的整个流程。这套系统在微信游戏业务上得到广泛应用,服务着几亿微信游戏玩家;它也服务腾讯知名app类游戏分发、游戏相关内容推荐和几万款小游戏分发,并且取得不错的业务效果。如果你对相关内容感兴趣,欢迎阅读和分享。
目录
1 项目背景
2 离线机器学习平台设计
2.1 底层基础库
2.2 算法库设计
2.3 深度学习流程设计
2.4 页面配置化设计方案
3 平台能力拓展
4 推荐引擎设计
5 推荐系统实时化方案
6 挑战与思考
微信游戏在微信场景下连接游戏玩家与游戏,给玩家提供丰富的游戏服务如攻略、战绩、视频、直播、王者周报、和平周报、群排行榜和礼包等。另外,微信游戏还为玩家提供建联渠道——玩家可以在游戏中心、游戏圈里面找到有共同兴趣、志同道合的小伙伴,实现「一起玩更精彩」。整体来说这个业务的核心工作就是「在微信场景下,帮助游戏玩家找到感兴趣的好游戏,并且让玩家们游戏玩得更好、更开心」。从上图列举的推荐场景可以看出,微信游戏的推荐系统也是围绕这个方向来落地应用的。在「帮助玩家找到好游戏」方面,微信游戏有精品 app 游戏分发、小游戏分发场景以及优化用户通过搜索找到目标游戏的场景;「游戏服务」方面,微信游戏有视频流、图文流、直播流等内容推荐场景;在「玩家建联」方面有玩家关注推荐、游戏 KOL 推荐、玩家广场。在上述各种类型的推荐场景之下,每个场景又有各种业务目标。比如精品 app 游戏除了下载启动,还有新游上线前预约推广需求。它需要找到对游戏 IP 感兴趣的潜在用户,提升预约率。而小游戏除了启动注册目标之外,还会有定向分享这种要建立用户对好友分享概率模型和分享好友对游戏兴趣双层目标场景;同时小游戏还有自己的商业化目标,即需要预估商业价值和以流量 ROI 为优化目标的场景。而游戏内容上,除了点击转换这个目标之外,还有视频完成度、下翻率、播放时长、人均 NV 等目标。事实上,上面列举的场景只是我们众多推荐场景中的一部分。从我们推荐系统管理端来看,已经建立 100+ 场景模型(一个taskgroup对应一个场景接口)。但现实矛盾点是我们只有四个推荐算法开发工作者来对接业务侧同事,负责后台的也只有一人。针对这个矛盾点,团队与后台同事讨论了理想的推荐场景开发流程,希望把开发过程尽可能配置化、低代码化。如上图右边部分所示,算法同事只需要开发场景相关的样本和特征,并将特征表和样本表配置到推荐管理平台。在管理平台上选择合适的算法并配置参数,提交一个离线训练任务,完成后将模型文件和推荐池子相关 hdfs 路径配置到管理平台分发到现网机器上。然后在实验系统 xlab(注:xlab实验系统是微信内部大部分业务做 abtest 的通用实验平台)上配置实验,将实验参数配置到统一推荐管理平台上对应的场景接口(taskgroup)参数中,打通场景接口和实验系统流量分配逻辑。最后把场景接口对应的 taskgroup 名字给到业务后台同事。业务后台同事根据这个名字调用统一的推荐接口,获取推荐打分接口,完成一个场景的推荐开发。为了达到上述的效果,我们设计了推荐系统的整体架构。主要包括四个部分,离线机器学习平台、统一推荐管理平台、在线推荐引擎以及周边系统。模块的核心功能负责离线数据处理。它包括特征、画像开发、场景样本开发、特征工程等相关工作;也负责离线模型训练、离线与线上数据交互、批量打分等等。推荐管理平台是推荐算法开发同事日常工作中使用最多的模块。配置算法、开发特征、配置模型、数据分发、定义推荐系统执行DAG图、接口debug、线上推荐问题排查等等,可以说推荐系统涉及到的工作都在这个平台上完成。在线推荐系统负责提供实时接口服务,包括三个核心部分:用户特征模块、推荐执行引擎、共享内存模块。 | |
| 用户画像和用户行为数据起初使用 strkv 来存储。根据数据更新周期分为实时特征、小时级特征、天级特征、月级特征。但是月级和天级用户量非常大、上线需要十几个小时,所以统一切到了 featurekv。featurekv 的写速度要远快于 strkv,但当时还不支持线上实时写入,所以实时特征还是用 strkv(注:strkv 和 featurekv 都是微信内部 key-value 存储通用组件,featurekv 相对于 strkv 来说写效率要高非常多,所以适合于10亿级 key 批量更新)。 |
| 它负责执行线上推荐逻辑调用,它包括接收业务请求,拉取特征,拼接预测样本,调用算法模块,完成推荐打分;其中核心模块是 controller,它负责解析和执行算法同事定义的 DAG 执行图(我们把推荐的执行逻辑定义为一个 DAG 图);算法模块负责具体算法实现,每个算法实现是 controller 可调度执行的最小单元(除了机器学习算法实现之外,我们把一些常用的业务逻辑如混排、过滤逻辑等也封装成这样的执行单元,也就是后文会提到的 stage )。 |
| 线上预测的时候,同一个接口不同用户请求推荐系统都会用到同一份item特征、模型文件、特征编码文件、推荐池子。这种类型数据常驻内存可以减少单次请求耗时,所以后台同事开发了管理这些数据的共享内存模块,结合架构部文件分发工具,算法同事可以指定文件 hdfs 路径,文件分发工具将文件分发到所有线上机器。共享内存模块检测到数据版本更新,则将最新数据加载内存,完成线上数据更新。 |
除了上述推荐系统运行相关的模块之外,还需要实验系统支持算法同事迭代场景模型和实时监控系统,使之可以观测到算法上线之后的效果和实现线上问题的快速定位。首先是实验系统。我们使用 xlab 实验系统,在推荐系统 controller 模块部署实验系统代理。算法同事在 xlab 上配置完实验之后,将实验系统参数配置到推荐接口中,就可以打通实验系统流量分配和推荐系统。实时监控系统:实时数据分析方面,我们搭建了 Druid 实时数据分析系统。它支持实时数据监控和效果可视化,整体流程在后文会展开讲述。为了实现上述推荐系统整体架构,我们前期做了大量的调研,邀请了公司内外团队过来分享,详细对比了各个解决方案、推荐系统各模块技术选型以及公共组件,下面是我们构建整套系统技术选型和对应的组件:机器学习方面,传统机器学习我们基于 spark 搭建模型训练 pipeline,而深度学习使用 tensorflow,增量更新模型使用 flink 来实现。涉及向量检索召回服务方面,我们搭建了 faiss 服务,实时数据分析则选择了 druid 。配置和存储方面,我们使用 pg 库存储离线数据处理相关的所有配置,使用mysql存储与线上相关所有配置,而 hdfs 是我们数据中转站。推荐引擎方面,我们基于微信内部的 svrkit 框架(svrkit框架是微信后台微服务框架)搭建的一套 C++ 推荐服务。实际搭建过程中,我们发现很多功能模块在公司各业务有非常好的实现,所以本团队进行借鉴,比如调度方面直接使用 telsa,实时计算方面直接用 Oceanus 工具,深度学习可以直接使用 spark-fuel 和 yard;存储方面,架构侧同事提供了文件分发工具、 featurekv、strkv 等。深度学习线上应用上,我们直接使用架构侧同事封装的 HIE(hybrid_inference_engine)。除了工具调研之外,业务团队还邀请了当时在推荐系统方向表现突出的团队进行分享交流,如看一看、微博团队。事实上,汲取先进经验,帮助我们搭建推荐系统少走了很多弯路。接下来会详细介绍各个模块实现细节。上面介绍整体架构,提到离线平台核心工作包括数据 ETL、特征开发、样本开发、特征工程、模型训练和预测、线上和离线数据打通等。其中特征工程、模型训练和预测和线上线下数据打通,是任何一个场景建模都是需要做的工作。离线平台通过工具化和流程化实现通用组件,避免重复工作。而数据 ETL、特征开发、样本开发与业务场景强绑定,很难通过自动化方式实现,需要算法开发者结合业务目标定制化实现。离线平台提供基础工具,帮助场景建模开发者同事提高开发效率。下图是离线平台实现逻辑:从下往上分别包括底层数据源接口、数据挖掘基础工具库。这两部分主要帮助算法同事提升日常数据开发效率。往上是场景建模过程中核心三个组件——算法库、特征工程、业务样本模块。算法库包含两个部分,一个部分是基于 spark 实现,另一部分是基于 tensorflow 实现。这里没有使用 telsa 训练组件,而是我们自己做了二次开发。主要原因是离线算法需要和线上引擎数据协议进行打通。我们需要将训练好的模型和相关文件封装到一个 pb 文件中,直接部署到现网机器可以拉到的地方,不需要每个算法同事单独实现数据部署逻辑。另外我们希望模型训练可以跟数据处理逻辑无缝衔接,减少数据中转;特征工程主要包括特征开发、特征处理、特征评估等涉及到特征相关的操作。它们都封装到单独模块中,方便算法同事对特征进行单独分析和测试。涉及到样本处理工作我们放在样本模块中完成。包括样本采样逻辑,不同目标样本处理,还有一些小流量场景需要多周期样本处理等等。再往上实现了基于 DAG 图的机器学习 pipeline,目的是将场景整个建模过程通过有向无环图的方式管理起来。数据读取逻辑被定义为节点,数据操作逻辑被定义为边,节点只关心从物理存储中拉取数据。边主要是数据处理逻辑定义而不关心数据来源,这样数据读取和数据操作进行节藕。我们根据定义的边和节点,可以组合出不同的DAG如模型训练、模型预测、号码包生成、外部接口等等,以供上面应用层调用。最上面是我们推荐系统管理端。我们希望与模型相关的工作都可以通过「配置化」方式进行。下面详细介绍核心模块实现细节: 2.1 底层基础库
基础库提供了开发过程中常用基础工具,目标是帮助开发同事提升日常数据处理的效率。这主要包括tdw(注:tdw是腾讯内部用的大数据平台)操作接口封装和通用工具封装,如下图所示:tdw 提供原始数据接口,在实际业务开发过程中并没有那么友好。举个例子,spark 读取 tdw 表数据需要先配置 spark 程序入口环境、初始化 tdw tool 工具、处理表分区,然后再调用表读取 api 进行读取。其中前面步骤都是通用重复的工作,所以在平台提供的 api 基础上,进行二次封装。在开发过程中,开发同事不需要关心环境变量和参数配置,直接初始化封装的类,然后使用简易接口进行表操作和表处理,提升工作效率。通用工具,其实是把日常工作中都会用到的能力进行封装。比如,日期处理、日志处理、编码解码以及类型转换等。另外与业务侧相关的工具我们也做了收拢,比如游戏ID间相互转化、游戏品类ID和名称相互转化等等。这些业务上通用的能力我们也集成到基础库中,不需要每个同事重复实现。 2.2 算法库设计
算法库设计的核心是扩展性。一方面,可以快速便捷扩展新的算法进来。实际业务应用中不需要花里胡哨、很难落地应用的算法,需要的是那些能解决业务问题、可以快速与线上打通并验证效果的算法。另外一方面算法库可以便捷集成外部团队能力,将外部专业团队成熟的能力快速应用到业务中,解决业务问题。出于上述考虑,我们设计了下面架构:算法基类定义了算法基本操作,并将算法通用操作提前实现。比如模型参数初始化、模型部署操作等,其他算法直接继承即可。然后分别定义了分类算法、聚类算法、相似度算法、深度学习类算法还有 NLP 相关的算法抽象操作。最后是具体算法实例的实现。分类算法实现了常用的LR 、FM、RF、xgb 等,聚类则有 kmean。相似度算法实现了三种类型相似——基于内容、基于用户行为序列、基于关系链。这三种相似算法都是将 item 或者 user 进行向量化,然后通过向量内积计算 item 或者 user 之间的相似度。在游戏中心视频相关推荐场景实验数据来看,基于内容向量化效果稳定性最好,推荐出来的内容基本符合认知。举个例子,鲁班七号的视频推荐都是与鲁班或者射手相关的内容。深度学习方面,核心是打通数据流程,训练和线上预测都依托 tensorflow 和 tf-serving 通用框架,本文接下来也会详细介绍整体流程。NLP 相关算法,主要是基于 hanNLP 来实现了包括常用的分词、实体识别、文本分类等功能。最后通过工厂方法实现对应算法实例路由。 2.3 深度学习流程设计
近年来游戏中心很多场景对混排和多目标优化能力诉求越来越强,包括内容 feeds 流、游戏 tab feeds 流里面精品游戏、小游戏等不同类型不同目标的混排。最开始我们采用的方案是增加规则混排逻辑,比如按比例混排,给定概率混排,根据模型打分分数运算混排等。后来还采用双层排序逻辑:先预估用户对不同类型的偏好分布,再根据这个偏好分布从不同类型的已排序好的列表中选择对应的 item,生成最终的排序列表。这些方式可以满足业务需求但指标没有显著提升。核心原因是层与层之间是割裂的,没有作为一个整体去优化。而业界成熟的做法是把不同层通过网络结构结合在一起。为此,我们在当前能力的基础上,开发了一套流程来支持 nn 能力。如上图所示,深度学习训练流程是在原有的机器学习平台的基础上,通过集成外部能力来实现的。根据不同的业务需求,我们实现了两条不同的训练路径,一个是在tesla平台(tesla也称太极机器学习平台,是腾讯内部的调度系统)上,基于spark-fuel来实现,应用于线上业务;另外一个是基于数据中心笛卡尔系统+yard来实现,应用于需要推荐、图像、文本、关系链挖掘等能力的业务。- 在算法库中增加一个算法模版,这个模版的工作是把业务训练数据生成下层对接系统可以处理的格式(当前推荐业务使用 libsvm 格式),并将生成好的数据持久化到hdfs上;
- 然后基于 tensorflow 实现网络结构、数据读取模块、以及模型持久化模块;
- 最后把模型推送到线上,而线上 tensorflow-serving 是同事基于架构侧 HIE 实现的。
需要注意的是在线上使用的时候,要保证特征编码文件与模型文件保持原子更新。有了这套训练和线上预测框架之后,算法同事可以发挥的空间就更大了。除了推荐算法上落地应用外,我们游戏直播里面的英雄识别等涉及图像、文本相关的服务也都应用这一套能力。 2.4 页面配置化设计方案
下图是我们现网统一推荐管理端页面,包含两个大模块:推荐系统和数据资源管理。包括我们特征管理、离线模型训练配置、线上推荐接口执行流(json 表征的 DAG 图)配置、还有就是推荐接口调试和问题排查的 debug 工具。主要涉及到离线数据和线上数据打通,包括用户特征导入 kv 和 item 特征、推荐池子、模型文件等推送到现网机器的定时分发任务配置。统一推荐管理端把现网推荐开发涉及到的工作都包含进来了,使得离线数据挖掘和线上业务上线推荐模型代码最小化,大大提升了场景算法迭代效率。管理端设计上,如下图所示,我们主要拆解成离线和在线两个部分:离线部分对接的是离线计算平台和离线数据部署模块。通过配置相关的参数,完成场景数据开发、模型训练、数据上线的整个过程。算法同事在离线部分页面的配置信息,我们会存储在 tpg 库中。离线训练模型的时候,通过指定场景配置的算法 ID,就可以检索到存储在 tpg 表中算法 ID 对应的算法相关的参数信息,并将它们封装到算法上下文中(algorithmContext)。然后根据配置信息拉取数据生成训练样本,调用算法同事指定的算法训练模型、输出评估指标。最后将模型部署在对应路径下,完成离线训练。- 第一个是离线特征出库有一个 dataDeploy 模块,会根据算法配置信息上的上线状态拉取需要上线的特征,将这些特征序列化成定义好的 pb 格式,写入 hdfs 中。
- 另一个是后台同事开发的数据 upload 工具,根据算法同事配置的数据相关信息(信息存储在后台mysql中)利用 Hadoop 客户端从 hdfs 路径拉数据,定时导入到现网 kv(featurekv)或者推送到现网机器。这部分工作对于场景算法开发同事来说是透明的,他只需要完成页面配置即可。
在线部分对接的是现网数据和推荐引擎。包括现网模块配置、stage配置、taskgroup配置、task 配置、场景相关的资源配置。这些配置里面的每一条记录,都会有一个名字标识,比如线上实现的每一个可执行算法单元,会在stage配置页面上使用唯一 name 进行标识。后面应用的时候通过这个 name 就可以调用这个算法单元。现网场景数据也是一样,配置的每个数据资源也会对应唯一 name。比如,小游戏推荐池子对应一个name A,精品游戏推荐池子对应的 name B。一个场景有了资源和需要执行算法单元之后,就可以配置一个推荐接口(taskgroup)。如下图所示:一个推荐接口(taskgroup)下面可以配置多个执行逻辑( task,可以理解成是一个接口下面会有多个实验)。如下图所示。游戏中心首页直播推荐接口,配置了两个 task:每个task对应着一个 json 表征的 DAG 图,它包括:执行流程是怎样(使用哪些算法,执行逻辑是串行还是并行)、每个算法使用哪些数据(特征、池子等,给定前面配置的资源name即可)。如下图是一个 task 配置:所有这些线上配置信息会存储在现网 MySQL 中,线上执行模块可以直接通过 name 访问对应的文件。完成上面配置之后,算法同事就可以在页面对单个 task 进行 debug。满足上线标准之后(排序是否正常、耗时是否满足线上标准等),就可以把 taskgroup name 给到业务后台同事,完成上线。从上面看出,因为通过页面配置打通了从离线到线上的全流程,所以负责流程中每个环节的同事工作效率都有很大提升。离线平台能力,我们希望将其打造成部门底层通用的基础能力。不仅仅在推荐业务上使用,还能拓展到画像、数据分析、安全以及所有跟机器学习相关的业务上。事实上,围绕着这个平台,我们也做了很多能力升级和拓展。比如在小游戏买量场景上,基于平台能力我们搭建了提供给小游戏开发者的自助精准号码包挖掘能力和广告 rta 能力,帮助他们提升用户获取能力和降低用户获取成本。从上图可以看出,以前开发者买量大多数只能针对号码包进行出价和投放;但现在在小游戏开发者管理端上,通过我们这套能力在 pv 维度上进行过滤和出价。对于低质量用户过滤或者降低出价,对于高质量可以用更高价获取,提升整体买量 roi。对于平台来说,以前为了帮助小游戏开发者更好的买量,需要投入专人挖掘精准号码包。有了这套系统之后,人力开始往能力建设迁移。此处简单提一下号码包平台设计思路。上面推荐管理端配置信息我们都是用户固定表格式来存储,但是号码包平台考虑到平台页面灵活性(未来配置信息变动大)。我们方案是后台同事将配置生成 json 串,然后通过参数的方式传入给平台(这里tesla接口有bug,直接传入json字符串会丢括号,所以我们对json串进行base64编码)。开发者提交挖包任务之后,直接通过 tesla api 调度挖包任务、完成挖包操作。整个流程都是开发者自助完成。这套系统已经在稳定运行多月,有几十个小游戏开发者用这些能力。而投放 roi 上也有明显提升,符合预期。业界大部分推荐团队一般包含一个算法组和引擎工程组,算法组负责场景离线建模和数据相关的工作。引擎工程组负责线上工程实现,包括算法模块开发、线上数据流处理以及系统调度相关工作。为了保证未来推荐系统能够适应业务快速迭代,我们认为推荐引擎设计上可以与其他推荐团队有所差异。在分工上,后台同事负责整体框架的开发,同时把基础能力接口化如读取用户特征、读取模型文件等。然后单独输出一个算法模块给算法同事,实现模型线上预测打分逻辑。也就是利用这些基础接口,算法同事可以完成数据读取、数据处理到最终模型预测开发。这样分工的好处是算法同事很清楚线上链路。当业务场景出现 badcase 时,可以快速定位到问题,同时也减少了很多沟通成本。后续业务需要使用到某些算法的时候,算法同事可以利用这些基础能力完成线上开发。当然,一些很难的算法实现,我们也会请求后台同事支持。在实际应用中,这样的分工让我们可以快速响应业务需求,特别是业务逻辑相关的如加权、过滤、混排等。另外一方面,我们很难像其他推荐团队一样有专门同事单独负责推荐的单个环节如召回、粗排、精排、混排,所以在引擎架构设计需要淡化这些层级关系。因此我们定义了 action、node、stage 这些算子。其中 stage表示可以调度的最小单元、node 是由 stage 组成的并行执行的最小单元、action 是由 node 组成的串行之行的最小单元。另外我们统一这些算子的输入输出,使得这些算子可以相互嵌套。通过这样的设计,算法同事可以根据业务的需求,自由组合这些基本单元,来达到召回、粗排、精排、混排的推荐架构。而算法使用上也比固定层级的设计要更加灵活,比如 FM 算法可以作为召回、粗排层或者精排、混排层。算法同事可以根据场景特点进行自由组合搭配。整个推荐引擎架构如下图所示:当用户请求业务时,推荐引擎会根据实验系统命中情况,确定用户命中哪个策略。拿到这个策略对应的 task 名字,根据 task 名字可以在 MySQL 中获取 task 对应的 DAG 执行图,controller 通过解析并执行这个 DAG 图,得到推荐结果,返回给业务后台。下图是后台同事在实现上函数逻辑调用关系。首先看一下线上算法实现部分。从上面架构也可以看出,线上最小执行单元是 stage。它本质上是一个 svrkit RPC 接口,也就是一个算法或者策略的实现。比如,我们现在现网有 LR、FM、xgb、tagRecall、tf、加权混排、根据概率分布混排等 stage。其中 stageMeta 是算法同事在统一推荐管理端在配置 task 执行 DAG 图的时候,需要提供的。它表示这个 stage 执行过程中使用哪些数据、执行参数是怎样的。而物理维度上的数据,是通过架构部的文件分发系统从 hdfs 或 ceph 拉取,并分发到现网机器磁盘。本地资源管理模块会定期 check 资源版本,有新版更新会解析并加载到内存中,供 stage 使用。为了满足 stage 之间相互组合,我们统一了 stage 的输入和输出。输入是一个 repeated<data> ,它可以是业务请求 stage 时带入的 data,也可以是上一个 stage 或者上层并行执行的多个 stage 的结果。这里提到 data 结构可以理解为候选列表以及列表内每个 item 所带的参数,比如上层打分、来源、权重等。而当前 stage 输出也是一个 data ,可以作为结果反馈给后台,也可以作为下一层 stage 的输入。一个 stage 内部包含两个部分:- 一个是根据 stagemeta 信息和输入的候选打分列表生成模型打分数据格式,然后将这个数据喂给模型打分,生成结果数据。
算法同事涉及到的另外一个部分工作,是数据结构设计和数据处理。下图列举了我们在线上使用的核心数据结构,包括特征结构、模型结构、特征交叉结构。为了保证线上线下一致性,我们都采用 protobuf 来表征。对于特征我们采用了双层 map 结构来存储,第一层是 featureDataSet 单个 item /单个用户所拥有的特征,map 的 key 是特征名字,value 是内层 map featureDataPairs,它是该特征的取值。比如游戏偏好这个特征,内层取值可能是 rpg、moba 等。单维度特征相关信息用 featureData 存储。对于特征交叉结构,我们使用二叉树来存储交叉信息。叶子结点表示交叉子特征,内节点表示交叉算子。通过这样的设计,我们就可以进行二阶交叉、三阶交叉…..n阶交叉。离线模型使用的交叉特征信息是与模型绑定在一个 pb 结构里面进行出库的,保证模型和交叉文件原子性。类似的还有特征名字和 index 之间的映射关系,也需要与模型 pb 进行原子更新。对于图中的模型结构一部分是单个算法的定义,把训练好的模型信息封装到 pb 里面。另外,需要把刚才提到涉及到原子更新的信息在模型持久化的时候,部署在一个 pb 文件中。这样做的目的是保证线上线下数据一致。这也是为什么说离线没有直接用 tesla 或者其他平台提供训练能力,而是自己实现里面的训练逻辑的原因。19年初的时候,组内同事搭建了一套基于 Oceanus+Druid 实时监控系统,帮助业务实时查看和监控业务指标和服务性能指标。同时把协议接入、数据清洗、协议转发kafka等各个环节进行了封装。推荐系统实时化也在延展。一方面监控推荐场景的实验实时效果;另外一方面在这基础上,增加实时特征和模型增量更新的能力。整体流程如下图所示:实时流配置模块,可以根据协议 ID 对协议数据进行清洗,筛选出场景对应的数据(Oceanus每个核只能处理 1W/s行,数据太多容易阻塞,所以清洗和筛选需要前置)。然后新建一个 kafka 的 topic 并关联协议数据。后面的算法大部分开发工作都是在 Oceanus 上完成的。对于实时特征,我们定义了实时特征的通用结构。在Oceanus 上完成特征逻辑的开发后台,item特征会落到 hdfs 上,通过文件分发系统分发到现网机器上。用户特征则是再写入到 kafka,经过后台一个 svrkit 服务将特征写入现网 kv。增量学习的流程是在 Oceanus 上完成样本逻辑开发之后,再将样本数据写入到 kafka。后台有一个 svrkit 服务读取样本数据,并调用现网预测部分的数据处理模块。根据样本信息生成 libsvn 格式数据写入到 kafka,然后再回 Oceanus 上完成模型训练、评估和部署。以上便是我们搭建推荐系统的整体流程。下面介绍我们在实际业务应用过程中遇到的几个问题和挑战,以及我们的应对方案。 6.1 挑战一:数据管理
从离线建模到模型部署上线的各个环节,都涉及到离线与线上的数据交互。一开始我们是每个场景算法开发同事各自维护各个场景数据上传脚本,并通过 crontab 定时导入到线上。随着接入推荐系统的场景越来越多,这种方式很快就遇到瓶颈了:维护成本高、数据分散、数据质量很难保证、线上出现 badcase 定位周期长等等。为了解决这个问题,我们从两个方面进行着手:- 首先与线上交互的数据统一放到一起管理,使用页面配置代替脚步;
- 同时把特征相关的数据上线使用 dataDeploy 模块进行统一出库和上线。并提供数据查询工具,快速查到现网数据,让算法同事确认数据是否正常上线。
6.2 挑战二:维护场景多,运维成本高
业务场景很多且负责场景建模的就固定几人。随着接入推荐系统场景越来越多,单个人的负担也越来越重,花在 badcase 定位和异常排查时间非常多。为了缓解这个问题,我们梳理了推荐整体流程,把重点环节监控起来。同时结合实时监控,帮助我们提前感知到线上异常。当发现异常时,结合 ilog 系统快速定位到问题发生的场景和原因。我们也搭建了推荐系统的白板系统。如下图所示,通过给定的 taskgroup 和对应 task ,把线上打分流程可视化,展示出打分过程中使用的特征细节。运维成本高还有另外一个非常大的挑战——我们活动资源推送带来瞬间流量峰值。这个瞬间流量峰值可能直接把推荐系统搞挂了。自动扩容还没启动,机器资源就跑满,从而导致大量的逻辑失败。印象最深的是某年该系统第一次经受春节考验,大量红点资源推送导致大量用户涌入游戏中心、把推荐系统撑挂了。好在后台有备选 list,后台同事手动把部分流量切到默认 list,才没有影响到现网用户。当前我们的做法根据流量自动分级处理:- 第二级是部署另外一个与推荐系统并行的模块。如果推荐系统模块挂了,自动切到这个模块,执行耗资源少的兜底逻辑;
下图所示的 ID=453 的 task 就是这个接口配置的兜底逻辑。另外一方面对耗时和耗资源代码进行优化。经过这些优化之后,系统再也没有出现大面积失败的情况了。 6.3 挑战三:请求耗时问题
第三个是所有推荐系统都会面临的耗时问题。开始我们只接入某业务的精品游戏推荐场景。因为 item 个数少,没有耗时问题。但是随着小游戏分发和内容分发场景的上线,耗时随着 item 个数增加而增加。后台同事把线上打分整个流程做了耗时评估,发现数据处理占了总体耗时的70%。进一步细化分析则发现核心问题是在涉及数据结构的时候埋下了一个隐患,pb 里面 map 结构使用 string key 性能非常差,后面改成 int64。同时对特征拼接流程进行优化,优先拼接好用户特征,再拼接上 item和交叉特征,而不是单个 uin-item pair 对进行拼接。经过这些优化,耗时明显改善。另外对应多 item 和多特征的场景,通过增加粗排层,在性能和准确度上进行权衡。上面是我们推荐系统从调研、设计、搭建再到运维整个过程的全部内容,希望能给大家带来一些启发。欢迎各位开发者在评论区交流分享。