5. 1 什么是混合推荐系统 迄今为止,推荐系统已经经历了2 0 多年的发展,但是仍然没有人给出一个精确的 定义。推荐系统成为一个相对独立的研究方向,一般被认为始于199 4 年GroupLen s 研 究组推出的GroupLen s 系统。该系统基于协同过滤(Collaborativ e Filterin g )完成推荐任 务,并对推荐问题建立了一个形式化模型。该形式化模型引领了之后推荐系统的发展方 向。基于该形式化模型,推荐系统要解决的问题总共有两个,分别是预测(Prediction)和 推荐(Recommendation)。 预测所解决的主要问题是推断Use r 对Ite m 的喜好程度,而推荐则是根据预测环节 的计算结果向用户推荐Item。推荐系统在不同的应用场景下完成预测和推荐任务,具有 众多算法,但是经过大量的实践,人们发现没有一种方法可以独领风骚,每种方法都有 其局限性。 上一章介绍了几种主流的推荐方法,它们在推荐时利用的信息和采用的框架各不相 同,在各自的领域表现出来的效果也各有千秋。基于内容的推荐方法依赖Ite m 的特征 描述,协同过滤会利用Use r 和Ite m 的特定类型的信息转化生成推荐结果,而社交网 络的推荐算法则根据Use r 的相互影响关系进行推荐。每种方法各有利弊,没有一种方 法利用了数据的所有信息,因此,我们希望构建一种混合(Hybrid)推荐系统,来结合 不同算法的优点,并克服前面提到的缺陷,以提高推荐系统可用性。本章将会结合特 征处理、特征选择、常见的排序模型等不同方面,向读者分步骤介绍如何搭建混合推荐 系统。 12 0 推荐系统与深度学习 | 5.1. 1 混合推荐系统的意义 5.1.1. 1 海量数据推荐 在很多海量数据推荐系统中通常存在三部分:在线(Online)系统、近线(Nearline)系 统和离线(O2ine)系统。在线系统与用户进行直接交互,具有高性能高可用性的特性, 通常利用缓存(Cach e )系统,处理热门请求的重复计算。近线系统接受在线系统的请 求,执行比较复杂的推荐算法,缓存在线系统的结果,并及时收集用户的反馈,快速调整 图5. 1 NetFli x 的实时推荐系统的架构图 第 5 章混合推荐系统121 | 推荐结果。离线系统利用海量的用户行为日志进行挖掘,进行高质量的推荐,通常具有 运算周期长、资源消耗大等特点。 在工业应用中面对海量的用户和物品,需要实时保证线上的用户请求得到可用结果。 这里存在一个矛盾,在线系统无法承担资源消耗大的算法,而离线系统实时推荐能力差。 因此,需要将在线系统|近线系统|离线系统组成混合系统来保证高质量的推荐。 NetFli x 公司曾花重金举办推荐系统竞赛,并公开其后台推荐系统的架构(图5.1) , 该系统为三段式混合推荐系统。 离线系统是传统的个性化推荐系统的主体,定期利用大量历史操作日志进行批处理 运算,然后进行特征构造及选取,最终建立模型并更新。 近线系统,是将用户产生的事件,利用流式计算得到中间结果,这些中间结果一方 面发送给在线部分用于实时更新推荐模型,另一方面将中间结果存储起来,例如,存储 在Memcache d 、Cassandra、MySQ L 等可以快速查询的存储中作为备份。在NetFli x 的系 统中,他们的流式计算是通过一个叫做NetFlix.Manhatta n 的框架来实现的,它是一个类 似于Stor m 的实时流式计算框架。 然后是在线部分。这一部分利用离线部分的主体模型并考虑近线部分的实时数据对 模型进行增量更新,然后得到实时的推荐模型,进而根据用户的行为来对用户进行实时 推荐。 近线和在线部分将会在后面的推荐系统架构章节中进一步进行介绍。 5.1.1. 2 高质量推荐 为了提升推荐系统的推荐精度以及推荐多样性,工业应用中通常会对推荐系统进行 特征、模型等多层面的融合来构建混合推荐系统。 YouTub e 所使用的推荐系统是现在业界规模最大的、最先进的推荐系统之一。You- Tub e 在201 6 年第十届AC M RecSy s 上介绍了其利用深度学习的混合推荐算法带来了系 统性能的巨大提升①。该算法会在下一章“深度学习在推荐系统中的应用”中详细进行介 绍。该系统主要分为两个部分:候选列表生成(Matching)和精致排序(Ranking)。Matchin g 阶段先“粗糙”召回候选集,Rankin g 阶段对Matchin g 后的结果采用更精细的特征计算 排序分数,并进行最终排序。 ① Dee p Neura l Network s fo r YouTub e Recommendations . Pau l Covingto n Google , Mountai n View , CA , US A 12 2 推荐系统与深度学习 | 5.1. 2 混合推荐系统的算法分类 前一节通过两个工业级典型应用介绍了混合推荐系统的意义,本节将对目前的混合 推荐系统进行简单分类,从系统、算法、结果、处理流程等不同的角度来分析不同混合 推荐系统。 从系统架构上看,常见的架构是在线{离线{近线三段混合系统,各系统一般分别负 责热门请求、短期计算和长期推荐计算。在上一节中已经做了相关介绍,通过多段的混 合推荐可以达到可靠的推荐结果。 从混合技术上看,Robi n Burk e 在其不同混合推荐算法设计方案的调研报告中将其 分为:加权型、切换型、交叉型、特征组合型、瀑布型、特征递增型、元层次型。下面将 对这几种方式分别进行介绍。 5.1.2. 1 加权型混合推荐 加权混合推荐即利用不同的推荐算法生成的候选结果 , 进行进一步的加权组合 (Ensemble) , 生成最终的推荐排序结果。例如,最简单的组合是将预测分数进行线性加 权。P-Tang o 系统利用了这种混合推荐,初始化时给基于内容和协同过滤推荐算法同样 的权重,根据用户的评分反馈进一步调整算法的权重。Pazzan i 提出的混合推荐系统未使 用数值评分进行加权,而是利用各个推荐方法对数据结果进行投票,利用投票结果得到 最终的输出。 加权混合推荐系统的好处是可以利用简单的方式对不同的推荐结果进行组合,提高 推荐精度,也可以根据用户的反馈进行方便的调整。但是在数据稀疏的情况下,相关的 推荐方法无法获得较好的结果,该系统往往不能取得较高的提升。同时,由于进行多个 方法的计算,系统复杂度和运算负载都较高。在工业界实际系统中,往往采用一些相对 简单的方案。 5.1.2. 2 切换型混合推荐 切换型推荐技术是根据问题的背景和实际情况来使用不同的推荐技术,通常需要一 个权威者根据用户的记录或者推荐结果的质量来决定在哪种情况下应用哪种推荐系统。 例如,DailyLearne r 系统使用基于内容和基于协同过滤的切换混合推荐,系统首先使用 基于内容的推荐技术,如果不能产生高可信度的推荐,然后再尝试使用协同过滤技术; NewsDud e 系统则首先基于内容进行最近邻推荐,如果找不到相关报道,就引入协同过 第 5 章混合推荐系统123 | 滤系统进行跨类型推荐。可以看出,不同的系统往往采用不同的切换策略,切换策略的 优化为这种方法的关键因素。由于不同算法的打分结果标准不一致,所以需要根据情况 进行转化,这也会增加算法的复杂度。 5.1.2. 3 交叉型混合推荐 交叉型推荐技术的主要动机是保证最终推荐结果的多样性。因为不同用户对同一件 物品的着眼点往往各不相同,而不同的推荐算法,生成的结果往往代表了一类不同的观 察角度所生成的结果。交叉型推荐技术将不同推荐算法的生成结果,按照一定的配比组 合在一起,打包后集中呈现给用户。比如,可以构建这样一个基于We b 日志和缓存数据 挖掘的个性化推荐系统,该系统首先通过挖掘We b 日志和缓存数据构建用户多方面的 兴趣模式,然后根据目标用户的短期访问历史与用户兴趣模式进行匹配,采用基于内容 的过滤算法,向用户推荐相似网页,同时,通过对多用户间的协同过滤,为目标用户预 测下一步最有可能的访问页面,并根据得分对页面进行排序,附在现行用户请求访问页 面后推荐给用户。 交叉型推荐技术需要注意的问题是结果组合时的冲突解决问题,通常会设置一些额 外的约束条件来处理结果的组合展示问题。 5.1.2. 4 特征组合型混合推荐 特征组合是将来自不同推荐数据源的特征组合,由一种单一的推荐技术使用。数据 是推荐系统的基础,一个完善的推荐系统,其数据来源也是多种多样的。从这些数据来 源中我们可以抽取出不同的基础特征。以用户兴趣模型为例,我们既可以从用户的实际 购买行为中,挖掘出用户的“显式”兴趣,也可以从用户的点击行为中,挖掘用户“隐 式”兴趣;另外从用户分类、人口统计学分析中,也可以计算出用户兴趣;如果有用户的 社交网络,那么也可以了解周围用户对该用户兴趣的投射,等等。而且从物品(Item)的 角度来看,也可以挖掘出不同的特征。 不同的基础特征可以预先进行组合或合并,为后续的推荐算法所使用。特征组合的 混合方式使得系统不再仅仅考虑单一的数据源(如仅用用户评分表),所以它降低了用 户对项目评分数量的敏感度。 5.1.2. 5 瀑布型混合推荐 瀑布型的混合技术采用了过滤的设计思想,将不同的推荐算法视为不同粒度的过滤 12 4 推荐系统与深度学习 | 器,尤其是面对待推荐对象(Item)和所需的推荐结果数量相差极为悬殊时,往往非常适 用。例如,Entree C 餐馆推荐系统,首先利用知识来基于用户已有的兴趣来进行推荐,后 面利用协同过滤再对上面生成的推荐进行排序。 设计瀑布型混合系统中,通常会将运算速度快、区分度低的算法排在前列,逐步过 渡为重量级的算法,这样的优点是充分运用不同算法的区分度,让宝贵的计算资源集中 在少量较高候选结果的运算上。 5.1.2. 6 特征递增型混合推荐 特征递增型混合技术,即将前一个推荐方法的输出作为后一个推荐方法的输入。这 种方法上一级产生的并不是直接的推荐结果,而是为下一级的推荐提供某些特征。一 个典型的例子是将聚类分析环节作为关联规则挖掘环节的预处理:聚类所提供的类别特 征,被用于关联规则挖掘中,比如对每个聚类分别进行关联规则挖掘。 与瀑布型不同的是,第二种推荐方法并没有使用第一种产生的任何等级排列的输出, 其两种推荐方法的结果以一种优化的方式进行混合。 5.1.2. 7 元层次型混合推荐 元层次型混合将不同的推荐模型在模型层面上进行深度的融合。比如,User-Base d 方法和Item-Base d 方法的一种组合方式是,先求目标物品的相似物品集,然后删掉所有 其他的物品(在矩阵中对应的是列向量),在目标物品的相似物品集上采用User-Base d 协同过滤算法。这种基于相似物品的邻居用户协同推荐方法,能很好地处理用户多兴趣 下的个性化推荐问题,尤其是候选推荐物品的内容属性相差很大的时候,该方法性能会 更好。 与特征递增型的不同在于:在特征递增型中使用一个学习模型产生某些特征作为第 二种算法的输入,而在元层次型中,整个模型都会作为输入。 上述类型的混合方式可以按照处理流程统一分为三类。 (1)整体式混合推荐系统。整体式混合推荐系统的实现方法是通过对算法进行内部 调整,可以利用不同类型的输入数据,并得到可靠的推荐输出,上述的特征组合型混合 推荐、特征递增型混合推荐和元层次型混合推荐属于此种类型。 (2)并行式混合推荐系统。并行式混合推荐系统利用混合机制将不同推荐系统的 结果进行集成,上述的加权型混合推荐、切换型混合推荐和交叉型混合推荐属于此种类 型。 第 5 章混合推荐系统125 | 图5. 2 整体式混合推荐系统 图5. 3 并行式混合推荐系统 (3)流水线式混合推荐系统。流水线式混合推荐系统利用多个流程顺序作用产生推 荐结果,上述的瀑布型混合推荐可以归为此种类型。 图5. 4 流水线式混合推荐系统 5. 2 推荐系统特征处理方法 “数据与特征决定了模型的上限,而模型算法则为逼近这个上限”,这句话是推荐系 统工程师的共识。特征的本质为一项工程活动,目的是最大限度地从原始数据中提取特 征以供算法模型使用。在实际构建推荐系统过程中,可以直接用于模型算法的特征并不 多,能否从原始数据中挖掘出来有用的特征将会直接决定推荐系统的质量。对于特征一 般的处理流程为特征获取、特征清洗、特征处理和特征监控,其中最核心部分为特征处 12 6 推荐系统与深度学习 | 理部分,本节中将会对特征处理进行详细介绍。 由于原始数据中的特征通常无法在算法模型中直接使用,需要经过特征转化与特征 选择后放入模型。特征转化包含对原始特征的各种变换,更好地表达原始数据的内在规 律,便于模型算法进行训练,而特征选择则选择提炼对模型表达有用的特征,希望建立 更灵活、更简单的模型。 5.2. 1 特征处理方法 由于数据源包含不同类型的变量,不同的变量往往处理方法不同,下面将针对不同 的变量类型对特征处理方法进行介绍。 5.2.1. 1 数值特征处理 方法一:无量纲处理 无量纲化使不同规格的数据转换到同一规格。常见的无量纲化方法有标准化和区间 缩放法。标准化的前提是特征值服从正态分布,标准化后,其转换成标准正态分布。区 间缩放法利用了边界值信息,将特征的取值区间缩放到某个特定的范围,例如[0 , 1 ] 等。 标准化变换后各维特征的均值为0,方差为1,也叫做Z-Scor e 规范化,计算方式如 下式,为特征值减去均值,除以标准差。 x x . = x . 1 (5-1) S 使用Pytho n 处理的代码如下: / / 标准化 impor t nump y a s n p fro m sklear n impor t preprocessin g x = np.array([ [ 1. , -1. , 2.] , [ 2. , 0. , 0.] , [ 0 . , 1 . , -1.]] ) x_scale d = preprocessing.scale(x ) prin t (x _ scale d ) 运行后,可以看到结果: 第 5 章混合推荐系统127 | [ [ 0 . -1.2247448 7 1.33630621 ] [ 1.2247448 7 0 . -0.26726124 ] [ -1.2247448 7 1.2247448 7 -1.06904497] ] 而区间缩放法又被称为最大|最小标准化,最大|最小标准化是对原始数据进行线 性变换,变换到[0,1 ] 区间。计算公式如下: x . = x . Mi n (5-2 ) 使用Pytho n 处理的代码如下: Ma x . Mi n / / 标准化 impor t nump y a s n p fro m sklear n impor t preprocessin g x = np.array([ [ 1. , -1. , 2.] , [ 2. , 0. , 0.] , [ 0 . , 1 . , -1.]] ) x_max_min_scale d = preprocessing.MinMaxScaler().fit_transform(x ) prin t (x _ max _ mi n _ scale d ) 运行后,可以看到结果: [ [ 0. 5 0 . 1 . ] [ 1 . 0. 5 0.33333333 ] [0 . 1 . 0 . ] ] 除了最大{最小标准化外,也可以使用二次核进行标准化,使用Pytho n 的处理方法如下: 使用二次型规范化的代码,参考如下: impor t nump y a s n p fro m sklear n impor t preprocessin g x = np.array([ [ 1. , -1. , 2.] , [ 2. , 0. , 0.] , [ 0 . , 1 . , -1.]] ) x_normaliz e =preprocessing.normalize(x , norm = 'l 2 ' ) prin t (x _ normaliz e ) 运行后,可以看到结果: 12 8 推荐系统与深度学习 | [ [ 0.4082482 9 -0.4082482 9 0.81649658 ] [1 . 0 . 0 . ] [ 0 . 0.7071067 8 -0.70710678] ] 方法二:非线性变换 很多情况下,对特征进行非线性变换来增加模型复杂度也是一个有效的手段。常用 的变换有基于多项式、基于指数函数和基于对数函数的变换等。 下面利用对数变换来说明,一般对数变换后特征分布更平稳。对数变换能够很好地 解决随着自变量的增加,因变量的方差增大的问题。另外一方面,将非线性的数据通过 对数变换,转换为线性数据,便于使用线性模型进行学习。关于这一点,可以类比一下 SVM,比如SV M 对于线性不可分的数据,先对数据进行核函数映射,将低维的数据映射 到高维空间,使数据在投影后的高维空间中线性可分。 方法三:离散化 有时数值型特征根据业务以及其代表的含义需要进行离散化,离散化拥有以下好处: 离散化后的特征对异常数据有很强的鲁棒性,比如一个特征是年龄>3 0 为1,否则为0。 如果特征没有经过离散化,一个异常数据“年龄10 0 岁”会给模型造成很大的干扰;特 征离散化后可以进行特征交叉,特征内积乘法运算速度快,进一步引入非线性,提升表 达能力,计算结果方便存储,容易扩展;特征离散化后,模型会更稳定,比如如果对用户 年龄离散化,20.3 0 作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不 同的人。但是处于区间相邻处的样本会刚好相反,所以如何划分区间也非常重要,通常 按照是否使用标签信息可以分为无监督离散化和有监督离散化。 无监督离散化:无监督的离散化方法通常为对特征进行装箱,分为等宽度离散化方 法和等频度离散化方法。等宽度离散方法,就是根据箱的个数得出固定的宽度,使得分 到每个箱中的数据的宽度是相等的。等频分箱法是使得分到每个箱中的数据的个数是相 同的。在等宽或等频划分后,可用箱中的中位数或者平均值替换箱中的每个值,实现特 征的离散化。这两种方法需要指定区间的个数,同时等宽度离散化方法对异常点较为敏 感,倾向于把特征不均匀地分到各个箱中,这样会破坏特征的决策能力。等频度的离散 化方法虽然会避免上述问题却可能会将具有相同标签的相同特征值分入不同的箱中,同 样会造成决策能力下降。 基于聚类分析的离散化方法也是一种无监督的离散化方法。此种方法包含两个步骤, 首先是将某特征的值用聚类算法(如K-mean s 算法)通过考虑特征值的分布以及数据点 第 5 章混合推荐系统129 | 的邻近性划分成簇,然后将聚类得到的簇进行再处理,处理方法可分为自顶向下的分裂 策略和自底向上的合并策略。分裂策略是将每一个初始簇进一步分裂为若干子簇,合并 策略则是通过反复地对邻近簇进行合并。聚类分析的离散化方法通常也需要用户指定簇 的个数,从而决定离散产生的区间数。 对于实际数据的离散化,具体可以根据业务的规律进行相应的调整,利用自然区间 进行相应的离散。 有监督离散化:有监督的离散化方法相较无监督的离散化方法拥有更多的表现形式 及处理方式 , 但目前比较常用的方法为基于熵的离散化方法和基于卡方的离散化方 法。 由于建立决策树时用熵来分裂连续特征的方法在实际中运行得很好,故将这种思想 扩展到更通常的特征离散化中,通过反复地分裂区间直到满足停止的条件。由此产生了 基于熵的离散化方法。熵是最常用的离散化度量之一。基于熵的离散化方法使用类分布 信息计算和确定分裂点,是一种有监督的、自顶向下的分裂技术。ID 3 和C4. 5 是两种 常用的使用熵的度量准则来建立决策树的算法,基于这两种方法进行离散化特征几乎 与建立决策树的方法一致。在上述方法上又产生了MDL P 方法(最小描述距离长度法 则),MDL P 的思想是假设断点是类的分界,依此得到许多小的区间,每个区间中的实例 的类标签都是一样的,然后再应用MDL P 准则衡量类的分界点中哪些是符合要求可以 作为端点,哪些不是端点需要将相邻区间进行合并。由此选出必要的断点,对整个数据 集进行离散化处理。 下面将利用 R 的discretizatio n 包以 R 自带的数据集iri s 为例进行MDL P 方法效果 展示。 / / MDL P 特征离散 化 librar y ( discretizatio n ) dat a ( iri s ) mdl p ( iri s ) $Dis c . dat a 以数据集iri s 为例进行MDL P 方法效果展示,图5. 5 左边为原始特征,右边为MDL P 方法离散后的数据。 不同于基于熵的离散化方法,基于卡方的离散化方法是采用自底向上的策略,首先 将数据取值范围内的所有数据值列为一个单独的区间,再递归找出最佳邻近可合并的区 间,然后合并它们,进而形成较大的区间。在判定最佳邻近可合并的区间时,会用到卡方 13 0 推荐系统与深度学习 | 统计量来检测两个对象间的相关度。最常用的基于卡方的离散化方法是ChiMerg e 方法, 它的过程如下:首先将数值特征的每个不同值看作一个区间,对每对相邻区间计算卡方 统计量,将其与由给定的置信水平确定的阈值进行比较,高于阈值则把相邻区间进行合 并,因为高的卡方统计量表示这两个相邻区间具有相似的类分布,而具有相似类分布的 区间应当进行合并成为一个区间。合并的过程递归地进行,直至计算得到的卡方统计量 不再大于阈值,也就是说,找不到相邻的区间可以进行合并,则离散化过程终止,得到 最终的离散化结果。 图5. 5 MDL P 特征离散化 下面将利用 R 的discretizatio n 包以 R 自带的数据集iri s 为例进行ChiMerg e 方法效 果展示。 / / ChiMerg e 特征离散化 ibrar y ( discretizatio n ) #--Discretizatio n usin g th e ChiMerg e metho d dat a ( iri s ) disc=chiM(iri s ,alph a =0.05 ) # --0.0 5 significanc e leve l #--discretize d dat a matri x b b = dis c $Dis c . dat a 第 5 章混合推荐系统131 | 针对数据集iri s 进行ChiMerg e 方法效果展示,图5. 6 左边为原始特征,右边为 ChiMerg e 方法离散后的数据。 图5. 6 ChiMerg e 特征离散化 5.2.1. 2 离散特征处理 方法一:One-Ho t 编码 在实际的推荐系统中,很多特征为类别属性型特征,通常会利用One-Ho t 编码将这 些特征进行编码。如果一个特征有 m 个可能值,那么通过One-Ho t 编码后就变成了 m 个二元特征,并且这些特征互斥。One-Ho t 编码可以将离散特征的取值扩展到欧式空间, 离散特征的某个取值就是对应欧式空间的某个点,可以方便在学习算法中进行相似度等 计算,并且可以稀疏表示,减少存储,同时可以一定程度上起到扩充特征的作用。 使用One-ho t 处理的参考代码如下: //On e -Ho t impor t nump y a s n p fro m sklear n impor t preprocessin g one_hot_enc = preprocessing.OneHotEncoder( ) one_hot_enc.fit([[1,1,2] , [0 , 1 , 0] , [0 , 2 , 1] , [1 , 0 , 3]] ) after_one_ho t = one_hot_enc.transform([[0 , 1 , 3]]).toarray( ) prin t ( afte r _ on e _ ho t ) 13 2 推荐系统与深度学习 | 方法二:特征哈希 特征哈希法的目标是把原始的高维特征向量压缩成较低维特征向量,且尽量不损失 原始特征的表达能力,是一种快速且很节省空间的特征向量化方法。在推荐系统中会存 在很多例如I D 类型特征(当然也可以利用embeddin g 方法,但哈希方法更节约资源) , 利用特征哈希,可以避免生成极度稀疏的数据,但是可能会引发碰撞,碰撞可能会降低 结果的准确性,也可能会提升结果的准确性,一般利用另外一个函数解决碰撞。其一般 描述为,设计一个函数 v = h(x) , 能够将 d 维度向量 x =(x(1);x(2) , , x(d) ) 转化成 ¢¢ · m 维度的新向量v,这里的 m 可以大于也可以小于d。通常使用方法为利用哈希函数将 x(1 ) 映射到v(h(1)) , 将x(d ) 映射到v(h(d))。Has h 函数能够将任意输入转换到一个固定 范围的整数输出。下面利用文本对此进行说明,可以看到程序将句子转化为一个固定维 度的向量,同样I D 类型的特征也可以利用同样的方法进行处理,将每一个单词对应为 一个ID。 使用特征has h 的参考代码如下: //has h -trac k de f hashing_vectorizer(s , N) : x = [ 0 fo r i i n xrange(N) ] fo r f i n s.split() : h = hash(f ) x[ h % N ] + = 1 retur n x prin t hashing_vectorizer ( 'mak e a has h featur e ' , 3 ) 方法三:时间特征处理 在推荐系统中通常会包含很多时间相关的特征,如何有效地挖掘时间相关特征也会 很大程度上影响推荐的效果。通常方案是按照业务逻辑以及业务目的进行相关特征的处 理,Christ , M 等提出了一种层次化处理时间特征的方案,如图5. 7 所示。其中包含了时 间窗口统计特征:最大、最小、均值、分位数,并利用标签相关性对特征进行选择。下面 简单介绍利用Pytho n 的tsfres h 工具对Robo t Executio n Failure s 数据集进行特征提取, 代码参考如下: 第 5 章混合推荐系统133 | 图5. 7 层次化时间按序列特征 fro m tsfresh.examples.robot_execution_failure s impor t download_robot_execution_failure s , \ loa d _ robo t _ executio n _ failure s downloa d _ robo t _ executio n _ failure s ( ) timeserie s , y = load_robot_execution_failures( ) fro m tsfres h impor t extract_feature s extracted_feature s = extract_features(timeserie s , column_id="id" , colum n _ sor t = " tim e " ) fro m tsfres h impor t select_feature s fro m tsfresh.utilities.dataframe_function s impor t imput e imput e ( extracte d _ feature s ) features_filtere d = select_features(extracted_feature s , y ) 13 4 推荐系统与深度学习 | 5.2. 2 特征选择方法 5.2.2. 1 单变量特征选择 单变量特征选择能够对每一个特征进行测试,衡量该特征和响应变量之间的关系, 根据得分丢弃不好的特征。这种方法比较简单,易于运行,易于理解,通常对于理解数据 有较好的效果,但其与设计的算法模型无关。单变量特征选择方法有许多改进的版本、 变种,下面介绍比较常用的几种。 方法一:皮尔森相关系数 皮尔森相关系数是一种最简单的、能帮助理解特征和响应变量之间关系的方法,该 方法衡量的是变量之间的线性相关性,结果的取值区间为[-1,1],- 1 表示完全的负相关 (这个变量下降,那个变量就会上升),+ 1 表示完全的正相关。 0 表示没有线性相关。皮 尔森相关系数表示两个变量之间的协方差与标准差的商,其计算公式见如下。 .X; Y = E[( X . 1 X )( Y . 1 Y ) ] (5-3) . X . Y 皮尔森相关系数计算速度快、易于计算,经常在拿到数据(经过清洗和特征提取之后 的)之后第一时间就可以执行。SciP y 的pearson r 方法能够同时计算相关系数和p-valu e 。 impor t nump y a s n p fro m scipy.stat s impor t pearson r np . rando m . see d (0 ) siz e = 30 0 x = np.random.normal(0 , 1 , size ) print ( "Lowe r noise" , pearsonr(x , x + np.random.normal(0 , 1 , size)) ) print ( "Highe r noise" , pearsonr(x , x + np.random.normal(0 , 10 , size)) ) 运行后可以得到: Lowe r nois e (0.71824836862138408 , 7.3240173129983507e-49 ) Highe r nois e (0.05796429207933815 5 , 0.31700993885324752 ) 在这个例子中,我们比较了变量在加入噪音之前和之后的差异。当噪音比较小的时 候,相关性很强,p-valu e 很低。但皮尔森相关系数有一个明显的缺陷是,它只对线性关 系敏感。 方法二:距离相关系数 第 5 章混合推荐系统135 | 距离相关系数是为了克服皮尔森相关系数的弱点而产生的。它是基于距离协方差进 行变量间相关性度量 , 它的一个优点为变量的大小不是必须一致的 , 其计算方法如 式(5-4)所示,注意通常使用的值为其平方根。 dCov(X; Y ) Dcor(X; Y ) = (5-4) ((dCov2(X;X)) 1 2 · (dCov2(Y; Y )) 1 2 ) 1 2 计算相关系数的参考代码如下: / / D i s t a n c e _ c o r r e l / / Y a r o s l a v a n d S a i m p o r t n u m p y a s a t i o n t r a j i t n p o n s k l e a r n m a i l i n g l i s t d e f d i s t ( x , y ) : # 1 d o n l y r e t u r n n p . a b s ( x [ : , N o n e ] - y ) d e f d _ n ( x ) : d = d i s t ( x , d n = d - d . m r e t u r n d n x ) e a n ( 0 ) - d . m e a n ( 1 ) [ : , N o n e ] + d . m e a n ( ) d e f d c o v _ a l l ( x , d n x = d _ n ( x ) d n y = d _ n ( y ) y ) : deno m = np.product(dnx.shape ) d c = (dn x * dny).sum( ) / deno m dv x = (dn x * * 2).sum( ) / deno m dv y = (dn y * * 2).sum( ) / deno m d r = d c / (np.sqrt(dvx ) * np.sqrt(dvy) ) retur n np.sqrt(dr ) x = np.random.uniform(-1 , 1 , 10000 ) d c = dcov_all(x , x * * 2 ) prin t (dc ) 13 6 推荐系统与深度学习 | 方法三:卡方检验 卡方检验最基本的思想就是通过观察实际值与理论值的偏差来确定理论的正确与否。 具体做的时候常常先假设两个变量确实是独立的,然后观察实际值与理论值的偏差程 度,如果偏差足够小,我们就认为误差是很自然的样本误差,是测量手段不够精确导致 或者偶然发生的,两者确确实实是独立的,此时就接受原假设;如果偏差大到一定程度, 使得这样的误差不太可能是偶然产生或者测量不精确所致,我们就认为两者实际上是相 关的,即否定原假设,而接受备择假设。 5.2.2. 2 基于模型的特征选择 单变量特征选择方法独立地衡量每个特征与响应变量之间的关系,而另一种主流的 特征选择方法是基于机器学习模型的方法。 方法一:逻辑回归和正则化特征选择 下面介绍如用回归模型的系数来选择特征。越是重要的特征在模型中对应的系数就 会越大,而跟输出变量越是无关的特征对应的系数就会越接近于0。在噪音不多的数据 上,或者是数据量远远大于特征数的数据上,如果特征之间相对来说是比较独立的,那 么即便是运用最简单的线性回归模型也一样能取得非常好的效果。 L 1 正则化将系数 w 的L 1 范数作为惩罚项加到损失函数上,由于正则项非零,这就 迫使那些弱的特征所对应的系数变成0。因此L 1 正则化往往会使学到的模型很稀疏(系 数 w 经常为0),这个特性使得L 1 正则化成为一种很好的特征选择方法。下面的例子在 波士顿房价数据上运行了Lasso,其中参数alph a 是通过gri d searc h 进行优化的。 fro m sklearn.linear_mode l impor t Lass o fro m sklearn.preprocessin g impor t StandardScale r fro m sklearn.dataset s impor t load_bosto n bosto n = load_boston( ) scale r = StandardScale r ( ) X = scaler.fit_transform(boston["data"] ) Y = boston["target" ] name s = boston["feature_names" ] lass o = Lass o ( alph a =.3 ) lasso.fit(X , Y ) 第 5 章混合推荐系统137 | prin t "Lass o model : " , pretty_print_linear(lasso.coef_ , name s , sor t = True ) 运行后可以得到: Lass o mode l : -3.70 7 * LSTA T + 2.99 2 * R M + -1.75 7 * PTRATI O + -1.08 1 * DI S + -0. 7 * NO X + 0.63 1 * B + 0.5 4 * CHA S + -0.23 6 * CRI M + 0.08 1 * Z N + -0. 0 * INDU S + -0. 0 * AG E +0. 0 * RA D + -0. 0 * TA X 可以看到,很多特征的系数都是0。如果继续增加alph a 的值,得到的模型就会越来 越稀疏,即越来越多的特征系数会变成0。 然而,L 1 正则化像非正则化线性模型一样也是不稳定的,如果特征集合中具有相关 联的特征,当数据发生细微变化时也有可能导致很大的模型差异。 L 2 正则化将系数向量的L 2 范数添加到了损失函数中。由于L 2 惩罚项中系数是二 次方的,这使得L 2 和L 1 有着诸多差异,最明显的一点就是,L 2 正则化会让系数的取值 变得平均。对于关联特征,这意味着他们能够获得更相近的对应系数。L 2 正则化对于特 征选择来说一种稳定的模型,不像L 1 正则化那样,系数会因为细微的数据变化而波动。 所以L 2 正则化和L 1 正则化提供的价值是不同的,L 2 正则化对于特征理解来说更加有 用:表示能力强的特征对应的系数是非零。 下面看 3 个互相关联的特征的例子,分别以1 0 个不同的种子随机初始化运行1 0 次, 来观察L 1 和L 2 正则化的稳定性。 fro m sklearn.linear_mode l impor t Ridg e fro m sklearn.metric s impor t r2_scor e siz e = 10 0 #W e ru n th e metho d 1 0 time s wit h differen t rando m seed s fo r i i n rang e (10 ) : prin t "Rando m see d %s " % i np . rando m . see d ( see d =i ) X_see d = np.random.normal(0 , 1 , size ) X 1 = X_see d + np.random.normal(0 , .1 , size ) X 2 = X_see d + np.random.normal(0 , .1 , size ) X 3 = X_see d + np.random.normal(0 , .1 , size ) Y = X 1 + X 2 + X 3 + np.random.normal(0 , 1 , size ) X = np.array([X1 , X2 , X3]). T 13 8 推荐系统与深度学习 | l r = LinearRegression( ) lr . fi t (X,Y ) prin t "Linea r model:" , pretty_print_linear(lr.coef_ ) ridg e = Ridg e ( alph a =10 ) ridg e . fi t (X,Y ) prin t "Ridg e model:" , pretty_print_linear(ridge.coef_ ) prin t 运行后可以得到: Rando m see d 0 Linea r mode l : 0.72 8 * X 0 + 2.30 9 * X 1 + -0.08 2 * X 2 Ridg e mode l : 0.93 8 * X 0 + 1.05 9 * X 1 + 0.87 7 * X 2 Rando m see d 1 Linea r mode l : 1.15 2 * X 0 + 2.36 6 * X 1 + -0.59 9 * X 2 Ridg e mode l : 0.98 4 * X 0 + 1.06 8 * X 1 + 0.75 9 * X 2 Rando m see d 2 Linea r mode l : 0.69 7 * X 0 + 0.32 2 * X 1 + 2.08 6 * X 2 Ridg e mode l : 0.97 2 * X 0 + 0.94 3 * X 1 + 1.08 5 * X 2 Rando m see d 3 Linea r mode l : 0.28 7 * X 0 + 1.25 4 * X 1 + 1.49 1 * X 2 Ridg e mode l : 0.91 9 * X 0 + 1.00 5 * X 1 + 1.03 3 * X 2 Rando m see d 4 Linea r mode l : 0.18 7 * X 0 + 0.77 2 * X 1 + 2.18 9 * X 2 Ridg e mode l : 0.96 4 * X 0 + 0.98 2 * X 1 + 1.09 8 * X 2 Rando m see d 5 Linea r mode l : -1.29 1 * X 0 + 1.59 1 * X 1 + 2.74 7 * X 2 Ridg e mode l : 0.75 8 * X 0 + 1.01 1 * X 1 + 1.13 9 * X 2 Rando m see d 6 Linea r mode l : 1.19 9 * X 0 + -0.03 1 * X 1 + 1.91 5 * X 2 Ridg e mode l :1.01 6 *X 0 +0.8 9 *X 1 +1.09 1 *X 2 Rando m see d 7 Linea r mode l : 1.47 4 * X 0 + 1.76 2 * X 1 + -0.15 1 * X 2 Ridg e mode l : 1.01 8 * X 0 + 1.03 9 * X 1 + 0.90 1 * X 2 Rando m see d 8 Linea r mode l : 0.08 4 * X 0 + 1.8 8 * X 1 + 1.10 7 * X 2 Ridg e mode l : 0.90 7 * X 0 + 1.07 1 * X 1 + 1.00 8 * X 2 第 5 章混合推荐系统139 | Rando m see d 9 Linea r mode l : 0.71 4 * X 0 + 0.77 6 * X 1 + 1.36 4 * X 2 Ridg e mode l :0.89 6 *X 0 +0.90 3 *X 1 +0.9 8 *X 2 可以看出,不同的数据上线性回归得到的模型(系数)相差甚远,但对于L 2 正则化 模型来说,结果中的系数非常地稳定,差别较小,都比较接近于1,能够反映出数据的内 在结构。 方法二:随机森林特征选择 随机森林具有准确率高、鲁棒性好、易于使用等优点,这使得它成为了目前最流行 的机器学习算法之一。随机森林提供了两种特征选择的方法:mea n decreas e impurit y 和 mea n decreas e accurac y 。 在波士顿房价数据集上使用sklear n 的随机森林回归给出一个单变量选择的例子: fro m sklearn.cross_validatio n impor t cross_val_scor e , ShuffleSpli t fro m sklearn.dataset s impor t load_bosto n fro m sklearn.ensembl e impor t RandomForestRegresso r #Loa d bosto n housin g datase t a s a n exampl e bosto n = load_boston( ) X = boston["data" ] Y = boston["target" ] name s = boston["feature_names" ] r f = RandomForestRegressor(n_estimators=20 , max_depth=4 ) score s = [ ] fo r i i n range(X.shape[1]) : scor e = cross_val_score(rf , X[: , i:i+1] , Y , scoring="r2" , cv=ShuffleSplit(len(X ) , 3 , .3) ) scores.append((round(np.mean(score) , 3) , names[i]) ) prin t sorted(score s , reverse=True ) 方法三:XGBoos t 特征选择 XGBoos t 为工业级用的比较多的模型,其某个特征的重要性(featur e score),等于它 被选中为树节点分裂特征的次数的和,比如特征 A 在第一次迭代中(即第一棵树)被选 中了 1 次去分裂树节点,在第二次迭代被选中 2 次,那么最终特征 A 的featur e scor e 就 是1+2,可以利用其特征的重要性对特征进行选择。XGBoos t 的特征选择的代码如下: 14 0 推荐系统与深度学习 | impor t nump y a s n p impor t panda s a s p d impor t xgboos t a s xg b impor t operato r impor t matplotlib.pyplo t a s pl t de f ceate_feature_map(features) : outfil e = open ( 'xg b . fma p ' , ' w ' ) i = 0 fo r fea t i n features : outfil e . writ e ( '{0} \ t {1} \ tq\ n '.format(i , feat) ) i = i + 1 outfil e . clos e ( ) i f __name_ _ = = '_ _ mai n _ _ ' : trai n = pd.read_csv("../input/train.csv" ) cat_se l = [ n fo r n i n train.column s i f n.startswith ( 'ca t ') ] #类别特征数值化 fo r colum n i n ca t _ se l : train[column ] = pd.factorize(train[column].value s , sort=True)[0 ] + 1 param s = { 'mi n _ chil d _ weigh t ' : 10 0 , 'et a ' : 0.0 2 , 'colsampl e _ bytre e ' : 0. 7 , 'ma x _ dept h ' : 12 , 'subsampl e ' : 0. 7 , 'alph a ' : 1 , 'gamm a ' : 1 , 'silen t ' : 1 , 'verbos e _ eva l ' : Tru e , 'see d ' : 1 2 } round s = 1 0 y = trai n [ 'los s ' ] X = trai n . dro p ( [ 'los s ' , 'i d '] , 1 ) 第 5 章混合推荐系统141 | xgtrai n = xgb.DMatrix(X , label=y ) bs t = xg b . trai n ( param s , xgtrai n , nu m _ boos t _ roun d = round s ) feature s = [ x fo r x i n train.column s i f x no t i n [ 'i d ' , 'los s '] ] ceat e _ featur e _ma p ( feature s ) importanc e = bst.get_fscore(fmap = 'xg b . fma p ' ) importanc e = sorted(importance.items() , key=operator.itemgetter(1) ) d f = pd.DataFrame(importanc e , columns= [ 'featur e ' , 'fscor e '] ) d f [ 'fscor e ' ] = df [ 'fscor e ' ] / df [ 'fscor e '] . su m ( ) df.to_csv("../input/feat_sel/feat_importance.csv" , index=False ) pl t . figur e ( ) df . plo t ( kin d = 'bar h ' , x = 'featur e ' , y = 'fscor e ' , legend=Fals e , figsiz e =(6 , 10) ) pl t . titl e ( 'XGBoos t Featur e Importanc e ' ) pl t . xlabe l ( 'relativ e importanc e ' ) pl t . sho w ( ) 方法四:基于深度学习的特征选择 对于图像特征的提取 , 深度学习具有很强的自动特征抽取能力,通常抽取其特征时 将深度学习模型的某一层当作图像的特征。在下一章会对此进行具体介绍。 5. 3 常见的预测模型 5.3. 1 基于逻辑回归的模型 逻辑回归模型是目前使用最多的机器学习分类方法,在推荐系统中的应用非常广泛, 数据产品经理每天都在从事类似的工作。例如,他们分析购买某类商品的潜在因素,日 后就可以判断该类商品购买的概率。通常的做法是挑选两组人群进行对比实验, A 组选 择的是购买该商品的人群, B 组选择未购买该商品的人群,这两组实验人群具有不一样 的用户画像特征和行为特征,比如性别、年龄、城市和历史购买记录等,产品经理经过 14 2 推荐系统与深度学习 | 统计找出购买某类商品的主要因素或者因素组合。例如,性别女、年龄25-3 0 岁、深圳、 买过婴儿床的人群买婴儿车的概率比较高。然而,随着近年来互联网飞速发展,渗透到 PC、PA D 和手机等多种设备中,这导致用户在互联网上的画像和行为特征数据异常丰 富,有时甚至达到千万级别。此时,通过产品经理来分析购买商品的潜在因素就不太合 适了,我们需要依靠机器学习方法来建立用户行为模型、商品推荐模型等实现产品的自 动推荐。逻辑回归模型是使用非常广泛的分类方法之一。 假定只考虑二分类问题,给定训练集合f(x1;y1; , (xn;yn))g,其中x i ∈ Rp 表示第 ¢¢ · i 个用户的 p 维特征,y i 2f0 , 1 } 表示第 i 个用户是否购买该商品。那么模型必定满足二 项式分布: P (yijxi) = u(xi)y( 1 . u(xi))(1.yi ) (5-5 ) 其中,u(xi)=1=( 1 + exp(.′(xi))) , ′(x1) = xi T μ , θ 表示模型参数(包含该商品的偏置项) , 我们通常采用最大似然估计来求解: L = P (y1 , ;ynx1 , ;xn ; μ) ¢¢ · j¢¢ · n P (yixi ; μ) = j (5-6 ) = i= 1 nY u(xi)y i ( 1 . u(xi))(1.yi ) i= 1 进一步,可以得到负对数似然函数: L(μ) = . lo g P (y1 , ¢¢ · ;ynjx1 , ¢¢ · ;xn ; μ;b ) n (5-7) .y i lo g u(xi ) + ( 1 . yi)log( 1 . u(xi))¢ = . i 我们通常采用随机梯度下降法来求数值解: n θ i 我们对参数 θ 得到 : .y i lo g u(xi ) + ( 1 . yi)log( 1 . u(xi))¢ θ = ar g mi n (5-8 ) = n i (g( x T i μ ) . yi)x i (5-9) @ L @ θ 其中,g(x)=1=( 1 . exp(.x) ) 进一步 , 可以得到 : μt+ 1 = μ t . .(g( x T i μ ) . yi)x i (5-10 ) 第 5 章混合推荐系统143 | 其中 0 <. < 1 是步长参数。此外,我们也可以采用批次梯度下降。两者对比,随机梯度下 降更快靠近到最小值但可能无法收敛,而是一直在最小值周围震荡。但在实践中,随机梯 度下降也能取得不错的效果。进一步,数值求解方法还有Newton-Raphso n 方法、Quasi- Newto n 方法等。下面是逻辑回归模型的代码实现 : impor t rando m impor t nump y a s n p clas s LogisticRegression(object) : de f __init__(sel f , x , y , lr=0.0005 , lam=0.1) : "" " x : feature s o f example s y : labe l o f example s lr : learnin g rat e lambda : penalit y o n thet a "" " self.l r = l r sel f . la m = la m self.thet a = np.array([0.0 ] * ( n + 1) ) de f _sigmoid(sel f , x) : z = 1. 0 / (1. 0 + np.exp((-1 ) * x) ) retur n z de f loss_function(self) : u = self.__sigmoid(np.dot(self.x , self.theta) ) c 1 = (-1 ) * self. y * np.log(u ) c 2 = (1. 0 -self.y ) * np.log(1. 0 -u ) # comput e th e cros s -entro y los s = np.average(sum(c 1 -c2 ) + 0. 5 * self.la m * sum(self.theta[1: ] * * 2) ) retur n los s de f _gradient(sel f , iterations) : # m i s th e numbe r o f example s , p i s th e numbe r o f features . m , p = self.x.shap e fo r i i n xrange(0 , iterations) : u = self._sigmoid(np.dot(self.x , self.theta) ) dif f = h_thet a -self. y fo r _ i n xrange(0 , p) : self.theta[_ ] = self.theta[_ ] -self.l r * (1. 0 / m ) * (sum ( dif f * self.x[: , _] ) + self.la m * m * self.theta[_] ) 14 4 推荐系统与深度学习 | cos t = self._loss_function( ) de f run(sel f , iterations) : sel f . _ gradien t ( iteration s ) de f predict(sel f , X) : pred s = self.__sigmoid(np.dot(x , self.theta) ) np.putmask(preds , pred s > = 0.5 , 1.0 ) np.putmask(pred s , pred s < 0.5 , 0.0 ) retur n pred s 5.3. 2 基于支持向量机的模型 2 0 世纪6 0 年代Vapni k 等人提出了支持向量算法(Suppor t Vecto r Algorithm)。199 8 年Joh n Plat t 提出Sequentia l minima l optimizatio n 算法解决二次规划问题,并发展出了 支持向量机(Suppor t Vecto r Machin e )理论,该算法在9 0 年代迅速成为机器学习中最好 的分类算法之一。 支持向量机模型把训练样本映射到高维空间中,以使不同类别的样本能被清晰的超 平面分割出来。而后,新样本继续映射到相同的高维空间,基于它落在超平面的哪一边 预测样本的类别,所以支持向量机模型是非概率的线性模型。 给定训练集合fx;y } = f(x1;y1; , (xn;yn))g,其中x i ∈ Rp 表示第 i 个用户的 p 维 ¢¢ · 特征,y i ∈ f.1 , 1 } 表示第 i 个用户是否购买该商品。任意的超平面满足 : j w t x + b | = 1 (5-11 ) 如果训练集合是线性可分的,那么我们选择两个超平面分割数据集,使得两个超平面之 间没有样本点并且最大化超平面之间的距离。 T (w x + b . 1; y = 1 w T x + b . 1; y = . 1 故,对于任意样本点,可以得到 : yi( x T x i + b ) . 1 (5-12 ) 进一步,可以得到 : ar g mi n 1 2 kw . 2 , st:yi( x T x i + b ) . 1 (5-13 ) w; b 第 5 章混合推荐系统145 | 为了求解优化问题,我们引入拉格朗日乘子 : n 1 .yi( w T x i + b . 1)) , st:. i . 0 (5-14) 2 L(w;b;.) = .i 2 kwk . i= 1 通过求导,可以得到 : n (5-15 ) . i= 1 @ L .iyix i = w . @ w n @ L .iy i = @ b i= 1 根据KK T 条件,可以得到 : T 8i , .i.yi(wx i + b . 1) . = 0 (5-16 ) 从而 : . i = 0 o r yi( w T x i + b ) = 1 (5-17 ) 然而 , 只有一些. i = 0 6,相应地,那些满足yi(w T x i + b)= 1 的x i 就是支撑向量。 如果训练集合是线性不可分的,即样本点线性不可分 : yi( w T x i + b . 1 ) . 1 (5-18 ) 我们可以弱化约束条件,使得 : yi( w T x i + b . 1 ) . 1 . .i;. i . 0 (5-19 ) 那么,优化问题变成 : n 1 2 + c T x i + b ) . 1 . .i argmi n st:yi( x (5-20) .i; 2 kwk w; b i= 1 为了求解优化问题,我们引入拉格朗日乘子 : nn n i= 1 ˉi.i , 1 T .yi(wx i +b)+. i .1¢ . 2 + c st:. i . 0 (5-21 ) L(w;b;.;.;ˉ) = . i . .i 2 kw . i= 1 i= 1 通过求导,可以得到 : n i= 1 .iyix i @ L = . w . @ w n @ L .iy i (5-22) = @ b i= 1 @ L @. i = c . . i . ˉ i 14 6 推荐系统与深度学习 | 根据KK T 条件,可以得到 : T . 8i , .i.yi(wx i + b . 1 ) + .i . = 0 8i , ˉi. i = 0 从而 : . . i = 0 o r yi( w T x i + b)= 1 . . i ˉ i = 0 o r . i = 0 下面是支持向量机模型的代码实现:[1 ] impor t nump y a s n p clas s SVM() : de f __init__(sel f , C=1.0 , kernel="rbf" , degree=3 , gamma=1.0 , coef0=0.0 , tol=1e-4 , alphatol=1e-7 , maxiter=10000 , numpasses=10 , random_state=None , verbose=0) : self. C = C sel f . kerne l = kerne l sel f . degre e = degre e sel f . gamm a = gamm a sel f . coef 0 = coef 0 sel f . to l = to l self.alphato l = alphato l self.maxite r = maxite r self.numpasse s = numpasse s self.random_stat e = random_stat e self.verbos e = verbos e de f fit(sel f , X , y) : """Fi t th e mode l t o dat a matri x X an d targe t y . X : arra y -lik e , shap e (n_sample s , n_features ) Th e inpu t dat a . y : arra y -lik e , shap e (n_sample s , ) Th e clas s label s . return s a traine d SV M "" " self.support_vectors _ = check_array(X ) self. y = check_array(y , ensure_2d=False ) random_stat e = check_random_state(self.random_state ) [1]实现方式修改来自http://github.com/geek-ai/irgan/master/ite m reconmendatio n 第 5 章混合推荐系统147 | sel f . kerne l _ arg s = { } i f sel f . kerne l = = " rb f " an d sel f . gamm a i s no t Non e : self.kernel_args["gamma" ] = self.gamm a eli f self.kerne l = = "poly" : self.kernel_args["degree" ] = self.degre e self.kernel_args["coef0" ] = self.coef 0 eli f self.kerne l = = "sigmoid" : self.kernel_args["coef0" ] = self.coef 0 K = pairwise_kernels(X , metric=self.kerne l , **self.kernel_args ) self.dual_coef _ = np.zeros(X.shape[0] ) self.intercept _ = _svm.smo ( K , y , self.dual_coef_ , self.C , random_state , self.tol , self.numpasse s , self.maxite r , self.verbose ) # I f th e use r wa s usin g a linea r kerne l , let s als o comput e an d stor e # th e weights . Thi s wil l spee d u p evaluation s durin g testin g time . i f self.kerne l = = "linear" : self.coef _ = np.dot(self.dual_coef _ * self.y , self.support _ vector s _ ) # onl y sample s wit h nonzer o coefficient s ar e relevan t fo r prediction s support_vector s = np.nonzero(self.dual_coef_ ) self.dual_coef _ = self.dual_coef_[support_vectors ] self.support_vectors _ = X[support_vectors ] self. y = y[support_vectors ] retur n sel f de f decision_function(sel f , X) : i f self.kerne l = = "linear" : retur n self.intercept _ + np.dot(X , self.coef_ ) els e : K = pairwise_kernels(X , self.support_vectors_ , metric=self.kerne l , **self.kernel_args ) retur n (self.intercept _ + np.sum(self.dual_coef_[np.newaxi s , : ] * self. y * K , axis=1) ) de f predict(sel f , X) : retur n np.sign(self.decision_function(X) ) 14 8 推荐系统与深度学习 | 5.3. 3 基于梯度提升树的模型 200 2 年Friedma n 等人提出Stochasti c gradien t boostin g 方法并发展成梯度提升树 (GBDT),该算法由于准确率高、训练快速等优点受到广泛关注。它被广泛应用到分类、 回归和排序问题中。该算法是一种Additiv e 树模型,每棵树学习之前Additiv e 树模型的 残差,它在被提出之初就和SV M 一起被认为是泛化能力较强的算法。此外,许多研究者 相继提出XGBoos t 、LightGB M 等,又进一步提升了GBD T 的计算性能。 假定只考虑二分类问题,给定训练集合f(x1;y1; , (xn;yn))g,其中x i ∈ Rp 表示第 ¢¢ · i 个用户的 p 维特征,y i 2f0 , 1 } 表示第 i 个用户是否购买该商品。模型的目标是选择合 适的分类函数 F (x ) 最小化损失函数 : n L = ar g mi n . L(yi; F (xi) ) (5-23 ) F n= 1 梯度提升模型以Additiv e 的形式考虑分类函数 F (x) : T F (x) = . fm(x ) (5-24 ) m= 1 其中 T 是迭代次数,(x) } 被定义成增量的形式,在mt h 步,f m 去优化目标值与fj m. 1 ffmj= 1 累积值之间的残差。对于梯度提升树模型,每个函数f m 是一组包含独立参数的基础分 类器(决策树),模型参数 θ 表示决策树的结构,比如用于分裂内部节点的特征和它的阀 值等。在mt h 步,优化函数可以近似成 : 1 L(yi;Fm.1(xi) + fm(xi) ) ≈ L(yi;Fm.1(xi) ) + gifm(xi) + fm(xi) 2 (5-25) 2 其中Fm.1(xi),g i 分别为 : m.1 Fm.1(xi) = . fj(xi);g i = @L(yi; F (xi) ) F (xi) = Fm.1(xi ) (5-26) @ F (xi ) j j= 1 通过最小化式子(5-25)的右式,可以得到 : n f m = ar g mi n . 1 (fm(xi ) . gi) 2 (5-27) 2 f m i= 1 下面是支持梯度提升树模型的代码实现 : 第 5 章混合推荐系统149 | clas s Tre e : de f __init__(self) : self.split_featur e = Non e self.leftTre e = Non e self.rightTre e = Non e self.real_value_featur e = Tru e self.conditionValu e = Non e self.leafNod e = Non e de f get_predict_value(sel f , instance) : i f self.leafNode : retur n self.leafNode.get_predict_value( ) i f no t self.split_feature : rais e ValueError("th e tre e i s null" ) i f self.real_value_featur e an d instance[self.split_feature ] < self . conditionValu e : retur n self.leftTree.get_predict_value(instance ) eli f no t self.real_value_featur e an d instance[self.split_feature ] = = sel f . conditionValu e : retur n self.leftTree.get_predict_value(instance ) retur n self.rightTree.get_predict_value(instance ) de f describe(sel f , addtion_info="") : i f no t self.leftTre e o r no t self.rightTree : retur n self.leafNode.describe( ) leftInf o = self.leftTree.describe( ) rightInf o = self.rightTree.describe( ) inf o = addtion_info+"{split_feature:"+str(self.split_feature)+",spli t _ valu e : " +str(self.conditionValu e +"[left_tree:"+leftInfo+",right_tree:" + rightInf o +"]} " retur n inf o clas s LeafNode : de f __init__(sel f , idset) : sel f . idse t = idse t self.predictValu e = Non e de f describe(self) : retur n "{LeafNode:"+str(self.predictValue)+"} " de f get_idset(self) : 15 0 推荐系统与深度学习 | retur n sel f . idse t de f get_predict_value(self) : retur n self.predictValu e de f update_predict_value(sel f , target s , loss) : self.predictValu e = loss.update_ternimal_regions(target s , self.idset ) de f FriedmanMSE(left_value s , right_values) : weighted_n_lef t , weighted_n_righ t = len(left_values) , len(right_values ) total_meal_lef t , total_meal_righ t = sum(left_values)/float(weighted_n _ left) , sum(right_values)/float(weighted_n_right ) dif f = tota l _ mea l _ lef t -tota l _ mea l _ righ t retur n (weighted_n_lef t * weighted_n_righ t * dif f * dif f / (weighted_n_lef t + weighted_n_right) ) 5. 4 排序学习 排序学习(Lear n t o rank,L2R)是机器学习和信息检索结合的产物,是一类通过监 督训练来优化排序结果的方法,主要优势在于用监督数据直接来优化排序的结果。排序 学习原本来自信息检索领域,用于对给定查询,根据查询和文档对之间的特征对文档进 行排序,也适用于各类泛检索的任务,例如协同过滤等推荐系统。在排序学习之前,通 用的检索方法,比如T F IDF、BM2 5 和语言模型等方法,除了很少量调参,基本不会用 · 到监督信息。随着互联网的发展,更多的数据积累和更高的精度要求模型能够很好地消 化数据以提高精度,排序学习应运而生。为了提升检索效果,一方面会雇佣人工显式地 标注文档与查询相关与否的标签,这类标注的数据量级一般来说比较小,但是质量很高; 另一方面大量的用户的操作行为(点击、浏览、收藏、购买等)隐式地成为了有效的监督 信号。排序学习使用这两类监督数据取得了非常好的结果,成为现代网页搜索的关键技 术之一①。 5.4. 1 基于排序的指标来优化 在常见的推荐场景下,系统需要预测用户对商品的偏好。之前大部分推荐系统都把 它当作一个回归的任务(CT R 预测),用模型去预测用户对商品的偏好,尝试去拟合整 ①L i Hang , A Shor t Introductio n t o Learnin g t o Ran k 第 5 章混合推荐系统151 | 个商品集合的分数值,力求模型预测的绝对值与标签尽可能一致。经典的回归预测的评 价指标是RMSE(均方根),其定义为: n . (y i . y^i) 2 RMS E = i= 1 n 该值计算了预测值和实际值的平均误差,一般来说,对所有的样本同等看待。如果所有 样本的预测值与标签目标值的绝对大小完全一致,los s 减小为0,才会停止优化。该指标 的计算直接跟每一个样本相关,没有把排序结果当成一个整体去考虑。此处的目标值y^ 一般是相关/不相关,或者是购买和未购买,分别取值 1 或者0。为了避免涉及给目标值 设计一个线性的分值,一般的取值不会超过两个离散的值。 但是在实际推荐系统的场景下,系统更可能关心的是头部预测的结果是否准确,Top N 个结果的偏序关系是否满足用户需求,比如,搜索引擎只要前一两页的结果能够满足用 户就够了,优化后面页面的结果对提升用户体验的效果有限。这样要求就从一个经典的 回归问题转变为考虑一个排序任务。作为排序任务,优化的目标是维持一个相对偏序关 系,对预测分数的绝对值不是那么敏感。优化的目标只要能够保证让正例尽可能排在前 面,而其他的负例只要相对值小一些,那么就可以在生产环境表现得不错。换句话说,推 荐系统希望所有的商品的相对偏序关系能够预测准确(排序方法),而不要求对预测值 的绝对值准确(回归方法)。还有,推荐系统更希望对排名靠前的头部商品更敏感,而基 于回归预测的方法对这种位置偏置并不很敏感。鉴于这两点,基于排序评价指标(而不 是基于回归的评价指标)来评价推荐系统显得更加合理。 经典的排序指标包括MAP(Mea n Reciproca l Rank)、MRR(Mea n Averag e Precision) , 这两类指标是基于分类标签的取值,只有相关(1)或者不相关(0)两个结果。当相关性 的取值不是0/ 1 的时候,例如有“非常相关”“很相关”“相关”“一般相关”和“不相 关”五级的相关性结果时,NDCG(Normalize d Discounte d Cumulativ e Gai n )是一个更常 用的指标。DC G 的定义为: T2l i DCG@ T ≡ . . 1 (5-28) log( 1 + i) i= 1 对于推荐系统给出一个排序列表(对一个用户/查询),l i 是当前系统给出的前 T 个 商品的评分(可以是0/ 1 或者取值更多的细粒度标签)。分子项是推荐l i 的收益,对高 分商品的推荐有指数级的收益,0 分商品没有收益。分母是对位置的偏置,位置越靠后 会有一个衰减系数,排在前面的商品得分越多会有更高的收益。排在后面的商品的收益 15 2 推荐系统与深度学习 | 会有一个跟位置相关的折扣,其对评价结果的影响越来越小,超过截断值 T 的商品对结 果没有影响。把前面 T 个推荐结果累加起来就是DCG,即折扣的累积收益。但是该值 的取值范围没有任何约束,需要归一化。归一化的方法是除以一个理想的最好的DC G 的结果,理想的排序结果是根据标注的结果,从高往低排出一个列表,即是该场景能够 得到的最大nc g 值maxDCG@T(该场景能做到的最大的排序收益)。将系统排序的结果 除以maxDCG@T,就会归一化到 0 到 1 之间,最好情况跟理想的排序结果一致,即是 1。RMS E 只跟单个样本的结果相关,不同样本的预测结果之间不会直接关联起来,而 NDC G 指标是针对整个排序的列表去计算,优化的是一个整体排序的结果。 5.4. 2 L2 R 算法的三种情形 L2 R 系列算法一般分成三类,分别是Point-wis e 、Pair-wis e 和List-wise。 5.4.2. 1 Point-wis e Point-wis e 的方案实现简单,基于单个样本去优化,排序问题退化成通用的回归/分 类问题,一般是一个二分类的任务,是机器学习的典型判别问题。对于用户(query)q , 两个商品D i 和Dj , 排序模型的核心就是根据两个商品的特征来学习一个分数映射f , 使得S i = f(Xi),x i 可以是一些手工特征(跟D i 有关或者是跟D i 和 q 都相关),也 可以是一些其他模型的结果放进来集成学习。 f 可以是一个逻辑回归模型、迭代决策树 GBDT(MART),也可以是一个多层的神经网络。 该问题有一个显著的问题就是,模型的分数是用户无关的,所有用户和商品的打分 会有统一的度量作为预测值。第一个问题是对头部的商品不敏感。第二个问题是无法有 效地容忍某个用户或者某个商品的偏置,例如,对于不同用户(query)而言,只要商品 (documen t )的标签是1(0/ 1 两个取值的标签),那么他们就会被归为一类。即使用户 A(query)的所有实际购买商品(documen t )的特征值算出来的预测值普遍都比较低,另 外一个用户B(query)的所有实际购买商品(documen t )的特征值算出来的预测值普遍 相对偏高,他们的标签的目标值都是1。 5.4.2. 2 Pair-wis e Pair-wis e 的方案将排序问题约减成一个对偏序对的二分类问题,即偏序对关系正确 还是错误,一个附带的好处是可以方便利用多粒度的相关性,即使用户对商品有着非线 第 5 章混合推荐系统153 | 性的多级评价程度,例如,P(perfec t )非常满意、G(Goo d )满意、满意B(Bad),也可 以方便地去构造这样的偏序对。 在给定查询 q 的场景下,文档对的差值归一化成一个概率分布(其实就是一个二项 分布,包含预测偏序关系成立和不成立的的两个概率),然后根据该分布与目标标签的差 异(例如交叉熵损失)来通过标准梯度下降方法进行优化。我们把两个分数的差值S i .S j 通过Sigmoi d 函数归一化到0.1(满足概率的定义),它的含义为D i 比D j 更好的概率: 1 Pi j ≡ P (U i . Uj ) ≡ 1 + e..(si.s j ) (5-29 ) 定义损失函数为交叉熵损失函数: C = .P^i j logPi j . ( 1 . P^ij)log( 1 . Pij ) (5-30 ) 其中P^是实际的标签,所以上式P^i j 和( 1 . P^ij ) 必有一个是零项,也就是上述两个子式 只有一项不为0。按照标准的梯度下降就可以优化这个损失函数。 这样的优化有一个典型的问题,如图5. 8 所示是一个排序结果的例子,一共有1 5 个 需要排序的商品(item/document),蓝色是正例,需要排在上面,当前排序第 1 和第1 4 错位,所以一共有1 3 个文档对的顺序错了,总的损失由1 3 个排序错误的损失相加。模型 图5. 8 Lear n t o ran k 的局限 15 4 推荐系统与深度学习 | 经过一轮迭代之后,第一个正例文档排到了第4,第二个负例文档排到了第10,此时有 1 1 个文档对排序错误,损失可能是减小了,但是评价结果却反而恶化了,因为最前面的 三个结果都是错的。我们需要一个对前面的排序结果更敏感的模型,像浅色箭头那样, 把能更好提升文档对顺序预测对。黑色的箭头虽然能够减少总的loss,但是实际并没有 提高评价指标,如NDCG。 对推荐任务有一个合理的评价指标是我们做推荐任务的一个前提,但是评价指标无 法直接嵌入到损失函数,优化的目标不能直接提升检索和推荐的性能。根据上一节的介 绍,实际上NDC G 评价指示的计算函数并不是连续的,也就是说在优化的时候,即使模 型参数有小的变动,虽然预测的分数会平滑地改变,但是如果分数改变没有改变其中任 意文档之间的相对大小,其NDC G 指标没有变化,这样的指标并不好直接定义成损失函 数。 在pair-wis e 的Lear n t o ran k 范式中,lambd a 系列算法的提出就是为了解决这个问 题,在训练阶段可直接优化评价指标。一个常见的方法是直接更改损失函数,lambd a 系 列(如LambdaRan k )算法正是如此。lambd a 的物理意义是梯度更新的方向和大小。对 于查询 q 对应的文档对d i 和dj,lambd a 定义为交换d i 和d j 排序结果的NDC G 的变化 值¢NDCG。梯度下降求解如下: λ = @C(s i . s j ) . σ ¢NDCG(5-31) @s i . 1 + e.(si.sj ) j | 在pair-wis e 的场景下,训练的样本是给定的查询 q 和一对文档d i 和d j ,lambd a 系 列算法的做法是在当前的样本的损失函数里面算上一个增益/折扣因子,该因子在反向 传播的时候,可以理解成一个常数,等价于对所有需要更新的参数的梯度上乘以一个该 增益/折扣因子。 以NDC G 为例,该增益/折扣因子就是,当前模型针对quer y 评价指标的优化结果。 直观的意义是如果这样一个文档偏序对交换顺序之后对NDC G 的影响很大,那么这次 梯度方向会更新更多的梯度,如果影响很小,会更新的更少,这样一个技巧就会给模型 带来很大性能提升。 这种方式是跟用户(query)相关的,单个用户(query)和所有商品的偏好预测值的绝 对值满足了排序关系,就无需继续优化。该方式存在一些问题,例如,不同用户(query ) 的偏序对的数量可能差异比较大,使得模型结果在偏序列对多的同用户(query)较好, 没有消除不同用户(query)的样本数量的偏置。 第 5 章混合推荐系统155 | 5.4.2. 3 List-wis e 基于整个排序列表去优化,对于单个用户(query)而言,把整个需要排序的列表当 成一个学习样本(instance),直接通过NDC G 等指标来优化。例如,AdaRan k 和ListNet , 直接使用定义在一个排序结果列表上的损失函数。AdaRan k 直接针对每一个quer y 对整 个排序列表计算与理想列表的差异,然后通过boos t 策略来调节不同quer y 的权重。一 般来说,基于list-wis e 比pair-wis e 更有效,而pair-wis e 比point-wis e 有效,实际经验上 的结果或许会有部分差异。 深度学习的爆发使得人工智能进一步发展,阿里巴巴、腾讯、百度先后建立了自己 的A I Lab s ,就连传统的厂商OPPO、VIV O 都在筹备建立自己的人工智能研究所。我们 都知道深度学习在图像、语言处理上有得天独厚的优势,并且已经得到了业界的认可和 验证。那么为什么推荐系统也需要引入深度学习呢?推荐系统从基于内容的推荐,到协 同过滤的推荐,协同过滤的推荐在整个推荐算法领域多年来独领风骚,从基本的基于用 户的协同过滤,基于ite m 的协同过滤,到基于mode l 的协同过滤,众多算法不断发展和 延伸。或许深度学习在推荐系统里面没有像在图像处理领域那样一枝独秀,但是深度学 习对于推荐系统在以下几个方面确实起到了不可替代的作用: 1)能够直接从内容中提取特征,表征能力强; 2)容易对噪声数据进行处理,抗噪能力强; 3)可以使用循环神经网络对动态或者序列数据进行建模; 4)可以更加准确地学习use r 和ite m 的特征。 并且从最近国外推荐系统论文的发表情况上看,深度学习已经深入扩展到推荐系统 领域。我们对深度学习在推荐系统应用的主要方法进行了系统整理,供读者深入了解 深度学习在推荐系统的进展。下面介绍几种基于深度学习的推荐算法,其中对于基于 DeepF M 的模型,我们将会一步一步地介绍其tensor°o w 实现方法,其他的模型代码可以 直接参考本书的GitHub。 6. 1 基于DN N 的推荐算法 推荐系统和类似的通用搜索排序问题共有的一大挑战为同时具备记忆能力与泛化能 力。记忆能力可以解释为学习那些经常同时出现的特征,发掘历史数据中存在的共现性。 泛化能力则基于迁移相关性,探索之前几乎没有出现过的新特征组合。基于记忆能力的 第 6 章基于深度学习的推荐模型157 | 推荐系统通常偏向学习历史数据的样本,直接与用户已经采取的动作相关;泛化能力相 比记忆能力则更趋向于提升推荐内容的多样性。 对工业界大规模线上推荐和排序系统中,广义线性模型(如逻辑回归)得到了广泛应 用,因为它们简单,可扩展,可解释。这些模型一般在二值稀疏特征上训练,这些特征一 般采用独热编码。举个例子,如果用户安装了腾讯视频,则二值特征use r installe d app = TencenVide o 的值为1。模型的记忆能力可以有效地通过稀疏特征之上的外积变换获得, 类似地,当用户安装了腾讯视频,随后又展示了Q Q 音乐,那么AND(use r installe d app = TencentVideo , impressio n app=QQMusic)的值为1。这解释了同时发生的一对特征是如 何与对应标签关联的。进一步我们可以通过使用小颗粒特征提高泛化能力,例如,AN D (use r installe d category=video , impressio n category=music),但这些特征常常需要人工来 选择。外积变换有一个限制,它对于不在训练数据中的查询项不具备泛化能力。 另一方面基于嵌入的模型(如factorizatio n machin e 和深度神经网络)对以前没有 出现过的查询项特征对也具备泛化能力,通过为每个查询和条目特征学习一个低维稠密 的嵌入向量,减轻了特征工程负担。但它很难有效学习低维表示,当query-ite m 矩阵稀 疏且高秩时,例如用户有特殊偏好,或者只有极少量需求的条目,这种情况下,大多数 query-ite m 是没有交集的,但稠密嵌入会给所有query-ite m 带来非零预测,从而可能过 度泛化,给出完全不相关的推荐。而使用外积特征变换的线性模型只需少量参数就能记 住这些“特殊偏好”。 所以自然而然地可以想到,通过联合训练一个线性模型组件和一个深度神经网络组 件得到Wid e & Dee p 模型(如下图所示)。这样用一个模型就可以同时获得记忆能力和 泛化能力。 图6. 1 Wid e & Dee p 模型结构 YouTub e 团队在推荐系统上进行了DN N 方面的尝试,发表在201 6 年 9 月的RecSy s 会议上,目前已经被百度、阿里巴巴、腾讯等各大互联网公司引入推荐系统中。一般来 说整个推荐系统分为召回(Matchin g 或candidat e generatio n )和排序(Ranking)两个阶 15 8 推荐系统与深度学习 | 段。召回阶段通过i2i/u2i/u2u/use r proˉl e 等方式“粗糙”地召回候选物品,召回阶段视 频的数量是百万级别;排序阶段对召回后的视频采用更精细的特征计算user-ite m 之间 的排序分,作为最终输出推荐结果的依据。 第一部分:召回阶段 我们把推荐问题建模成一个“超大规模多分类”问题。即在时刻t,为用户U(上下 文信息C)在视频库 V 中精准地预测出视频 i 的类别(每个具体的视频视为一个类别, i 即为一个类别),用数学公式表达如下: vi;u e P (w t = iU;C) = Pj2 V evi; u (6-1) j 很显然上式为一个softma x 多分类器的形式。向量 u ∈ R N 是 信息 的高维“embedding”,而向量v j ∈ R N 则是视频 j 的embeddin g 向量。所以DN N 的目标 就是在用户信息和上下文信息为输入条件下学习用户的embeddin g 向量u。用公式表达 DN N 就是在拟合函数 u = f DNN(use r info;contex t info)。 图6. 2 推荐系统的召回和排序两个阶段 在这种超大规模分类问题上,至少要有几百万个类别,实际训练采用的是Negativ e Sampc e (负采样),或是采用前面我们介绍word2ve c 方法时提到的SkipGra m 方法。 整个模型架构是包含三个隐层的DN N 结构。输入是用户浏览历史、搜索历史、人口 统计学信息和其余上下文信息conca t 成的输入向量;输出分线上和离线训练两个部分。 第 6 章基于深度学习的推荐模型159 | 离线训练阶段输出层为softma x 层,输出公式(6-1)表达的概率。而线上则直接利用use r 向量查询相关商品,最重要问题在性能方面。我们利用类似局部敏感哈希的算法为用户 提供最相关的 N 个视频。 图6. 3 召回模型结构 类似于word2ve c 的做法,每个视频都会被embeddin g 到固定维度的向量中。用户的 观看视频历史则是通过变长的视频序列表达,最终通过加权平均(可根据重要性和时间 进行加权)得到固定维度的watc h vecto r 作为DN N 的输入。 除历史观看视频外还包括以下其他特征。 历史搜索query:把历史搜索的quer y 分词后的toke n 的embeddin g 向量进行加权平 均,能够反映用户的整体搜索历史状态; 人口统计学信息:性别、年龄、地域等; 其他上下文信息:设备、登录状态等。 在有监督学习问题中,最重要的选择是labe l (目标变量)了,因为labe l 决定了模型 的训练目标,而模型和特征都是为了逼近labe l 。YouTub e 也提到了如下设计: 使用更广的数据源:不仅仅使用推荐场景的数据进行训练,其他场景比如搜索等的 数据也要用到,这样也能为推荐场景提供一些探索功能。 为每个用户生成固定数量训练样本:我们在实际中发现一个训练技巧,如果为每个 | 提升线上效果。 推荐系统与深度学习 用户固定样本数量上限,平等地对待每个用户,避免loss被少数活跃用户代表,能明显 抛弃序列信息:对过去观看视频/历史搜索query的embedding向量进行加权平均。 不对称的共同浏览(asymmetriccowatch)问题:所谓asymmetriccowatch指的是用 户在浏览视频时候,往往都是序列式的,开始看一些比较流行的,逐渐找到细分的视频。 下图所示图(a)是heldout方式,利用上下文信息预估中间的一个视频;图(b)是 predictingnextwatch的方式,则是利用上文信息,预估下一次浏览的视频。我们发现图 (b)的方式在线上A/Btest中表现更佳。而实际上,传统的协同过滤类算法,都是隐含 地采用图(a)的heldout方式,忽略了不对称的浏览模式。 图6. 4 序列信息 第二部分:排序阶段 排序阶段最重要任务就是精准地预估用户对视频的喜好程度。不同于召回阶段面临 的是百万级的候选视频集,排序阶段面对的只是百级别的视频集,因此我们可以使用更 多更精细的特征来刻画视频(item)以及用户与视频(user-item)的关系。比如用户可能 很喜欢某个视频,但如果列表页选用的“缩略图”选择不当,用户也许因此不会点击,等 等。此外,召回阶段的来源往往很多,没法直接相互比较,排序阶段另一个关键的作用 是能够把不同来源的数据进行有效的比较。 在目标的设定方面,单纯CT R 指标是有迷惑性的,有些靠关键词吸引用户高点击 的视频未必能够被播放。因此设定的目标基本与期望的观看时长相关,具体的目标调整 则根据线上的A/ B 进行调整。 排序阶段的模型和召回基本相似,不同的是模型最后一层是一个weighte d L R 层,而 线上服务阶段激励函数用的是ex。 第 6 章基于深度学习的推荐模型161 | 图6. 5 排序模型结构 尽管深度学习在图像、语音和NL P 等场景都能实现end-to-en d 的训练,取消了人工 特征工程工作。然而在搜索和推荐场景,我们很难把原始数据直接作为FN N 的输入,特 征工程仍然很重要。特征工程中最难的是如何建模用户时序行为(tempora l sequenc e o f use r actions),并且将这些行为和要排序的ite m 相关联。 YouTub e 发现最重要的特征是描述用户与商品本身或相似商品之间交互的特征,这 与Faceboo k 在201 4 年提出LR+GBD T 模型的论文(Practica l Lesson s fro m Predictin g Click s o n Ad s a t Faceboo k )中得到的结论是一致的。比如我们要度量用户对视频的喜欢 程度,可以考虑用户与视频所在频道间的关系。 数量特征:浏览该频道的次数;时间特征:比如最近一次浏览该频道距离现在的时 间。这两个连续特征的最大好处是具备非常强的泛化能力。另外除了这两个偏正向的特 征,用户对于视频所在频道的一些P V 但不点击的行为,即负反馈Signa l 同样非常重要。 另外,我们还发现,把召回阶段的信息,比如推荐来源和所在来源的分数,传播到排 序阶段同样能取得很好的提升效果。 N N 更适合处理连续特征,因此稀疏的特别是高基数空间的离散特征需要embeddin g 到稠密的向量中。每个维度(比如query/use r id)都有独立的embeddin g 空间,一般来说 空间的维度基本与log(去重后值的数量)相当。实际并非为所有的i d 进行embedding , 比如视频id,只需要按照点击排序,选择to p N 视频进行embedding,其余置为 0 向量即 可。而对于像“过去点击的视频”这种multivalen t 特征,与Matchin g 阶段的处理相同, 进行加权平均即可。同时,同维度不同特征采用的相同I D 的embeddin g 是共享的(比如 16 2 推荐系统与深度学习 | “过去浏览的视频id”“see d 视频id”),这样可以大大加速训练过程,但显然输入层仍 要分别填充。 众所周知,N N 对输入特征的尺度和分布都是非常敏感的,实际上,基本上除了 TreeBase d 的模型(比如GBDT/RF),机器学习的大多算法都如此。我们发现归一化方 法对收敛很关键,推荐一种排序分位归一到[0 , 1 ] 区间的方法,即x1 = . x d f ,累计分 位点。 . ∞ 除此之外,我们还把归一化后的x1的平方根px1和平方x1 2 作为网络输入,以期使网 络能够更容易得到特征的次线性(sub-linear)和(super-linea r )超线性函数。 最后,模型的目标是预测期望观看时长。有点击的为正样本,有P V 无点击的为负样 本,正样本需要根据观看时长进行加权。因此,训练阶段网络最后一层用的是weighte d logisti c regressio n 。 正样本的权重为观看时长Ti,负样本权重为1。这样的话,L R 的期望为: T i (6-2) N . k 其中 N 是总的样本数量, k 是正样本数量,T i 是第 i 个正样本的观看时长。一般 来说, k 相对 N 比较小,因此上式的期望可以转换成E[ T ]=( 1 + P ),其中 P 是点击率。 点击率一般很小,这样目标期望接近于E[T],即期望观看时长。因此在线上servin g 的 inferenc e 阶段,采用e x 作为激励函数,就是近似的估计期望观看时长。 下图是离线利用hold-ou t 一天数据在不同N N 网络结构下的结果。如果用户对模型 预估高分的反而没有观看,则认为是预测错误的观看时长。weighted , per-use r los s 就是预 测错误观看时长占总观看时长的比例。 图6. 6 不同N N 的效果 YouTub e 对网络结构中隐层的宽度和深度方面都做了测试,从下图结果看增加隐层 第 6 章基于深度学习的推荐模型163 | 网络宽度和深度都能提升模型效果。而对于102 4 → 51 2 → 25 6 这个网络,测试不对预测 目标(观看时长)进行归一化,los s 增加了0.2%。而如果把weighte d L R 替换成LR,效 果下降达到4.1%。 6. 2 基于DeepF M 的推荐算法 除了Deep&Wid e 模型,DeepF M 也是一个被广泛应用在点击率预测中的深度学习 模型,该模型的设计思路来自哈工大&华为诺亚方舟实验室,主要关注如何学习use r behavio r 背后的组合特征(featur e interactions),从而最大化推荐系统的CTR。论文提出 构建一个端到端的可以同时突出低阶和高阶featur e interaction s 的学习模型DeepFM。 DeepF M 是一个集成了FM(Factorizatio n Machin e )和DN N 的神经网络框架,思路 和上文提到的Wide&Dee p 有相似的地方。本节将具体介绍该模型的原理、网络结构设 计方法以及其与Wide&Dee p 模型的比较。 我们都知道Logisti c Regressio n 是CT R 预估中最常用的算法。但L R 有一个大前提, 即假设特征之间是相互独立的,没有考虑特征之间的相互关系。换句话说,L R 在模型侧 忽略了featur e pai r 等高阶信息。比如,在一些场景下,我们发现用户年龄和性别是十分 重要的特征,但L R 只能单独处理这 2 个特征,比如女性比男性点击率高,年纪越小点 击率越高。如果需要得到20.3 0 岁的女性,15.2 0 岁男性点击率高这样更精确的组合特 征,需要人工对两个特征进行交叉。两个特征尚能做人工的交叉,但几十维的特征两两 交叉起来,特征工程将会十分巨大。所以F M 算法在CT R 预估中才会比较重要。F M 简 要思路参考: 假设L R 算法决定追加考虑任意两个特征之间的关系,则模型改写成: nn.1n μ(x) = w 0 + . wix i + X . wijxix j (6-3 ) i= 1 i= 1 j=i+ 1 其中wi j 是featur e pair 的交叉权重。相对于L R 模型,式6- 3 会有如下问 题。 1)参数空间大幅增加,由线性增加至平方级。 2)样本比较稀疏。 因此,我们需要一种在模型侧计算高阶信息的低复杂度方法。F M 就是其中一种方 16 4 推荐系统与深度学习 | 法,它把wi j 分解成 2 个向量 : nn.1n μ(x) = w 0 + . wix i + X . xix j (6-4 ) i= 1 i= 1 j=i+ 1 直观来看,F M 认为当一个特征w i 需要与其他特征w j 考虑组合特性的时候,只需 要一组 k 维向量v i 即可代表xi,而不需针对所有特征分别计算出不同的组合参数wij。 这相当于将特征映射到一个 k 维空间,用向量关系表示特征关系。这种思想与前面我们 介绍的矩阵分解(SVD)的思想是一致的。 单独使用F M 算法考虑到了低阶特征的组合问题,但是无法解决高阶特征的挖掘问 题,所以才有引入DeepF M 的必要性。 DeepF M 是一个集成了F M 和DN N 的神经网络框架,思路和Googl e 的Wide&Dee p 有相似的地方,都包括wid e 和dee p 两部分。W& D 模型的wid e 部分是高维线性模 型,DeepF M 的wid e 部分则是F M 模型;两者的dee p 部分是一致的,都是DN N 层。 图6. 7 DeepF M 模型结构(网络左边为F M 层,右边为DN N 层) W& D 模型的输入向量维度很大,因为wid e 部分的特征包括了手工提取的pair-wis e 特征组合,大大提高计算复杂度。和W& D 模型相比,DeepF M 的wid e 和dee p 部分共 享相同的输入,可以提高训练效率,不需要额外的特征工程,用F M 建模低阶的特征组 合,用DN N 建模高阶的特征组合,因此可以同时从ra w featur e 中学习到高阶和低阶的 特征交互。在真实应用市场的数据集上实验验证,DeepF M 在CT R 预估的计算效率和 AU C 、LogLos s 上超越了现有的模型(LR、FM、FNN、PNN、W&D)。 前面介绍了DeepFM算法的基本原理,下面将具体介绍如何用tensor°o w 搭建DeepFM。 第 6 章基于深度学习的推荐模型165 | 1)实现F M 中的一阶部分 : F M 中一阶部分和L R 模型类似,主要是将特征分别乘上对应的系数 : á(x) = w 0 + n wixi . i= 1 + nn . j=1+ 1 . i= 1 wi j xix j 对应下面网络图中框出的部分: 图6. 8 F M 一阶部分 网络结构代码实现: # ----------firs t orde r ter m --------- ##初始化wi j feature_bia s = tf.Variable(tf.random_uniform([feature_siz e , 1] , 0.0 , 1.0) , name="feature_bias_0" ) # featur e _ siz e * 1 y_first_orde r = feature_bia s # # wi j * xi j y_first_orde r = tf.reduce_sum(tf.multiply(y_first_order , feat_value) , 2 ) # Non e * F ##增加,dropou t 防止过拟合 y_first_orde r = tf.nn.dropout(y_first_orde r , dropout_keep_fm[0] ) # Non e * F 2 ) 实现F M 中的二阶部分 : F M 中的二阶部分,主要是对x i 和x j 两两组合,并且找到它们分别对应的特征 向 16 6 推荐系统与深度学习 | 量。 n 对应下面网络图中框出的部分: . i= 1 nn xix j i= 1 j=1+ 1 á(x) = w 0 + wix i + 图6. 9 F M 二阶部分 为了更方便实现二阶部分,我们进一步进行推导:最后转化为和平方与平方和两部 分; nXnX(vi , vj)xix j = 1 i= 1 2 nXn . j=i+ 1 (vi , vj)xix nX j . 1 (vi , vj)xixj 2 = 1 2 . . nXnXk . i= 1 j= 1 vi; f v i= 1 j= 1 f= 1 i= 1 j; f xix j . nXkXvi; f vi; f xix i i= 1 f= 1 . . = 1 kX . . . nX!nX . . . A nX 2 2 v . A vi;fx i vj; f x j . i; f xi 2 · i= 1 j= 1 i=1 f= 1 0 . 1 . . n . i= 1 ! 2 vi; f x i . n i= 1 1 2 2 2 vi; f x i = 网络结构代码实现: @ . 和平方 平方和 第 6 章基于深度学习的推荐模型167 | # ----------firs t orde r ter m --------- # ----------secon d orde r ter m -------------- #v i * x i embedding s = tf.multiply(embedding s , feat_value ) #和平方 summed_features_em b = tf.reduce_sum(embedding s , 1 ) # Non e * K summed_features_emb_squar e = tf.square(summed_features_emb ) # Non e * K #平方和 squared_features_em b = tf.square(embeddings ) squared_sum_features_em b = tf.reduce_sum(squared_features_emb , 1 ) # Non e * K #和平方与平方和按公式组合 y_second_orde r = 0. 5 * tf.subtract(summed_features_emb_squar e , square d _ su m _ feature s _ emb ) # Non e * K y_second_orde r = tf.nn.dropout(y_second_order , dropou t _ kee p _f m [1] ) # Non e * K 3 ) 实现DNN : 传统的多层感知机,增加dropou t 防止过拟合。 wit h tf.name_scope("deep") : # ----------Dee p componen t --------- y_dee p = tf.reshape(embedding s , shape=[-1 , featur e _ siz e * embeddin g _ siz e ] ) # Non e * (F*K ) y_dee p = tf.nn.dropout(y_dee p , dropout_keep_deep[0] ) weight s = dict( ) ##初始化各层的权重 input_siz e = feature_siz e * embedding_siz e gloro t = np.sqrt(2. 0 / (input_siz e + deep_layers[0]) ) weights["layer_0" ] = tf.Variable ( np.random.normal(loc=0 , scale=glorot , size=(input_size , deep_layers[0])) , dtype=np.float3 2 , name="weights_layer0" ) weights["bias_0" ] = tf.Variable(np.random.normal(loc=0 , 16 8 推荐系统与深度学习 | scale=gloro t , size=(1 , deep_layers[0])) , dtype=np.float3 2 ,name="weights _ bias 0 " ) num_laye r = len(deep_layers ) fo r i i n range(1 , num_layer) : gloro t = np.sqrt(2. 0 / (deep_layers[i-1 ] + deep_layers[i]) ) weights["layer_%d " % i ] = tf.Variable ( np.random.normal(loc=0 , scale=gloro t , size=(deep_layers[i-1] , deep_layers[i])) , dtype=np.float3 2 ,name="weights_layer"+str(i) ) # layers[i-1 ] * layers[i ] weights["bias_%d " % i ] = tf.Variable ( np.random.normal(loc=0 , scale=gloro t , size=(1 , dee p _ layer s [i]) ) , dtype=np.float3 2 ,name="weights_bias"+str(i) ) # 1 * laye r [i ] ##对dnn的各层进行连接 fo r i i n range(0 , len(deep_layers)) : y_dee p = tf.add(tf.matmul(y_dee p , weights["layer_%d " %i]) , weight s [ " bia s _%d"% i ] ) # Non e * layer[i ] * 1 #i f sel f . batc h _ nor m : # self.y_dee p = self.batch_norm_layer(self.y_deep , train_phase=self.train_phas e , scope_bn="bn_%d " %i ) # Non e * layer[i ] * 1 y_dee p = tf.nn.relu(y_deep ) y_dee p = tf.nn.dropout(y_dee p , dropout_keep_deep[1+i] ) # dropou t a t eac h Dee p laye r 这里对权重初始化使用了glorot,根据输入与输出层的神经元个数进行分布初始化, 减少梯度爆炸和梯度弥散的风险。 4)DNN+F M 融合 将两者的输出进行连接,并线性组合起来,通过sigmoi d 函数转换成最后的得分。 如果是DEE P 与F M 融合,则将 2 个部分的输出进行concat,如果只是单一的DN N 或者F M 则只使用一部分的输出。代码中由MODETYP E 控制网络类型。 第 6 章基于深度学习的推荐模型169 | # ----------DeepF M --------- wit h tf.name_scope("deepfm") : concat_inpu t = tf.concat([y_first_orde r , y_second_orde r , y_deep] , axi s =1 ) i f MODETYP E ==0 : ##deepf m concat_inpu t = tf.concat([y_first_orde r , y_second_orde r , y_dee p ] , axi s =1 ) input_siz e = feature_siz e + embedding_siz e + deep_layers[-1 ] eli f MODETYP E ==1 : ##f m onl y concat_inpu t = tf.concat([y_first_orde r , y_second_order] , axis=1 ) input_siz e = feature_siz e + embedding_siz e eli f MODETYP E ==2 : ##dn n onl y concat_inpu t = y_dee p inpu t _ siz e = dee p _ layer s [ -1 ] gloro t = np.sqrt(2. 0 / (input_siz e + 1) ) weights["concat_projection" ] = tf.Variable(np.random.normal(loc=0 , scale=gloro t , size=(input_siz e , 1)) , dtype=np.float3 2 ,name="concat_projection0" ) # layer s [ i -1 ] * layer s [i ] weights["concat_bias" ] = tf.Variable(tf.constant(0.01) , dtype=np.float3 2 ,name="concat_bias0" ) ou t = tf.add(tf.matmul(concat_inpu t , weight s [ " conca t _ projectio n "] ) , weight s [ " conca t _ bia s "] , nam e = 'ou t ' ) score=tf.nn.sigmoid(ou t ,name = 'scor e ' ) ##观看变量 tf . summar y . histogra m ( " dee p +f m "+" / scor e " , scor e ) 5 ) 评估器的设计 自定义损失函数,常用的损失函数最小平方误差准则(MSE)和交叉熵等。同时我 们可以利用TensorBoar d 观测模型AU C 等指标的变化情况: i f arg s . mode l _ typ e ==0 : estimat e _ nam e = " DeepF m _ Estimat e " 17 0 推荐系统与深度学习 | eli f arg s . mode l _ typ e ==1 : estimat e _ nam e ="Fm _ Estimat e " eli f arg s . mode l _ typ e ==2 : estimat e _ nam e = " Dee p _ Estimat e " wit h tf.name_scope(estimate_name) : #损失函数的定义:均方差 los s = tf.reduce_mean(tf.reduce_sum(tf.square( y -prediction) , reduction_indice s =[1]) ) 观看常量 tf . summar y . scala r ( 'los s ',los s ) au c = tf.contrib.metrics.streaming_auc(predictio n ,tf.convert_to_tenso r (y) ) ##观看常量 tf . summar y . scala r ( 'auc 1 ',au c [0] ) tf . summar y . scala r ( 'auc 2 ',au c [1] ) 6 ) 通过TensorBoar d 观察模型各项指标: 在前面的模型结构构建过程中,我们已经在tf.summar y 中增加score、loss、au c 的监 控。在训练迭代过程中,我们注意将每次的中间结果merg e 进去,即可在TensorBoar d 中 观察收敛过程。 wit h tf.Session( ) a s sess : save r = tf.train.Saver( ) ses s . ru n ( ini t ) ses s . ru n (tf . loca l _ variable s _ initialize r () ) #合并到Summary中 merge d = tf.summary.merge_all( ) #选定可视化存储目录 write r = tf.summary.FileWriter ( '. / tmp / deepf m ',grap h = tf . get _ defaul t _ grap h () ) fo r _ i n rang e (400 ) : sess.run(train_ste p , feed_dict={x : x_dat a , y : y_data} ) i f _ %5= = 0 : #print(str ( _)+" : los s = " ) 第 6 章基于深度学习的推荐模型171 | print("[%d]loss:%s"%(_,sess.run(los s , feed_dict={x : x_dat a , y : y _ dat a })) ) resul t = sess.run(merge d ,feed_dict={x : x_dat a , y : y_data} ) #merged也是需要run的 writer.add_summary(resul t ,_ ) #result是summary类型的,需要放入writer中,i步数(x轴 ) 图6.1 0 FM/DNN/DeepF M 的比较 los s 和au c 的变化:可以看到DeepF M 比F M 有显著提升,对比DN N 也有一定幅 度提升。使用相同的样本数据训练,在测试集上DN N 的AU C 为0.73,DeepF M 的AU C 为0.75,F M 的AU C 为0.70(特征工程仍然是最重要的,特征越多,差异越明显) 6. 3 基于矩阵分解和图像特征的推荐算法 传统的推荐系统往往会遇到行为数据稀疏、冷启动等问题,比如在Net°i x 数据库, 平均每个用户只参与20 0 个电影的评分,但实际上,数据库里有上万部电影,稀疏的评 17 2 推荐系统与深度学习 | 分数据不利于模型的学习。因此,寻找一些附加信息帮助模型训练是非常有用的手段。 近年来,基于上下文环境的推荐系统引起了大家的广泛关注。这些上下文环境包括 电影的属性、用户画像特征、电影的评论等等。研究人员希望通过这些附加信息来缓解 评分数据稀疏等问题,对于那些没有评分数据的电影,可以基于上下文环境来推荐,从 而进一步提升推荐系统的质量。 研究人员观察到一个有趣的现象,电影的海报和一些静止帧图片能提供许多有价值 的附加信息。如图6.11,作者展示了两部电影,每部电影有一张海报和两张静止帧图片。 虽然这两部电影有着不同风格和完全不同的演员阵容,但从调查研究中发现,喜欢电影 1 的用户也会对电影 2 感兴趣。实际上,电影 2 的拍摄和制作是受到电影 1 的启发,从 视觉角度出发,两部电影的海报和静止帧图片有一定程度的相似性。因此,研究人员认 为应该把视觉特征作为附加信息用于提升推荐系统的质量。为此,作者们提出了一种基 于矩阵分解和图像特征的推荐算法(Matri x Factorization + ,缩写为MF+)。 图6.1 1 电影静止帧图片举例 具体来说,假定有稀疏偏好矩阵 X ∈ Rm£ n , 其中 m 代表用户的数量 , n 代表商品的 数量。矩阵 X 里的每个元素xu v 代表用户 u 对商品 v 的偏好。如果用户 u 对商品 v 没 有点评,那么xu v = 0 。 I 是所有能观察到的(u;v ) 集合。在基于评分的推荐系统里,偏好 定义成离散的数值[1 , 2; , 5],分数越高代表偏好越强。我们用X v 表示电影 v 的海报, ¢¢ · 用. v 代表多张静止帧图片。模型的目标是基于用户 u 的历史评分数据预测用户 u 对电 第 6 章基于深度学习的推荐模型173 | 影 v 的偏好x^uv,可以写成: x^u v = μ + b u + b v + U T ¤u(V¤ v + ′ ) (6-5 ) 其中,U¤ u 是用户 u 的偏好向量,V¤ v 是电影 v 的偏好向量, μ 是总评分偏置项,b u 和 b v 分别是用户 u 和电影 v 的偏置项。 η 是电影的视觉特征,可以写成: 1 η = kN(μ;v)k . 2 Ps2N(μ;v ) μsvX^ s (6-6) á(v ) 其中,μs v 表示电影 v 和 s 的相似度,N(μ;v ) 表示相似度大于 θ 的电影集合,'(v ) 是缩 放因子,表示海报和静止帧图片的一致性。X^ s 表示海报和多张静止图片的组合,可以写 成: X^ s =(Xs;.s ) (6-7 ) 其中,X v 表示电影 v 的海报,. v 表示多张静止帧图片,并通过下列模型提取图像特征。 图6.1 2 Alex-Ne t 卷积网络 公式(6-5)里的参数,可以通过优化下面的目标函数求出: mi n X..1b 2 + .2 W 2 2 + .5μ 2 u sv ¤v b¤;W¤;μ¤;U¤;V . (u;v ) + .3jjU¤uj | 2 + .4jjV¤vj | (6-8 ) +(xu v . μ . b u . W T . v . U T (V + ′))2¢ ¤v¤u¤ v 为了简化符号,定义eu v = xu v . x^uv。对于每个用户和电影对(u;v),参数可以通过下面 17 4 推荐系统与深度学习 | 公式来更新: b u = b u + .1(eu v . .1bu ) W = W + .2(euv. v . .2W) ¤ v ¤ v ¤v U = U + .3(euv(V + ′ ) . .3U) ¤ u ¤ u ¤ v ¤u (6-9 ) V = V + .4(euvU¤ u . .4V¤ V ) ¤ v ¤ v 8 s ∈ N(μ;v) : 2 μs v = μs v + .5(euvUN(μ;v) . 1 X^ s . .5μsv) ¤u 其中,f.1 , ;.5 } 是优化算法的学习步长。 jj ¢¢ · 6. 4 基于循环网络的推荐算法 传统的推荐系统,比如基于协同过滤的推荐算法等,都假设用户偏好和电影属性是 静态的,但本质上,它们是随着时间的推移而缓慢变化的。例如,一部电影的受欢迎 程度可能由外部事件(如获得奥斯卡奖)所改变或者用户的兴趣随年龄的增长而改变, 在传统的算法系统中,这些问题经常被大家忽视。如图6.1 3 所示,左图是与时间无关 的推荐系统,用户偏好和电影属性都是静态的,评分数据来自分布p(rijjui;mj)。相反, 右边是与时间有关的推荐系统,用户和电影都采用马尔科夫链建模,评分数据来自分布 p(rijjtjuijt;mjjt)。 图6.1 3 左图:时间无关的推荐系统。右图:时间相关的推荐系统 第 6 章基于深度学习的推荐模型175 | 除了时序问题,很多传统的推荐算法使用未来的评分数据来预测当前的电影偏好。 譬如,过去非常著名的Net°i x 竞赛,也有类似问题,他们并没有按照时间来划分训练和 测试集,而是把数据集随机打乱,用插值的方法来预测评分。在一定程度上,它们都违 背了统计分析中的因果关系,因此那些研究成果很难应用到实际场景中。 通常有许多方法可以解决时序和因果问题,例如,马尔科夫链模型、指数平滑模型 等方法。马尔科夫链通常采用消息传递或者粒子滤波的方式求解,比如基于时序的蒙特 卡洛采样方法等,这些方法只能求出近似解,也不适合用于海量数据集。 进一步,数据科学家提出基于循环神经网络分别对用户和电影的时序性建模,该方 法也满足统计分析中的因果关系,根据历史的评分数据预测将来的用户偏好。如图6.1 4 所示,通过两个循环神经网络分别对用户和电影的时序性建模,用户的隐藏状态依赖于 用户在当前时刻对电影的评分yi;t. 1 和前一时刻用户的状态,电影的隐藏状态依赖于当 前时刻其他用户对这部电影的评分yj;t. 1 以及前一时刻电影的状态。此外,该模型还结 合了通过矩阵分解得到的用户和电影的静态属性u i 和mj。 图6.1 4 基于循环神经网络的推荐系统 具体来说,假定ui t 和mj t 分别代表用户i、电影 j 在第 t 时刻的隐藏状态。那么用 户 i 在第 t 时刻对电影 m 的评分可以写成: r^ij t = f(uit;mjt;ui;m j )= + (6-10 ) 其中,u^i t 和m^j t 可以当成ui t 和mj t 的仿射变换,可以写成: u^i t = Wusrui t + busr;m^j t = Wmovmj t + bmo v (6-11 ) 17 6 推荐系统与深度学习 | 其中,ui t 和mj t 表示用户i、电影 j 在第 t 时刻的隐藏状态,通过长短时记忆网络(LSTM ) 建模: ui t = LSTM(ui;t.1;yit ) (6-12 ) mj t = LSTM(mj;t.1;yjt ) 其中,yi t 和yj t 分别代表用户 i 和电影 j 在第 t 时刻的输入,可以写成: yi t = Wa[xit , 1new-usr;.t;.t.1 ] (6-13 ) 其中,1new-us r = 1 和1new-mo v = 1 分别代表新用户和新电影,. t 代表第 t 时刻的时钟,W a 和W b 分别是用户和电影的参数投影矩阵,xi t ∈ RV 表示用户 i 在第 t 时刻看过电影的 评分, V 是电影数量。xj t ∈ RU 表示在第 t 时刻所有用户对电影 j 的评分, U 是用户数 量。 模型参数可以通过优化下面的目标函数求出: mi n . (rij t . r^ijt) 2 + R(μ ) (6-14 ) θ (i;j;t ) 其中,R(μ ) 表示模型的正则化项。 6. 5 基于生成对抗网络的推荐算法 迄今为之GA N 在推荐上的应用还是屈指可数,因为GA N 网络本身的对抗设计比较 有技巧性,训练的稳定性不高,目前在推荐系统上的实用性还不强。但是长远来看,GA N 网络在推荐系统中应该还是有巨大的潜力的,有很多“坑”等着学术界和工业界去填平。 这里介绍在SIGI R 201 7 的高分论文IRGA N 的工作,该论文获得了201 7 年的最佳论文提 名奖,也是早期GA N 在推荐系统应用的论文。GA N 原本应用在信息搜索任务上,推荐 系统被当成是排序任务中的一种,这里我们只重点介绍GA N 在推荐系统上的应用。需 要提及的一点是,这里的IRGA N 不应该被认为是推荐场景中GA N 应用的标准范式,它 仅仅提供一种思路更好地去拓展GA N 在推荐系统中的应用。它是一个初步的尝试,而 不应作为范例而限制大家的思路。 在IRGA N 的设计中,生成器和判别器都被理解成是一个ran k 模型,他们的实现可 以是任何传统的或者基于深度学习的打分模型。任务将用户商品的购买记录分成测试集 第 6 章基于深度学习的推荐模型177 | 和训练集,对于给定用户,ran k 模型根据训练集历史交互记录以及用户/商品本身的特 征对所有商品打分,根据分数排序来挑选适合用户的商品。如果推荐的商品在测试集中 确实被用户购买,那么用户与该商品被认为一个正例,否则是一个负例。 但是生成器和判别器的目标不相同,对于给定的一个用户,生成器负责在待排序的 候选池(通常是除掉正例之后的所有样本,包含负例,还有在半监督任务中未被标注的 正例)中将潜在高质量的商品挑选出来,用以混淆当前的判别器。因为此处推荐的商品 是一个离散的样本,论文中通过polic y gradien t 的方式来将判别器的奖励和惩罚信号传 递给生成器。挑选出来的高质量商品,可能是高仿正例的负例,也可能是未被标注的正 例。判别器的目标就是为了区分真实正例和生成器推荐的商品。 图6.1 5 判别器 传统的GA N 仅仅利用生成器的效果,判别器只是一个附属产物,与之不同的是, IRGA N 的生成器和判别器作为两个独立的打分模型,都可以有实用目的。因为两者的优 化目标是相左的,意即生成器如果能够完全骗过判别器,那么生成器的预测结果完全拟 合了从数据特征本身就可以得到该商品是否为相应用户所喜欢;另一种可能就是判别器 基本能够完全理解理解正例和生成器生成的商品之间的差异。我们来分析这两种结果。 在IRGA N 的设计中,生成器和判别器是一个零和博弈问题,优化目标是相反的,所以是 一个此消彼长的过程。对于生成器而言,它会尽力去拟合正例的生成分布,如果在一个 半监督的场景下,样本有很多虽然是正例,但是未被标注,淹没在“负例”候选池当中, 这样生成器很大可能就从候选“负例”池中找到了一些潜在的正例,真实的正例和潜在 未被标注的正例一起喂给判别器,那么判别器很难学习到正负例之间的差异,最后产生 接近于瞎猜的结果。 17 8 推荐系统与深度学习 | 图6.1 6 生成器 如果当前的推荐任务是一个监督任务,而不是一个半监督的任务,即所有的正例都 已经被标注,不存在未被模型感知的正例样本,那么生成器的候选集合只会有负例,喂 给判别器与它基本无法混淆,判别器一直是在正确学习一个正例和负例的差异,那么判 别器一般来说会表现很好。每次会喂给当前模型更难判别的歧义样本,额外地增加当前 分得不好的负例给判别器,类似于常用的集成学习boos t 的思路,给分得不好的样本以 更大的权重。如果我们的判别器和生成器是相同的模型,让其参数都是共享的,就直接 可以退化成一个Dynami c Negativ e Sampl e 的经典trick①。 我们将这个任务用一个形象的例子来说明它在半监督任务中的潜力。如图6.1 7 所示 是一个判别器的打分分布图,横坐标随机分布一些样本(观察到的正例,以及未被标注 的数据,其可能是未被标注的正例,也可以是负例),纵坐标是判别器的打分。两个任务 分别是,判别器要把观察到的正例和其他样本分离开,生成器采样生成部分数据获得判 别器的奖励或者惩罚(如果采样全部数据的效率会很低),生成器采样的数据在图中的 标记为“Obsere d positiv e sample s (观察到的正例)。判别器会尽量把观察到的正例往上 提,且把其他的样本(未观察的正例和负例)往下按,在往上提和往下按的同时也会影 响到其他样本的打分。一般来说,判别器没有差别地把未观察到到正例和负例一起往下 按,但是与此同时也会将正例往上提,由于观察到的正例和未观察到正例有相似的特征 和表示,因此会有更多的协同性,所以在向上提的过程中一些未观察到的正例也会跟着 正例一起获得更高的得分。对于生成器而言,生成器会更具生成的过的得分采样出一个 ①Optimizin g top- n collaborativ e ˉlterin g vi a dynami c negativ e ite m sampling . SIGI R 2013(pp . 785-788) . ACM . 第 6 章基于深度学习的推荐模型179 | 小集合,根据小集合里的样本来得到判别器奖励。一般来说,生成器会把高分负例往上 提以获得更高的判别器奖励。如图6.1 7 所示,生成的四个样本,在判别器中的得分越高, 则会得高更好的奖励。最后,希望生成器可以通过打分模型(生成)采样所有高分的样 本,这样的打分模型会较好地拟合一个半监督的正例分布。 图6.1 7 IRGA N 说明 6.5. 1 IRGA N 的代码实现 为了简单地表现模型的对抗,IRGA N 的生成器和判别器是基于一个最简单的隐式矩 阵分解模型,实际上生成器和判别器可以是任意复杂的模型。我们定义一个基类的M F 模型如下: clas s MF(object) : de f __init__(self , itemNum , userNum , emb_dim , lamda , initdelta=0.05 , learning_rat e =0.05) : wit h tf.variable_scope ( 'generato r ') : self.user_embedding s = tf.Variable ( tf.random_uniform([userNu m , emb_dim] , minval=-initdelt a , maxva l = initdelt a , dtyp e = tf . float3 2 ) ) self.item_embedding s = tf.Variable ( tf.random_uniform([itemNu m , emb_dim] , minval=-initdelt a , maxva l = initdelt a , dtyp e = tf . float3 2 ) ) self.item_bia s = tf.Variable(tf.zeros([itemNum]) ) sel f . param s = [ sel f . use r _ embedding s , sel f . ite m _ embedding s , sel f . ite m _ 18 0 推荐系统与深度学习 | bia s ] self. u = tf.placeholder(tf.int32 ) self. i = tf.placeholder(tf.int32 ) self.rewar d = tf.placeholder(tf.float32 ) self.u_embeddin g = tf.nn.embedding_lookup(self.user_embedding s , self . u ) self.i_embeddin g = tf.nn.embedding_lookup(self.item_embedding s , self . i ) self.i_bia s = tf.gather(self.item_bia s , self.i ) self.optimize r = tf.train.GradientDescentOptimizer(learning_rate ) 用户和商品都嵌入到一个相同维度的隐式空间。 然后我们分别定义生成器和判别器,判别器定义如下: de f __init__(self , itemNum , userNum , emb_dim , lamda , initdelta=0.05 , learning_rat e =0.05) : super(DIS , self).__init__(itemNum , userNum , emb_dim , lamda , initdelt a =0.05 , learning_rate=0.05 ) self.labe l = tf.placeholder(tf.float32 ) self.pre_logit s = tf.reduce_sum(tf.multiply(self.u_embeddin g , self.i _ embedding) , 1 ) + self.i_bia s sel f . los s = tf.nn.sigmoid_cross_entropy_with_logits(labels=self.labe l , logit s = sel f . pr e _ logit s ) + lamd a * ( tf.nn.l2_loss(self.u_embedding ) + tf.nn.l2_loss(self.i_embedding ) + tf.nn.l2_loss(self.i_bias ) ) self.updat e = self.optimizer.minimize(self.los s , var_list=self.param s ) 第 6 章基于深度学习的推荐模型181 | self.reward_logit s = tf.reduce_sum(tf.multiply(self.u_embeddin g , sel f .i _ embeddin g ) , 1 ) + self.i_bia s self.rewar d = 2 * (tf.sigmoid(self.reward_logits ) -0.5 ) # fo r tes t stag e , self.u : [batch_size ] self.all_ratin g = tf.matmul(self.u_embeddin g , self.item_embedding s , transpose_a=Fals e , transpose_b=True ) + self.item_bia s 除去正常的二分类,判别器还需对生成器生成的商品提供奖励。 生成器是一个生成模型,不直接需要真实的数据标签,会通过判别器的奖励来指导 自己生成的过程。 clas s GEN(MF) : de f __init__(self , itemNum , userNum , emb_dim , lamda , initdelta=0.05 , learning_rat e =0.05) : super(GEN , self).__init__(itemNum , userNum , emb_dim , lamda , initdelta=0.05 , learning_rate=0.05 ) self.all_logit s = tf.reduce_sum(tf.multiply(self.u_embeddin g , self . item_embeddings) , 1 ) + self.item_bia s self.i_pro b = tf.gather ( tf.reshape(tf.nn.softmax(tf.reshape(self.all_logit s , [1 , -1]) ) , [ -1] ) , sel f .i ) self.los s = -tf.reduce_mean(tf.log(self.i_prob ) * self.reward ) + lamd a * ( tf.nn.l2_loss(self.u_embedding ) + tf.nn.l2_loss(self.i_embedding ) + tf.nn.l2_loss(self.i_bias) ) self.update=self.optimizer.minimize(self.los s , var_list=self.params ) # fo r tes t stag e , self.u : [batch_size ] self.all_ratin g = tf.matmul(self.u_embeddin g , self.item_embedding s , transpose_a=Fals e , transpose_b=True ) + self.item_bia s 18 2 推荐系统与深度学习 | 迭代训练的代码如下: fo r d_epoc h i n range(100) : i f d_epoc h % 5 = = 0 : generate_for_d(sess 1 , generato r , DIS_TRAIN_FILE ) train_siz e = ut.file_len(DIS_TRAIN_FILE ) input_use r , input_ite m , input_labe l = ut.get_batch_data(DIS_TRAIN_FIL E , inde x , BATC H _ SIZ E ) _ = sess2.run(discriminator.updat e , feed_dict={discriminator.u : input_use r , discriminator.i : input_ite m , discriminator.label : input_label} ) fo r g_epoc h i n range(50) : # 5 0 fo r u i n user_pos_train : sampl e _ lambd a = 0. 2 po s = user_pos_train[u ] ratin g = sess1.run(generator.all_logit s , {generator.u : u} ) exp_ratin g = np.exp(rating ) pro b = exp_ratin g / np.sum(exp_rating ) # pro b i s generato r distributio n p_\thet a p n = ( 1 -sampl e _ lambd a ) * pro b pn[pos ] + = sample_lambd a * 1. 0 / len(pos ) # No w , p n i s th e P n i n importanc e samplin g , pro b i s generato r distributio n p_\thet a sampl e = np.random.choice(np.arange(ITEM_NUM) , 2 * len(pos) , p=pn ) # Ge t rewar d an d adap t i t wit h importanc e samplin g rewar d = sess2.run(discriminator.rewar d , {discriminator.u : u , discriminator.i : sample} ) rewar d = rewar d * prob[sample ] / pn[sample ] _ = sess1.run(generator.updat e ,{generator.u : u , generator.i : sampl e , generator.reward : reward} ) 作为大数据时代最重要的几个信息系统之一,推荐系统主要有下面几个作用。第一, 提升用户体验。通过个性化推荐,帮助用户快速找到感兴趣的信息。第二,提高产品销 售。推荐系统帮助用户和产品建立精准连接,从而提高产品转化率。第三,发掘长尾价 值。根据用户兴趣推荐,使得平时不是很热门的商品可以销售给特定的人群。第四,方 便移动互联网用户交互。通过推荐,减少用户操作,主动帮助用户找到感兴趣的内容。 为了更好地服务用户,推荐系统需要从业务的目标出发,针对不同功能进行定制。 本章介绍推荐系统的架构设计。首先,第7. 1 节给出一个推荐系统基本模型,在此基础 上,第7. 2 节给出常见的推荐系统架构。接着,第7. 3 节列举一些常用的软件,供进行架 构设计时选用。最后,第7. 4 节陈述了一些常见的问题。 7. 1 推荐系统基本模型 从本质上来说,推荐系统是监督学习的一个应用,既然如此,它就离不开监督学习 的基本模型。如图7. 1 所示,监督学习分为学习和预测两个过程。在学习过程中,学习系 统利用给定的训练样本,通过训练得到一个模型,这个模型随后会被用于预测系统的预 测。在预测过程中,预测系统对于给定的测试样本,由模型给出相应的预测结果。 推荐系统的目的,就是通过学习系统有效地训练模型,使得预测系统的结果贴近测 试样本真实的结果。预测的内容,可以是对一条资讯、一首歌或是一个视频的喜欢程度, 也可以是购买某个商品的概率。推荐系统的优化,在于通过任何有力的方法(模型、算 法、数据、特征),来提高预测结果的准确度,这个系统更懂用户,给用户推荐的物品更 贴近用户真实的喜好,从而提高商品销售和用户体验。 比如,图中简单的例子,学习系统的输入是 5 个不同用户的行为,对于男性用户A , 18 4 推荐系统与深度学习 | 《王者荣耀》推荐给这个用户。 他喜欢的是《王者荣耀》这个游戏,对于女性用户B,她喜欢的则是《奇迹暖暖》,那么 对于这5个用户统计得到的模型是男性用户喜欢《王者荣耀》的概率是0.67,而女性用 户喜欢《奇迹暖暖》的概率是1。有了这个简单的模型以后,如果在预测系统中有一个新 的用户请求,该请求来自一个男性用户,那么按照前面的模型,会根据概率的大小,把 图7. 1 监督学习基本模型 在实际的推荐系统中,学习系统处理的用户数据量会更大,数据的维度也更多,用 到的推荐模型也会更复杂,常用的有协同模型、内容模型和知识模型。其中,协同模型主 要通过我的朋友喜欢什么来猜测我喜欢什么;内容模型则是根据物品本身来预测用户喜 欢 A 所以也可能喜欢B;知识模型则是根据用户的限定条件,按照他的需要进行推荐。 推荐系统架构设计,是在监督学习基本模型的基础上,按照业务的需要定制,将训 练和预测过程中的每个步骤细化,将训练和预测过程中使用到的模型、特征、工具都实 例化,从而打磨出一个适合业务需要的推荐系统。比如,在学习系统中,需要对数据进 行上报、清洗、特征构造等操作,就需要有一个用于存储和处理数据的平台。取决于数 据量的大小和数据类型的不同,可能需要对学习系统进行定制。在预测系统中,需要将 预测请求服务化,封装成AP I 供业务调用。同时,还需要保证线上服务的可靠性和可扩 展性。 第 7 章推荐系统架构设计185 | 接下来的小节中,我们会先介绍推荐系统常用的架构,然后在了解了这些架构的基 础上介绍每个模块常用的一些组件,最后介绍推荐系统的一些常见问题。 7. 2 推荐系统常见架构 推荐系统的架构离不开实际的业务,本节介绍的几种推荐系统架构,并不是互相独 立的关系,实际的推荐系统可能会用到其中一种或者几种的架构。在实际设计的过程中, 读者可以把本文介绍的架构作为一个设计的起点,更多地结合自身业务特点进行独立思 考,从而设计出适合自身业务的系统。 根据响应用户行为的速度不同,推荐系统可以大致分为基于离线训练和在线训练的 推荐系统。根据使用的机器学习方法不同,又可以分为使用传统机器学习和使用深度学 习的推荐系统。另外,由于业务的重要性,再单独介绍一个类别:面向内容的推荐系统。 每一节的最后,会简单介绍一些实际系统设计中遇到的常见问题,以供设计时进行参考。 7.2. 1 基于离线训练的推荐系统架构设计 基于离线训练的推荐系统架构是最常见的一种推荐系统架构。这里的“离线”训练 指的是使用历史一段时间(比如一周或者几周)的数据进行训练,模型迭代的周期较长 (一般以小时为单位)。模型拟合的是用户的中长期兴趣。代表的场景有手机应用市场、 音乐推荐等。相对应地,“在线”训练指的是增量的、实时的训练,要求模型对于每个训 练样本快速地响应。比如,用户当前观看了一个美食的视频并且停留了很长时间,那么 下一个视频推荐系统察觉到你短期的兴趣后可以给你推荐更多相似的视频。训练数据的 更新频率以秒为单位。代表场景有资讯、购物、短视频推荐等。 基于离线训练的推荐系统中使用的常用的算法有:逻辑回归(Logisti c Regression) , 梯度提升决策树(Gradien t Boostin g Deciso n Tre e )和因子分解机(Factorizatio n Machine ) 等。 如图7. 2 所示,一个典型的基于离线训练的推荐系统架构由数据上报、离线训练、在 线存储、实时计算和A/ B 测试这几个模块组成。其中,数据上报和离线训练组成了监督 学习中的学习系统,而实时计算和A/ B 测试组成了预测系统。另外,除了模型之外,还 有一个在线存储模块,用于存储模型和模型需要的特征信息供实时计算模块调用。图中 18 6 推荐系统与深度学习 | 的作用。 的各个模块组成了训练和预测两条数据流,训练的数据流搜集业务的数据最后生成模型 存储于在线存储模块;预测的数据流接受业务的预测请求,通过A/B测试模块访问实时 计算模块获取预测结果。取决于业务的大小,一般来说训练的数据流需要处理大量的训 练数据,更新的周期较长,以小时来计,所以对应的这种架构称为基于离线训练的架构; 而预测的数据流一般用于互联网线上的业务,对延时的要求在几十毫秒以内,这也使得 对于训练和预测两条数据流上的各个模块有不同的架构要求。下面具体看一下每个模块 图7. 2 基于离线训练的推荐系统架构设计 数据上报数据上报模块的作用是搜集业务数据组成训练样本。一般分为收集、验证、 清洗和转换几个步骤。首先,需要收集来自业务的数据。业务驱动,从物品、用户、场景 几个维度收集,核心数据样本要保证质量。量化一切,越细越好。其次,对上报的数据进 行准确性的验证,避免上报逻辑错误、数据错位或数据缺失等问题。第三,为了保证数 据的可信度,需要清理脏数据。常见的数据清洗有:空值检查、数值异常、类型异常、数 据去重等。最后,通过数据转换,将收集的数据转化为训练所需要的样本格式,保存到 离线存储模块。数据的质量非常重要,推荐系统的预测结果是否准确,一方面取决于模 型的强弱,更重要的是训练数据的质量和数量。如果数据质量不行,再好的模型也没法 得到好的预测结果,所谓“垃圾进垃圾出(Garbag e I n Garbag e Out)”。 离线训练离线训练模块又细分为离线存储和离线计算。实际业务中使用的推荐系统一 般都需要处理海量的用户行为数据,所以离线存储模块需要有一个分布式的文件系统或 者存储平台来存储这些数据。离线计算常见的操作有:样本抽样、特征工程、模型训练、 相似度计算等。 第 7 章推荐系统架构设计187 | 图7. 3 数据上报模块 样本抽样通过合理地设计样本,为模型训练提供高质量的输入,从而训练出一个比 较理想的模型。首先需要合理定义正负样本,实际业务中,经常会遇到正负样本不均衡 的情况,一方面可以通过惩罚权重和组合等方法解决,一方面要结合业务理解,合理设 计正负样本。其次,设计样本时应尽量保证用户样本数的均衡。对于恶意的刷流量、机 器人用户,通过样本去重保证用户样本数的均衡。第三,适当考虑样本的多样性。通过 采集和当前推荐算法独立无关的用户样本来丰富样本来源。 特征工程利用领域相关的知识,从原始数据中获取尽可能多的信息,组成特征用于 提高模型训练效果。首先,特征选择通过评价函数、停止准则、验证过程等步骤,从特 征集合中挑选一组最具统计意义的特征子集。其次,特征提取通过成分分析、判别分析 等方法,对原始特征进行转换和组合,构建新的具有业务或统计意义的核心特征。第三, 特征组合通过多模态embeddin g 等方法,将来自用户、物品和背景的特征向量组合到一 起,达到信息互补。 有了前两步之后,模型训练利用给定的数据集,通过训练得到一个模型,用于描述 输入和输出变量之间的映射关系。实际业务中,考虑到需要处理大规模的训练集,一般 会选择可以分布式训练的近似线性时间的算法。 图7. 4 离线训练模块 在线存储除了图7.1,图7. 2 的推荐系统中提到的模块,还有一个在线存储的模块,这是 18 8 推荐系统与深度学习 | 盘(Soli d Stat e Drive s )等。 因为线上的服务对于时延都有严格的要求。比如,某个用户打开手机APP,他肯定希望 APP能够快速响应,如果耗时过长,就会影响用户的体验。一般来说,这就要求推荐系 统在几十毫秒以内处理完用户请求返回推荐结果,所以,针对线上的服务,需要有一个 专门的在线存储模块,负责存储用于线上的模型和特征数据。一般来说,在线存储模块 需要使用本机内存或者分布式内存。为了在线存储能够尽可能地快,在开源软件的基础 上,还可以进行一些定制,比如,采用缓存策略、增量策略、延迟过期策略,使用固态硬 图7. 5 推荐系统中的存储分层 实时推荐实时推荐模块的功能是对来自业务的新请求进行预测。比如,用户打开手机 应用市场APP,AP P 后台会发送一个请求给服务器,服务器收到请求后根据用户以前在 应用市场的历史行为猜测其喜好,然后返回一个推荐的应用列表给手机APP,再在AP P 界面呈现给用户。这个过程中,实时计算模块需要进行以下计算:(1)获取用户特征,系 统根据请求中的用户ID,从在线存储模块中读取用户的画像以及历史行为,构建出该用 户的模型特征;(2)调用推荐模型,结合用户特征调用推荐系统的算法模型,得到用户 对某个AP P 候选池中每个物品的喜好概率;(3)结果排序,对候选池的打分结果进行排 序,然后返回结果列表给手机APP。从前面的例子中可以看出,实时计算模块需要从在 线存储模块读取很多的数据,同时需要在很短时间内完成大量的模型打分工作,所以对 于该模块有很高的性能要求。一般来说,该模块需要有一个分布式的计算框架来完成计 算任务。 在实际应用中,因为业务的物品列表太大,如果实时计算对每一个物品使用复杂的 模型进行打分,就有可能耗时过长而影响用户满意度。所以,一种常见的做法是将推荐 第 7 章推荐系统架构设计189 | 列表生成分为召回和排序两步。召回的作用是从大量的候选物品中(例如上百万)筛选 出一批用户较可能喜欢的候选集(一般是几百)。排序的作用是对召回得到的相对较小 的候选集使用排序模型进行打分。更进一步,在排序得到推荐列表后,为了多样性和运 营的一些考虑,还会加上第三步| — 重排过滤,用于对精排后的推荐列表进行处理。重 排过滤步骤会给用户提供一些探索性的内容,避免用户在平台上看到的内容过于同质化 而失去兴趣,同时过滤掉低俗和违法的内容,保持一个良好的平台环境。常见的架构如 图7. 6 所示。 图7. 6 在线预测的几个阶段 A/ B 测试对于互联网产品来说,A/ B 测试基本上是一个必备的模块,对于推荐系统 来说也不例外,它可以帮助开发人员评估新算法对客户行为的影响。除了离线的指标外, 一个新的推荐算法上线之前一般都会经过A/ B 测试来测试新算法的有效性。在A/ B 测 试模块中,我们需要设置两组或者多组用户,一组设置为对照组,采用已有的算法,另 外一组或者几组为实验组,采用新版算法。通过对比不同算法的核心指标(比如点击率 或者用户时长),来确认哪个算法更好。A/ B 测试的目的是通过流量分割和科学采样,在 小流量测试中获得具有代表性的实验结论。另外,A/ B 测试模块还应包含数据可视化的 功能,将统计的结果尽快地呈现给算法开发者,帮助他们快速地分析和定位问题。 除了上述几个模块之外,在实际设计过程中,还需要注意一些问题。 推荐结果反馈在数据上报模块搜集的数据中,还需要包括对推荐结果的反馈。这样做 的好处是,通过分析不同算法的推荐结果,完善数据的反馈机制,不断优化较差的算法, 从而形成数据闭环。这里指的不同的推荐算法,可以是完全不同的两个模型,也可以是 同一个模型的不同参数配置,又或者是同一个模型使用了不同的特征。通过对比不同推 荐算法得到的反馈统计结果,可以分析不同算法的优劣,从而帮助算法开发者来调优模 型的参数和特征。 19 0 推荐系统与深度学习 | 模型更新的健壮性作为个性化服务的重要一环,大多数的推荐系统需要在线上提供不 间断的服务。另一方面,因为推荐系统例行的模型训练,每天或者每隔数个小时都会更 新新的模型。为了保证服务2 4 小时可用,这就要求系统在模型更新的时候仍然能够正 常服务。程序开发的时候,需要在模型计算的时候,考虑即使在某些特征缺失或者不匹 配的情况下,也能够最大程度上返回较准确的计算结果。 图7. 7 推荐系统通用性设计 海量服务和其他互联网服务一样,推荐系统同样需要服务于海量的用户,这就使得这 个系统的线上服务需要做到高可靠、高吞吐、低延迟。几种常见的优化方案有:(1)过载 保护,对于突发的业务流量进行过载保护,防止服务的雪崩;(2)流式计算,通过分布式 流式计算框架应对大量在线请求;(3)共享内存组件,对于一切常用的模型数据,可以考 虑放在共享内存中,使得在线存储部分的性能开销尽可能降低。 通用性设计在实际业务中,一套推荐系统常常会用于支持众多的业务和场景。对于不 同的场景,用到的数据、算法和模型都会有很多不同之处,如果对于每个场景都从头开 发,将会耗费非常多的时间和人力。那么有没有好的方法使得同一个推荐算法可以复用 到不同的推荐场景呢?这就需要对推荐算法库进行通用化设计。我们将推荐系统通常划 分为四个部分:样本库、特征库、算法和模型。其中,样本库存储从流水日志中提取的用 第 7 章推荐系统架构设计191 | 户行为和特征;特征库存储用户和物品的属性等特征;算法是用于训练模型用到的机器 学习算法;模型库存储的是从样本和特征计算得到的训练模型。为了不同的算法可以用 于不同的样本和特征,如图7. 7 所示,我们可以使用一个算法配置表来存储数据、算法 和模型的映射关系,将模型、算法、样本和特征的关系解耦,使得算法可以复用。 用户画像用户画像系统和推荐系统关系非常密切。用户画像是一个标签化的用户模型, 用于描述用户的基础属性、生活习性和关系链等信息,对于业务了解用户具有非常重要 的意义,可以帮助大幅度的提升推荐的准确度。在设计推荐系统的同时,也需要重视对 画像系统的构建。 7.2. 2 面向深度学习的推荐系统架构设计 深度学习近年来在图像处理、自然语言理解、语音识别和在线广告等领域均取得了 突破性的进展,如何将深度学习有效地用于推荐系统,提高推荐系统的准确性和用户 满意度是面向深度学习的推荐系统架构设计需要考虑的主要问题。和传统的推荐系统相 比,面向深度学习的推荐系统有着自动提取特征、建模用户时序行为和融合多方数据源 的优点。 面向深度学习的推荐系统中使用的常用算法有:受限玻尔兹曼机(RBM)、自编码器 (AE)、卷积神经网络(CNN)、深度神经网络(DNN)和宽深学习(Wid e & Dee p )等。 图7. 8 面向深度学习的推荐系统架构设计 一个典型的面向深度学习的推荐系统架构如图7. 8 所示,和前一节提到的架构相比, 面向深度学习的推荐系统架构增加了特征提取和模型服务两个模块。其中,特征提取模 块用于从样本中构建特征,提升模型效果;模型服务模块用于服务深度学习框架的预测 请求,对其进行适配。总体的学习流程与前一节类似,通过数据上报得到的样本先通过 19 2 推荐系统与深度学习 | 特征提取 用于线上的用户向量。 未来发展的一个重要的趋势。 特征提取模块构建特征,然后通过模型训练得到模型。特征提取模块得到的特征一方面 保存到离线存储后用于模型训练,一方面存储到线上存储用于预测时调用。模型预测流 程先由模型服务模块拉取模型到线上,再通过A/B测试模块和实时推荐模块接收来自 业务的请求,通过模型服务模块得到预测结果返回给业务。 相比图7.2中的架构,面向深度学习的推荐系统架构在训练流程中增加了一 个很重要的模块:特征提取。深度学习的最大优势之一,就是能够通过一种通用的模型 学习到数据的特征,自动获取到数据的高层次表示而不依赖于人工设计特征。常见的特 征提取方法有:多层感知机、卷积神经网络和循环神经网络等。 针对不同的业务,特征提取模块会有不同的任务。比如,在第6.1节介绍的Google 的手机APP推荐中,通过查询和条目的特征学习到一个低维稠密的嵌入向量,用于泛 化模型中的输入。又比如YouTube的视频推荐中,输入是用户浏览历史、搜索历史、人 口统计学信息和其余上下文信息组成的输入向量,输出则是用于离线训练的概率值以及 和第7.2.1节中的离线训练模块相比,本节介绍的特征提取使用深度学习的方法代 替了人工,大大减少了特征工程的工作量,而且具有更好的泛化能力,所以是推荐系统 图7. 9 利用深度学习进行特征提取 从系统的角度看,特征提取模块设计时常常需要考虑下面两个问题。首先,特征生 成的流水线需要自动化。和业务定义好数据源的接口后,特征提取模块定期地调用特征 提取任务生成特征;其次,需要有一套特征管理系统对来自不同业务、使用不同方法得 第 7 章推荐系统架构设计193 | 到的特征进行管理。实际应用中,一个业务会有多个推荐的场景,一个公司内会有多个 业务,这些来自不同业务的不同推荐场景往往可以复用某些相同的特征。所以,设计的 时候需要考虑同一个特征被用于多个模型的训练的情况。通过一个特征管理系统,将特 征提取模块得到的特征注册到特征库,这个特征库负责管理原始特征以及各种经过特征 工程处理后的特征,每个特征用唯一的特征I D 标识。在进行特征工程的时候,通过调用 特征ID,同一份原始特征可以用于不同的特征工程输入;在模型训练的时候,经过特征 工程处理的某一份特征又可以被多个算法使用。 模型服务和前一节的架构相比,面向深度学习的推荐系统架构增加了模型服务这个模 块。该模块的主要功能是对业务请求进行预测。类似于7.2. 1 节中的实时计算模块,加载 推荐模型进行预测计算,之所以把模型服务模块独立出来,是因为区别于传统的机器学 习框架,目前的深度学习框架往往同时提供了模型预测服务的功能,我们可以在现有的 基础上,封装后使用在业务中。 模型服务提供了加载模型、请求调用和模型版本管理的功能。首先,加载模型到服 务进程,等待请求调用;其次,提供一个RP C 接口,供业务方请求;第三,提供模型版 本管理的功能,加载最新的模型版本,同时在某个版本失败时提供回滚的功能。目前较 为成熟的方案是TensorFlo w Servin g ,提供了RP C 框架、自动模型版本管理等的功能。 分布式训练现有的深度学习框架大都提供了分布式计算的框架,使用分布式训练的原 因是为了使训练的速度更快,训练的模型更大。在一些场景,Googl e 甚至已经用到了百 亿级的参数。为了快速地训练超大规模的模型,必须使用分布式的方式来进行计算。 图7.1 0 参数服务器架构 19 4 推荐系统与深度学习 | 对于大规模的参数更新,很重要的一点是如何更新模型的分布式存储和计算,目前 最常用的解决方案是参数服务器(Paramete r Server)。最早版本的参数服务器是201 0 年 用在自然语言处理中,最初只是使用了Memcache d 的分布式(key , valu e )作为同步机 制。后来Googl e 、Microsoft、Baidu、Yaho o 、腾讯都开发了各自的版本。其他开源的还有 PS-Lite、Petuu m 、Glin t 等。总的来说,参数服务器主要提供了下面几个功能:(1)对参 数的分布式存储;(2)提供参数更新机制(同步和异步)(3)对参数的划分和放置。 ; 典型的参数服务器集群的架构如图7.1 0 所示,集群中的节点可以分为计算节点和参 数服务节点两种。其中,计算节点将更新参数的任务分配到不同节点上执行;参数服务 节点采用分布式存储的方式,各自存储全局参数的一部分,并作为服务方接受计算节点 的参数查询和更新请求。 深度推荐系统实用技巧除了上面提到的内容,在实践过程中,还有一些实用技巧。首 先从简单的模型和特征开始训练,不要一开始就使用非常复杂的模型和特征,这样模型 会变得非常难以训练,从而导致很长时间看不到模型的效果。所以,建议一开始使用简 单的模型和特征。如果是从传统模型转到深度学习,甚至可以利用之前已经验证过有效 的特征输入到深度学习系统中,来帮助调试和改进模型效果。 注重训练效率。因为深度学习的训练是一个迭代的过程,要经过多次的调参、验证, 再调参、再验证的过程。所以,训练的代码效率非常重要,要尽可能地改进训练的效率, 这样才能加快迭代速度,条件允许的话,可以考虑分布式或者多GP U 训练,所谓磨刀不 误砍柴功。 注意模型的可扩展性。对于深度学习的模型来说,输入特征维度的大小,一定程度 上决定了最后模型的效果,所以要尽可能选用可扩展的模型,支持大规模的输入向量, 这样才能有效地利用海量数据。 对比公有数据集上的结果。公有的数据集提供了一个很好的实验场,有很多的论文 可以提供实验结果帮助我们分析,如果我们要提出自己的新模型,不要只是在自己的数 据集上做,同时可以试试在公有数据集上的效果。 7.2. 3 基于在线训练的推荐系统架构设计 推荐系统的发展,总是伴随着业务的需求而演进。前面两节提到的设计方案中,我 们通过分布式存储系统和分布式计算来提升处理数据的规模和训练的速度。但是对于一 第 7 章推荐系统架构设计195 | 些互联网应用来说,业务还需要追求更快的反馈和更大的数据规模。比如,假设一个互 联网广告的场景,用户的数量是十亿级别,推荐的广告内容是十万级别,对于这样的规 模,即使使用了分布式训练,模型训练的速度也必须以小时计算。而对于业务来说,我 们希望用户对于上一个广告的反馈(喜欢或者不喜欢,有没有点击),可以很快地用于下 一个广告的推荐中。这就要求我们用另一种方法来解决这个问题,这个方法就是在线训 练。 基于在线训练的推荐系统架构适合于广告和电商等高维度大数据量且对实时性要求 很高的场景。相比较基于离线训练的推荐系统(图7.2),基于在线训练的推荐系统不区 分训练和测试阶段,每个回合都在学习,通过实时的反馈来调整策略。一方面,在线训 练要求其样本、特征和模型的处理都是实时的,以便推荐的内容更快地反映用户实时的 喜好。比如,五分钟前发生的新闻事件,就可以通过用户的喜好推送到不同的用户;另 一方面,因为在线训练并不需要将所有的训练数据都存储下来,所以不需要巨大的离线 存储开销,使得系统具有很好的伸缩性,可以支持超大的数据量和模型。对于数据量很 大的业务场景,基于在线训练的推荐系统提高了处理问题规模的上限,从而能够带来更 多的收益。 最新的推荐系统演进,是将深度学习和在线学习结合起来。充分利用两者的优点,使 得推荐系统既能够处理海量的用户和物品数据,又能够自动从这些数据中提取出特征用 于训练,找出推荐对象的相关性,并且快速响应用户的反馈。 基于在线训练的推荐系统中使用的常用的算法有:FTRL-Proxima l 、AdPredictor、 Adaptiv e Onlin e Learnin g 和PBOD L 等。 图7.1 1 基于在线训练的推荐系统架构设计 样本处理和基于离线训练的推荐系统相比,在线训练在数据上报阶段的主要不同体现 在样本处理上。对于两者来说,数据的采集并没有太多不同,都是通过在业务中埋点然 后实时上报到推荐系统。不同的是,对于离线训练来说,上报后的数据先是被存储到一 19 6 推荐系统与深度学习 | 定义采样需要。 实时特征 个分布式文件系统,然后等待离线计算任务来对样本进行处理;对于在线训练来说,对 样本的去重、过滤和采样等计算都需要实时进行。 实时训练对于样本的正确性、采集质量和采样分布,有着更严格的要求。实际的业 务中上报的数据因为种种原因可能会出现数据缺失、冗余和错报等情况,这就要求样本 处理能够容忍缺失和错报,以及过滤掉冗余的数据。同时对于过于稀疏的数据或者噪音 数据,系统会对其进行丢弃。另外,因为在线学习的算法一般都是使用已经观察到的一 个数据窗口的数据来对测试的数据进行预测,非常容易进行过拟合,所以在样本处理的 时候需要使实时采样得到的样本的分布和累积的样本分布尽量相似,以避免模型的效果 变差。另一方面,对于常用的操作,可以提取通用的采样规则,以便不同算法程序员自 实时特征模块通过实时处理样本数据拼接训练需要的特征构造训练样本,输 入流式训练模块用于更新模型。该模块的主要的功能是特征拼接和特征工程。 特征拼接对特征进行读取、选择、组合等操作。如图7.12所示,首先根据算法的配 置,从样本中选择需要的特征,从相应的存储接口读取该特征,将读取到的用户、物品 和场景特征拼接在一起,以便下一步处理。然后根据从拼接好特征的样本中进行特征选 择、特征交叉等操作,并将处理的结果写入流处理消息队列,用于输出至模型训练和模 型评估模块进行流式训练。特征工程按照特征组合规则,对特征进行内积、外积和笛卡 尔积等操作,构造出新的特征,同时将新的特征和特征库写入到特征配置表中。 图7.1 2 在线学习之实时特征处理 数据的采样和特征处理一般是以时间窗口的形式进行。窗口的大小,取决于模型的 效果和业务实时性要求之间的折中。开发者可以根据业务的情况选择一个适合的时间窗 口,每搜集一定量的用户数据就更新一次模型。对于大规模的特征集合,可以将模型参 数存储在参数服务器中。 第 7 章推荐系统架构设计197 | 流式训练流式训练模块的主要作用是使用实时训练样本来更新模型。推荐算法中增量 更新部分的计算,通过流式计算的方式来进行更新。在线训练的优势之一,是可以支持 模型的稀疏存储。虽然训练使用的特征向量的维度可能是上十亿维,但是对于某一个样 本实例来说可能只有几百个非零值。这使得在线训练可以对大规模的数据集进行流式训 练,每个训练样本只需要被处理一次。模型方面,FTRL-Proxima l 结合了OGD(Onlin e Gradien t Descen t )和RDA(Regularize d Dua l Averagin g )的优点,在准确度和稀疏性上比 这两个模型都更优。同时,在工程中使用二次采样等技巧来提高训练的速度和减少模型 的大小。 训练方面,在线模型不一定都是从零开始训练,而是可以将离线训练得到的模型参 数作为基础,在这个基础上进行增量训练。这样,不但缩短了在线训练模型收敛的时间, 也避免了训练启动阶段模型不佳的情况。 模型存储和加载模型一般存储在参数服务器中。模型更新后,将模型文件推送到线上 存储,并由线上服务模块动态加载。 另外,基于在线训练的推荐系统还需要考虑的问题有下面几个方面。 模型健壮性在线学习对模型健壮性有更高的要求。因为在线学习使得推荐系统的反应 变得更加敏捷,但是同时对于脏数据或者扰动也变得更加敏感,实时处理的数据流中的 任何一个扰动或者故障都可能给模型训练或模型预测造成干扰,进而影响推荐结果。这 就要求在线学习模型和算法足够强壮,能够应对训练数据中的波动。常用的方法有正则 化、自适应学习率、低覆盖率的特征过滤等。从特征方面,也可以引入统计量作为特征, 以此作为平滑手段。 7.2. 4 面向内容的推荐系统架构设计 推荐引擎使得内容分发的方式产生了重大的变革,从早期的门户网站,到通过微博、 朋友圈传播的资讯,直至以今日头条、抖音为代表的个性化资讯,推荐系统使得用户获 取有趣内容变得更加容易。 针对内容分发设计的推荐系统,和其他推荐系统相比,对于内容处理有更高的要求。 常见的做法是将要用于推荐的内容进行处理后表示成神经网络可以识别的向量和标签, 然后输入到召回和排序模型中使用。 19 8 推荐系统与深度学习 | 图7.1 3 面向内容的推荐系统架构设计 新闻资讯对于新闻资讯类的推荐,因为新闻的时效性要求很高,所以对内容的理解非 常重要。常用的内容理解工具有文本分类、关键词提取和主题提取等,分别从不同层次 对新闻内容进行理解。文本分类是粒度较粗的一个特征,用于感知用户兴趣的类别。关 键词提取进一步理解用户更细的兴趣,比如某个明星或者球队。主题提取则是从语义的 方面丰富对内容的理解,使得对用户的兴趣覆盖更全面。 视频音乐要对视频和音乐进行推荐,首先需要对内容进行理解。可以使用第6. 1 节中 用到的方法,通过word2ve c 等方法将原视频或音频表示为一个低维稠密的嵌入向量,然 后通过协同过滤来计算物品之间的相似度用于召回阶段,然后再使用DN N 等算法进行 精排。 图7.1 4 用于推荐的内容池 广告购物针对广告和购物的场景,很重要的一件事情就是对广告物品和商品的识别。 比如,一个典型的服装广告图片,可能会包含模特、衣服和广告词等因素。如果能对男 第 7 章推荐系统架构设计199 | 女性别、服装分类、服饰分类以及广告词进行OC R 识别,就能够更好地理解广告的内 容,从而产生更精准的推荐。 文字识别(Optica l Characte r Recognitio n )的作用是识别图片中的文字内容,提取广 告和商品等图片中的语义特征,加深对图片创意和用户偏好的理解,从而更好地对物品 进行推荐。目前在文本检测领域,很多基于深度卷积神经网络的算法已经成为主流的方 法。 内容抓取如图7.1 4 所示,内容抓取模块负责从互联网或者自属业务中抓取需要推荐的 内容存储到内容池中。一般来说,这一功能是由分布式爬虫来实现的。抓取的关键在于 统一数据格式。虽然我们抓取的内容可能是来自不同的数据源,有着不同的格式,但是 为了后续的使用,需要对数据的存储格式进行统一规范。第一,要规范抓取内容的字段 和格式,仔细考虑业务和算法需要的字段;第二,要对内容字段进行标准化的处理,比 如存储成相同的音视频格式或者文本编码;第三,选择合适的存储介质保存;第四,预 留一些字段用于不时之需,避免在抓取过程中遇到没有字段可用的情况。另外,抓取过 程中需要重视版权问题。 抓取内容后,通常还需要对内容池中的内容建立索引。通过提取内容的关键特征作 为索引,使得推荐系统其他模块在使用内容池时候能够根据内容索引找到相应的内容。 内容生成随着内容分发的进一步发展,推荐系统甚至可以和内容生成相结合,来为不 同用户生成定制的内容。比如,在广告的场景里面,可以针对用户的喜好挑选背景和广 告词;在电影推荐里面,可以针对不同用户生成不同题材的海报,例如同一部电影里有 两个主演,对于不同的观众,推荐系统甚至可以决定在海报中使用哪个主演作为海报的 内容。 7. 3 推荐系统常用组件 7.3. 1 数据上报常用组件 Apach e Kafk a 是一个开源的流处理平台。它提供了对实时数据源的高吞吐低延迟的 统一处理框架。从逻辑上来看,Kafk a 是对多生产者多消费者队列的分布式实现。消息通 过主题(Topi c )来进行管理,一个主题可以有多个生产者和多个消费者。生产者产生消 20 0 推荐系统与深度学习 | 息并推送到某一主题,订阅这一主题的消费者则从主题中拉取消息。 图7.1 5 Apach e Kafk a 逻辑架构 7.3. 2 离线存储常用组件 HDFS(Hadoo p Distribute d Fil e Syste m )是目前使用极为广泛的分布式文件系统。它 的设计目标是低成本、高可靠性和高吞吐率。其容错机制使得HDF S 可以基于廉价的硬 件来构建分布式文件系统,在即使有组件失效时仍然可以提供可靠存储。 Hiv e 是一个基于Hadoo p 的数据仓库,提供了比较完整的SQ L 功能,使用HDF S 作 为存储底层。其设计目标是方便熟悉SQ L 的工程师对数据进行操作而不需要进行复杂 的编程。Hiv e 支持的数据规模可以达到上百PB,并且支持结构化数据的存储。 7.3. 3 离线计算常用组件 Apach e Spar k 是一个基于内存数据处理的高性能分布式计算框架,它提供简单、灵 活、强大的AP I 帮助用户开发高效的程序用于复杂的数据分析。Spar k 提供了和Hadoo p 类似的MapReduc e 计算模型,但是和Hadoo p 不同的是,Spar k 使用了基于内存的中间 数据结构,使得它能更好地支持需要多轮迭代的工作负载。 TensorFlo w 是一个开源的软件框架,用于对数据流图的数值计算。它提供了强大而 多样的AP I 供机器学习研究者来开发各种应用。 分布式TensorFlo w 提供了对参数服务器的支持。和其他参数服务器不一样的是, TensorFlo w 的参数服务器对参数的更新是隐式的,也就是说,程序员不用手动去pus h 和 pul l 这些参数。这使得基于TensorFlo w 的参数服务器的开发变得尽可能简单。而编写分 布式TensorFlo w 程序的主要任务,也就成了如何将参数合理地分布到不同的参数服务器 上,通过集群配置接口、指定设备接口和同步模式接口来进行参数配置。 第 7 章推荐系统架构设计201 | 7.3. 4 在线存储常用组件 Redi s 是一个开源的基于内存数据结构的存储系统,它可以用作数据库、缓存和消息 中间件,是目前最为常用的key-valu e 数据库之一。 Memcache d 是一种通用的高性能分布式内存缓存系统。它一般用于将动态的数据缓 存到内存中帮助提升读取外部数据的速度。Memcache d 提供基于内存的key-valu e 存储。 RocksD B 是另外一个高性能的key-valu e 数据库。它基于LevelD B 改进,针对多核和 固态硬盘进行了优化,使其对于I O 密集的负载非常友好。 共享内存是最基础的存储方式之一,如果合理使用,可以将数据放置在离计算尽可 能近的位置。实际使用中,配合数据拉取需要有一些定制的开发。 7.3. 5 模型服务常用组件 TensorFlo w Servin g 可以用于搭建机器学习模型的服务,它是面向生产环境设计的, 灵活而高效。TensorFlo w Servin g 提供了和TensorFlo w 模型的无缝衔接,其主要特点有 自动加载新模型、批量处理请求、可水平扩展等。 7.3. 6 实时计算常用组件 Apach e Stor m 是一个开源的分布式实时计算系统。它提供了简单易用的编程模型, 使得数据的实时流处理变得更简单,并且可以方便对计算拓扑进行管理和扩展。另外, 它和Apach e Kafk a 之间可以很好地适配。其常见的应用场景为数据实时统计、聚合分 析、模型预测等。 Spar k Streamin g 是对Spar k 核心AP I 的一个扩展,它提供了对实时数据流的可扩 展、高吞吐、高可靠的流处理。Spar k Strea m 对数据流提供了一个DStrea m 的抽象,方 便开发者对流式数据进行处理。 7. 4 推荐系统常见问题 7.4. 1 实时性 在基于离线训练的推荐系统架构里面,业务数据需要通过离线存储和离线训练两个 20 2 推荐系统与深度学习 | 耗时较长的模块,所以整个模型迭代的过程至少是以小时为周期的。这种模式使得数据 的时效性受到了很大的限制,仅适用于对数据时效性要求不高的业务场景。 针对这个问题,可以使用基于在线训练的推荐系统里面的架构对特征进行实时提取 和实时拼接,这样基本可以做到秒级的特征反馈,适用于捕捉用户的短时兴趣。 7.4. 2 多样性 对于推荐系统而言,大多数算法关注的都是如何提高推荐算法准确性,却忽略了推荐结 果的多样性,结果是给用户推荐的结果越来越同质化,使得用户的新鲜感降低,最终影响用 户的使用体验。以购物为例,给用户推荐的物品越来越集中,很难激发用户新的购物需求。 所以,推荐内容的多样性,对于用户长期的满意度提高,有着积极和重要的影响。 常见的多样性算法有热传导、二次优化、社会化网络等。通过设定合理的相似性、集 中指数和覆盖度,来提高推荐结果的多样性和新颖性。 7.4. 3 曝光打击和不良内容过滤 推荐系统给用户带来了便利,帮助用户更好更快地发现自己感兴趣的信息。但是一 个平台上的用户内容,并不总是积极健康的。对于一个影响力巨大的互联网平台,有责 任对不良内容进行过滤,对恶俗的内容进行曝光打击,以传递正确的价值观。对于谣言 和色情等违法内容,应坚决进行过滤。对于恶俗、恶搞和“标题党”等内容,应降低这些 类别的权重。虽然对恶俗的内容进行曝光打击会影响到用户时长,但是,一个成功的应 用程序需要关注自己的品牌形象,并为用户带来长期的、有益的价值。 7.4. 4 评估测试 模型准备就绪后,一般会先通过离线指标来评估模型的好坏,然后再决定能否上线 测试。离线算法评估常见的指标包括准确率、覆盖度、多样性、新颖性和AU C 等。在线 测试一般通过A/ B 测试进行,常见的指标有点击率、用户停留时间、广告收入等,需要 注意分析统计显著性。同时,需要注意短期的指标和长期的指标相结合,一些短期指标 的提升有时候反而会导致长期指标下降。比如,经常推荐美女或者搞笑类的内容会带来 短期的点击率提高,但是可能会引起长期的用户粘性下降。设计者需要从自己的产品角 度出发,根据产品的需要制定评估指标,这样才能更好地指导推荐系统的优化方向。 根据作者的经验,推荐系统是一个结合了产品设计、算法设计、架构设计、交互设 计的复杂系统,并不能仅仅依赖算法的提升。一般来说,若读者需要规划一个应用于线 上的推荐系统,需要注意以下问题: 1)推荐系统的应用场景 作者认为推荐系统是平台或者AP P 的辅助配套技术之一,用来帮忙用户挖掘多样 性的内容,而不能夸大推荐系统的功能,过分依赖推荐系统。首先平台需要有足够规模 的内容生产生态,推荐系统的个性化分发才有意义;其次如果平台在细分领域过于垂直, 内容单一,其实也无需引入推荐系统。 2)精准推荐依赖完善的产品设计 提升推荐准确度不能仅仅依靠算法。还需要产品体系上的支持,最核心的两个问题 就是:a)准确和完善的数据上报体系。只有丰富的数据才能支撑推荐系统的建立。b)合 理的正负反馈交互机制。推荐系统中一大难点就是收集用户的兴趣,并且兴趣包括了正 向的兴趣和负向的兴趣。刚刚开始搭建推荐系统时,收集正向的兴趣会非常重要,而当 推荐系统开始迭代优化时,负向兴趣的收集也同样重要。 3)更多数据V S 更好的模型 更多数据V S 更好的模型在很多文章中均有所提及。算法工程师往往会希望通过模 型的优化提升最后的效果,但是实际上往往调整之后并没有明显提升。相反,他们发现 增加一些数据能提高已在用的算法的预测精确性。有许多增加更多数据的途径,Net°i x 是通过增加特征的数量和类型,这样能够提高问题空间的维度。谷歌也曾经说:Googl e 并没有更好的算法,只是有更多数据而已。但是,在真实的Net°i x 应用场景中,增加超 过20 0 万的训练样本几乎没有什么效果。所以关于更多数据与更好算法孰优孰劣的讨论 并没有一定的结果。更多数据能够形成更多训练样本。当数据增长到一定量级之后,带 有巨量特征的复杂模型会导致“hig h variance”,但是在大多数情况下这还是有用的。有 时,你需要更多数据,有时你也需要改进你的算法,有时两种没有什么区别,过分专注 20 4 推荐系统与深度学习 | 于一个则另外一个会离优化越远。 4)推荐的召回策略多优于少 协同过滤被认为是目前最经典也最有效的推荐算法,往往大部分推荐系统在召回阶 段都使用该技术。但是在召回策略上,不能依赖单一的召回方法,例如,可以通过标签 或其他内容属性进行召回。较多召回策略一般都会优于较少的召回策略。 5)推荐系统也需要规则干预 以视频推荐系统来说,算法一般都会倾向于推荐点击率高的作品。但是很遗憾,高点 击的作品往往会夹杂暴力色情等内容。所以推荐系统往往需要制定很多策略把不适合的 内容过滤掉。另外,推荐系统也会倾向于推荐同质化的作品,所以往往又需要对同类作 品进行推荐频次的限制。这些看似简单的规则,实际上会大大增加推荐系统的复杂度。