目录
1 传统模型的演化关系图
2 协同过滤-经典的推荐算法
2.1 什么是协同矩阵
2.2 用户相似度计算
2.3 最终结果的排序
2.4 ItemCF
2.5 UserCF 与 ItemCF 的应用场景
2.6 协同过滤的下一步发展
3 矩阵分解算法-协同过滤的进化
3.1 矩阵分解算法原理
3.2 矩阵分解的求解过程
3.3 消除用户和物品的打分偏差
3.4 矩阵分解的优点与局限性
4 逻辑回归
5 从FM到FFM-自动特征交叉的解决方案
5.1 POLY2模型-特征交叉的开始
5.2 FM模型-隐向量特征交叉
5.3 FFM模型-引入特征域的概念
5.4 从POLY2到FFM的模型演化过程
6 GBDT+LR——特征工程模型化的开端
6.1 GBDT+LR 组合模型的结构
6.2 GBDT进行特征转换的过程
6.3 GBDT+LR组合模型开启的特征工程新趋势
7 LS-PLM - 阿里巴巴曾经的主流推荐模型
7.1 LS-PLM 模型的主要结构
7.2 LS-PLM模型的优点
7.3 从深度学习的角度重新审视LS-PLM模型
8 总结
协同过滤:就是协同大家的反馈、评价和意见一起对海量的信息进行过滤,从中筛选出目标用户可能感兴趣的信息的推荐过程。
推荐过程:
(1)图(a)表示商品库里的四件商品(,小说,杂志和)
(2)我们要决定是否推荐给用户X。可利用X对其他商品的历史评价数据及其他用户对这些商品的历史评价数据
(3)为了便于计算,将有向图转换成共现矩阵的形式,用户行为作为行向量,物品表现作为列向量,将和的行为数据转换成矩阵中相应的元素值,例如为1,为0。
(4)找到与用户X兴趣最相似的n个用户,综合相似用户对的评价,得出用户X对评价的预测(图中B和C是与X相似的top2用户,其中B、C均为负面评价,可以推测X对的评价也是负面的,不推荐)。
共现矩阵中的行向量是用户向量,计算用户i和用户j的相似度,即是计算行向量i和行向量j之间的相似度,理论上任何合理的“向量相似度定义方式”都可以作为相似用户计算的标准,常用的如下:
余弦相似度:夹角越小,余弦相似度越大,两个用户越相似。
皮尔逊相关系数:相比于余弦相似度,皮尔逊相关系数使用用户平均分对个独立评分进行修正,减小了用户评分偏置,其中表示用户i对物品p的评分,
表示用户i对所有物品的平均评分,P代表所有物品的集合。
相比于余弦相似度,引入物品平均分,减少物品评分偏置对结果的影响,表示物品p得到的所有评分的平均分,
表示P中所有物品的平均评分向量。
最常用的方式是利用用户相似度和相似用户的评价的加权平均获得目标用户的评价预测。其中,权重是用户u和用户s的相似度,
是用户s对物品p的评分。
符合直觉“兴趣相似的朋友喜欢的物品我也喜欢”。
简而言之,对给定的目标用户我们是这样决定候选集合中的物品的展示顺序的,先找到与该用户相似的Top n个用户,利用这n个用户对候选物品的加权打分来预测目标用户对该候选物品的评分(缺点中的用户历史向量往往非常稀疏的问题,可能每个用户只对其中的一两个商品有过点击,由于相似度是按元素相乘,没有行为的可能是0,点击的数据过少,我们很难根据重合的这一两个数据来说他们就是兴趣相似的用户,即是计算出的相似度很高,也不一定他们兴趣相似度高,存储量增长的问题,感觉可能是保留了用户与剩下的所有用户的相似度信息,二不仅仅是保留了Top n个相似用户的相似度,因为后者的存储增长是n)。
缺点:
ItemCF是基于物品相似度进行推荐的协同过滤算法,通过计算共现矩阵中物品列向量的相似度得到物品之间的相似矩阵,再找到用户的历史正反馈物品的相似物品进一步排序和推荐,ItemCF的具体步骤如下:
(1)构建共现矩阵,行向量是用户向量,列向量是物品相关
(2)计算共现矩阵晾凉列向量之间的相似度,构建n*n为的物品相似度矩阵
(3)获得用户历史行为数据中的正反馈物品列表
(4)利用物品相似度矩阵,针对目标用户历史行为中的正反馈物品,找出相似的Top K个物品,组成相似物品集合。
(5)对相似物品集合中的物品,利用相似度分值排序,生成最终的推荐列表
在第5步中,若一物品与多个用户历史行为中的正反馈相似,则该物品最终的相似度十多个相似度的累加,其中H是用户正反馈物品集合,是物品p与物品h的相似度,
是用户u对物品h的已有评分。
UserCF基于用户相似度,具有更强的社交性。非常适用于新闻推荐场景,因新闻本身的兴趣点往往是分散的,相比用户对不同新闻的兴趣偏好,新闻的及时性、热点性往往是更重要的,UserCF正适用于发现热点,以及跟踪热点的趋势。
ItemCF更适用于兴趣变化比较稳定的应用,例如推荐商品,电影,电视剧等。
协同过滤可解释性强,但不具备强的泛化能力,协同过滤无法将两个物品相似这一信息推广到其他物品的相似性计算上,导致-热门的商品头部效应明显,容易跟大量物品产生相似性;尾部物品特征向量稀疏,很少与其他物品产生相似性,很少被推荐。简言之,推荐结果的头部效应明显,处理稀疏向量能力弱。
举个,A、B和C之间的相似度为0,与A、B和C都相似的物品均为D,故在ItemCF下,D将会被推荐给所有对A、B和C有正反馈的用户,实际上D与其余物品相似仅是因为D是一件热门商品,无法找出A、B和C之间的相似度仅是因为其特征向量非常稀疏,缺乏相似性计算的直接数据。
为解决上述问题,并提升泛化能力,矩阵分解技术被提出,该方法在协同过滤的基础上,使用更稠密的隐向量表示用户和物品,挖掘用户和物品之间的隐含兴趣和隐含特征,在一定程度上弥补了协同过滤处理稀疏矩阵能力不足的问题。
另外,协同过滤仅利用了用户和物品的交互信息,无法引入用户年龄、性别、商品描述、当前时间等用户特征、物品特征以及上下文特征。为了引入这些特征,逐渐发展到以逻辑回归为核心的、能够综合不同特征的机器学习模型的道路上。
协同过滤的推荐如(a)所示,基于用户的历史观看,找到与目标用户Joe看过同样视频的相似用户,然后找到这些相似用户喜欢看的其他视频推荐给目标用户Joe。矩阵分解算法则期望为每一个用户和视频生成一个隐向量,将用户和视频定位到隐向量的表示空间上,距离相近的用户和视频表明特点接近,在推荐过程中,应把距离相近的视频推荐给用户,例如发现里Dave的用户向量最近的两个视频是Ocean's 11 和 The Lion King,可依据向量距离由近到远的顺序生成Dave的推荐列表。隐向量表达用户和物品,还要保证相似的用户及用户可能喜欢的物品的距离相近,在矩阵分解的框架下,用户和物品的隐向量是通过分解协同过滤生成的共现矩阵得到的。
矩阵分解算法将m*n维的共现矩阵分解为m*k维的用户矩阵
和k*n维的物品矩阵
相乘的形式。其中m是用户数,n是物品数,k是隐向量的数量,k的大小决定了隐向量表达能力的强弱。k越小,隐向量包含的信息越少,模型泛化程度越高;反之,k越大,包含信息越多,泛化程度越低。此外,k取值还与求解复杂度直接相关。
基于用户矩阵和物品矩阵
,用户u对物品i的评估加分如下,其中
是用户u在用户矩阵
中的行向量,
是物品i在物品矩阵
中的列向量,简言之,用户u对物品i的评分即是用户向量
与物品向量
的点积。
三种常用的矩阵分解法:特征值分解(Eigen Decomposition)、奇异值分解(Singular Value Decomposition,SVD)和梯度下降(Gradient Descent)。其中特征值分解只能作用于方阵。
奇异值分解描述如下,之前有记录过Matlab作SVD的过程https://blog.csdn.net/qq_xuanshuang/article/details/79639240:
设是n*m的矩阵,则必存在一个分解
,其中
是m*m的正交阵,
是n*n的正交阵,
是m*n的对角阵。取
中较大的k个与纳素作为隐含特征,删除
的其他维度及
和
中对应的维度,矩阵
被分解为
。
奇异值分解不适用于互联网环境,(1):奇异值分解要求原始共现矩阵是稠密的;(2)传统奇异值分解计算复杂度。
下面讲述梯度分解,式(7)是求解矩阵分解的目标函数,其中K是所有用户评分样本的集合:
在式(7)的基础上加上正则化项,以避免过拟合:
对和
分别求偏导,得:
在矩阵分解中,由于隐向量的存在,是任意的用户和物品之间都可以得到预测值,隐向量的生成过程其实是对共现矩阵进行全局拟合的过程,具有更强的泛化能力;而协同过滤,若两个用户没有相同的历史行为,两个物品没有相同的人购买,则这俩用户和俩物品相似度都将为0。
下面附上python实现的矩阵分解:
代码中R指原始的共现矩阵,U指用户矩阵,V指物品矩阵,写代码的时候梯度(哪个矩阵在前,是否需转置)可以用形状来判断,第一组中,原始矩阵不含0元素,即均有值,我们可以看出当隐向量的维度是2的时候,误差就很小,即预测值和原始值很接近;第二组,原始矩阵中含有0元素,当隐向量维度比较小时(此处是2),预测出的矩阵在原矩阵为0的地方也有值,误差相对较大,当隐向量维度较大时(此处是5),预测出的矩阵与原矩阵很接近,原矩阵为0的地方,预测的矩阵也近乎为0,误差相对较小。
import numpy as np
class MF:
def __init__(self):
self.U = None
self.V = None
def train(self, R, k, L2=None, l2_lambda = 0):
def J(R, U, V, L2=None, l2_lambda=0.):
j = 0.5 * np.sum((R - U.dot(V)) ** 2)
if L2:
j = 0.5 * l2_lambda * (np.sum(U.dot(U.T)) + np.sum(V.T.dot(V)))
return j
def gradient(R, U, V, L2=None, l2_lambda=0.):
dV = U.T.dot(U.dot(V) - R)
dU = (U.dot(V) - R).dot(V.T)
if L2:
dU = dU + l2_lambda * U
dV = dV + l2_lambda * V
return dU, dV
U = np.random.random(size=(R.shape[0], k))
V = np.random.random(size=(k, R.shape[1]))
eps, n_iters, iter, lr = 1e-8, 5000, 0, 0.001
while iter < n_iters:
last_U, last_V = U, V
dU, dV = gradient(R, U, V, L2, l2_lambda)
U = U - lr * dU
V = V - lr * dV
if abs(J(R, last_U, last_V, L2, l2_lambda) - J(R, U, V, L2, l2_lambda)) < eps:
break
iter += 1
self.U, self.V = U, V
def predict(self):
return self.U.dot(self.V)
if __name__ == "__main__":
R = np.array([[1, 2, 3], [4, 5, 6], [0.1, 0.22, 0.31], [2, 4, 6]])
mf = MF()
mf.train(R, 2, "L2", 0.1)
R_hat = mf.predict()
print(R)
print(mf.U)
print(mf.V)
print(R_hat)
print(np.sum(R - R_hat) ** 2)
引入全局偏差常数(也可以学习),物品偏差系数
(可以使用物品i所获得的评分的均值,也可以直接学习),用户偏差系数
(可以使用用户u给出的评分的均值,也可以直接学习)
相应的目标函数改为
加入用户和物品的打分偏差项之后,矩阵分解得到的隐向量更能反应不同用户对不同物品的“真实”态度的差异,也就更容易捕捉评价数据中有价值的信息,避免推荐结果有偏。
待解决:
书中说
是全局偏差常数,是否是一个定值的意思,还是可学习的?以及
是直接用均值替代,还是亦是可学习的。
附上代码
这边关于的偏导,还是可以通过形状来推,这边借住了np.add.reduce_sum,但是不确定是不是可以这样,不过在纸上举过,像
确实会出现多次,故求和应该也没啥问题。
db_u = np.reshape(np.add.reduce(UdV, 1),newshape=(b_u.shape)) db_v = np.reshape(np.add.reduce(UdV, 0),newshape=(b_v.shape)) dmu = np.sum(UdV)
import numpy as np
class MF_bias:
def __init__(self):
self.U = None
self.V = None
self.b_u = None
self.b_v = None
self.mu = 0.
def train(self, R, k, L2=None, l2_lambda = 0):
def J(U, V, b_u, b_v, mu, L2=None, l2_lambda=0.):
j = 0.5 * np.sum((R - U.dot(V) - mu - np.reshape(b_u, newshape=(U.shape[0],1))
- np.reshape(b_v, newshape=(1,V.shape[1]))) ** 2)
if L2:
j = 0.5 * l2_lambda * (np.sum(U.dot(U.T)) + np.sum(V.T.dot(V))
+ b_u.T.dot(b_u) + b_v.T.dot(b_v))
return j
def gradient(U, V, b_u, b_v, mu, L2=None, l2_lambda=0.):
UdV = U.dot(V) + mu + np.reshape(b_u, newshape=(U.shape[0],1)) \
+ np.reshape(b_v, newshape=(1,V.shape[1])) - R
dV = U.T.dot(UdV)
dU = (UdV).dot(V.T)
db_u = np.reshape(np.add.reduce(UdV, 1),newshape=(b_u.shape))
db_v = np.reshape(np.add.reduce(UdV, 0),newshape=(b_v.shape))
dmu = np.sum(UdV)
if L2:
dU = dU + l2_lambda * U
dV = dV + l2_lambda * V
db_u = db_u + l2_lambda * b_u
db_v = db_v + l2_lambda * b_v
return dU, dV, db_u, db_v, dmu
U = np.random.random(size=(R.shape[0], k))
V = np.random.random(size=(k, R.shape[1]))
b_u = np.zeros(shape=(R.shape[0]))
b_v = np.zeros(shape=(R.shape[1]))
# print(b_u, b_v)
mu = 0.
eps, n_iters, iter, lr = 1e-8, 5000, 0, 0.001
while iter < n_iters:
last_U, last_V,last_b_u, last_b_v, last_mu = U, V, b_u, b_v, mu
dU, dV, db_u, db_v, dmu = gradient(U, V, b_u, b_v, mu, L2, l2_lambda)
U = U - lr * dU
V = V - lr * dV
b_u = b_u - lr * db_u
b_v = b_v - lr * db_v
mu = mu - lr * dmu
if iter % 1000 == 0:
print(abs(J(last_U, last_V, last_b_u, last_b_v, last_mu, L2, l2_lambda)
- J(U, V, b_u, b_v, mu, L2, l2_lambda)), J(U, V, b_u, b_v, mu, L2, l2_lambda))
if abs(J(last_U, last_V, last_b_u, last_b_v, last_mu, L2, l2_lambda)
- J(U, V, b_u, b_v, mu, L2, l2_lambda)) < eps:
break
iter += 1
self.U, self.V, self.b_u, self.b_v, self.mu = U, V, b_u, b_v, mu
def predict(self):
return self.U.dot(self.V) - np.reshape(self.b_u, newshape=(self.U.shape[0],1)) \
- np.reshape(self.b_v, newshape=(1,self.V.shape[1])) - self.mu
if __name__ == "__main__":
R = np.array([[1, 2, 3], [4, 5, 6], [0.1, 0.22, 0.31], [2, 4, 6]])
mf = MF_bias()
mf.train(R, 5)
R_hat = mf.predict()
print(R)
print(mf.b_u)
print(mf.b_v)
注:这边的代码没有想到好的验证正确性的方法,但是大概看出来没啥太大问题,首先目标函数的值确实在减小,也确实达到一个较小的值,然后从两个偏差系数的角度,用户偏差系数也确实在在用户打分偏高的时候偏大,在打分偏低的时候偏低;物品偏差系数同理。
相比协同过滤,优点如下:
- 泛化能力强,一定程度缓解数据稀疏的问题
- 空间复杂度低,协同过滤需要存储用户相似度矩阵(m*m)或者物品相似度矩阵(n*n),矩阵分解只需存储用户和物品隐向量((n+m)*k)
- 更好的扩展性和灵活性,最终产出用户和物品隐向量,与Embedding的思想不谋而合,故其便于与其他特征进行组合和拼接,便于与深度学习网络进行无缝结合。
与协同过滤一样,无法引入用户年龄、性别、商品描述、当前时间等用户特征、物品特征以及上下文特征。为了引入这些特征,逐渐发展到以逻辑回归为核心的、能够综合不同特征的机器学习模型的道路上。
https://blog.csdn.net/qq_xuanshuang/article/details/104432710
相比于协同矩阵和矩阵分解利用用户和物品的“相似度”进行推荐,logistic回归将推荐问题看成一个分类问题,通过预测正样本的概率对物品进行排序,因此,逻辑回归模型将推荐问题转换成了一个点击率预估问题。
逻辑回归作为广义线性模型的一种,他的假设是因变量y服从伯努利分布(线性回归的假设是因变量y服从高斯分布)。
优点
缺点
表达能力不强,无法进行特征交叉特征筛选等一系列操作,为解决这一问题,衍出了因子分解机等高维的复杂模型。
POLY2模型的数学形式:
该模型对所有特征进行了两两交叉(特征和
),并对所有特征组合赋予权重
,在一定程度上解决了特征组合的问题,本质上仍是线性模型,训练方法与逻辑回归无区别。
缺点
下面给出FM的数学形式,与POLY2相比,主要区别是用两个向量内积取代了单一权重系数
,具体来说,FM为每个特征学了一个隐权重向量,FM将矩阵分解的隐向量思想进行了进一步扩展,从单纯的用户、物品隐向量扩展到所有特征上。
其中二阶部分展开如下,自己不和自己交叉。
梯度:
由上式可知,的训练只需要样本的
特征非0即可,适合于稀疏数据,就能够方便得到所有的
梯度,上述偏导结果求和公式中没有
,即与
无关,只与
有关,显然计算所有
的
的复杂度是
,模型参数一共有
个。
从式(12)可看,将POLY2模型的级别的权重参数数量减少到了
(k为隐向量维度,
),从简化式可看,FM将训练复杂度从POLY2的
级别降低到了
级别,同时隐向量的引入使FM能更好的解决数据稀疏性的问题,举个:有俩特征频道和品牌,某组合特征是(ESPN,Adidas),在POLY2中,只有当ESPN和Adidas同时出现在一个训练样本中,模型才能学厨这个组合的权重;在FM中,ESPN的隐向量也可以通过(ESPN,Gucci)样本更新,Adidas的隐向量也可以通过(NBC,Adidas)样本更新,即使对于从未出现的特征组合(NBC,Gucci),由于已经学习过NBC和Gucci的隐向量,具备了计算该特征组合权重的能力。
相比POLY2,FM虽然丢失了某些具体特征组合的精确记忆能力,但是提高了泛化能力,同样可利用梯度下降法学习。
在FM模型的基础上引入了特征域感知这一概念,于FM的主要区别就是隐向量由之前的变为了
,这意味着每个特征对应的不是唯一一个隐向量而是一组隐向量,其中
是第
1个特征所属的特征域,在FFM模型的训练过程中,需要学习n个特征在f个域上的k维隐向量,参数数量共
个,由于域的引入,不能像FM模型那样简化,因此其复杂度是
。
POLY2模型直接学习每个交叉特征的权重,权重数量共个。
FM模型学习每个特征的k维隐向量,交叉特征由相应特征隐向量的内积得到,权重数量共n*k个。
FFM模型引入了特征域这一概念,在做特征交叉时,每个特征选择与对方域对应的隐向量做内积运算得到交叉特征的权重。参数数量共n*k*f个。
FFM模型采用引入特征域的方式增强了模型的表达能力,但无论如何,FFM只能够做二阶的特征交叉,如果要继续提高特征交叉的维度,不可避免的会发生组合爆炸和计算复杂度过高的情况。
可以利用GBDT自动进行特征筛选和组合,进而生成新的离散特征向量,再把该特征向量当作LR模型输入,预估CTR的模型结构。
用GBDT构建特征工程,和利用LR预估CTR两步是独立训练的。所以自然不存在如何将LR的梯度回传到GBDT这类复杂的问题。
GBDT模型训练好后,一个训练样本在输入GBDT的某一子树后,会依据每个节点的规则最终落入某一叶子节点,把该叶子节点置为1,其他置为0,所有叶子节点组成的向量即形成了该棵树的特征向量,把所有子树的特征向量连接起来,即形成了后续LR输入的特征向量。
举个,如上图所示,GBDT由三颗子树构成,每个子树有4个叶子节点,一个训练样本进来后,先后落入“子树1”的第3个叶节点中,那么特征向量就是[0,0,1,0],“子树2”的第1个叶节点,特征向量为[1,0,0,0],“子树3”的第4个叶节点,特征向量为[0,0,0,1],最后连接所有特征向量,形成最终的特征向量[0,0,1,0,1,0,0,0,0,0,0,1]。
决策树的深度就决定了特征交叉的维度。如果决策树的深度为4,通过三次节点分裂,最终的叶节点实际上是进行了3阶特征组合后的结果,如此强的特征组合能力显然是FM系的模型不具备的。但由于GBDT容易产生过拟合,以及GBDT这种特征转换方式实际上丢失了大量特征的数值信息,因此我们不能简单说GBDT由于特征交叉的能力更强,效果就比FFM好,在模型的选择和调试上,永远都是多种因素综合作用的结果。
GBDT+LR比FM重要的意义在于,它大大推进了特征工程模型化这一重要趋势,某种意义上来说,之后深度学习的各类网络结构,以及embedding技术的应用,都是这一趋势的延续。
LS-PLM(大规模分段线性模型),又被称为MLR(混合逻辑回归),其结构与三层神经网络极其相似。
MLR可以看做是对LR的自然推广,它在LR的基础上采用分而治之的思路,先对样本进行分片,再在样本分片中应用LR进行CTR预估。在LR的基础上加入聚类的思想,其动机其实来源于对计算广告领域样本特点的观察 。举个,如果CTR模型要预估的是女性受众点击女装广告的CTR,显然我们并不希望把男性用户点击数码类产品的样本数据也考虑进来,因为这样的样本不仅对于女性购买女装这样的广告场景毫无相关性,甚至会在模型训练过程中扰乱相关特征的权重。为了让CTR模型对不同用户群体,不同用户场景更有针对性,其实理想的方法是先对全量样本进行聚类,再对每个分类施以LR模型进行CTR预估。MLR的实现思路就是由该动机产生的。
MLR目标函数的数学形式如上,首先用聚类函数π对样本进行分类(这里的π采用了softmax函数,对样本进行多分类),再用LR模型计算样本在分片中具体的CTR,然后将二者进行相乘后加和。其中超参数分片数m可以较好地平衡模型的拟合与推广能力。当m=1时MLR就退化为普通的LR,m越大模型的拟合能力越强,但是模型参数规模随m线性增长,相应所需的训练样本也随之增长。
LS-PLM可以看作加入了注意力机制的三层神经网络模型,输入层:样本的特征向量;中间层:m个神经元组成的隐层;输出层:对CTR预估来说,输出层是单一神经元。注意力机制:在隐层和输出层之间,神经元之间的权重由分片函数得出的注意力得分来确定,即样本属于哪个分片的概率就是其注意力得分,即式(14)中的。
参考:深度学习推荐系统,王喆
王喆知乎的帖子 https://zhuanlan.zhihu.com/p/61154299