协同过滤CF

在这里插入图片描述我门需要三个方面进行给用户推荐,前两个是基于用户的,最后一个是基于内容的(这部分需要切词)。

用户的历史记录,会有一个user-item矩阵,根User-Based CF 计算user与user的相似度矩阵。Item-Based CF 就算item-item的相似度矩阵。

在这里插入图片描述
CF的优点
– 充分利用群体智慧(历史行为数据)
– 推荐精度高于CB
– 利于挖掘隐含的相关性(啤酒尿布)
• 缺点
– 推荐结果解释性较差
– 对时效性强的Item不适用(我们算的都是T+1的,就是一天前的数据)
– 冷启动问题
在这里插入图片描述
用户购买多次的物品,做一个count统计,大于一次的放入消耗类物品List中。
过滤User1已经购买的物品时、这个消耗物品List里面的物品不做过滤。要在用户已经购买的购物清单处过滤,这样推荐给user1用户的物品里面就没有消耗类List了。
在这里插入图片描述
在这里插入图片描述

统计一个用户有偶然性,因为我买了一个墨镜后来丢了,排出这种可能性,增加保守的统计,统计多个的话,这种偶然性的占比就很小了。找出购买可乐的user用户几个,3个作为分母,再找出大于1个的作为分子,就是2/3。然后自己设置一个概率,概率大于60%的放入消耗List中去。
在这里插入图片描述
根据User-item矩阵能计算出:User-BasedCF,能计算出User-User的相似度矩阵在这里插入图片描述

A和B的欧式距离:sqrt((5-1)^2 + (1-5)^2 + (2-5)^2 + (2-5)^2)
计算用户A和用户B的相似度:
余弦相似度cos = (15+15+25+25)/sqrt(25+1+4+4)sqrt(1+25+25+25)
C,titanic
A 0.73,B:0.97,D:0.87取相似最大的前两个用户
B:0.97,D:0.87
C用户对titanic的电影评价是 = (5
0.97+3*0.87)/(0.97+0.87 )

Item—BasedCF,计算出Item-Item之间的相似性。算出用户以前购买的物品或者喜欢的相似物品,item-item矩阵可以存到内存中或者是线上要用的redis中,这样用户浏览时可以直接推给他,比user_cf要快。

在这里插入图片描述

在这里插入图片描述
冷 启 动问题:
分为三类
–1. 用户冷启动
• 提供热门排行榜,等用户数据收集到一定程度再切换到个性化推荐
• 利用用户注册时提供的年龄、性别等数据做粗粒度的个性化
• 利用用户社交网络账号,导入用户在社交网站上的好友信息,然后给用户推荐其好友喜欢的物品
• 在用户新登录时要求其对一些物品进行反馈,收集这些兴趣信息,然后给用户推荐相似的物品
– 2.物品冷启动
• 给新物品推荐给可能对它感兴趣相似的物品的用户,利用内容信息,将他们推荐给喜欢过和他们相似的物品的用户。
• 物品必须能够在第一时间展现给了,否则经过一段时间后,物品的价值就大大降低了
• UserCF和ItemCF都行不通,只能利用 Content based解决该问题,频繁更新相关性数据。
—3.系统冷启动
•引入专家知识,通过一定高效方式迅速建立起物品的相关性矩阵

基于user_cf的算法实现
generate_train_data.py

import pandas as pd
from recall import user_cf
import operator
from recall import item_cf

data_path = 'C:\\Users\\zheng\\PycharmProjects\\webpy_test\\cf\\data\\u.data'

udata = pd.read_csv(data_path,
                    sep='\t',
                    header=None,
                    names=['user_id', 'item_id', 'rating', 'timestamp'])
#做成训练数据,train的数据格式:{user_id:{item_id:rating}}
train = dict()
# for _,row in udata.iloc[:2,:].iterrows():
for _, row in udata.iterrows(): # 对每一行数据进行迭代,_是第一列的0,1,2,3,4
    user_id = str(row['user_id']) # 取user_id的数据
    item_id = str(row['item_id']) # 取item_id的数据
    rating = row['rating']        # 取rating的数据
    if train.get(user_id, -1) == -1: # 如果在train中没有user_id这个值,则我们把user_id至为字典形式,就是{user_id:{}}
        train[user_id] = dict()
    train[user_id][item_id] = rating # {user_id:{item_id:rating}}


# ###################user_cf test###################
#找相似用户
W = user_cf.user_similarity(train)
# 按相似度值做排序key=operator.itemgetter(1),逆序,取前10个
print(sorted(W.get('1').items(), key=operator.itemgetter(1), reverse=True)[:10])
# 相似用户的物品集合
rec_item_list = user_cf.recommend('1', train, W, 10)
print(sorted(rec_item_list.items(), key=operator.itemgetter(1), reverse=True)[:20])

# ###################item_cf test###################
W2 = item_cf.item_similarity(train)
item_list = item_cf.recommend(train,'1',W2,10)
print(sorted(item_list.items(), key=operator.itemgetter(1), reverse=True)[:20])

user_cf.py(基于用户的协同过滤)

import operator
import math

# train 格式 :{user:{item:rating}}

#相似用户
def user_similarity(train):
    # 建立item->users倒排表(一个item有多少个user购买了它),目的是,如果有相似的item,就计算users里面的user之间的相似度,如果没有,就不计算user的相似度,这样的话,就可以不用计算除了我以外全部的user相似度。
    item_users = dict()
    for u, items in train.items():  # 按字典形式遍历u=user_id, items={item_id:rating}
        for i in items.keys():      # items.keys()这个字典中的item_id取出
            if i not in item_users:  # 如果item_id不在这个item_users倒排表里面
                item_users[i] = set() # 我们定义一个set加进去
            item_users[i].add(u)     # 如果item_users中有这个item, 则把物品的用户给加进去,一个user里面有多个item,按个遍历下,每个item都add这个用户,其他的用户也add
                                   #train:      {user:{item:rating}}  {'196': {'242': 3}, '186': {'302': 3}}
                                   #item_users: {item:{users...}}     {'242': {'196'}, '302': {'186'}}

    # 计算相似user共同的物品数量(item_users里面的user和user的相似度),这块要加一个热门物品的衰减,热门的物品你们买的一样,不一定你们两个兴趣相似,要是冷门的物品你们两买的一样,说明相似,所以给热门物品做衰减
    C = dict()  # 相似用户之间相同物品的数量  交集
    N = dict()  # 存储每个用户拥有的Item数量  分母  C/N大致的相似度(两个交集的大小/u购买item的集合大小*v购买item的集合大小)
    for i, users in item_users.items(): # item_users= {item_id:{user_id,user_id...}}
        for u in users:
            if N.get(u, -1) == -1: # 初始化一下,先让每个用户拥有的item数量为0,后面再加
                N[u] = 0
            N[u] += 1
            if C.get(u, -1) == -1: # 初始化一下,先让相似用户相同的物品的数量为dict(),因为这个存的不仅有用户u,还有用户v,所以用dict来存。
                C[u] = dict()    # 每个用户对应其他的用户和它们对应的物品数量
            for v in users:
                if u == v:
                    continue
                elif C[u].get(v, -1) == -1:
                    C[u][v] = 0    # 初始化一下
                C[u][v] += 1    # 因为它们都是对应的一个item,所以是1,等在for循环,就是下一个item,就在+1
                # C[u][v] += 1 / math.log(1 + len(users))   1/log(1+用户数量)
    # 得到最终的相似度矩阵W(只计算和它购买相似物品的user的相似度,不需要计算除了他自己以外全部user的相似度)
    W = dict()
    for u, related_users in C.items(): # related_users 是和u相似的其他用户集合{user_id:和u相同的item数量}
        if W.get(u, -1) == -1:
            W[u] = dict()     # 初始化
        for v, cuv in related_users.items(): #v是和u相似的用户, cuv是相同item的数量(交集)
            W[u][v] = cuv / math.sqrt(N[u] * N[v] * 1.0)   #交集/sqrt(u用户购买的item*v用户购买的item)
    return W


# 相似用户的物品集合(取前几个相似用户,然后把他们对应item拿出来)
def recommend(user, train, w, k):  # 得传入user,这样才能拿到与user1相似的用户,进而拿到相似用户对应的item
    rank = dict()  #粗排
    interacted_items = train[user].keys()  #train[user].keys()拿到user1购买过物品的集合
    for v, wuv in sorted(w[user].items(),   #wuv是v和u的相似度,w[user].items()取和user1相似的用户和他们对用的相似度
                         key=operator.itemgetter(1),
                         reverse=True)[0:k]:
        for i, rvi in train[v].items():  #取相似用户对应的item,i是item,rvi是对应的打分rating,
            if i in interacted_items:  # 过滤已经购买过的物品
                continue
            elif rank.get(i, -1) == -1:
                rank[i] = 0   #初始化,这个是粗排
            rank[i] += wuv * rvi  #相似用户之间的分值*相似用户对它的物品打分(具体看下面图)
    return rank

建立倒排索引的目的:
就是我们计算可乐的user.只计算购买它的users之间的相似度。user2和user3,user2和user1,user3和user1之间的相似度就可以,不用把user4也加进来计算。
在这里插入图片描述

计算user和user的相似度:

在这里插入图片描述

item_cf.py(基于物品的协同过滤)

import math
import operator

'''
input: train:{user_id:{item_id:rating(score)},...}
'''
# item-item的相似度{item_id:{item_id:相似度}}
def item_similarity(train):
    # 计算item与item相同的user的数量
    C = dict()  # 存item1与其他item相同user的个数 分子{item_id:{item_id:user个数}} (和user_cf那个相反)
    N = dict()  # item对应的用户数量 分母
    for u, items in train.items():   # {user_id:{item_id:rating}}
        for i in items:           # 取{item_id:rating}中的item
            if N.get(i, -1) == -1:
                N[i] = 0       #初始化一下
            N[i] += 1     #根据for循环+1
            if C.get(i, -1) == -1:
                C[i] = dict()   #初始化,因为多个item和他对应的user,所以用dict存储
            for j in items:    #相同的物品不用计算了
                if i == j:
                    continue
                elif C[i].get(j, -1) == -1:
                    C[i][j] = 0  #初始化,对于i这个物品来说,没有物品j,所以初始化
                C[i][j] += 1   #对于这个u用户数量+1
    # 加分母计算相似度
    W = dict()
    for i, related_items in C.items():   # {item_id:{item_id:user个数}}
        if W.get(i, -1) == -1:
            W[i] = dict()    #初始化,多个item的相似度,所以用dict存储
        for j, cij in related_items.items(): # j是和i相似的物品。cij就是user的数量
            if W[i].get(j, -1) == -1:
                W[i][j] = 0     #初始化,因为下面有个 W[i][j] += 操作,所以必须有个初始值。不然报错了
            W[i][j] += cij / math.sqrt(N[i] * N[j])  # user个数/sqrt(i物品对应的user数*j物品对应的user数)
    return W

# 最终推荐,从item-item相似度矩阵中计算(与item1,与item2,与item3...相似的物品),然后推给用户
def recommend(train, user, w, k):  #user要传进来,因为我们要从user里面取出对应的item
    rank = dict()
    ru = train[user]  #取出item的集合 {item_id:rating}
    for i, pi in ru.items():   #i是item_id,pi就是打分rating
        for j, wj in sorted(w[i].items(),   # 取出和i相似的k个相似item。相似的item j。wj是i物品和j物品之间的相似度。
                            key=operator.itemgetter(1),
                            reverse=True)[0:k]:
            if j in ru:  #如果j物品在之前购买的item集合中,则过滤掉
                continue
            elif rank.get(j, -1) == -1:  #得到j物品的分值
                rank[j] = 0   #没有则初始化一下,因为下面做rank[j] += 操作
            rank[j] += pi * wj  # item对用的rating*item与item的相似度
    return rank

Item_cf的协同过滤:

在这里插入图片描述

计算item相同的user数量,
在这里插入图片描述

两个推荐引擎都做完各自的排序后,然后按照他们的key做一个过滤,加set(key),过滤掉他们推荐重复的item

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值