机器不学习:蘑菇街推荐算法架构实战
机器不学习-专注机器学习,深度学习,自然语言处理,大数据,个性化推荐,搜索算法,知识图谱
推荐一直是电商平台的重要流量入口。以往在电商平台上,推荐的场景更多的覆盖在交易的各个环节,比如详情页、购物车、订单及支付等。近年来推荐发展逐渐的多样化,场景上逐渐覆盖到各流量入口,推荐的实体也扩展到活动、类目、运营位等。
蘑菇街作为一家社会化导购电商平台,近1年推荐业务发展也非常快。早期我们更多进行商品的推荐促进成交,在16年321和双11大促活动中引入了个性化猜你喜欢,带来非常大的效果提升,接下去推荐作为一种常规的资源位效率提升手段,渗透到更多的场景中。包括导购类目、专辑甚至搜索提示词都是通过推荐系统来支持。目前接入的推荐场景已经有一百多个。
本文将介绍蘑菇街的推荐系统工程实现,主要介绍在线推荐服务、埋点及效果统计。
推荐流程
完成一次个性化推荐,一般可以分成召回、排序两个步骤。
召回:圈出候选集,一般召回策略会有很多种,比如召回用户实时点击过的相似商品。
排序:即点击率预估,从候选集中进行在线排序。
以上是一个简单的推荐猜你喜欢实现。首先算法离线训练好与该商品的相似商品集,当在线用户请求过来的时候,有一个用户实时点击过的相似商品召回策略,该策略先获取用户实时点击过的商品列表,再取每个商品的相似商品。从而得到一个候选集。再通过在线排序进行点击率预估。取其中top商品从而得到一个推荐结果集。
实际的场景中,我们将在离线训练、召回策略、排序策略等环节不断优化。
召回策略中,除了支持实时个性化外,还可以不断发掘新的策略,进行策略ee,引入强化学习进行意图识别等。在线排序中支持lr、gbdt等模型外,还需要平衡性能与特征维度,支持模型的在线学习等。
系统架构
整个推荐系统可以分为在线服务层、近实时计算层、离线计算层3大块。
-
在线服务:包含abtest实验、结果集召回、点击率预估、字段补全、埋点几部分。
-
近实时计算:根据用户实时行为,提取用户实时特征、在线模型训练。
-
离线计算:根据用户历史行为,进行相关性训练、商品初排分、离线特征提取等。
在线推荐服务
我们将系统分成推荐投放系统prism、精排系统kepler、推荐引擎、用户特征服务、abtest、字段补全服务。
-
prism:推荐统一接口,负责召回规则、abtest、埋点、字段补全。
-
kepler:点击率预估。各打散置顶等业务层排序。
-
用户特征:离线和实时的用户特征存储。
-
推荐引擎:离线算法训练的结果集存储。
-
字段补全:商品等正排信息补全。比如价格、标题等。
推荐投放prism
作为通用化的推荐平台,接入100+个推荐场景,可以分成20+类推荐规则。并且推荐的实体也包含商品、店铺、社会化内容、类目词等等。我们希望提供一个推荐平台,让算法工程师自助实现推荐需求。
投放框架层的功能如下:
1. 提供统一的推荐接口。
2. 各个场景的召回策略规则。可热部署。
3. 提供投放sdk,提供通用数据源接口、工具类。方便算法推荐规则编写。
4. 提供测试框架
5. 算法实验以及埋点统计
6. 推荐工作台
推荐投放整体架构图
主要包含投放框架、推荐策略(脚本)、投放sdk、测试框架、工作台几部分。以下分别介绍。
推荐实体关系
投放框架的实体模型分为场景、实验、脚本、配置。脚本里面承载了具体的推荐逻辑,为了脚本复用,增加了对脚本的配置。
推荐策略
推荐策略是整个推荐逻辑的核心,比如猜你喜欢场景,有用户实时特征做相关性的召回策略,也有用户离线特征的相关性召回策略。并且按照一定的配比merge出结果集,再调用精排系统的lr模型做点击率预估。这些都是需要写在策略脚本里。
我们为每个算法团队分配这样一个策略脚本工程。脚本本质是一个java类,算法迭代时提交代码到git,通过推荐工作台可视化界面,来完成热部署功能。
脚本层面再封装一层召回策略池,在候选集触发层进行策略ee。
投放sdk
sdk是为了降低编写策略的成本,封装了底层数据源调用,比如用户信息、离线推荐结果集等,并且提供了算法的常用实现工具类。
数据源接口通过spi的方式,还提供了本地调试的实现。开发机在通过测试框架调用数据源接口时,会被代理到访问远程服务器的接口获取数据。方便开发本机调试程序。
测试框架和稳定性
测试框架是解决算法本地测试存在,将问题提前暴露。主要的功能有:
1. 配置模拟,还原线上的各种配置场景。
2. 数据源接口,如上所述。
3. 线上用户请求采样,可自定义请求回放后的数据分析逻辑。
4. 对当前脚本的性能评估。通过线上采样请求来做为请求数据源。
此外在脚本越来越多以后,防止出现一个脚本性能问题,我们做了容量规划。每日使用线上采样请求,凌晨对系统进行压测。观察系统容量。
推荐存储
主要承载了用户特征以及离线推荐结果集。存储系统对读写性能要求非常高。
1. 整条推荐链路我们希望在50ms内完成。一次复杂的推荐请求会请求上百次(以存储中的key为单位)存储数据。存储需要在1ms内返回。
2. 对时延要求比较高,比如我们需要收集用户的曝光行为数据做降权,同时曝光数据的量非常大,对内存有挑战。
存储方案
早期我们使用redis来存储,后来自研了蘑菇街的存储引擎,采用mmap方式,定制了一套List<Map>格式的存储数据结构。
存储采用mmap方式,天然解决了很多问题:
1. mmap的方式,拥有内存的读写性能。
2. 操作系统保证落盘,避免重启时数据丢失。当然出现宕机情况下,系统能够从hdfs以及消息中间件中恢复数据。
3. 堆外内存,在大量写数据场景时不会影响gc。造成性能抖动。
采用自写存储的方式,同时也为我们提供更多扩展性:
1. 召回+过滤逻辑,算法离线推荐训练一般是天级别训练一次,我们实时过滤一些下架等状态不符合的商品。算法训练的数据也可以减少,比如只需要训练一份item to item的相关性结果集,如果女装类目需要使用可以在线从结果集里面做类目过滤。
系统实现上我们会存算法训练结果的索引表和商品正排表,并且支持一些基础筛选语句。
2. 性能提升,针对一个推荐场景查询上百次存储,我们提供批量查询接口,并且在存储服务端提供并行方式,单个查询速度非常快,由单独一个线程执行很快会结束,上下文切换频繁导致并行性能并不好。我们做了一个策略优化,将线程分成n组,上百个查询会均匀落到其中一个,每个分组同步执行。
3. 自定义插件,将常用的召回逻辑抽象成插件,比如查询用户实时曝光过但未点击的商品,在某些业务场景做降权。插件逻辑里获取用户的曝光商品列表和点击商品列表做剔除。方便业务开发并且统一逻辑。同时也利用了存储系统的计算资源(存储是io密集型)。
存储的瓶颈在于内存,我们目前按照垂直业务划分部署,单个业务(比如item 2 item结果集)目前还未能超过单机内存限制。还未做分片方案。
存储系统结构
大致分成接口、脚本、索引、正排几个部分。
-
接口支持分实例部署,每个实例可以部署多台机器。每台机器状态对等。
-
脚本层将自定义一些插件,抽象常用的逻辑。
-
索引层即推荐算法训练结果集。算法spark离线训练放到hdfs中,触发存储去取,支持全量和行更新。针对用户实时特征场景,也支持从消息队列corgi中增量更新。
-
正排存放商品的基础信息,用于做筛选过滤,来源于我们搜索dump平台的全量(hdfs)和增量消息。
精排系统kepler
精排系统的职责是对候选集进行排序,其中核心点在于模型和特征。理想情况下系统尽可能支持多的模型和特征,但是在线计算需要较小的时延,这就要求我们要平衡效果和性能。
模型一般有线性和线性,目前我们支持LR和GBDT。模型一般离线更新,针对双11大促等场景我们也会使用在线学习实时更新。
线性模型公式:
x是特征,θ是权重。一个模型通常有几十维特征,这些特征的计算和存储就成为系统最大的挑战。以下是我们的几点应对方式:
1. 控制候选集数量在千级别。候选集一大整体计算就比较慢,rt也会上升。
2. 实体(商品)特征本地存储,每次需要排序1000个商品,本地存储可以极大缓解网络压力。同样我们内嵌了推荐引擎的存储模块,拥有内存的速度,又解决持久化的问题。
3. 针对内存瓶颈,我们将用户相关特征迁移到远程,考虑每次查询只会查几次用户特征数据,开销不大。
4. 并行计算,复杂模型下,组装特征和计算还是比较费时,为了提升rt我们进行并行计算,充分利用cpu的资源。在系统容量不变的情况下提升rt。
系统架构图
整个精排服务同时为搜索排序(topn)、推荐(prism)提供排序服务。输入商品列表,输出排序后的商品列表。整个链路包含当前模型参数获取、特征数据准备、特征预处理及打分、预测、业务层排序、业务打散等环节。
-
keplerService是接口层,服务会按照业务分实例部署。每个实例的内存状态也不同。应对一块业务,比如推荐、搜索。接口层除了需要传需要排序的商品列表,还需要传模型code。
-
模型配置获取,排序后台会配置好每个模型的算法、特征、权重等信息。同步到配置服务器(metabase)下发到kepler系统。
-
特征数据准备,针对当前算法预先加载特征数据,特征数据较多,统一获取特征可以批量操作节约性能。特征部分存在本地,用户数据需要访问远程服务。
-
rerank包括特征预处理、打分、预测。这部分可以多线程并行执行。减少时延。
-
业务排序,只要执行业务置顶、加权等逻辑,
-
业务打散,针对一个结果集可能太同质,会进行类目、店铺等打散。
数据流
推荐算法固然非常重要,但是缺少稳定可靠的数据流,算法的效果追踪就没有说服力。早期蘑菇街的埋点耦合到各个业务层,并且严重依赖url,一方面维护工作量很大,另一方面系统重构,产品迭代整个打点经常发生丢失,我们在15年重建了abtest和数据流体系,经历1年左右时间已经彻底解决顽疾,并且为算法业务提供了很大的扩展性。
我们推行一套打点规范,命名为acm。acm中包含了我们推荐的位置信息、实验信息、算法自定义埋点信息等。每个推荐商品都会有自己的acm标识。推荐接口端统一生成并且和终端约定好统一的埋点格式。
acm让我们彻底抛弃以往对url的依赖,同时自定义信息能够帮助算法实现各种分析需求,比如分析各召回策略的曝光、点击、交易占比。也为在线特征分析以及强化学习提供了数据源。
以搜索排序场景举例:
在一个常规的算法实验过程中,流程如下:
1. 算法在abtest控制台进行实验切分。
2. 实验信息会通过zk推送给推荐投放系统。
3. 投放端进行实验分流,执行召回排序逻辑,为每个商品进行埋点,透出结果集。
4. 终端统一埋点,发送给日志收集服务器。
5. acm采集系统将收集到acm日志流,执行清洗反作弊等逻辑。输出实时消息流,并且定时保存到离线hive表中。
6. acm通用聚合系统将多维度聚合资源位、实验等维度的统计信息,持久化到es、db。
7. 可视化组件可以自定义从db中拿到多维度数据,进行实时、离线数据的监控分析。
其他
在线推荐还有一个字段补全服务,采用redis存储,存储的数据用protobuf进行序列化。
总结
系统历时1年半多的发展,基本实现我们平台化的目标。算法同学可以专注在算法效果提升,工程同学可以专注框架升级。
最早我们只有一个推荐引擎做离线推荐,随着业务接入越来越多,场景定制的召回策略凸显出来,我们开始搭建了推荐投放系统。算法为了提升效果,实时个性化就是基本需求,于是我们搭建了用户特征服务。紧接着就是精排系统进行点击率预估。基本上主流算法功能都能够实现。
数据流系统是我们一开始就规划的,我们在做推荐的同时也负责搜索服务,一直吃数据质量问题的苦头。一开始我们想解决abtest跟效果追踪的问题,随着项目进行,我们发现顺带也解决了算法策略数据分析的问题,打下了很好的基础。
在16年中,蘑菇街和美丽说技术体系合并,并且蘑菇街推荐在16年321大促上表现优异,推荐场景发展迅速。系统一下子接入了很多业务方,支持的算法也包含了北京的团队。此时我们就考虑进行平台化,将算法操作自助化,不依赖工程的日常发布。之后,我们将算法脚本发布的权利交于算法时,考虑到系统稳定性,我们开始做了一系列的保障工作。
关于后续,我们第一优先的还是优化细节,降低使用成本,提升系统效率。实实在在的让各方受益。其次会在算法的效果上进行尝试,比如召回策略上引入强化学习,排序特征扩维度等。当然存储是最大的挑战,当前单机部署能够满足现有业务,我们也进行按照业务算法做分组部署,在业务扩大10倍的情况下,单个业务算法结果可能就会突破单机限制,存储架构就需要升级,支持分片。
原文:https://zhuanlan.zhihu.com/p/28009200
更多精彩内容,机器不学习官方网站()