隐语义模型LFM和LSI,LDA,Topic Model其实都属于隐含语义分析技术,是一类概念,他们在本质上是相通的,都是找出潜在的主题或分类。这些技术一开始都是在文本挖掘领域中提出来的,近些年它们也被不断应用到其他领域中,并得到了不错的应用效果。比如,在推荐系统中它能够基于用户的行为对item进行自动聚类,也就是把item划分到不同类别/主题,这些主题/类别可以理解为用户的兴趣。
对于一个用户来说,他们可能有不同的兴趣。就以作者举的豆瓣书单的例子来说,用户A会关注数学,历史,计算机方面的书,用户B喜欢机器学习,编程语言,离散数学方面的书, 用户C喜欢大师Knuth, Jiawei Han等人的著作。那我们在推荐的时候,肯定是向用户推荐他感兴趣的类别下的图书。那么前提是我们要对所有item(图书)进行分类。那如何分呢?大家注意到没有,分类标准这个东西是因人而异的,每个用户的想法都不一样。拿B用户来说,他喜欢的三个类别其实都可以算作是计算机方面的书籍,也就是说B的分类粒度要比A小;拿离散数学来讲,他既可以算作数学,也可当做计算机方面的类别,也就是说有些item不能简单的将其划归到确定的单一类别;拿C用户来说,他倾向的是书的作者,只看某几个特定作者的书,那么跟A,B相比它的分类角度就完全不同了。
对于一个用户,首先得到他的兴趣分类,然后从分类中挑选他可能喜欢的物品。这里要解决3个问题:
- 如何给物品分类?
- 如何确定用户对哪些类感兴趣,以及感兴趣的程度?
- 对于一个给定的类,选择哪些属于这个类的物品推荐给用户,以及如何确定这些物品在该类中的权重?
LFM解决这些问题的方法:
- 根据用户的行为实现物品的自动聚类。如果2个物品被很多用户同时喜欢,那么他们属于一个隐类的概率很大。
- 用户的兴趣通过用户的行为统计得到。
- 在一个隐类中,如果一个物品被很多用户喜欢,那么它必定权重较大。
对于一个给定的用户行为数据集(数据集包含的是所有的user, 所有的item,以及每个user有过行为的item列表),使用LFM对其建模后,我们可以得到如下图所示的模型:(假设数据集中有3个user, 4个item, LFM建模的分类数为4)
其中,R矩阵是user-item兴趣度矩阵,矩阵值Rij表示的是user i 对item j的兴趣度;P是user-class兴趣度矩阵,Pij表示user i对class j的兴趣度;Q是item-class权重矩阵,Qij表示class i中item j的权重。
![](https://i-blog.csdnimg.cn/blog_migrate/6f89a06ca6ac44e9b90340a52d78aef4.png)
LFM通过如下公式计算用户u对物品i的兴趣度:
![](https://i-blog.csdnimg.cn/blog_migrate/14c01e3a55960d597320a97c993fc9de.png)
该公式中,
,
是LFM模型的参数,表示矩阵P,Q中的值,需要估计它们的值。
![](https://i-blog.csdnimg.cn/blog_migrate/29f716e1df1708541ee6bf1ecd521b2c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/813a0a9ddf1a838337c20e096f547b17.png)
要估计参数值,首先必须要有一个训练样本集,针对每个用户,选择用户的行为数据集作为正样本,正样本中都是用户发生过行为(喜欢)的物品,设
=1。然后还需要选择一些数据作为负样本,选择负样本的原则:1、对于一个用户,要保证正、负样本的数量相近;2、对一个用户采样负样本时,要选择那些很热门、而用户没有发生行为的物品(很热门而用户却没有发生行为,更能说明用户不喜欢,冷门物品用户可能根本没有看到),负样本的物品,设
=0。
![](https://i-blog.csdnimg.cn/blog_migrate/19390193ce69ff66b32261e5ed406dfe.png)
![](https://i-blog.csdnimg.cn/blog_migrate/19390193ce69ff66b32261e5ed406dfe.png)
LFM的损失函数仍然是平方差+正则项,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/63449d20a5c9f32282f728f6e19c8d00.png)
其中,后面的正则项是为了防止过拟合,参数lamda通过实验可以得到。
通过最小化损失函数C来估计参数,采用梯度下降法来训练模型。
梯度下降法首先需要计算损失函数C的偏导数:
![](https://i-blog.csdnimg.cn/blog_migrate/0e9cd20e024671b8892fb9e1abdb14aa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/829883d531cdc7d05095165226927721.png)
根据梯度下降法,需要将待估计参数pp沿着最速下降方向(梯度的反方向)向前推进,即:pp = pp - gradient,因此可以得到如下参数更新公式:
![](https://i-blog.csdnimg.cn/blog_migrate/c5e88b0ed40cbb021697e3983aac6e13.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b3f186f62afea22c64df044b4e8057f3.png)
LFM模型训练就是参数不断更新直至收敛的过程,步骤如下:
1、初始化参数矩阵P、Q。给矩阵中待估计的参数设置一个初始值,可以为每个参数设置一个相同的概率均值。
2、初始化LFM模型的其他参数,包括:隐类的个数K,学习率
的初始值,
的初始值。其中,K的值不能太大,也不能太小,它控制的是粒度的粗细程度;
与
的取值可以根据应用场景取一个合适的初始值。
![](https://i-blog.csdnimg.cn/blog_migrate/786f0b153ae343c7e6dfde176232dd96.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f13232363f9e42a71250360667037c58.png)
![](https://i-blog.csdnimg.cn/blog_migrate/786f0b153ae343c7e6dfde176232dd96.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f13232363f9e42a71250360667037c58.png)
3、模型训练。
一次迭代的步骤:
训练时,一个用户一个用户的依次迭代:
先从训练样本集中取用户u1的数据:
![](https://i-blog.csdnimg.cn/blog_migrate/835b3ec33822a38cbddd112d2df1d10e.png)
从该数据集中依次选取一个
,更新
,
的值。N个item,
要更新N次,
要更新1次。
![](https://i-blog.csdnimg.cn/blog_migrate/e230285c9a145ecb1c9ceb1f88d6ec88.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a7ba4a147d9bd5ce6912a8e5f595358d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c77676b6c8d7d5ba072e514f9a3803dc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8c772660902c3b0d914cbaf9ecbae70f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6e0a4a1f4a9e5ec89dc0f8763431c354.png)
再依次用其他用户u2,u3,…数据不断更新参数。
一次迭代完成后,得到一个P、Q,并得到损失函数C的一个值。
不断的多次迭代,直至损失函数C的值收敛(或者,循环结束条件设为一个迭代次数N,迭代N次后结束)。
LFM的伪代码可以表示如下:
-
def LFM(user_items, F, N, alpha, lambda): -
#初始化P,Q矩阵 -
[P, Q] = InitModel(user_items, F) -
#开始迭代 -
For step in range(0, N): -
#从数据集中依次取出user以及该user喜欢的iterms集 -
for user, items in user_item.iterms(): -
#随机抽样,为user抽取与items数量相当的负样本,并将正负样本合并,用于优化计算 -
samples = RandSelectNegativeSamples(items) -
#依次获取item和user对该item的兴趣度 -
for item, rui in samples.items(): -
#根据当前参数计算误差 -
eui = eui - Predict(user, item) -
#优化参数 -
for f in range(0, F): -
P[user][f] += alpha (eui Q[f][item] - lambda * P[user][f]) -
Q[f][item] += alpha (eui P[user][f] - lambda * Q[f][item]) -
#每次迭代完后,都要降低学习速率。一开始的时候由于离最优值相差甚远,因此快速下降; -
#当优化到一定程度后,就需要放慢学习速率,慢慢的接近最优值。 -
alpha *= 0.9