【原创】python协同过滤算法(基于用户)的demo

久未见 提交于 2019-12-12 17:57:10

python协同过滤算法(基于用户)的demo

0. 背景

有个协同过滤项目需要处理。从网上查阅资料,发现可用的资料不多。
发现一个简书的博客可以用,原文地址 传送门, 数据地址 传送门
此处只谈代码,不考虑原理。

1. 经过

本着尝试的心态打开了代码,发现原文代码运行效率极低。原文博主认为是算法本身所导致。
我略经修改,主要利用numpy函数和一些自带函数,优化了原文博主语句,使得运算效果大大提高。

2. 代码


import numpy as np
import pandas as pd
from scipy.spatial.distance import pdist, squareform, cosine
from sklearn.model_selection import train_test_split

npa = np.array
pd.set_option('display.max_rows', 500)
pd.set_option('display.max_columns', 500)
pd.set_option('display.width', 1000)

moviesPath = ".\\ml-latest-small\\movies.csv"
ratingsPath = ".\\ml-latest-small\\ratings.csv"
moviesDF = pd.read_csv(moviesPath, index_col=None)
ratingsDF = pd.read_csv(ratingsPath, index_col=None)

trainRatingsDF, testRatingsDF = train_test_split(ratingsDF, test_size=0.2)
print("total_movie_count:" + str(len(set(ratingsDF['movieId'].values.tolist()))))
print("total_user_count:" + str(len(set(ratingsDF['userId'].values.tolist()))))
print("train_movie_count:" + str(len(set(trainRatingsDF['movieId'].values.tolist()))))
print("test_movie_count:" + str(len(set(testRatingsDF['movieId'].values.tolist()))))
print("train_user_count:" + str(len(set(trainRatingsDF['userId'].values.tolist()))))
print("test_user_count:" + str(len(set(testRatingsDF['userId'].values.tolist()))))

trainRatingsPivotDF = pd.pivot_table(trainRatingsDF[['userId', 'movieId', 'rating']], columns=['movieId'],
                                     index=['userId'], values='rating', fill_value=0)

# enumerate返回穷举序列号与值
# 8981部电影
moviesMap = dict(enumerate(list(trainRatingsPivotDF.columns)))
# 610个用户
usersMap = dict(enumerate(list(trainRatingsPivotDF.index)))
# 矩阵变成list 每一行变成list的一个值 长度为610 每个值大小为8981
ratingValues = trainRatingsPivotDF.values


def calCosineSimilarity(l1, l2):
    return np.sum(l1 * l2) / np.sqrt(np.sum(l1 ** 2) * np.sum(l2 ** 2))


# 根据用户对电影的评分,来判断每个用户间相似度

userSimMatrixArray = pdist(ratingValues, calCosineSimilarity)
userSimMatrix = squareform(userSimMatrixArray)

# 找到与每个用户最相近的前K个用户
userMostSimDict = {i: sorted(enumerate(list(userSimMatrix[i])), key=lambda x: x[1], reverse=True)[:10] for i, v in
                   enumerate(ratingValues)}

# 用这K个用户的喜好中目标用户没有看过的电影进行推荐
userRecommendValues = np.zeros((len(ratingValues), len(ratingValues[0])), dtype=np.float32)  # 610*8981

ind_i, ind_j = np.where(ratingValues == 0)
for i, j in zip(ind_i, ind_j):
    val_array = (ratingValues[user][j] * sim for (user, sim) in userMostSimDict[i])
    userRecommendValues[i, j] = sum(val_array)

userRecommendDict = {i: sorted(enumerate(list(userRecommendValues[i])), key=lambda x: x[1], reverse=True)[:10] for i, v
                     in enumerate(ratingValues)}

# 将一开始的索引转换为原来用户id与电影id
userRecommendList = []
for key, value in userRecommendDict.items():
    user = usersMap[key]
    for (movieId, _) in value:
        userRecommendList.append([user, moviesMap[movieId]])

# 将推荐结果的电影id转换成对应的电影名
recommendDF = pd.DataFrame(userRecommendList, columns=['userId', 'movieId'])
recommendDF = pd.merge(recommendDF, moviesDF[['movieId', 'title']], on='movieId', how='inner')
print(recommendDF.tail(10))

3. 运行结果

在这里插入图片描述

4. 后记

  1. 代码还有可以优化的空间, 欢迎指点与讨论
  2. 算法框架仍需要优化, 目前基于numpy矩阵,难以应用到实际项目,需要基于数据库进行优化
  3. 感悟: 代码的语法效率其实挺重要的,不能简单的通过实际测试区推断算法效率

5. 致谢

再次致谢“不可能打工”的博客 https://www.jianshu.com/p/45c9010a3083

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!