机器学习->推荐系统->LFM

LFM(latent factor model)隐语义模型核心思想是通过隐含特征联系用户兴趣和物品。
相比USerCF算法(基于类似用户进行推荐)和ItemCF(基于类似物品进行推荐)算法;我们还可以直接对物品和用户的兴趣分类。对应某个用户先得到他的兴趣分类,确定他喜欢哪一类的物品,再在这个类里挑选他可能喜欢的物品。

基于上面的思想,基于兴趣分类的方法大概需要解决3个问题:
①:如何对物品分类
②:如何确定用户对哪些物品分类,以及感兴趣的程度
③:确定了用户的兴趣,选择这个类的哪些物品推荐给用户?以及如何确定这些物品在这个类中的权重?

以图书分类为例,下面讲讲人为分类的缺点和不足:
①:编辑的分类意见不能代表各种用户的意见
②:编辑很难控制分类的粒度,何为粒度?比如《数据挖掘》这本书在粗粒度上属于计算机技术,在细粒度上属于数据挖掘。对于不同的用户需要不同的粒度。
③:编辑很难给一个物品多个类。
④:编辑很难给出多个维度的类。
⑤:编辑很难觉得一个物品在一个分类中的权重。

对于这些缺点不足,我们可以从数据出发,自动找到那些类然后进行个性化推荐。隐语义分析采取基于用户行为统计的自动聚类,较好解决了上面五个问题。

LFM通过如下公式计算用户u对物品i的兴趣:
这里写图片描述
这个公式中Puk表示用户u的兴趣和第k个隐类的关系,而Qik表示物品i与第k个隐类的关系。

实际上用户行为就是某个特定用户u兴趣和特定物品i的关系,我们可以中间分拆一步加个隐类(隐特征),将用户u兴趣将其与第k个隐类联系起来,再将物品i与第k个隐类联系起来。(这就是根据隐含特征将其分类)

数据集包含的是所有的user, 所有的item,以及每个user有过行为的item列表,使用LFM对其建模后,我们可以得到如下图所示的模型:(假设数据集中有3个user, 4个item, LFM建模的分类数为4)
机器学习->推荐系统->LFM_第1张图片

R矩阵是user-item矩阵,矩阵值Rij表示的是user i 对item j的兴趣度,这正是我们要求的值。对于一个user来说,当计算出他对所有item的兴趣度后,就可以进行排序并作出推荐。LFM算法从数据集中抽取出若干主题,作为user和item之间连接的桥梁,将R矩阵表示为P矩阵和Q矩阵相乘。其中P矩阵是user-class矩阵,矩阵值Pij表示的是user i对class j的兴趣度;Q矩阵式class-item矩阵,矩阵值Qij表示的是item j在class i中的权重,权重越高越能作为该类的代表。所以LFM根据如下公式来计算用户U对物品I的兴趣度。

我们发现使用LFM后,

①:我们不需要关心分类的角度,结果都是基于用户行为统计自动聚类的,全凭数据自己说了算。
②:不需要关心分类粒度的问题,通过设置LFM的最终分类数就可控制粒度,分类数越大,粒度约细。
③: 对于一个item,并不是明确的划分到某一类,而是计算其属于每一类的概率,是一种标准的软分类。
④:对于一个user,我们可以得到他对于每一类的兴趣度,而不是只关心可见列表中的那几个类。
⑤:对于每一个class,我们可以得到类中每个item的权重,越能代表这个类的item,权重越高。

在隐形反馈数据集上(隐形反馈数据集上只有正样本)应用LFM解决TopN推荐第一个解决的问题是如何给每个用户生成负样本。
有如下几个方法:
①:对于一个用户,用他所有没有过行为的物品作为负样本。这种方法显然不合理,一是这样负样本数量过大,正负样本数量差距太大,二是没有行为的样本或许并不是用户不喜欢,或者是根本没发现呢?
②:对于一个用户,从他没有过行为的物品中均匀采样出一些物品作为负样本。
③:对于一个用户,从他从来没有行为的物品中采样出一些物品作为负样本,并且保证采样的负样本和正样本数量相当。
④:对于一个用户,从他没有过行为的物品中采样出一些物品作为负样本,并且保证采样的是不热门的。这个方法显然不和,不热门不代表用户不喜欢,可能是用户根本没发现。

以上办法③优于②优于④优于①

对于采负样本要有以下原则:
①:对于每个用户,保证正负样本数量平衡。
②:对于每个用户采负样本,要选取那些很热门的,而用户没有行为的。

数据集应该包含所有的user和他们有过行为的(也就是喜欢)的item。所有的这些item构成了一个item全集。对于每个user来说,我们把他有过行为的item称为正样本,规定兴趣度RUI=1,此外我们还需要从item全集中随机抽样,选取与正样本数量相当的样本作为负样本,规定兴趣度为RUI=0。因此,兴趣的取值范围为[0,1]。

采样之后原有的数据集得到扩充,得到一个新的user-item集K={(U,I)},其中如果(U,I)是正样本,则RUI=1,否则RUI=0。损失函数如下所示:
这里写图片描述

上式中后两项的是用来防止过拟合的正则化项,λ需要根据具体应用场景反复实验得到。损失函数的优化使用随机梯度下降算法:
机器学习->推荐系统->LFM_第2张图片

迭代计算不断优化参数(迭代次数事先人为设置),直到参数收敛。
机器学习->推荐系统->LFM_第3张图片

其中,α是学习速率,α越大,迭代下降的越快。α和λ一样,也需要根据实际的应用场景反复实验得到。

Latent Factor Model,很多人称为SVD,其实是比较伪的SVD,一直是最近今年推荐系统研究的热点。但LFM的研究一直是在评分预测问题上的,很少有人用它去生成TopN推荐的列表,而且也很少有人研究如何将这个数据用到非评分数据上。其实LFM在评分预测和在TopN上应用的道理是一样的。

我们可以通过大量数据:利用梯度下降法分解出PQ矩阵
在预测评分时:
机器学习->推荐系统->LFM_第4张图片
其中R表示对应的user对特定的item的评分。
机器学习->推荐系统->LFM_第5张图片
其中P矩阵表示:特定用户对特定类的喜好程度,Q表示特定电影属于特定类的权重。这样就实现了由用户行为对电影自动聚类。如果推荐一部电影给某个特定用户,通过查询这部电影在PQ矩阵内的具体值就能预测这个用户对这部电影的评分。

在TopN问题上,道理是一样的,(隐反馈数据集上R只有1,0,分别表示感兴趣,不感兴趣,并且原始数据中只有明确1类的正样本,负反馈数据需要我们自己收集生成,如何生成负样本是个需要解决的问题,上面也讲到依据什么原则解决,但是还需要改进,正负样本比例是个比较重要参数)获取PQ矩阵,就可以向某个特定用户推荐他喜欢的电影类内电影中权重最高的电影或者按权重从大到小排序推荐N个电影给他。
这里聊聊这个LFM在TopN上的应用,现在很少有LFM算法应用在TopN上,具体怎么实现LFM应用在TopN的代码我也在研究之中,这方面的相关论文还非常少。不过我觉得LFM在实践部分还是有其前景的。不得不说,在项亮的《推荐系统实战》中讲到LFM在TopN上的应用,但他却没有写出完整的实现代码,我在相关网站上查到项亮说到这个问题,说先卖个关子。我觉得既然编书就要有毫无私心的奉献出你的所有想法。编书不是为了让别人觉得你牛。在开源这点上,说实话中国人比不上外国人。

实战:分别实现LFM算法在预测评分和TopN上的代码:
预测评分,分解出PQ:

#coding:utf-8

'''
本函数用来实现推荐系统里面的LFM算法,并且求出QR矩阵
运用梯度下降法来进行参数更新
'''

import numpy as np
import math
import random
import pandas as pd

def qr(k,learningRate,lamda_user,lamda_item,noOfIteration,file_training):
    '''

    :param k: 隐含的特征个数,其实就是将用户兴趣分成k类,将物品分成k类
    :param learningRate:在梯度下降更新参数过程中的学习率
    :param lamda_user:Pu的正则化参数
    :param lamda_item:Qr的正则化参数
    :param noOfIteration:最大迭代次数
    :param file_training:字符串;文件路径及文件名
    :return:
    '''
    maximumRating=0
    lines = pd.read_csv(file_training, delim_whitespace=True, header=None)
    numberOfUsers=0
    numberOfItems=0
    userID=np.zeros((len(lines)))
    itemID=np.zeros((len(lines)))
    rating=np.zeros((len(lines)))
    count=0

    for i in range(len(lines)):
        userID[count] = int(lines.iloc[i][0])-1
        if userID[count]>(numberOfUsers-1):
            numberOfUsers = userID[count]+1
        itemID[count] = int(lines.iloc[i][1])-1
        if itemID[count]>(numberOfItems-1):
            numberOfItems= itemID[count]+1
        rating[count] = float(lines.iloc[i][2])
        if rating[count]>maximumRating:
            maximumRating = rating[count]
        count=count+1

    maximumRating=float(maximumRating)

    ####初始化LFM的矩阵P和矩阵Q,采用随机初化的办法进行初始化,以我的经验,这样比全零初始化更快达到最优。
    p=np.array([[float(random.uniform(0,math.sqrt(maximumRating/k))) for i in range(k)] for j in range(int(numberOfUsers))])
    q=np.array([[float(random.uniform(0,math.sqrt(maximumRating/k))) for i in range(k)] for j in range(int(numberOfItems))])

    ##利用梯度下降法更新参数
    error=np.zeros((noOfIteration))
    for i in range(noOfIteration):
        for j in range(len(lines)):
            p[int(userID[j]), :] = p[int(userID[j]), :] + learningRate * ((rating[j] -np.dot(p[int(userID[j]), :],q[int(itemID[j]), :])) * q[int(itemID[j]), :] - lamda_user * p[int(userID[j]), :])
            q[int(itemID[j]), :] = q[int(itemID[j]), :] + learningRate * ((rating[j] -np.dot(p[int(userID[j]), :],q[int(itemID[j]), :])) * p[int(userID[j]), :] - lamda_item * q[int(itemID[j]), :])

        for j in range (len(lines)):
            error[i]= error[i] + math.pow(rating[j] - np.dot(p[int(userID[j]),:], q[int(itemID[j]),:]),2)

        error[i]=math.sqrt(error[i])/len(lines)
    return error,p,q


if __name__=='__main__':
    (error,p,q)=qr(10, 0.02, 0.01, 0.01, 1000, 'u.data')
    print p,q

你可能感兴趣的:(机器学习--推荐系统,推荐系统)