mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-11-17 14:43:42 +08:00
mod
This commit is contained in:
194
极客时间专栏/推荐系统三十六式/原理篇 · 模型融合/13 | 经典模型融合办法:线性模型和树模型的组合拳.md
Normal file
194
极客时间专栏/推荐系统三十六式/原理篇 · 模型融合/13 | 经典模型融合办法:线性模型和树模型的组合拳.md
Normal file
@@ -0,0 +1,194 @@
|
||||
<audio id="audio" title="13 | 经典模型融合办法:线性模型和树模型的组合拳" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a9/1e/a99e92cc2a1105d8232f58f27cc1811e.mp3"></audio>
|
||||
|
||||
推荐系统在技术实现上一般划分为三个阶段:挖掘、召回、排序。
|
||||
|
||||
## 为什么要融合?
|
||||
|
||||
挖掘的工作就是对用户和物品做非常深入的结构化分析,庖丁解牛一样,各个角度各个层面的特征都被呈现出来,并且建好索引,供召回阶段使用,大部分挖掘工作都是离线进行的。
|
||||
|
||||
接下来就是召回,为什么会有召回?因为物品太多了,每次给一个用户计算推荐结果时,如果对全部物品挨个计算,那将是一场灾难,取而代之的是用一些手段从全量的物品中筛选出一部分比较靠谱的。
|
||||
|
||||
最后就是排序,针对筛选出的一部分靠谱的做一个统一的论资排辈,最后这个统一的排序就是今天要讲的主题:融合。
|
||||
|
||||
前面巴拉巴拉说了一段,画成图的话会好理解一些,示意图如下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/4a/db/4ac92e6cd98d597faa130b61edd024db.png" alt="" />
|
||||
|
||||
为什么要融合呢?这还得倒回去说一说召回是什么,以及这个阶段到底发生了什么?
|
||||
|
||||
在召回阶段,其实就是各种简单的、复杂的推荐算法,比如说基于内容的推荐,会产生一些推荐结果,比如基于物品的协同过滤会产生一些结果,矩阵分解会产生一些结果,等等。
|
||||
|
||||
总之,每种算法都会产生一批推荐结果,一般同时还附带给每个结果产生一个推荐分数,是各自算法给出来的。
|
||||
|
||||
于是问题就来了,这些不同算法产生的推荐分数,最后要一起排个先后,难道依据各自的分数吗?
|
||||
|
||||
这样是不行的,为什么?有几个原因:
|
||||
|
||||
1. 有个算法可能只给出结果,不给分数,比如用决策树产生一些推荐结果;
|
||||
1. 每种算法给出结果时如果有分数,分数的范围不一定一样,所以不能互相比较,大家各自家庭背景不一样;
|
||||
1. 即使强行把所有分数都归一化,仍然不能互相比较,因为产生的机制不同,有的可能普遍偏高,有的可能普遍偏低。
|
||||
|
||||
既然来自各个地方的状元凑在一起,谁也不服谁,那只能再举行一次入学考试了,这个入学考试就是融合模型。也就是,不同算法只负责推举出候选结果,真正最终是否推荐给用户,由另一个统一的模型说了算,这个就叫做模型的融合。
|
||||
|
||||
模型融合的作用除了统一地方军阀,还有集中提升效果的作用。在机器学习中,有专门为融合而生的集成学习思想。
|
||||
|
||||
今天要讲的一个典型的模型融合方案是:逻辑回归和梯度提升决策树组合,我可以给它取个名字叫做“辑度组合”。
|
||||
|
||||
## “辑度组合”原理
|
||||
|
||||
在推荐系统的模型融合阶段,就要以产品目标为导向。举个简单的例子,信息流推荐,如果以提高CTR为目标,则融合模型就要把预估CTR作为本职工作,这个工作谁最能胜任呢,一直以来就是逻辑回归。
|
||||
|
||||
下面,我就来简单介绍一些常见的逻辑回归。
|
||||
|
||||
### 逻辑回归
|
||||
|
||||
CTR预估就是在推荐一个物品之前,预估一下用户点击它的概率有多大,再根据这个预估的点击率对物品排序输出。
|
||||
|
||||
逻辑回归常常被选来执行这个任务,它的输出值范围就是0到1之间,刚好满足点击率预估的输出,这是一个基础。因为逻辑回归是广义线性模型,相比于传统线性模型,在线性模型基础上增加了sigmoid函数。
|
||||
|
||||
下面就简单说说,逻辑回归如何做CTR预估?
|
||||
|
||||
我还是按照一直以来的套路来讲,先讲它在真正使用时怎么做的,再一步步往回看怎么得到所需要的条件。
|
||||
|
||||
在对召回阶段不同算法给出的候选物品计算CTR预估时,需要两个东西:
|
||||
|
||||
1. 特征;
|
||||
1. 权重。
|
||||
|
||||
第一个是特征,就是用量化、向量的方式把一个用户和一个物品的成对组合表示出来。这里说的量化方式包括两种:实数和布尔。实数好理解,比如一个用户的年龄,一个用户平均在某个品类上每个月的开销,类似等等。
|
||||
|
||||
布尔,就是取值0或者1,针对两种类别形式的,比如用户所在的省、市,当时是白天还是晚上,物品的每一个标签。
|
||||
|
||||
用户和每一个候选物品都组一下CP,然后以这种特征化的方式表达出来,就可以计算了,否则类别形式的字段不能直接参与计算。
|
||||
|
||||
第二个是权重,每个特征都有一个权重,权重就是特征的话事权。在这场决定哪些物品最终有机会能走到前台的选秀过程中,用户和物品这对CP的所有特征都有投票权,只是同人不同命,每个特征的权重不一样,对最终计算CTR影响有大有小。
|
||||
|
||||
这个权重就很重要了,显然不能由愚蠢的人类来指定,需要模型自主从大量的历史数据中学习得到。
|
||||
|
||||
有了特征,它是一个向量,假如把它叫做x;还有特征的权重,也是一个维度和特征一样的向量,假如叫做w。
|
||||
|
||||
我们通过对x和w做点积计算,就得到了一个传统线性模型的输出,再用sigmoid函数对这个值做一个变换,就得到一个0到1之间的值,也就是预估的CTR。
|
||||
|
||||
这里所说的sigmoid函数长这个样子:
|
||||
|
||||
$$\sigma(w\times{x}) = \frac{1}{1+e^{-w\times{x}}}$$
|
||||
|
||||
这个函数曲线如图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d5/3d/d5aef83fc6693d22554778c8133bfb3d.png" alt="" />
|
||||
|
||||
那看上去其实要做的就是两件事了:搞特征、学权重。
|
||||
|
||||
事实上的确如此,甚至前者占据更多的时间。逻辑回归特特征的取值都要求要在0到1之间。
|
||||
|
||||
甚至在一些领域,比如搜索广告,特征全都是布尔取值,只有出现和不出现两种,一旦遇到实数取值的特征,就将它划分成多个区间段,也变成了布尔取值。
|
||||
|
||||
除此之外,由于逻辑回归是广义线性模型,所谓广义就是因为加了sigmoid函数,所以很多非线性关系它无能为力。
|
||||
|
||||
比如说,有一天你发现“ID为233的用户喜欢买各种钢笔”这个事实,它可以有两个特征组合出来,一个是“ID为233”,是一个布尔特征,另一个是“物品为钢笔”,也是一个布尔特征,显然构造一个新特征,叫做“ID为233且物品为钢笔”。
|
||||
|
||||
只有两个原始特征都取值为1时,这个构造出的特征才会取值为1,这种组合就是非线性,逻辑回归本身对两个原始特征仅仅是线性加权,并不能很好地刻画这个组合关系,非得组合才能助它一臂之力。
|
||||
|
||||
类似这样的工作,行话都叫做特征工程,刚才举例所说的特征组合叫做二阶组合,还有三阶组合,只要你高兴,也没人拦着你搞四阶组合。
|
||||
|
||||
但是要注意,特征组合的难点在于:组合数目非常庞大,而且并不是所有组合都有效,只有少数组合有效。
|
||||
|
||||
需要不断去弄脏双手,脚上沾泥地从数据中发现新的、有效的特征及特征组合。
|
||||
|
||||
特征工程+线性模型,是模型融合、CTR预估等居家旅行必备。
|
||||
|
||||
权重那部分就是老生常谈了,简单说就是你准备好样本,喂给优化算法,优化算法再挤出新鲜的权重。
|
||||
|
||||
权重的学习主要看两个方面:损失函数的最小化,就是模型的偏差是否足够小;另一个就是模型的正则化,就是看模型的方差是否足够小;都是希望模型能够有足够的生命力,在实际生产线上最好能和实验阶段表现一样好。
|
||||
|
||||
除了要学习出偏差和方差都较小的模型,还需要能够给工程上留出很多余地,具体来说就是两点,一个是希望越多权重为0越好,权重为0称之为稀疏,可以减小很多计算复杂度,并且模型更简单,方差那部分会可控。
|
||||
|
||||
另一个是希望能够在线学习这些权重,用户源源不断贡献他们的行为,后台就会源源不断地更新权重,这样才能实现生命的大和谐。
|
||||
|
||||
要学习逻辑回归的权重,经典的方法如梯度下降一类,尤其是随机梯度下降,这在前面讲矩阵分解时已经提到过,可以实现在实时数据流情形下,更新逻辑回归的权重,每一个样本更新一次。
|
||||
|
||||
但是随机梯度下降常被人诟病的是,它什么也表现不好,很难得到稀疏的模型,效果收敛得也很慢。
|
||||
|
||||
也就是模型预测结果在通往真正想要到达的靶心路上看上去像是喝醉了酒一样,歪歪斜斜,像是很随机,但是趋势上还是在朝损失函数下降的方向。
|
||||
|
||||
后来Google在2013年KDD上发表了新的学习算法:FTRL,一种结合了L1正则和L2正则的在线优化算法,现在各家公司都采用了这个算法。
|
||||
|
||||
这里也顺便提一句,这个专栏重点讲解的是推荐系统落地会用到的东西,尽量通俗易懂。如果深入到机器学习和人工智能其他分支,可以参考极客时间上洪亮劼老师的“AI技术内参”专栏。
|
||||
|
||||
对于我给你讲过的原理,希望可以让你有个直观的理解,在专栏结束后的图书出版计划中,我会在书中更加细致深入地讲原理,就有更多的代码和公式。
|
||||
|
||||
### 梯度提升决策树GBDT
|
||||
|
||||
前面提到,特征组合又能有效表达出数据中的非线性事实,但是发现成本却很高,需要花大量的人力和物力,那么有没有算法能够在这个阶段帮助到你呢?
|
||||
|
||||
答案是,有!就是用树模型。
|
||||
|
||||
树模型,可以理解为苏格拉底式的诘问,想象不断对一个样本提问:是男用户吗?是的话再问:是北上广的用户吗?不是的话则可以问:是月收入小于5000的用户吗?
|
||||
|
||||
这种不断提问按照层级组织起来,每次回答答案不同后再提出不同的问题,直到最后得出最终答案:用户对这个推荐会满意吗?
|
||||
|
||||
这就是树模型。树模型天然就可以肩负起特征组合的任务,从第一个问题开始,也就是树的根节点,到最后得到答案,也就是叶子节点,这一条路径下来就是若干个特征的组合。
|
||||
|
||||
树模型最原始的是决策树,简称DT,先驱们常常发现,把“多个表现”略好于“随机乱猜”的模型以某种方式集成在一起往往出奇效,所以就有树模型的集成模型。最常见的就是随机森林,简称RF,和梯度提升决策树,简称GBDT。
|
||||
|
||||
先讲一下剃度提升决策树的原理。按照其名字,我把它分成两部分:一个是GB,一个是DT。GB是得到集成模型的方案,沿着残差梯度下降的方向构建新的子模型,而DT就是指构建的子模型要用的决策树。
|
||||
|
||||
梯度提升决策树其实本意是用来做回归问题的,怎么回事呢?
|
||||
|
||||
举个例子好了。假如这里有以下这么几条样本。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/41/2d/41b2d14f5dd93dcac50da314b325642d.png" alt="" />
|
||||
|
||||
现在有个任务是根据是否喜欢养花,喜欢打游戏,喜欢帽子来预测年龄,模型就是梯度提升决策树GBDT。假设我们设定好每个子树只有一层,那么三个特征各自按照取值都可以构成两分支的小树枝。
|
||||
|
||||
树根节点为:是否喜欢养花,左分支就是不喜欢,被划分进去的样本有13、14、15,35这四个年龄;右边的就是样本25、49、68、71、73。左边的样本均值是19.25,右边的样本均值是57.2。
|
||||
|
||||
树根节点为:是否喜欢打游戏,左分支是不喜欢,被划分进去就有49,71,73;右边是喜欢,被划分进去的样本有13、14、15、25、35、68。左边的均值是64,右边的均值是28.3。
|
||||
|
||||
树根节点为:是否喜欢帽子,左分支是不喜欢,被划分进去就有14、15、49、71;右边是喜欢,右边是13、25、35、68、73,左边均值是37.25,右边是42.8。
|
||||
|
||||
叶子节点上都是被划分进去的样本年龄均值,也就是预测值。这里是看哪棵树让残差减小最多,分别拿三个方案去预测每个样本,统计累积的误差平方和,三个分别是1993.55、2602、5007.95,于是显然第一棵树的预测结果较好,所以GBDT中第一棵树胜出。
|
||||
|
||||
接下来第二棵树如何生成呢?这里就体现出GBDT和其他提升算法的不同之处了,比如和Ada boost算法不同之处,GBDT用上一棵树去预测所有样本,得到每一个样本的残差,下一棵树不是去拟合样本的目标值,而是去拟合上一棵树的残差。这里,就是去拟合下面这个表格。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/fc/af/fc8d3431f6fda7c845fa9c15fa6959af.png" alt="" />
|
||||
|
||||
新一轮构建树的过程以最后一列残差为目标。构建过程这里不再赘述,得到第二棵树。如此不断在上一次建树的残差基础上构建新树,直到满足条件后停止。
|
||||
|
||||
在得到所有这些树后,真正使用时,是将它们的预测结果相加作为最终输出结果。这就是GBDT的简单举例。
|
||||
|
||||
这里有两个问题。
|
||||
|
||||
第一个,既然是用来做回归的,上面这个例子也是回归问题,如何把它用来做分类呢?那就是把损失函数从上面的误差平方和换成适合分类的损失函数,例如对数损失函数。
|
||||
|
||||
更新时按照梯度方向即可,上面的误差平方和的梯度就刚好是残差。对于CTR预估这样的二分类任务,可以将损失函数定义为:
|
||||
|
||||
$$-ylog§ - (1-y)log(1-p)$$
|
||||
|
||||
第二个,通常还需要考虑防止过拟合,也就是损失函数汇总需要增加正则项,正则化的方法一般是:限定总的树个数、树的深度、以及叶子节点的权重大小。
|
||||
|
||||
第三个,构建每一棵树时如果遇到实数值的特征,还需要将其分裂成若干区间,分裂指标有很多,可以参考xgboost中的计算分裂点收益,也可以参考决策树所用的信息增益。
|
||||
|
||||
### 二者结合
|
||||
|
||||
前面介绍了逻辑回归LR,以及剃度提升决策树GBDT的原理。实际上可以将两者结合在一起,用于做模型融合阶段的CTR预估。这是Facebook在其广告系统中使用的方法,其中GBDT的任务就是产生高阶特征组合。
|
||||
|
||||
具体的做法是:GBDT产生了N棵树,一条样本来了后,在每一棵树上都会从根节点走到叶子节点,到了叶子节点后,就是1或者0,点或者不点。把每一棵树的输出看成是一个组合特征,取值为0或者1,一共N棵树,每棵树i有 $M_i$ 个叶子就相当于有M种组合,一棵树对应一个one-hot(独热)编码方式,一共就有$\sum_{i=1}^{N}{M_i}$个维度的新特征,作为输入向量进入LR模型,输出最终的结果。
|
||||
|
||||
示意图如下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c6/57/c613b5946ccbfc2c1321bcec4fef5257.png" alt="" />
|
||||
|
||||
每一条样本,样本内容一般是把用户、物品、场景三类特征拼接在一起,先经过N棵GBDT树各自预测一下,给出自己的0或者1的预测结果,接着,这个N个预测结果再作为N个one-hot编码特征拼接成一个向量送入逻辑回归中,产生最终的融合预估结果。
|
||||
|
||||
另外,由于两者结合后用来做推荐系统的模型融合,所以也可以考虑在输入特征中加入各个召回模型产生的分数,也许会有用。
|
||||
|
||||
以上就是咱们的“辑度组合”原理,虽然简单,但在实际应用中非常的有效。
|
||||
|
||||
## 总结
|
||||
|
||||
今天我主要讲了简单的逻辑回归和梯度提升决策树,两者都是不太复杂的模型。并且无论是逻辑回归,还是梯度提升决策树,都有非常成熟的开源实现,可以很快落地。
|
||||
|
||||
由于篇幅限制,在梯度提升决策树那部分有一些细节被我略过了,你能自己手算出例子中的第二棵树是什么样的吗?欢迎留言一起讨论。感谢你的收听,我们下期再见。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/87/b0/873b086966136189db14874181823fb0.jpg" alt="" />
|
||||
145
极客时间专栏/推荐系统三十六式/原理篇 · 模型融合/14 | 一网打尽协同过滤、矩阵分解和线性模型.md
Normal file
145
极客时间专栏/推荐系统三十六式/原理篇 · 模型融合/14 | 一网打尽协同过滤、矩阵分解和线性模型.md
Normal file
@@ -0,0 +1,145 @@
|
||||
<audio id="audio" title="14 | 一网打尽协同过滤、矩阵分解和线性模型" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/48/3f/4843ba23231bddea7789a7bee03ce73f.mp3"></audio>
|
||||
|
||||
在上一篇文章中,我讲到了使用逻辑回归和梯度提升决策树组合的模型融合办法,用于CTR预估,我还满怀爱意地给这对组合起了个名字,叫做辑度组合,因为这对组合的确可以在很多地方帮到我们。
|
||||
|
||||
这对组合中,梯度提升决策树,也就是人们常说的GBDT,所起的作用就是对原始的特征做各种有效的组合,一棵树一个叶子节点就是一种特征组合。
|
||||
|
||||
这大概就是逻辑回归的宿命吧,作为一个广义线性模型,在这个由非线性组成的世界里,唯有与各种特征组合办法精诚合作,才能活下去。
|
||||
|
||||
## 从特征组合说起
|
||||
|
||||
对逻辑回归最朴素的特征组合就是二阶笛卡尔乘积,但是你有没有想过这样暴力组合的问题所在。
|
||||
|
||||
1. 两两组合导致特征维度灾难;
|
||||
1. 组合后的特征不见得都有效,事实上大部分可能无效;
|
||||
1. 组合后的特征样本非常稀疏,意思就是组合容易,但是并不能在样本中找到对应的组合出现,也就没办法在训练时更新参数。
|
||||
|
||||
如果把包含了特征两两组合的逻辑回归线性部分写出来,就是:
|
||||
|
||||
$$\hat{y} = \omega_{0} + \sum_{i=1}^{n}{\omega_{i}x_{i}} + \sum_{i=1}^{n}{\sum_{j=i+1}^{n}{\omega_{ij}x_{i}x_{j}}}$$
|
||||
|
||||
这和原始的逻辑回归相比,就多出来了后面那一大坨,特征两两组合那部分,也需要去学习对应的参数权重。
|
||||
|
||||
问题就是两两组合后非常有可能没有样本能够学习到 $w_{ij},不但没有样本可以用来学习到参数,而且在应用时,如果遇到了这样的组合,也就只能放弃,因为没有学到权重。
|
||||
|
||||
针对这个问题,就有了一个新的算法模型:因子分解机模型,也叫做FM,即Factorization Machine。因子分解机也常常用来做模型融合,今天就和你聊聊因子分解机的来龙去脉。
|
||||
|
||||
## FM模型
|
||||
|
||||
### 1.原理
|
||||
|
||||
因子分解机模型是在2010年被提出来的。因为逻辑回归在做特征组合时样本稀疏,从而无法学到很多特征组合的权重,所以因子分解机的提出者就想,能不能对上面那个公式中的$w _{ij}$做解耦,让每一个特征学习一个隐因子向量出来。
|
||||
|
||||
就好像前面讲矩阵分解时,为每一个用户和每一个物品各自都学习一个隐因子向量一样,这样一来,任何两个特征不小心在实际使用时相遇了,需要组合,那么各自掏出自己随身携带的隐因子变量做一个向量点积,就是两者组合特征的权重了。
|
||||
|
||||
还是针对逻辑回归的线性部分,用公式写一下更清楚:
|
||||
|
||||
$$\hat{y} = \omega_{0} + \sum_{i=1}^{n}{\omega_{i}x_{i}} + \sum_{i=1}^{n}{\sum_{j=i+1}^{n}{<V_{i}, V_{j}>x_{i}x_{j}}}$$
|
||||
|
||||
这个公式和前面特征组合的公式相比,不同之处就是原来有个 $w_{ij}$,变成了这里的两个隐因子向量的点积 $<V_{i}, V_{j}>$。
|
||||
|
||||
不要小看这个变化。它其实认为两个特征之间,即使没有共同出现在一条样本中,也是有间接联系的。比如说特征A和特征B曾在一些样本中一起出现过,特征B和特征C曾在一些样本中出现过,那么特征A和特征C无论是否在样本中一起出现过,仍然是有些联系的。
|
||||
|
||||
如果在实际预测CTR时,特征A和特征C真的在一起出现了,如果你用的是因子分解机模型,这时候你的预测程序就不慌不忙走向数据库,从中取出早已准备好的特征A和特征C的隐因子向量,拿出来做一个点积运算,就得到了两者组合的权重。
|
||||
|
||||
也许逻辑回归见到这一切不禁要问:居然还有这种操作?是的,因子分解机的先进之处就在于此。
|
||||
|
||||
现在聪明如你,一定也想到了,既然二阶特征组合可以学到隐因子向量,那么三阶特征组合也可以加进来,四阶,五阶……想一想是不是有点小激动?不要急,组合越多,计算复杂度就会陡增,所以一般在实际使用中,因子分解机就表演到二阶特征组合就OK了。
|
||||
|
||||
## 2模型训练
|
||||
|
||||
因子分解机的参数学习并无特别之处,看目标函数,我在这里是把它当作融合模型来看的,用来做CTR预估,因此预测目标是一个二分类,因子分解机的输出还需要经过sigmoid函数变换:
|
||||
|
||||
$$\sigma(\hat{y}) = \frac{1}{1+e^{-\hat{y}}}$$
|
||||
|
||||
因此,损失目标函数也就是常用的logistic loss:
|
||||
|
||||
$$ loss(\theta)= -\frac{1}{m}\sum_{i=1}^{m}{[y^{(i)}log(\sigma(\hat{y}))+(1-y^{(i)})log(1-\sigma(\hat{y}))]} $$
|
||||
|
||||
公式中 $\sigma(\hat{y})$ 是因子分解机的预测输出后经过sigmoid函数变换得到的预估CTR, $y^{(i)}$ 是真实样本的类别标记,正样本是1,负样本是0,m是样本总数。
|
||||
|
||||
对这个损失目标函数使用梯度下降或者随机梯度下降就可以得到模型的参数,和前面文章里的没有区别,注意损失函数实际上还需要加上正则项,这在上一篇专栏中已经总结过机器学习损失函数的两板斧,就是偏差和方差。
|
||||
|
||||
## 3预测阶段
|
||||
|
||||
假如现在已经得到了因子分解机的模型参数,你忍不住跃跃欲试想端着它冲上战场,且慢,因子分解机中二阶特征组合那一坨,在实际计算时,复杂度有点高,如果隐因子向量的维度是k,特征维度是n,那这个复杂度就是$O(kn^2)$,其中n方是特征要两两组合,k是每次组合都要对k维向量计算点积。需要对此稍微做一下改造,改造过程如下:
|
||||
|
||||
$$<br>
|
||||
\begin{aligned}<br>
|
||||
\sum_{i=1}^{n}\sum_{j=i+1}^{n}{<V_{i}, V_{j}>x_{i}x_{j}}={}& \frac{1}{2}\sum_{i=1}^{n}\sum_{j=1}^{n}{<V_{i}, V_{j}>x_{i}x_{j}} - \frac{1}{2}\sum_{i=1}^{n}{<V_{i}, V_{j}>x_{i}x_{i}} \<br>
|
||||
&=\frac{1}{2}(\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{f=1}^{k}{v_{i,f}v_{j,f}x_ix_j}-\sum_{i=1}^{n}\sum_{f=1}^{k}{v_{i,f}v_{i,f}x_ix_i}) \<br>
|
||||
&=\frac{1}{2}\sum_{f=1}^{k}((\sum_{i=1}^{n}{v_{i,f}x_i})(\sum_{j=1}^{n}v_{j,f}x_j)-\sum_{i=1}^{n}{v_{i,f}^2x_i^2}) \<br>
|
||||
&=\frac{1}{2}\sum_{f=1}^{k}{((\sum_{i=1}^{n}{v_{i,f}x_i})^2-\sum_{i=1}^{n}{v_{i,f}^2x_i^2})}<br>
|
||||
\end{aligned}<br>
|
||||
$$
|
||||
|
||||
看上去这个有点复杂,你如果不想理解也没关系,我直接告诉你最后该怎么算。
|
||||
|
||||
```
|
||||
loop1 begin: 循环k次,k就是隐因子向量的维度,其中,循环到第f次时做以下事情
|
||||
|
||||
loop2 begin:循环n个特征,第i次循环时做这样的事情
|
||||
|
||||
1. 从第i个特征的隐因子向量中拿出第f维的值
|
||||
2. 计算两个值:A是特征值和f维的值相乘,B是A的平方
|
||||
|
||||
loop2 end
|
||||
|
||||
把n个A累加起来,并平方得到C,把n个B也累加起来,得到D
|
||||
|
||||
用C减D,得到E
|
||||
|
||||
loop1 end
|
||||
|
||||
把k次循环得到的k个E累加起来,除以2
|
||||
|
||||
```
|
||||
|
||||
这就是因子分解机中,二阶组合部分的实际计算方法,现在这样做的复杂度只是O(kn),原来的平方复杂度不见了。
|
||||
|
||||
## 4.一网打尽其他模型
|
||||
|
||||
下面继续带你见识一些因子分解机的神奇之处。看下面这张图,这里示意了一批样本。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/07/e5/07dad32f420ad51602302e36d7c745e5.png" alt="">
|
||||
|
||||
这张图中每一条样本都记录了用户对电影的评分,最右边的y是评分,也就是预测目标;左边的特征有五种,用户ID、当前评分的电影ID、曾经评过的其他分、评分时间、上一次评分的电影。
|
||||
|
||||
好,现在我们来看因子分解机如何一网打尽其他模型的,注意,这里说的一网打尽并不是打败,而是说模型可以变形成其他模型。
|
||||
|
||||
前面已经说了因子分解机可以实现带有特征组合的逻辑回归。
|
||||
|
||||
现在假设图中的样本特征只留下用户ID和电影ID,因子分解机模型就变成:
|
||||
|
||||
$$\hat{y} = \omega_{0} + \omega_{u} + \omega_{i} + <V_{u}, V_{i}>$$
|
||||
|
||||
解释一下如何变成这样的。因为用户ID和电影ID,在一条样本中,各自都只有一个维度是1,其他都是0,所以在一阶部分就没有了求和符合,直接是wu和wi,二阶部分特征乘积也只剩下了一个1,其他都为0了。你瞧,这不就是带有偏置信息的SVD吗?
|
||||
|
||||
现在继续,在SVD基础上把样本中的特征加上用户历史评过分的电影ID,再求隐因子向量,这就是SVD++呀!
|
||||
|
||||
再加上时间信息,就变成了time-SVD。
|
||||
|
||||
所以因子分解机是把我之前讲过的矩阵分解一网打尽了,顺便还干起了逻辑回归的工作,也正因如此,因子分解机常常用来做模型融合,在推荐系统的排序阶段肩负起对召回结果做重排序的任务。
|
||||
|
||||
## 5.FFM
|
||||
|
||||
因子分解机的故事已经讲完,但我后来在因子分解机基础上改进了一下。改进的思路是:不但认为特征和特征之间潜藏着一些不可告人的关系,还认为特征和特征类型有着千丝万缕的关系。
|
||||
|
||||
这个特征类型,就是某些特征实际上是来自数据的同一个字段,比如用户ID,占据了很多维度,变成了很多特征,但他们都属于同一个类型,都叫“用户ID”。这个特征类型就是字段,即Field。这种改进叫做Field-aware Factorization Machines,简称FFM。
|
||||
|
||||
再回顾一下,因子分解机模型的样子是这样:
|
||||
|
||||
$$\hat{y} = \omega_{0} + \sum_{i=1}^{n}{\omega_{i}x_{i}} + \sum_{i=1}^{n}{\sum_{j=i+1}^{n}{<V_{i}, V_{j}>x_{i}x_{j}}}$$
|
||||
|
||||
之前因子分解机认为每个特征有一个隐因子向量,FFM改进的是二阶组合那部分,改进的模型认为每个特征有f个隐因子向量,这里的f就是特征一共来自多少个字段(Field),二阶组合部分改进后如下:
|
||||
|
||||
$$ \sum_{i=1}^{n}{\sum_{j=i+1}^{n}{<V_{i,fj}, V_{j,fi}>x_{i}x_{j}}} $$
|
||||
|
||||
FFM模型也常用来做CTR预估。在FM和FFM事件过程中,记得要对样本和特征都做归一化。
|
||||
|
||||
## 总结
|
||||
|
||||
今天,我给你介绍了另一种常用来做CTR预估的模型,因子分解机。因子分解机最早提出在2010年,在一些数据挖掘比赛中都取得了很好的乘积,后来被引入工业界做模型融合,也表现不俗。严格来说,因子分解机也算是矩阵分解算法的一种,因为它的学习结果也是隐因子向量,也是用过隐因子向量的点积代替原来的单个权重参数。
|
||||
|
||||
最后,由于不断提到特征组合的重要性,前有GBDT,现有FM,都是在特征组合上花功夫,你能不能在这里分享一下,你所用过的特征组合办法有哪些呢?欢迎留言一起讨论。<br>
|
||||
<img src="https://static001.geekbang.org/resource/image/87/b0/873b086966136189db14874181823fb0.jpg" alt="">
|
||||
182
极客时间专栏/推荐系统三十六式/原理篇 · 模型融合/15 | 深度和宽度兼具的融合模型 Wide and Deep.md
Normal file
182
极客时间专栏/推荐系统三十六式/原理篇 · 模型融合/15 | 深度和宽度兼具的融合模型 Wide and Deep.md
Normal file
@@ -0,0 +1,182 @@
|
||||
<audio id="audio" title="15 | 深度和宽度兼具的融合模型 Wide and Deep" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/11/fb/11b81561fe08f64c7b632886fdb768fb.mp3"></audio>
|
||||
|
||||
我在前面已经提到过一个事实,就是推荐系统的框架大都是多种召回策略外挂一个融合排序。召回策略的姿势繁多,前面的专栏文章已经涉及了一部分内容。今天我们继续说融合排序。
|
||||
|
||||
## 要深还是要宽
|
||||
|
||||
融合排序,最常见的就是CTR预估,你一定不要把自己真的只局限在C上,这里说的CTR预估的C,可以是产品中的任何行为,视频是不是会看完,看完后是不是会收藏,是不是会分享到第三方平台,查看的商品是不是会购买等等,都可以看成那个可以被预估发生概率的CTR。
|
||||
|
||||
CTR预估的常见做法就是广义线性模型,如 Logistic Regression,然后再采用特征海洋战术,就是把几乎所有的精力都放在搞特征上:挖掘新特征、挖掘特征组合、寻找新的特征离散方法等等。
|
||||
|
||||
这种简单模型加特征工程的做法好处多多:
|
||||
|
||||
1. 线性模型简单,其训练和预测计算复杂度都相对低;
|
||||
1. 工程师的精力可以集中在发掘新的有效特征上,俗称特征工程;
|
||||
1. 工程师们可以并行化工作,各自挖掘特征;
|
||||
1. 线性模型的可解释性相对非线性模型要好。
|
||||
|
||||
特征海洋战术让线性模型表现为一个很宽广(Wide)的模型,可以想象逻辑回归中那个特征向量在特征工程的加持下,越来越宽的样子。
|
||||
|
||||
最近十年,是深度学习独步天下的十年,犹如异军突起,一路摧城拔寨,战火自然也烧到了推荐系统领域,用深度神经网络来革“线性模型+特征工程”的命,也再自然不过。
|
||||
|
||||
用这种“深模型”升级以前的“宽模型”,尤其是深度学习“端到端”的诱惑,可以让每天沉迷搞特征无法自拔的工程师们主动投怀送抱。
|
||||
|
||||
深度学习在推荐领域的应用,其最大好处就是“洞悉本质般的精深”,优秀的泛化性能,可以给推荐很多惊喜。
|
||||
|
||||
硬币总有正反面,深度模型的泛化强于线性模型,也会导致推荐有时候看上去像是“找不着北”,就是大家常常自问的那句话:“不知道这是怎么推出来的?”用行话说,就是可解释性不好。
|
||||
|
||||
以前全面搞特征时,你叫人家“宽模型”小甜甜,现在新模型换旧模型,“深模型”一出,就叫“宽模型”牛夫人,这样不好,还是要两者合作,才能最大限度地发挥效果。
|
||||
|
||||
因此,Google在2016年就发表了他们在Google Play应用商店上实践检验过的CTR预估方法:Wide & Deep模型,让两者一起为用户们服务,这样就取得了良好效果。
|
||||
|
||||
下面,我就为你详细介绍一下这个深宽模型。
|
||||
|
||||
## Wide & Deep模型
|
||||
|
||||
一个典型的推荐系统架构,其实很类似一个搜索引擎,搜索由检索和排序构成。推荐系统也有召回和排序两部构成,不过,推荐系统的检索过程并不一定有显式的检索语句,通常是拿着用户特征和场景特征去检索召回,其中用户特征也就是在前面的专栏中提到的用户画像。
|
||||
|
||||
示意图如下.
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7e/e1/7e3c2abe330e98829c01a73b0f7125e1.png" alt="" />
|
||||
|
||||
简单描述一下这个示意图。
|
||||
|
||||
首先使用用户特征和上下文场景特征从物品库中召回候选推荐结果,比如得到100个物品,然后用融合模型对这100个物品做最终排序,输出给用户展示。
|
||||
|
||||
同时开始记录展示日志和用户行为日志,再把收集到的日志和用户特征、上下文场景特征、物品特征拉平成为模型的训练数据,训练新的模型,再用于后面的推荐,如此周而复始。
|
||||
|
||||
今天要说的深宽模型就是专门用于融合排序的,分成两部分来看。一部分是线性模型,一部分是深度非线性模型。整个示意图如下:<br />
|
||||
<img src="https://static001.geekbang.org/resource/image/5c/20/5ca21e4b2511cd4567fb630db5b14520.png" alt="" />
|
||||
|
||||
我来解释一下这个示意图,这个示意图有三部分。最左边是宽模型,中间是深宽模型,最右边是纯的深度模型。
|
||||
|
||||
首先,线性模型部分,也就是“宽模型”,形式如下:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ea/3f/ea301585c357cbfc77aba992048a103f.png" alt="" />
|
||||
|
||||
再次强调一下,这是线性模型的标准形式,逻辑回归只是在这基础上用sigmoid函数变换了一下。
|
||||
|
||||
模型中的X是特征,W是权重,b是模型的偏置,也是线性模型的截距。线性模型中常用的特征构造手段就是特征交叉。
|
||||
|
||||
例如:“性别=女 and 语言=英语。”就是由两个特征组合交叉而成,只有当“性别=女”取值为1,并且“语言=英语”也取值为1时,这个交叉特征才会取值为1。线性模型的输出这里采用的Logistic Regression。
|
||||
|
||||
好,现在把头转到右边,看看深度模型。深度模型其实就是一个前馈神经网络。
|
||||
|
||||
深度模型对原始的高维稀疏类别型特征,先进行嵌入学习,转换为稠密、低维的实值型向量,转换后的向量维度通常在10-100这个范围。
|
||||
|
||||
这里的嵌入学习,就是先随机初始化嵌入向量,再直接扔到整个前馈网络中,用目标函数来优化学习。
|
||||
|
||||
由于本专栏并不会专门讲深度学习的原理,后面还会继续讲到深度学习和推荐系统的结合使用,所以有必要在这里简单普及一下深度学习的基本概念,不然我自顾自地开车,你可能会觉得辣眼睛。
|
||||
|
||||
就以这里的“深”模型,也就是示意图中最右边的图为例,一个深度神经网络由输入层,隐藏层,输出层构成。
|
||||
|
||||
那这个和逻辑回归的区别在哪呢?你可以认为逻辑回归是个残缺的神经网络,只有输入层和输出层,没有隐藏层。
|
||||
|
||||
逻辑回归的输入层就是特征向量,原来我们熟悉的特征权重,就是神经网络的参数,就存在于这个残缺的神经网络输入层和输出层的连线上,后面都可以这么理解,深度神经网络参数都在那些连线上。
|
||||
|
||||
这个残缺神经网络的输出层做了两件事,这时特征值在经过连线送到输出层时已经乘以了连线上的参数,第一件事就是把这些值加起来,第二件事就是用sigmoid函数变换一下。
|
||||
|
||||
把逻辑回归当成一个残缺的神经网络理解后,再回头看真正的神经网络,这里多了一个隐藏层,这个多出来的隐藏层干的事就是刚才提到的输出层的两板斧。
|
||||
|
||||
只不过一个隐藏层可以有多个神经元在干这两件事,隐藏层的这多个神经元就相当于输出层的输入层。
|
||||
|
||||
这个增加的隐藏层有什么意义呢?意义就是给模型提供了非线性转换。
|
||||
|
||||
所谓深度学习,就是深度神经网络,就是有不止一层的隐藏层存在。层数越多,非线性越强,模型越复杂。还有两点需要说明:
|
||||
|
||||
1. 隐藏层的激活函数不一定是sigmoid函数,甚至往往不用sigmoid函数;
|
||||
1. 输出层的函数也不一定是sigmoid函数,这个根据预测目标而定,回归任务就是i 直接输出求和部分,二分类是sigmoid函数,多分类则是softmax。
|
||||
|
||||
好,插播深度学习概念结束,回到主题来。深模型中,每一个隐藏层激活方式表示如下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/14/6f/14aa900ea6ec08c3bff830a74680a16f.png" alt="" />
|
||||
|
||||
其中l表示第l个隐藏层,f是激活函数,通常选用ReLU,也叫整流线性单元,为什么选用ReLU而不是sigmoid函数,原因主要是sigmoid函数在误差反向传播时梯度容易饱和。
|
||||
|
||||
如果你不明白这句话可以不用管,不影响你上车。这里我用示意图说明了一下常用激活函数的形状。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/92/c8/92c0ef095e33a13f9b8a1bee6d8369c8.png" alt="" />
|
||||
|
||||
1. 紫色是sigmoid函数,就是逻辑回归用的那个,输入值是任意范围,输出是0到1之间;
|
||||
1. 草绿色是反正切函数,和sigmoid函数样子很像,输入值是任意范围,输出是-1到1之间;
|
||||
1. 红色就是ReLU函数,当输入小于0时,输出为0,当输入大于0时,输出等于输入;
|
||||
1. 蓝色是softplus函数,是一条渐近线,输入趋向于负无穷时,输出趋于0,输入趋于正无穷时,输出趋向于等于输入。
|
||||
|
||||
最后,看看两者的融合,即深宽模型。深模型和宽模型,由逻辑回归作为最终输出单元,深模型最后一个隐藏层作为特征接入逻辑回归,宽模型的原始特征与之一起接入逻辑回归,然后训练参数。
|
||||
|
||||
参数学习就是通常说的端到端,把深模型和宽模型以及最终融合的权重放在一个训练流程中,直接对目标函数负责,不存在分阶段训练。它与机器学习中的集成学习方法有所区别,集成学习的子模型是独立训练的,只在融合阶段才会学习权重,这里是整体。
|
||||
|
||||
把深宽模型的最后输出过程表示成公式就是:<br />
|
||||
<img src="https://static001.geekbang.org/resource/image/5b/e8/5b9f863e1301539b000ecc345435a1e8.png" alt="" />
|
||||
|
||||
其中,Y是我们要预估的行为,二值变量,如购买,或点击,Google的应用场景为“是否安装APP”。σ是sigmoid函数,$W_wide^T $宽模型的权重,Φ(X)是宽模型的组合特征,$W_deep^T $应用在深模型输出上的权重,$a^(l_f ) $是深模型的最后一层输出,b是线性模型的偏置。
|
||||
|
||||
## 几点技巧
|
||||
|
||||
这个深宽模型已经在TensorFlow中有开源实现,具体落地时整个数据流如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/3e/ae/3e409c03607276cd105adff9db8ddfae.png" alt="" />
|
||||
|
||||
整个流程分为三大块:数据生成,模型训练,模型应用。
|
||||
|
||||
### 1数据生成
|
||||
|
||||
数据生成有几个要点:
|
||||
|
||||
每一条曝光日志就生成一条样本,标签就是1/0,安装了App就是1,否则就是0。
|
||||
|
||||
将字符串形式的特征映射为ID,需要用一个阈值过滤掉那些出现样本较少的特征。
|
||||
|
||||
对连续值做归一化,归一化的方法是:对累积分布函数P(X<=x)划分nq个分位,落入第i个分位的特征都归一化为下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/5b/72/5b347c032c2d8bfae560a6c466f50172.png" alt="" />
|
||||
|
||||
### 2模型训练
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b6/d1/b67786ddda65b04fb99cd2bba4f3e3d1.png" alt="" />
|
||||
|
||||
整个模型的示意如图所示。其要点,在深度模型侧:
|
||||
|
||||
1. 每个类别特征embedding成一个32维向量;
|
||||
1. 将所有类别特征的embedding变量连成一个1200维度左右的大向量;
|
||||
1. 1200维度向量就送进三层以ReLU作为激活函数的隐藏层;
|
||||
1. 最终从Logistic Regreesion输出。
|
||||
|
||||
宽模型侧就是传统的做法:特征交叉组合。
|
||||
|
||||
当新的样本集合到来时,先是用上一次的模型来初始化模型参数,然后在此基础上进行训练。
|
||||
|
||||
新模型上线前,会先跑一遍,看看会不会出事,算是一个冒烟测试。
|
||||
|
||||
### 3 模型应用
|
||||
|
||||
模型验证后,就发布到模型服务器。模型服务,每次网络请求输入的是来自召回模块的App候选列表以及用户特征,再对输入的每个App进行评分。评分就是用我们的“深宽模型”计算,再按照计算的CTR从高到低排序。
|
||||
|
||||
为了让每次请求响应时间在10ms量级,每次并不是串行地对每个候选App计算,而是多线程并行,将候选App分成若干并行批量计算。
|
||||
|
||||
正因为有这些小的优化点,GooglePlay的App推荐服务,就是在峰值时每秒计算千万级的App。
|
||||
|
||||
## 总结
|
||||
|
||||
将传统的“宽模型”和新的“深模型”结合,虽然更多的是一种工程上的创新,但是非常有实用性,模型也容易很理解。
|
||||
|
||||
简单画一下全文重点。
|
||||
|
||||
1. 深宽模型是一个结合了传统线性模型和深度模型的工程创新。
|
||||
1. 这个模型适合高维稀疏特征的推荐场景,稀疏特征的可解释性加上深度模型的泛化性能,双剑合璧。
|
||||
1. 这个模型已经开源在TensorFlow中,大大减小了落地成本,感兴趣可自行取用。
|
||||
1. 为了提高模型的训练效率,每一次并不从头开始训练,而是用上一次模型参数来初始化当前模型的参数。
|
||||
1. 将类别型特征先做嵌入学习,再将嵌入稠密向量送入深度模型中。
|
||||
1. 为了提高服务的响应效率,对每次请求要计算的多个候选App采用并行评分计算的方式,大大降低响应时间。
|
||||
|
||||
嗯,这真的是一个既博学又精深的模型啊。
|
||||
|
||||
最后一点后话,这个模型在线上效果还是不错的,以GooglePlay的App推荐效果为例,用户安装表现良好,对照实验结果如图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/33/d8/33598c5cbef846e0eb4692d09898ced8.png" alt="" />
|
||||
|
||||
可以看到,线上效果直接相对于对照组(纯线性模型+人工特征)有3.9%的提升,但是线下的AUC值提高并不明显,这其实也给你提了个问题,AUC值是不是最佳的线下评估方式?欢迎留言一起讨论。
|
||||
|
||||
## 本周知识图
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e4/23/e424954514e0796abc0ff779372c5d23.jpg" alt="" />
|
||||
Reference in New Issue
Block a user