UserCF算法在MovieLens数据集的运用

MovieLens数据集介绍和ItemCF算法参考链接http://t.csdn.cn/XIcmJ 

UserCF算法仍然分为两个部分:

  • 训练阶段
  • 推荐阶段

一、对于训练阶段,可分为以下几步:

1、数据预处理,建立User-Item表

2、建立Item-User倒排表

        对于用户列表{A,B,C,D},我们对其中两两用户都使用余弦相似度算法计算相似度。这种算法的时间复杂度是O(N^2),当用户量很大的时候,计算非常耗时,在实际运用中不可取。观察一下相似度计算公式会发现,其实如果用户u和用户v没有共同喜欢的物品(比如表中的用户B和用户C),那么他们之间的相似度为0,也就没有必要计算了。        
        也就是说我们没必要对任意两个用户之间都计算相似度,我们只需要计算那些彼此之间有共同喜爱物品的用户之间的相似度,故首先可以想到建立倒排表。

3、建立用户物品交集矩阵

4、建立用户相似度矩阵

二、对于推荐阶段,可分为以下几步:

1、寻找与被推荐用户最相似的K个用户

        例如要对用户C进行物品推荐,通过查表可以知道(第四行),用户A和用户D是比较相似的两个用户。再通过User-Item表查询到用户A喜欢的物品列表{a,b,d},用户D喜欢的物品列表{c,d,e}, 故用户A、D喜欢物品的交集是{a,b,c,d,e},其中用户C喜欢的列表是{b,e},为了避免重复推荐用户已经喜欢的物品,所以要先从物品列表中去掉用户C已经喜欢的物品,故最终待推荐的物品列表为{a,c,d}。

2、计算用户对物品的感兴趣列表并逆序排列

p(C,a) = W[C][A] = 0.41
p(C,c) = W[C][D] = 0.41
p(C,d) = W[C][A] + W[C][D] = 0.82

接着按照用户C对待推荐物品感兴趣程度对待推荐列表进行逆序排序,得到最终的推荐列表{d,a,c},我们可以将整个推荐列表或者取前K个物品推荐给用户C。

二、代码解释

使用以下代码来读取数据并建立User-Item表:

import random
import pandas as pd

def LoadMovieLensData(filepath, train_rate):
    ratings = pd.read_table(filepath, sep="::", header=None, names=["UserID", "MovieID", "Rating", "TimeStamp"],\
                            engine='python')
    ratings = ratings[['UserID','MovieID']]
    train = []
    test = []
    random.seed(3)
    for idx, row in ratings.iterrows():
        user = int(row['UserID'])
        item = int(row['MovieID'])
        if random.random() < train_rate:
            train.append([user, item])
        else:
            test.append([user, item])
    return PreProcessData(train), PreProcessData(test)

def PreProcessData(originData):
    """
    建立User-Item表,结构如下:
        {"User1": {MovieID1, MoveID2, MoveID3,...}
         "User2": {MovieID12, MoveID5, MoveID8,...}
         ...
        }
    """
    trainData = dict()
    for user, item in originData:
        trainData.setdefault(user, set())
        trainData[user].add(item)
    return trainData

使用了python的dict里面嵌套set来实现倒排表:

def UserItemTable(userItemTab):
    """
    建立User-Item倒排表
    :param userItemTab: user-item表
    :return:
    """
    item_user = dict()
    for user, items in userItemTab.items():
        for item in items:
            item_user.setdefault(item, set())
            item_user[item].add(user)

 保存用户物品交集的矩阵采用了双重dict来实现:

def UserInterSection(item_user):
    """
    建立用户物品交集矩阵W, 其中C[u][v]代表的含义是用户u和用户v之间共同喜欢的物品数
    :param item_user: item_user 倒排表
    """
    userInterSection = dict()
    for item, users in item_user.items():
        for u in users:
            for v in users:
                if u == v:
                    continue
                userInterSection.setdefault(u, defaultdict(int))
                userInterSection[u][v] += 1  # 将用户u和用户v共同喜欢的物品数量加一

 用户相似度矩阵只需要在用户物品交集矩阵的基础上除以用户u和用户v各自喜爱物品列表数量的乘积:

def UserSimMatrix(userItemTab, userInterSection):
    """
    建立用户相似度矩阵
    :param userItemTab: User-Item表
    :param userInterSection: 用户物品交集矩阵
    :return: 
    """
    userSimMatrix = dict() #用户相似度矩阵
    for u, related_user in userInterSection.items():
        for v, cuv in related_user.items():
            nu = len(userItemTab[u])
            nv = len(userItemTab[v])
            userSimMatrix[u][v] = cuv / math.sqrt(nu * nv)

完整代码:

import math
import random
import pandas as pd
# from Utils import modelsave
from collections import defaultdict
from operator import itemgetter

def LoadMovieLensData(filepath, train_rate):
    ratings = pd.read_table(filepath, sep="::", header=None, names=["UserID", "MovieID", "Rating", "TimeStamp"],\
                            engine='python')
    ratings = ratings[['UserID','MovieID']]

    train = []
    test = []
    random.seed(3)
    for idx, row in ratings.iterrows():
        user = int(row['UserID'])
        item = int(row['MovieID'])
        if random.random() < train_rate:
            train.append([user, item])
        else:
            test.append([user, item])
    return PreProcessData(train), PreProcessData(test)

def PreProcessData(originData):
    """
    建立User-Item表,结构如下:
        {"User1": {MovieID1, MoveID2, MoveID3,...}
         "User2": {MovieID12, MoveID5, MoveID8,...}
         ...
        }
    """
    trainData = dict()
    '''
    检查user这个键是否存在于trainData字典中,
    如果不存在,则将user这个键和空集合set()添加到字典中,并返回这个空集合;
    如果存在,则返回该键的值,也就是之前添加到该键下的集合。
    最后,对于每个user,我们将item添加到它的集合中,以便在训练时能够迭代它。
    '''
    for user, item in originData:
        trainData.setdefault(user, set())
        trainData[user].add(item)
    return trainData

class UserCF(object):
    """ User based Collaborative Filtering Algorithm Implementation"""
    def __init__(self, trainData, similarity="cosine"):
        self._trainData = trainData
        self._similarity = similarity
        self._userSimMatrix = dict() # 用户相似度矩阵

    def similarity(self):
        # 建立User-Item倒排表
        item_user = dict()
        for user, items in self._trainData.items():
            for item in items:
                item_user.setdefault(item, set())
                item_user[item].add(user)

        # 建立用户物品交集矩阵W, 其中C[u][v]代表的含义是用户u和用户v之间共同喜欢的物品数
        for item, users in item_user.items():
            for u in users:
                for v in users:
                    if u == v:
                        continue
                    self._userSimMatrix.setdefault(u, defaultdict(int))
                    if self._similarity == "cosine":
                        self._userSimMatrix[u][v] += 1 #将用户u和用户v共同喜欢的物品数量加一
                    elif self._similarity == "iif":
                        self._userSimMatrix[u][v] += 1. / math.log(1 + len(users))

        # 建立用户相似度矩阵
        for u, related_user in self._userSimMatrix.items():
            # 相似度公式为 |N[u]∩N[v]|/sqrt(N[u]||N[v])
            for v, cuv in related_user.items():
                nu = len(self._trainData[u])
                nv = len(self._trainData[v])
                self._userSimMatrix[u][v] = cuv / math.sqrt(nu * nv)

    def recommend(self, user, N, K):
        """
        用户u对物品i的感兴趣程度:
            p(u,i) = ∑WuvRvi
            其中Wuv代表的是u和v之间的相似度, Rvi代表的是用户v对物品i的感兴趣程度,因为采用单一行为的隐反馈数据,所以Rvi=1。
            所以这个表达式的含义是,要计算用户u对物品i的感兴趣程度,则要找到与用户u最相似的K个用户,对于这k个用户喜欢的物品且用户u
            没有反馈的物品,都累加用户u与用户v之间的相似度。
        :param user: 被推荐的用户user
        :param N: 推荐的商品个数
        :param K: 查找的最相似的用户个数
        :return: 按照user对推荐物品的感兴趣程度排序的N个商品
        """
        recommends = dict()
        # 先获取user具有正反馈的item数组
        related_items = self._trainData[user]
        # 将其他用户与user按照相似度逆序排序之后取前K个
        for v, sim in sorted(self._userSimMatrix[user].items(), key=itemgetter(1), reverse=True)[:K]:
            # 从与user相似的用户的喜爱列表中寻找可能的物品进行推荐
            for item in self._trainData[v]:
                # 如果与user相似的用户喜爱的物品与user喜欢的物品重复了,直接跳过
                if item in related_items:
                    continue
                recommends.setdefault(item, 0.)
                recommends[item] += sim
        # 根据被推荐物品的相似度逆序排列,然后推荐前N个物品给到用户
        return dict(sorted(recommends.items(), key=itemgetter(1), reverse=True)[:N])

    def train(self):
        self.similarity()


if __name__ == "__main__":
    train, test = LoadMovieLensData("../Data/ratings.dat", 0.8)
    print("train data size: %d, test data size: %d" % (len(train), len(test)))
    UserCF = UserCF(train)
    UserCF.train()

    # 分别对测试集中的前4个用户进行电影推荐
    print(UserCF.recommend(list(test.keys())[0], 5, 80))
    print(UserCF.recommend(list(test.keys())[1], 5, 80))
    print(UserCF.recommend(list(test.keys())[2], 5, 80))
    print(UserCF.recommend(list(test.keys())[3], 5, 80))

运行结果:

 参考资料:

https://www.jianshu.com/p/7c5d9c008be9

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值