BasicSVD

1 前言

众所周知,协同过滤算法是完全没有利用到物品本身或者是用户自身的属性, 仅仅利用了用户与物品的交互信息就可以实现推荐,是一个可解释性很强, 非常直观的模型, 但是也存在一些问题。但是协同过滤当中依旧存在着很多问题。

1.1 协同过滤处理稀疏矩阵的能力太差了。

在真实的数据集当中,用户和商品的数量是很多的,因此用户与商品之间很多时候都是没有交互信息的。就比如小明作为用户,假设商品有1亿但是小明一生当中所购买过的商品数量可能不足10W因此有很多交互信息是为0的,所以在真实的数据集当中,这个矩阵是一个系数矩阵。

就比如下图

       

1.2 协同过滤当中相似度矩阵维护难度较大

在真实的数据集当中,若采用协同过滤算法,我们应该主要维护两个矩阵,即用户相似度矩阵(m x m)和 商品相似度矩阵(n x n)

我们可以简单的计算一下维护的数量级为mxm+nxn,假设有1000W个用户 1亿个商品。那么这维护的结果的数字是很庞大的,难以计算。

因此有人提出了矩阵分解算法。矩阵分解算法在2006年Netfix的推荐算法大赛中大放异彩, 并开始流行, 它的核心思想是通过隐含特征来联系用户兴趣和物品, 首先会解释一下我们总说的隐语义到底是啥意思,这个词语总是感觉会比较抽象, 这个其实类似于NLP里面的embedding, 那么我后面会举一个例子, 让它变得具体一些。

2 隐语义模型

隐语义模型最早在文本领域被提出,用于找到文本的隐含语义。在2006年, 被用于推荐中, 它的核心思想是通过隐含特征(latent factor)联系用户兴趣和物品(item), 基于用户的行为找出潜在的主题和分类, 然后对item进行自动聚类,划分到不同类别/主题(用户的兴趣)。这么说可能有点抽象,所以下面拿项亮老师《推荐系统实践》里面的那个例子看一下:

如果我们知道了用户A和用户B两个用户在豆瓣的读书列表, 从他们的阅读列表可以看出,用户A的兴趣涉及侦探小说、科普图书以及一些计算机技术书, 而用户B的兴趣比较集中在数学和机器学习方面。 那么如何给A和B推荐图书呢?

先说说协同过滤算法, 这样好对比不同:

  • 对于UserCF,首先需要找到和他们看了同样书的其他用户(兴趣相似的用户),然后给他们推荐那些用户喜欢的其他书。
  •  对于ItemCF,需要给他们推荐和他们已经看的书相似的书,比如作者B看了很多关于数据挖掘的书,可以给他推荐机器学习或者模式识别方面的书。

而如果是隐语义模型的话, 它会先通过一些角度把用户兴趣和这些书归一下类, 当来了用户之后, 首先得到他的兴趣分类, 然后从这个分类中挑选他可能喜欢的书籍。

那下面依然是解密如何通过隐含特征把用户的兴趣和物品分类的:我们下面拿一个音乐评分的例子来看一下隐特征矩阵的含义。

假设每个用户都有自己的听歌偏好, 比如A喜欢带有小清新的, 吉他伴奏的, 王菲的歌曲,如果一首歌正好是王菲唱的, 并且是吉他伴奏的小清新, 那么就可以将这首歌推荐给这个用户。 也就是说是小清新, 吉他伴奏, 王菲这些元素连接起了用户和歌曲。 当然每个用户对不同的元素偏好不同, 每首歌包含的元素也不一样, 所以我们就希望找到下面的两个矩阵:

潜在因子—— 用户矩阵Q
这个矩阵表示不同用户对于不同元素的偏好程度, 1代表很喜欢, 0代表不喜欢, 比如下面这样:

               
潜在因子——音乐矩阵P
表示每种音乐含有各种元素的成分, 比如下表中, 音乐A是一个偏小清新的音乐, 含有小清新的Latent Factor的成分是0.9, 重口味的成分是0.1, 优雅成分0.2…

              

 

利用上面的这两个矩阵, 我们就能得出张三对音乐A的喜欢程度:
张三对小清新的偏好 * 音乐A含有小清新的成分 + 张三对重口味的偏好 * 音乐A含有重口味的成分 + 张三对优雅的偏好 * 音乐A含有优雅的成分…,

在NLP当中下图就叫作隐向量

根据隐向量其实就可以得到张三对音乐A的打分,即:
0.6∗0.9+0.8∗0.1+0.1∗0.2+0.1∗0.4+0.7∗0=0.69

最后我们可以得到评分矩阵

         

3 矩阵分解的知识

由于真实的数据集当中的矩阵大部分是缺失的,因此我们就拿王喆老师《深度学习推荐系统》的那个图来看一下矩阵分解的过程:

   

 

 

 4 Basic SVD

这里其实就和训练模型的思路差不多, 我们拿到了一个用户物品的评分矩阵, 而我们要去计算两个参数矩阵U和V, 我们就可以用求解神经网络模型参数的思路计算这两个矩阵:

首先先初始化这两个矩阵
把用户评分矩阵里面已经评过分的那些样本当做训练集的label, 把对应的用户和物品的隐向量当做features, 这样就会得到(features, label)相当于训练集
通过两个隐向量乘积得到预测值pred
根据label和pred计算损失
然后反向传播, 通过梯度下降的方式,更新两个隐向量的值
未评过分的那些样本当做测试集, 通过两个隐向量就可以得到测试集的label值
这样就填充完了矩阵, 下一步就可以进行推荐了

具体的公式推导

 



import random
import math


class LFM(object):

    def __init__(self, rating_data, F, alpha=0.1, lmbd=0.1, max_iter=500):
        """
        :param rating_data: rating_data是[(user,[(item,rate)]]类型
        :param F: 隐因子个数
        :param alpha: 学习率
        :param lmbd: 正则化
        :param max_iter:最大迭代次数
        """
        self.F = F
        self.P = dict()  # R=PQ^T,代码中的Q相当于博客中Q的转置
        self.Q = dict()
        self.alpha = alpha
        self.lmbd = lmbd
        self.max_iter = max_iter
        self.rating_data = rating_data

        '''随机初始化矩阵P和Q'''
        for user, rates in self.rating_data:
            self.P[user] = [random.random() / math.sqrt(self.F)
                            for x in range(self.F)]
            for item, _ in rates:
                if item not in self.Q:
                    self.Q[item] = [random.random() / math.sqrt(self.F)
                                    for x in range(self.F)]
        print(self.P)
    def train(self):
        """
        随机梯度下降法训练参数P和Q
        :return: 
        """
       
        for step in range(self.max_iter): #遍历次数max_iter
            for user, rates in self.rating_data:
                for item, rui in rates:
                    hat_rui = self.predict(user, item)  #预测得分
                    err_ui = rui - hat_rui  #计算误差
                    for f in range(self.F): #梯度下降更新
                        self.P[user][f] += self.alpha * (err_ui * self.Q[item][f] - self.lmbd * self.P[user][f])
                        self.Q[item][f] += self.alpha * (err_ui * self.P[user][f] - self.lmbd * self.Q[item][f])
            self.alpha *= 0.9  # 每次迭代步长要逐步缩小

    def predict(self, user, item):
        """
        :param user:
        :param item:
        :return:
        预测用户user对物品item的评分
        """
        return sum(self.P[user][f] * self.Q[item][f] for f in range(self.F))


if __name__ == '__main__':
    '''用户有A B C,物品有a b c d'''
    rating_data = list()
    rate_A = [('a', 2.0), ('b', 1.0)]
    rating_data.append(('A', rate_A))
    rate_B = [('b', 1.0), ('c', 1.0)]
    rating_data.append(('B', rate_B))
    rate_C = [('c', 1.0), ('d', 1.0)]
    rating_data.append(('C', rate_C))

    lfm = LFM(rating_data, 2)
    lfm.train()
    for item in ['a', 'b', 'c', 'd']:
        print(item, lfm.predict('A', item)) # 计算用户A对各个物品的喜好程度

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值