首先看一下58同城推荐系统整体架构,一共分数据层、策略层和应用层三层,基于58平台产生的各类业务数据和用户积累的丰富的行为数据,我们采用各类策略对数据进行挖掘分析,最终将结果应用于各类推荐场景。





  • 数据层:主要包括业务数据和用户行为日志数据。业务数据主要包含用户数据和帖子数据,用户数据即58平台上注册用户的基础数据,这里包括C端用户和企业用户的信息,帖子数据即用户在58平台上发布的帖子的基础属性数据。这里的帖子是指用户发布的房源、车源、职位、黄页等信息,为方便表达,后文将这些信息统称为帖子。用户行为日志数据来源于在前端和后台的埋点,例如用户在APP上的筛选、点击、收藏、打电话、微聊等各类操作日志。这些数据都存在两种存储方式,一种是批量存储在HDFS上以用作离线分析,一种是实时流向Kafka以用作实时计算。
  • 策略层:基于离线和实时数据,首先会开展各类基础数据计算,例如用户画像、帖子画像和各类数据分析,在这些基础数据之上便是推荐系统中最重要的两个环节:召回和排序。召回环节包括多种召回源的计算,例如热门召回、用户兴趣召回、关联规则、协同过滤、矩阵分解和DNN等。我们采用机器学习模型来做推荐排序,先后迭代了LR、FM、GBDT、融合模型以及DNN,基于这些基础机器学习模型,我们开展了点击率、转化率和停留时长多指标的排序。这一层的数据处理使用了多种计算工具,例如使用MapReduce和Hive做离线计算,使用Kylin做多维数据分析,使用Spark、DMLC做大规模分布式机器学习模型训练,使用theano和tensorflow做深度模型训练。


  • 数据层:主要包括业务数据和用户行为日志数据。业务数据主要包含用户数据和帖子数据,用户数据即58平台上注册用户的基础数据,这里包括C端用户和企业用户的信息,帖子数据即用户在58平台上发布的帖子的基础属性数据。这里的帖子是指用户发布的房源、车源、职位、黄页等信息,为方便表达,后文将这些信息统称为帖子。用户行为日志数据来源于在前端和后台的埋点,例如用户在APP上的筛选、点击、收藏、打电话、微聊等各类操作日志。这些数据都存在两种存储方式,一种是批量存储在HDFS上以用作离线分析,一种是实时流向Kafka以用作实时计算。
  • 策略层:基于离线和实时数据,首先会开展各类基础数据计算,例如用户画像、帖子画像和各类数据分析,在这些基础数据之上便是推荐系统中最重要的两个环节:召回和排序。召回环节包括多种召回源的计算,例如热门召回、用户兴趣召回、关联规则、协同过滤、矩阵分解和DNN等。我们采用机器学习模型来做推荐排序,先后迭代了LR、FM、GBDT、融合模型以及DNN,基于这些基础机器学习模型,我们开展了点击率、转化率和停留时长多指标的排序。这一层的数据处理使用了多种计算工具,例如使用MapReduce和Hive做离线计算,使用Kylin做多维数据分析,使用Spark、DMLC做大规模分布式机器学习模型训练,使用theano和tensorflow做深度模型训练。
  • 再往上就是应用层,我们通过对外提供rpc和http接口来实现推荐业务的接入。58同城的推荐应用大多是向用户展示一个推荐结果列表,属于topN推荐模式,这里介绍下58同城的几个重要的推荐产品:
  • 猜你喜欢:58同城最重要的推荐产品,推荐场景包括APP首页和不同品类的大类页,目标是让用户打开APP或进入大类页时可以快速找到他们想要的帖子信息,这主要根据用户的个人偏好进行推荐。
  • 详情页相关推荐:用户进入帖子详情页,会向用户推荐与当前帖子相关的帖子。该场景下用户意图较明显,会采用以当前帖子信息为主用户偏好信息为辅的方式进行推荐。
  • 搜索少无结果推荐:用户会通过品类列表页上的筛选项或搜索框进入品类列表页获取信息,若当前筛选项或搜索条件搜索出的结果较少或者没有结果,便会触发推荐逻辑进行信息推荐。此时会结合当前搜索条件的扩展以及用户偏好信息进行推荐。
  • 个性化推送(Push):在用户打开APP前,将用户感兴趣的信息推送给他们,促使用户点击,提高用户活跃度。这里包含推送通知的生成和推送落地页上帖子列表的生成两个推荐逻辑。值得一提的是推送是强制性的推荐,会对用户形成骚扰,因此如何降低用户骚扰并给用户推荐真正感兴趣的信息尤为重要。
  • Feed流推荐:我们的推荐产品在某些推荐场景下是以Feed流的形式展现的,例如APP消息中心的今日推荐场景、推送落地页场景。用户可以在这些页面中不断下拉刷新消费信息,类似时下火热的各大资讯Feed流推荐。

推荐系统是一个复杂的工程,涉及算法策略、工程架构和效果数据评估三方面的技术,后文将分别从这三方面介绍58同城推荐系统。


算法



推荐涉及了前端页面到后台算法策略间的各个流程,我们将推荐流程抽象成如下图所示的召回、排序、规则和展示四个主要环节:





  • 召回环节即使用各种算法逻辑从海量的帖子中筛选出用户感兴趣的帖子候选集合,一般集合大小是几十到上百。
  • 排序即对候选集合中的帖子进行打分排序,这里一般会使用机器学习排序模型,排序环节会生成一个排序列表。
  • 规则环节即我们可能对排序列表采取一定的规则策略,最终生成一个包含N条结果的列表。例如在规则环节我们可能会采取不同的去重策略,如文本去重、图片去重、混合去重等,可能会采取不同的列表打散策略,可能会迭代产品经理提出的各种规则逻辑。由于推荐系统的最终评价是看统计效果,因此各种人为的规则都会影响最终结果,我们抽象出规则环节后便可以对任何逻辑做线上ABTest,最终评价相关逻辑是否合理。
  • 生成N条推荐结果列表后,不同的前端展示方式也会影响最终的推荐效果,例如不同的UI设计,采用大图模式还是小图模式,页面上展示哪些字段都会影响用户在推荐列表页上的点击,因此在推荐产品迭代过程中不同的展示样式迭代也很重要。

在上述的四个环节中,召回和排序是推荐系统最重要的两个环节。规则和展示样式一般变化周期较长,而召回和排序有很大的挖掘空间,会被不断的迭代,我们的推荐算法工作也主要是围绕召回和排序进行。下图是我们推荐算法的整体框架,主要包括基础数据的计算以及上层的召回策略和排序模型的迭代。






  • 基础数据计算主要包括用户标签和帖子标签的挖掘,这部分工作由用户画像、搜索和推荐多个团队共同完成,最终各团队共享数据。基于用户注册时填写的基础属性信息和用户行为日志,可以挖掘出用户人口属性和兴趣偏好信息,如用户的年龄、性别、学历、收入等基础属性,用户感兴趣的地域商圈、二手房均价、厅室、装修程度等偏好信息。帖子标签挖掘包括提取帖子的固定属性、挖掘衍生属性以及计算动态属性。固定属性直接从帖子数据库提取即可,如分类、地域、标题、正文、图片、房源价格、厅室、小区等。我们还会基于贴子信息是否完备、价格是否合理、图片质量好坏、发帖人质量等多个维度来计算帖子质量分。基于用户行为日志数据可以计算帖子的PV、UV、点击率、转化率、停留时长等动态属性。这些数据最终会在召回环节和排序环节使用,例如基于用户标签和帖子标签可以进行兴趣召回,将用户标签和帖子标签作为特征迭代机器学习模型。
           召回主要负责生成推荐的候选集,我们采用多种召回源融合的方式来完成该过程。我们先后迭代了如下各类召回策略:
  • 热门召回。基于曝光和点击日志,我们会计算不同粒度的热门数据。以二手车业务线为例,从粗粒度到细粒度的数据包括:城市下的热门商圈、商圈下的热门车系和品牌、特定车系和品牌下的热门车源等。每一个车源的热度我们通过最近一段时间内帖子的PV、UV、CTR等指标来衡量,这里的CTR会通过贝叶斯和COEC做平滑处理。热门召回策略会在冷启动时被大量采用。
  • 地域召回。58同城是向用户提供本地生活服务类信息,用户的每次访问都会带上地域信息,如选择的城市、定位的地点等。我们主要结合地域信息和热门数据做召回,如附近最新或最热帖子召回、城市热门帖子召回等。
  • 兴趣召回。基于帖子基础属性字段和帖子标签信息,我们构建了一套帖子检索系统,通过该系统能够以标签或属性字段检索出最新发布的帖子。在用户画像中,我们计算了每个用户的兴趣标签,因此基于用户兴趣标签便能在检索系统中检索出一批帖子,这可以作为一种召回源。此外,在帖子详情页相关推荐场景中,我们也可以利用当前帖子的属性和标签信息去检索系统中检索出相关帖子作为召回数据源。这两种检索召回其实就是我们常说的基于内容的推荐。
  • 关联规则。这里并非直接采用传统Apriori、FP-growth关联规则算法,而是参考关联规则思想,将最近一段时间中每个用户点击所有物品当做一次事务,由此计算两两物品之间的支持度,并在支持度中融入时间衰减因子,最终可以得到每个物品的topK个关联性强的物品。这种召回方式其实类似协同过滤中的item相似度矩阵计算,我们主要将其应用在详情页相关推荐中。
  • 协同过滤。我们使用Spark实现了基于User和基于Item的批量协同过滤计算,由于数据量大,批量计算会较消耗时间,我们又实现了基于Item的实时协同过滤算法。通常情况下我们会直接将用户的推荐结果列表作为一种召回源,而在详情页相关推荐场景,我们还会使用协同过滤计算出的Item相似度矩阵,将帖子最相似的topK个帖子也作为一种召回源。
  • 矩阵分解。我们引入了SVD算法,将用户对帖子的点击、收藏、分享、微聊和电话等行为操作看作用户对帖子进行不同档次的评分,从而构建评分矩阵数据集来做推荐。
  • DNN召回。Google在YouTube视频推荐上使用了DNN来做召回,我们也正在进行相关尝试,通过DNN来学习用户向量和帖子向量,并计算用户最相近的topK个帖子做为召回源。

上述不同的召回算法都产生出了一部分推荐候选数据,我们需要将不同的召回数据融合起来以提高候选集的多样性和覆盖率,这里我们主要使用两种召回融合策略:

  • 分级融合。设置一个候选集目标数量值,然后按照效果好坏的次序选择候选物品,直至满足候选集大小。假设召回算法效果好坏的顺序是A、B、C、D,则优先从A中取数据,不足候选集目标数量时则从B中取数据,依次类推。我们的系统支持分级融合策略的配置化,不同召回算法的先后顺序可以灵活配置。这里的效果好坏顺序是根据离线评价和线上评价来决定的,例如离线我们会比较不同召回算法的召回率和准确率,线上我们会比较最终点击或转化数据中不同召回算法的覆盖率。
  • 调制融合。按照不同的比例分别从不同召回算法中取数据,然后叠加产生最终总的候选集。我们的系统也支持调制融合策略的配置化,选择哪些召回算法、每种召回算法的选择比例均可以灵活配置。这里的比例主要根据最终线上点击或转化数据中不同召回算法的覆盖率来设置。

排序环节我们主要采用Pointwise方法,为每个帖子打分并进行排序,通过使用机器学习模型预估帖子的点击率、转化率和停留时长等多指标来做排序。早期我们主要优化点击率,目前我们不仅关注点击率外还会注重转化率的提高。在58同城的产品场景中,转化主要指用户在帖子详情页上的微聊、打电话操作。

排序离线流程主要包括样本生成和选择、特征抽取、模型训练和评价。首先对埋点日志中的曝光、点击、转化和停留时长等数据做抽取解析,如基于曝光序列号关联各类操作、解析埋点参数(例如日志中记录的实时特征)、解析上下文特征等,并同时打上label,生成模型样本。然后对样本进行过滤,例如过滤恶意用户样本、过滤无效曝光样本等。然后对样本做特征抽取,生成带特征的样本,我们主要从用户、帖子、发帖人和上下文四个维度做特征工程。之后,按照一定正负样本比例做采样,最终进行模型训练和评估,离线评估指标主要参考AUC,离线效果有提升后会进行ABTest上线,逐步迭代。我们先后迭代上线了如下排序策略:

  • 规则序。早期未上线机器学习模型时,对候选集中的帖子会直接使用刷新时间、统计CTR或者一些产品规则来做排序。
  • 单机器学习模型。我们最早实践的是LR模型,它是线性模型,简单高效、可解释性好,但对特征工程要求较高,需要我们自己做特征组合来增强模型的非线性表达能力,早期我们使用LibLinear来训练模型,后来迁移到了Spark上。之后我们引入了XGBoost树模型,它非线性表达能力强、高效稳定,是目前开源社区里最火热的模型之一,最初我们采用单机版本训练,后期将XGBoost部署在我们的yarn集群上,使用分布式版本进行训练。同时,我们应用了FM模型,相比于LR模型它引进了特征组合,能够解决大规模稀疏数据下的特征组合问题,我们主要使用分布式FM (DiFacto,FM on Yarn)来进行模型训练。上述这些模型都是批量更新,通常是一天更新一次,为了快速捕捉用户行为的变化,我们还引入Online Learning模型,主要尝试应用FTRL方式去更新LR模型,在某些场景下获得了稳定的效果提升。
  • 融合模型。类似Facebook、Kaggle的做法,我们实践了GBDT+LR和GBDT+FM的模型融合方案。首先利用XGBoost对原始特征做处理生成高阶特征,然后输入到LR和FM模型中,目前我们的点击率预估模型中效果最佳的是GBDT+LR融合模型,转化率预估模型中效果最佳的是GBDT+FM融合模型。此外,我们还会尝试将某个单指标(如点击率)下多个模型的预测结果进行融合(如相加或相乘等),也会将多个指标(点击率、转化率和停留时长)的模型进行融合(如相乘)以观察效果。
  • 深度模型。深度学习正逐渐被各大公司应用于推荐系统中,我们也正在进行尝试。目前,我们已将FNN(Factorisation machine supported neuralnetwork)模型应用在我们的推荐排序中,相比单机器学习模型,FNN有较稳定的效果提升,但比融合模型效果要稍差,目前我们正在进行深度模型的调优,并在尝试引入Wide&Deep等其他深度模型。

基于上述基础机器学习工具,目前我们主要会迭代点击率、转化率和停留时长预估模型,线上会ABTest上线单指标模型、多指标融合模型,以提高推荐效果。

架构

       对于推荐系统来说,一套支撑算法策略高效迭代的推荐后台系统至关重要,我们基于微服务架构设计了推荐后台系统,它扩展性好、性能高,系统架构如下图所示,系统分为数据层、逻辑层和接入层,数据层提供各类基础数据的读取,逻辑层实现召回和排序策略并支持不同策略的ABTest,接入层对外提供了通用的访问接口。


数据层提供推荐逻辑所需要的各类数据,这些数据存储在 WRedis 、文件、 WTable 等多种设备上,我们将所有数据的读取都封装成 RPC 服务,屏蔽了底层的存储细节。这里包括检索服务、召回源读取服务、帖子特征中心和用户特征中心:


  • 检索服务。我们搭建了一套搜索引擎用做召回检索,支持基于各类搜索条件去检索数据,例如可以检索出价格在200万至300万之间的回龙观两室的房源、检索出中关村附近的最新房源。该服务主要应用于这几类场景:在猜你喜欢推荐场景中基于用户标签去检索帖子、在相关推荐场景中基于当前帖子属性去检索相关帖子、冷启动时基于地域信息召回附近的帖子等。
  • 召回源读取服务。提供各类召回源数据的读取,这些召回源数据通过离线或实时计算得到,包括热门数据、协同过滤数据、关联规则数据、矩阵分解结果等。该服务设计得较灵活,支持任意召回源的增加。
  • 帖子特征中心。提供帖子所有属性字段的读取,在召回、排序和推荐主体逻辑中会使用到这些帖子属性,一般情况我们会在召回环节读取出所有帖子属性,然后应用于排序和规则逻辑中。召回得到的候选集大小一般是几十到几百,为了支持高性能的批量读取,我们选择使用WRedis集群存储帖子属性,并通过多线程并发读取、缓存、JVM调优等多项技术保证服务性能。目前,该服务每天承接数亿级请求,平均每次读取150条数据,耗时保证在2ms之内。
  • 用户特征中心。UserProfile数据包括用户离线/实时兴趣标签、人口属性等,该数据会在召回环节使用,例如使用用户兴趣标签去检索帖子作为一种召回源,也会在排序环节使用,例如将用户标签作为机器学习排序模型的特征。