一、CF(Collaborative-Filtering)
主要思想:
协同过滤推荐方法的主要思想是:利用已有用户群过去的行为或意见预测当前用户最可能喜欢哪些东西或对哪些东西感兴趣。
1、基于用户的协同过滤
潜在假设:
- 如果用户过去有相似的偏好,那么他们未来也会有相似的偏好;
- 用户偏好不会随时间而变化
主要思想:
- 首先,给定一个评分数据集和当前用户的ID作为输入,找出与当前用户过去有相似偏好的其他用户;
- 然后,对当前用户没有评价过的物品,利用最近邻对物品的评分计算预测值。
(1)协同推荐的评分数据库(例子1)
物品1 | 物品2 | 物品3 | |
---|---|---|---|
用户1 | 5 | 4 | ? |
用户2 | 3 | 4 | 5 |
用户3 | 4 | 5 | 3 |
(2)算法步骤
- 利用用户已有的物品评分
rate
计算用户之间的相似度$sim(a, b)$,相似度计算方法可以是pearson
相关系数、改进余弦相似度、spearman
秩相关系数、均方差等。(计算相似度是用两个用户拥有共同行为物品的评分来计算) - 针对要预测的每个用户,选择与其相似度排名最靠前的
k
个用户对指定物品的评分做加权计算来得到该用户对该物品的评分。
(3)面临的挑战
- 用户量和物品量很大时,算法性能不高
- 当需要扫描大量潜在近邻时,很难做到实时计算预测值
2、基于物品的协同过滤
主要思想:
- 利用物品间的相似度来计算预测值
3、疑难解答
1. 相似度的改进=热门item或者活跃user带来的问题
二、LR(Logistic Regression)+FTRL
1、逻辑回归
FTRL本质上是一种优化方法,最早由google提出并用于CTR预估。常被用于逻辑回归的优化,因此先简单介绍一下逻辑回归的内容。
1.1 sigmoid函数
由于二分类结果是1或者0,这与数学的阶跃函数很类似,但是阶跃函数在x=0的位置会发生突变,这个突变在数学上很难处理。所以一般使用sigmoid函数来拟合:
具体应用到逻辑回归算法中:
其中表示样本属性(对于我们而言,就是标签IP)的值, 表示这个属性对应的系数(也就是算法需要计算的内容)。注意这里将与也代入了上述公式,其中前者恒为1。于是问题就变成了在训练样本中,已知属性x与最终分类结果y(1或者0)时,如何求得这些系数 ,使得损失最小。
1.2 极大似然估计MLE与损失函数
在机器学习理论中,损失函数(loss function)是用来衡量模型的预测值与真实值的不一致程度,它是一个非负实值函数,损失函数越小,模型越优(还需考虑过拟合等问题)。损失函数是经验风险函数的核心部分,也是结构风险函数重要组成部分。模型的结构风险函数包括了经验风险项和正则项,通常可以表示成如下式子
其中m表示样本的数量。对于逻辑回归,其loss function是log损失,这可以通过极大似然估计进行推导得到。
首先,给定一个样本,可以使用一个线性函数对自变量进行线性组合,即上述的(2)式子:
根据sigmoid函数,我们可以得出预测函数的表达式为:
上式表示的预测函数为。在这里,假设因变量服从伯努利分布,那么可以得到下列两个式子:
而对于上面的两个表达式,通过观察,我们发现,可以将其合并为以下表达式:
根据上面的式子,给定一定的样本之后,我们可以构造出似然函数,然后可以使用极大似然估计MLE的思想来求解参数。但是,为了满足最小化风险理论,我们可以将MLE的思想转化为最小化风险化理论,最大化似然函数其实就等价于最小化负的似然函数。对于MLE,就是利用已知的样本分布,找到最有可能(即最大概率)导致这种分布的参数值;或者说是什么样的参数才能使我们观测到目前这组数据的概率最大。使用MLE推导LR的loss function的过程如下。
首先,根据上面的假设,写出相应的极大似然函数(假定有个样本):
上述式子中的及均为向量,并未显示其转置。
直接对上面的式子求导会不方便,因此,为了便于计算,我们可以对似然函数取对数,经过化简可以得到下式的推导结果:
因此,损失函数可以通过最小化负的似然函数得到,即下式:
在周志华版的机器学习中,将sigmiod函数代入,并使用ln代替log,上述公式表示为:
在某些资料上,还有另一种损失函数的表达形式,但本质是一样的,如下【推导见下面1.4】:
1.3 梯度下降
我们以梯度下降为例对逻辑回归进行求解,其迭代公式的推导过程如下:
上述中表示第个样本的第个属性的取值。
于是,的更新方式为:
对于随机梯度下降,每次只取一个样本,则的更新方式为:
其中为这个样本的特征值,为这个样本的真实值,为这个样本第个属性的值。
使用周志华版的损失函数更容易得出这个结论。
1.4 另一种形式的损失函数及其梯度
与上面相同,根据sigmoid函数,我们可以得出预测函数的表达式为:
上式表示的预测函数为。
但与上面不同,我们假设样本的分布为{-1,1},则
对于sigmoid函数,有以下特性(简单推导一下就可以得到):
于是(14)(15)式可以表示为:
同样,我们使用MLE作估计,
对上式取对数及负值,得到损失为:
即对于每一个样本,损失函数为:
对上式求梯度,容易得到:
2、FOBOS与RDA
2.1 FOBOS基本原理
FOBOS算法由John Duchi和Yoram Singer提出,是梯度下降的一个变种。
与梯度下降不同,它将权重的更新分为2个步骤:
从上面的2个步骤可以看出:
第一个步骤是一个标准的梯度下降。
第二个步骤是对梯度下降的结果进行微调。这里可以分为2部分:(1)前半部分保证了微调发生在第一个步骤结果(取梯度下降结果)的附近。(2)后半部分用于处理正则化,产生稀疏性。
根据次梯度理论的推断,可以得出不仅仅与迭代前的状态有关,而且与迭代后的相关
2.2 L1-FOBOS
FOBOS在L1正则化条件下,特征权重的更新方式为:(推导过程暂略)
其中为梯度在维度i上的取值
2.3 RDA基本原理
简单截断、TG、FOBOS都是建立在SGD基础上的一个变种,属于梯度下降类型的方法,这类方法的精度比较高,同时也能得到一定的稀疏性。而RDA是从另一个方面来求解,它的精度比FOBOS等略差,但它更有效的提升了稀疏性。
在RDA中,特征权重的更新策略为:
其中表示梯度对W的积分平均值(积分中值);为正则项;为一个辅助的严格的凸函数;是一个非负且非自减序列。
2.4 L1-RDA
在L1正则化条件下,RDA的各个维度的权重更新方式为:
亦即当某个维度上的累积梯度平均值的绝对值 小于阈值时,该维度权重被置为0。
3、FTRL
理论及实验均证明,L1-FOBOS这类基于梯度下降的算法有比较高的精度,但L1-RDA却能在损失一定精度的情况下产生更好的稀疏性。
把这二者的优点结合成一个算法,这就是FTRL算法的来源。
3.1 从L1-FOBOS和L1-RDA推导FTRL
我们令是一个非增正序列,同时代入L1正则项,得到L1-FOBOS的形式如下:
将这2个公式合并到一起,得到L1-FOBOS的形式如下:
将上式分解为N个独立的维度进行最优化求解:
- 上述推导的最后一步是通过除以得到的。
由于上式中的最后一项是一个与无关的量,因此上式可简化为:把N个独立优化的维度重新合并,L1-FOBOS可写成以下形式:
另一方面,L1-RDA可以表达为:
其中。
我们令,可以得到。那么L1-FOBOS与L1-RDA可以写为以下格式:
比较以上2式的区别:
- (1)L1-FOBOS考虑的是当前梯度的影响,L1-RDA则考虑了累积影响。
- (2)L1-FOBOS限制不能离太远,而L1-RDA的W则不能离0太远,因此后者更容易产生稀疏性。
3.2 FTRL权重更新的最终形式
在google2010公布的理论文章中并没有使用L2正则项,但在2013年公布工程实施方案时引入了L2。
因此,综合上述的L1-FOBOS及L1-RDA,FTRL算法的权重更新方式为:
将上式展开,得到
由于上式的最后一项相对于W来说是一个常数,同时令,上式可表示为:
各个维度可以独立表示为:
使用与L1-FOBOS相同的分析方法可以得到:
根据上面的定义:
我们使用下面介绍的学习率,以及令,则可以得到FTRL的最终形式:
3.3 学习率
1、per-coordinate learning rate
在FTRL中,学习率的定义如下:
其中是自定义的参数。
在一般梯度下降算法中,使用的是一个全局的学习率策略:。这个策略保证了学习率是一个正的非增序列,这个值对于每一个特征都是一样的。
考虑一个极端的情况,我们有多个稀疏特征,其中一个特征出现非常频繁,而另一个特征很少出现。比如第一个样本,和同时出现了,但接下来的99个样本都只出现了,然后第100个样本又出来了,由于此时的学习率为,远小于第一个样的影响了。也就是说假如第一个样本是正样本,第10个样本是负样本,此时由于学习率的不同,模型会认为会是一个有用的正相关的特征,即。但事实上,这个特征只出现了2次,而且一正一负,因此这应该是一个无用的特征,即。
在FTRL中,我们会使用一个特征的累积梯度,由于中间的99个数据没有这个特征,因此其对应的梯度为0,因此第二次的负样本对应的学习率只是略小于第一个正样本。
3.4 工程实现计算过程
3.4.1 一些定义
对于每一个样本,我们计算以下数值。
(1)
使用当前的代入sigmoid函数得出的预测值,即:
(2)
损失函数在某一个维度上的梯度,对于逻辑回归而言:
其中y为当前样本的实际值,即0或者1。
(3)
这是该维度梯度的累积值,即:
(4)
这是该维度的学习率,它与累积梯度有关。定义为:
其中为用户定义的2个参数。
(5)
其中是一个中间计算值,没有实际含义,其定义为:
(6)
其中也是一个辅助计算的中间值,它的定义为:
于是的更新为:
即:
3.4.2 FTRL算法
(1)设定以下4个输入参数,这些参数根据经验而定,可以参考以下数据:
(2)初始化以下数值:
(3)对于每一个样本的所带的每一个维度,更新
(4)使用上面更新后的,预测这个样本的值,即代入sigmoid函数计算
(5)对于每一个样本的每一个维度,更新,即上面所说的:
3.4.3 特征的独立性
如果特征是非独立的,会导致特征的权重不符合预期。
比如,有2个特征A和B,然后加了一个组合标签A&B,假如A和B都是和label非常正相关的,A&B效果更好。但结果会发现A&B这个标签的权重值可能还不如A或者B的权重高。
这是因为,带了A&B标签的,必然带A或者B,大概率被预测成高概率值,而LR梯度为:
对于正样本,,则梯度并不大,所以A&B提升并不大;而对于负样本,,则梯度非常大,导致惩罚很大,权重降低很多。
所以最终结果A和B的权重都可能比A&B要大,但对于带A&B标签的用户来说,他由于同时带3个特征,所以预测概率值还是比较高的。
当然最好的办法是尽量避免这种关联性太强的特征。
4、FTRL的工程应用
这里只列出了google提供的一些建议,我们自身的工程应用并不包含在内。
(1)减少样本的数量
- Poisson Inclusion:以p的概率接受样本
- Bloom Filter Inclusion:只有当特征出现次数达到一定数量后,特征才会被加入模型
(2)使用更少的位来进行浮点数编码
一般而言,特征对应的在(-2,2)之间,所以我们可以将32,或者64位的浮点数编码规则改为q2.13编码方式,即2位整数,1位小数点,13位小数。
(3)多个类似的模型
把多个同类型/相似的模型放在一起保存,可以不需要记录多个key,只需要记录一次Key,以及各个模型中的。这可以节省内存、带宽、CPU、存储。
(4)只保存一个模型
在上面的基础上更进一步,所有模型共用一份,以及以一个bit来记录本模型有哪些特征。当模型更新某个后,取它与之前那个值的平均值。
与共用一个模型对比,好处是记录了这个模型有哪些有效特征,共用模型就无法区分了。
(5)近似计算学习率
只使用正样本P与负样本数据N来近似估计梯度的平方和,前提是假设特征有类似的分布。
(6)减少负样本数量
减少负样本数量,然后在训练时弥补其损失。
三、FM(Factorization Machine)
1.1 背景
1.1.1 线性模型
常见的线性模型,比如线性回归、逻辑回归等,它只考虑了每个特征对结果的单独影响,而没有考虑特征间的组合对结果的影响。
对于一个有n维特征的模型,线性回归的形式如下:
其中为模型参数,为特征。
从(1)式可以看出来,模型的最终计算结果是各个特征的独立计算结果,并没有考虑特征之间的相互关系。
举个例子,我们认为“USA”与”Thanksgiving”,”China”与“Chinese new year”这样的组合特征是很有意义的,在这样的组合特征下,会对某些商品表现出更强的购买意愿,而单独考虑国家及节日都是没有意义的。
1.1.2 二项式模型
我们在(1)式的基础上,考虑任意2个特征分量之间的关系,得出以下模型:
这个模型考虑了任意2个特征分量之间的关系,但并未考虑更高阶的关系。
模型涉及的参数数量为:
对于参数的训练,只要这个样本中对应的不为0,则可以完成一次训练。
但对于参数的训练,需要这个样本中的和同时不为0,才可以完成一次训练。
在数据稀疏的实际应用场景中,二次项的训练是非常困难的。因为每个都需要大量和都不为0的样本。但在数据稀疏性比较明显的样本中,和都不为0的样本会非常稀少,这会导致不能得到足够的训练,从而不准确。
1.2 FM
1.2.1 FM基本原理
为了解决二项式模型中由于数据稀疏引起的训练不足的问题,我们为每个特征维度引入一个辅助向量:
其中为辅助变量的维度,依经验而定,一般而言,对于特征维度足够多的样本,。
将表示为:
简单的说,我们不再简单的使用样本训练具体的,而是先训练2个隐变量以及,然后使用式(5)求出最终的。
具体而言,与有相同的项,也就是只要样本中的不为0,且最少具有一个其它特征,则这个样本则可用于训练,这就解决了数据稀疏性导致的问题。
于是,在FM中,模型可以表达为:
1.2.2 数据分析
我们的目标是要求得以下交互矩阵W:
由于直接求解W不方便,因此我们引入隐变量V:
令
如果我们先得到V,则可以得到W了。
现在只剩下一个问题了,是否一个存在V,使得上述式(9)成立。
理论研究表明:当k足够大时,对于任意对称正定的实矩阵,均存在实矩阵,使得。
理论分析中要求参数k足够的大,但在高度稀疏数据的场景中,由于 没有足够的样本,因此k通常取较小的值。事实上,对参数的限制,在一定程度上可以提高模型的泛化能力。
1.2.3参数个数
假设样本中有n个特征,每个特征对应的隐变量维度为k,则参数个数为。
正如上面所言,对于特征维度足够多的样本,。
1.2.4 计算时间复杂度
下面我们分析一下已经知道所有参数,代入式(6)计算预测值时的时间复杂度。从式(6)中一看,
可以看出时间复杂度是。但我们对上述式子的最后一项作变换后,可以得出一个的时间复杂度表达式。
上述式子中的只需要计算一次就好,因此,可以看出上述模型的复杂度为。
也就是说我们不要直接使用式(6)来计算预测结果,而应该使用式(10),这样的计算效率更高。
1.2.5 梯度
FM有一个重要的性质:multilinearity:若记表示FM模型的所有参数,则对于任意的,存在与无关的与,使得式(6)可以表示为:
从式(11)中可以看出,如果我们得到了与,则对于参数的梯度为。下面我们分情况讨论。
当 时,式(6)可以表示为:
上述中的蓝色表示,红色表示。下同。
从上述式子可以看出此时的梯度为1.当时,
此时梯度为。
当时
此时梯度为。
综合上述结论,关于的偏导数为:
1.2.6 训练时间复杂度
由上述式(15)可以得到:
对于上式中的前半部分,对于每个样本只需要计算一次,所以时间复杂度为,对于k个隐变量的维度分别计算一次,则复杂度为。其它项的时间复杂度都小于这一项,因此,模型训练的时间复杂度为。
详细一点解释:
(1)我们首先计算,时间复杂度为n,这个值对于所有特征对应的隐变量的某一个维度是相同的。我们设这值为C。
(2)计算每一个特征对应的,由于总共有n个特征,因此时间复杂度为n,至此,总的时间复杂度为n+n。
(3)上述只是计算了隐变量的其中一个维度,我们总共有k个维度,因此总的时间复杂度为.
四、FFM(Field Factorization Machine)
2.1 背景及基本原理
在FM模型中,每一个特征会对应一个隐变量,但在FFM模型中,认为应该将特征分为多个field,每个特征对应每个field分别有一个隐变量。
举个例子,我们的样本有3种类型的字段:publisher, advertiser, gender,分别可以代表媒体,广告主或者是具体的商品,性别。其中publisher有5种数据,advertiser有10种数据,gender有男女2种,经过one-hot编码以后,每个样本有17个特征,其中只有3个特征非空。
如果使用FM模型,则17个特征,每个特征对应一个隐变量。
如果使用FFM模型,则17个特征,每个特征对应3个隐变量,即每个类型对应一个隐变量,具体而言,就是对应publisher, advertiser, gender三个field各有一个隐变量。
2.2模型与最优化问题
2.2.1 模型
根据上面的描述,可以得出FFM的模型为:
其中表示特征的索引。我们假设特征属于这个field,特征属于这个field,则表示这个特征对应(所属的field)的隐变量,同时表示这个特征对应(所属的field)的隐变量。
事实上,在大多数情况下,FFM模型只保留了二次项,即:
2.2.2 最优化问题
根据逻辑回归的损失函数及分析,可以得出FFM的最优化问题为:
上面加号的前面部分使用了L2范式,后面部分是逻辑回归的损失函数。m表示样本的数量,表示训练样本的真实值(如是否点击的-1/1),表示使用当前的V代入式(18)计算得到的值。
注意,以上的损失函数适用于样本分布为{-1,1}的情况。
2.2.3 自适应学习率
与FTRL一样,FFM也使用了累积梯度作为学习率的一部分,即:
其中表示对于这个变量的梯度向量,因为是一个向量,因此也是一个向量,尺寸为隐变量的维度大小,即k。
而表示从第一个样本到当前样本一直以来的累积梯度平方和。
2.2.4 FFM算法的最终形式
其中G为累积梯度平方:
g为梯度,比如为这个特征对应这个field的梯度向量:
其中为:
2.3完整算法流程
使用随机梯度下降(SGD)训练FFM模型的完整过程如下:
2.3.1 计算梯度
对于每一个样本的每一对特征组合都要计算以下梯度向量。
其中为式(19)后半部分对应的梯度,即:
再重申一次,与都是k维的向量,在python中可以作为一个向量计算,在java/c++等需要通过一个循环进行计算。
详细推导(21)式如下:
(1)在SGD中,式(19)可以转化为:
(2)上式对求偏导,可得:
2.3.2 计算累积梯度平方和
计算从第一个样本,到当前样本(第d个)以来的累积梯度平方和:
2.3.3 更新隐变量
2.3.4 关于初始参数的设定
文献1中如此建议:
(1):没有具体的建议,用户根据经验指定即可,一般会取0.1,0.01,0.001。
(2):在区间间的随机值,均匀分布即可。
(3):设置为1,以避免出现很大的值。
2.4 时间复杂度
2.4.1 计算时间复杂度
由于式(18)无法做类似于式(10)的简化,因此FFM的计算时间复杂度为。
2.4.2 训练时间复杂度
由于训练时,需要先根据式(18)计算,复杂度为,计算得到后,还需要按照式(22)计算1次,按照式(21)计算2k次,按照式(23)计算2k次,按照式(24)计算2k次,也就是说,总的训练时间复杂度为:
因此,训练时间复杂度为。
2.5 计算速度优化
2.5.1 openMP
OpenMP提供的这种对于并行描述的高层抽象降低了并行编程的难度和复杂度,这样程序员可以把更多的精力投入到并行算法本身,而非其具体实现细节。对基于数据分集的多线程程序设计,OpenMP是一个很好的选择。同时,使用OpenMP也提供了更强的灵活性,可以较容易的适应不同的并行系统配置。线程粒度和负载平衡等是传统多线程程序设计中的难题,但在OpenMP中,OpenMP库从程序员手中接管了部分这两方面的工作。
openPM原生支持C/C++/Fortran,但java可以通过jomp等引入,未测试。
2.5.2 SSE3
SSE3 中13个新指令的主要目的是改进线程同步和特定应用程序领域,例如媒体和游戏。这些新增指令强化了处理器在浮点转换至整数、复杂算法、视频编码、SIMD浮点寄存器操作以及线程同步等五个方面的表现,最终达到提升多媒体和游戏性能的目的。Intel是从Prescott核心的Pentium 4开始支持SSE3指令集的,而AMD则是从2005年下半年Troy核心的Opteron开始才支持SSE3的。但是需要注意的是,AMD所支持的SSE3与Intel的SSE3并不完全相同,主要是删除了针对Intel超线程技术优化的部分指令。
SSE3指令采用128位的寄存器,可以同时操作4个单精度浮点数或者整数,因此非常类似于向量运算。这对于有大量向量计算的的FFM模型是有用的。
但事实上,计算是几乎无用,而这是最耗时间的部分。
2.5.3 ParameterServer
https://www.zybuluo.com/Dounm/note/517675
Paraeter Server框架中,每个server都只负责分到的部分参数(server共同维持一个全局共享参数)。server节点可以和其他server节点通信,每个server负责自己分到的参数,server group共同维持所有参数的更新。server manage node负责维护一些元数据的一致性,例如各个节点的状态,参数的分配情况。
2.6模型优化
2.6.1 特征编码连续
如果特征的编码不连续,比如编码是有意义的,或者预留空间给之后的编码。如果直接使用最大编码值的作为参数数据尺寸,则会导致大量内存空间的浪费,因此有2种解决方案:
(1)使用hashmap,而非数组。
(2)将有意义的编码映射到一个连续的编码空间。
目前我们使用方式(1),理论上方式(2)的计算速度会更快。
2.6.2 一次项缺失的影响
正如上面式(18)所言,我们经常会忽略了一次项的影响,因此我们可以为每个样本加上一个辅助特征,这项特征的值恒为1,这相当于引入了一次项。
2.6.3 样本归一化
文献1还建议,将样本向量的长度归一化后,性能有少量的提升。
2.6.4 特征归一化
某些特征(如购买个数)有可能很大,而一些类别参数则恒为1,这将导致不同特征最终对模型的影响相关很大,这很不合理。