基于用户的协同过滤算法

最近在做一个古诗推荐功能,以前只会根据点击量,收藏量进行排序,给用户展示点击量、排行量前几的数据。

最近新发现一个算法——基于用户的协同过滤算法。

原理:

1、找到与目标用户兴趣相投的用户。

2、根据找到的用户集合中,相似用户喜欢的,而目标用户没有听说过的物品,进行推荐。


一、首先连接数据库获取数据

import pymysql
import json
# 连接database
conn = pymysql.connect(host="127.0.0.1", user="root", password="tian", database="tangshi", charset="utf8")
# 得到一个可以执行SQL语句的光标对象
cursor = conn.cursor()
# 定义要执行的SQL语句
sql = """select user_id,poetry_id from poetry_collection ;"""
# 执行SQL语句
cursor.execute(sql)
data = cursor.fetchall()
res = {}

# 更改格式
for info in data:
    if (info[0] in res.keys()):
        res[info[0]].append(info[1])
    else:
        res [info[0]]=[info[1]]
for k,v in res.items():
    res[k]=tuple(v)
json_str = json.dumps(res, indent=4)#注意这个indent参数
with open('test_data.json', 'w') as json_file:
     json_file.write(json_str)
# 关闭光标对象
cursor.close()
# 关闭数据库连接
conn.close()

 格式如下:key是用户id,vaue是古诗id

二、获取相似的用户

通常用 Jaccard 公式或者余弦相似度计算两个用户之间的相似度。设 N(u) 为用户 u 喜欢的物品集合,N(v) 为用户 v 喜欢的物品集合,那么 u 和 v 的相似度是多少呢:

余弦相似度:

用户47喜欢的古诗有: 85 , 80 ,87

则用户47和用户51的相似度为:2/(\sqrt{3}*\sqrt{3})=2/3

但是要计算所有用户的相似度,时间复杂度为O(n^{2})所以需要建立倒排表:

以下为抄袭:↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

假设目前共有4个用户: A、B、C、D;共有5个物品:a、b、c、d、e。用户与物品的关系(用户喜欢物品)如下图所示:

 

如何一下子计算所有用户之间的相似度呢?为计算方便,通常首先需要建立“物品—用户”的倒排表,如下图所示:

      然后对于每个物品,喜欢他的用户,两两之间相同物品加1。例如喜欢物品 a 的用户有 A 和 B,那么在矩阵中他们两两加1。如下图所示:

      计算用户两两之间的相似度,上面的矩阵仅仅代表的是公式的分子部分。以余弦相似度为例,对上图进行进一步计算:

      到此,计算用户相似度就大功告成,可以很直观的找到与目标用户兴趣较相似的用户。

三、推荐物品

    首先需要从矩阵中找出与目标用户 u 最相似的 K 个用户,用集合 S(u, K) 表示,将 S 中用户喜欢的物品全部提取出来,并去除 u 已经喜欢的物品。对于每个候选物品 i ,用户 u 对它感兴趣的程度用如下公式计算:

      其中 rvi 表示用户 v 对 i 的喜欢程度,在本例中都是为 1,在一些需要用户给予评分的推荐系统中,则要代入用户评分。

      举个例子,假设我们要给 A 推荐物品,选取 K = 3 个相似用户,相似用户则是:B、C、D,那么他们喜欢过并且 A 没有喜欢过的物品有:c、e,那么分别计算 p(A, c) 和 p(A, e):

      看样子用户 A 对 c 和 e 的喜欢程度可能是一样的,在真实的推荐系统中,只要按得分排序,取前几个物品就可以了。

四、代码实现

import math
from operator import *
import json

file = open('test_data.json', 'r')
js = file.read()
dic = json.loads(js)
for k, v in dic.items():
    for i in range(len(v)):
        v[i] = str(v[i])
    v = tuple(set(v))
file.close()


# 计算用户兴趣相似度
def Usersim(dicc):
    item_user = dict()
    for u, items in dicc.items():
        for i in items:
            if i not in item_user.keys():
                item_user[i] = set()  # i键所对应的值是一个集合
            item_user[i].add(u)  # 向集合中添加用户。

    C = dict()
    N = dict()
    for item, users in item_user.items():
        for u in users:
            if u not in N.keys():
                N[u] = 0
            N[u] += 1  # 每个商品下用户出现一次就加一次,就是计算每个用户一共购买的商品个数。

            for v in users:
                if u == v:
                    continue
                if (u, v) not in C.keys():  # 同上,没有初始值不能+=
                    C[u, v] = 0
                C[u, v] += 1
    # 到这里倒排阵就建立好了,下面是计算相似度。
    W = dict()
    for co_user, cuv in C.items():
        W[co_user] = cuv / math.sqrt(N[co_user[0]] * N[co_user[1]])
    # 把相似度为1的变为0,不能删除,否则倒排阵不成立。两者完全相似,也或者是收藏数比较少。这样没有推荐
    for k, v in W.items():
        if v == 1.0:
            W[k] = 0
    return W


def Recommend(user, dicc, W2, K):
    """
    :param user: 要推荐的用户
    :param dicc: 数据
    :param W2: 倒排矩阵
    :param K:取相似用户,前多少个相似的
    :return:
    """
    rvi = 1  # 这里都是1,实际中可能每个用户就不一样了。就像每个人都喜欢beautiful girl,但有的喜欢可爱的多一些,有的喜欢御姐多一些。
    rank = dict()
    related_user = []
    # 要推荐的用户喜欢的
    interacted_items = dicc[user]
    for co_user, item in W2.items():
        if co_user[0] == user:
            related_user.append((co_user[1], item))  # 先建立一个和待推荐用户兴趣相关的所有的用户列表。
    # 找到相似用户喜欢的,而待推荐用户没有喜欢过的
    # print(related_user[:30])
    for v, wuv in sorted(related_user, key=itemgetter(1), reverse=True)[0:K]:
        # 找到K个相关用户以及对应兴趣相似度,按兴趣相似度从大到小排列。
        for i in dicc[v]:
            if i in interacted_items:
                continue
            if i not in rank.keys():
                rank[i] = 0
            rank[i] += wuv * rvi
    return rank


if __name__ == '__main__':
    # 推荐个数
    advice_num = int(input('请输入推荐页数:')) * 12

    W3 = Usersim(dic)
    # 要推荐的用户id'2313'
    Last_Rank = Recommend('2313', dic, W3, 20000)
    porety_num = 12  # 分页,每页古诗12
    for k, v in Last_Rank.items():
        if porety_num % 12 == 0:
            print('************************第%d页***********************' % (porety_num / 12))
        print('古诗id:', k)
        advice_num -= 1
        porety_num += 1
        if advice_num == 0:
            break
    # print('推荐:', Last_Rank)

 

用户协同过滤算法是一种常见的推荐算法,它通过分析用户之间的相似性,来给用户推荐相似用户喜欢的物品。具体实现方式是,首先根据用户之间的历史行为数据(如购买记录、评分记录等),计算用户之间的相似度,然后利用相似用户的历史行为数据,推荐给目标用户他们喜欢的物品。 下面是一个简单的基于用户协同过滤算法的Python代码: ```python import numpy as np # 历史行为数据 data = np.array([ [5, 3, 0, 1, 4], [1, 0, 0, 5, 1], [4, 3, 0, 0, 5], [0, 1, 5, 4, 4], [5, 4, 4, 0, 0] ]) # 计算用户相似度 def similarity(user1, user2): return np.dot(user1, user2) / (np.linalg.norm(user1) * np.linalg.norm(user2)) # 给定目标用户id,计算推荐物品列表 def recommend(target_id): target_user = data[target_id] similarities = [] for i in range(len(data)): if i != target_id: sim = similarity(target_user, data[i]) similarities.append((i, sim)) similarities.sort(key=lambda x: x, reverse=True) top_similarities = similarities[:2] recommended_items = [] for item_id in range(len(target_user)): if target_user[item_id] == 0: score = 0 count = 0 for sim_id, sim_score in top_similarities: if data[sim_id][item_id] != 0: score += sim_score * data[sim_id][item_id] count += sim_score if count > 0: recommended_items.append((item_id, score/count)) recommended_items.sort(key=lambda x: x, reverse=True) return recommended_items # 测试代码 print(recommend(0)) # 给id为0的用户推荐物品 ``` 代码中的`data`是历史行为数据矩阵,其中每一行表示一个用户对各个物品的行为数据,0表示未有行为。`similarity`函数用于计算两个用户之间的相似度,这里采用的是余弦相似度计算方法。`recommend`函数则是用于给定目标用户id,计算出推荐的物品列表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值