推荐系统实践:基于数据集MovieLens构造简单推荐系统

Posted 多鱼的夏天

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了推荐系统实践:基于数据集MovieLens构造简单推荐系统相关的知识,希望对你有一定的参考价值。

摘要

本文基于 MovieLens 数据集构造了用户-电影项目评价矩阵,并基于评价矩阵计算两用户间的相似度,取出相似度最高的 N 个用户作为候选用户序列。接着筛选出这 N 个候选用户的高分电影项目且当前用户尚未观看,并根据这高分候选电影项目集合的电影类型,与当前用户所观影的全部电影类型做一个标签共现,预测当前用户对这些高分电影项目的评分,把预测评分最高的 M 个电影项目推荐给当前用户。

关键词:协同过滤;标签共现;电影推荐;Demo 实战验证

MovieLens 是蛮经典的数据集,在验证推荐算法的初期经常会使用到。本文的数据集没有很大,是基于 ml-latest-small(size:1MB)这个超小的数据集来做微型验证的,反正就是验证思路对不对,管它数据集的 size 咋样,对叭!数据集可以去 MovieLens 官网下载,这里就不提供了嘤嘤嘤。

碎碎念

哦对了,本文是给纯小白入门用的,浅浅打代码确实菜的一批,这次的推荐系统算法验证没有用很花哨的程序实现,一行行带小白入门好了,顺带着我也做个总结,为毕业设计中的一个小模块贡献出自己绵薄的力量呜呜呜,这样子一说就感觉自己形象变高大了呢!什么?想了解浅浅的毕业设计是什么?哦,不告诉你,反正浅浅出品,必属精品嘿嘿嘿。好了废话不多说,就开始入门叭。

实战验证

导入第三方库与数据文件

首先是导入一些必备的第三方库:

import numpy as np
import pandas as pd
import csv
import math

NumPy、Pandas、CSV、Math 版本随意,这个没所谓。其实我压根就没觉得 NumPy 要放进去,只是图个方便,顺手就写了。万一有读者想说后续有个去重操作想用 NumPy 搞一搞,这不就方便了嘛。

接着是利用 pd.read_csv() 进行数据的导入,这里用 values.tolist() 就顺手把 dataframe 类型转换成 list 类型了,嗐,我果然是个转换类型小天才,低调低调:

movies=pd.read_csv("movies.csv").values.tolist()
links=pd.read_csv("links.csv").values.tolist()
ratings=pd.read_csv("ratings.csv").values.tolist()
tags=pd.read_csv("tags.csv").values.tolist()

嗷,你们可能对这四个文件很懵逼,来,浅浅让你们开开眼(虽然说可以自行官网下载数据,完了之后再自己看看,让文章更加充实些,我贴一下样本吧)。

数据变量名说明

movies.csv

links.csv

ratings.csv

tags.csv

我对里面表头做个说明:

变量名含义
movieId电影编号
title电影标题
genres电影类型
imdbId在 imdb 里的编号(反正我没用到这个)
tmdbId在 tmdb 里的编号(反正我没用到这个)
userId用户编号
rating评分(0.5~5)
timestamp时间戳(反正我没用到这个)
tags用户对电影的打标,但我更倾向于类似弹幕(反正我没用到这个)

转换为评价矩阵

但是有上面的数据,看着好像蛮好做的,但是我比较倾向于处理成用户-项目的评价矩阵进行相似性计算,所以,数据处理过程如下:

#产生评价矩阵:

movieId=[]#movieId 列表

for index in range(len(movies)):
    movieId.append(movies[index][0])

#print(len(movies))这里也是我输出打锚点的地方

Q=[]#评价矩阵

userRow=[0 for i in range(len(movies)+1)]

#userRow 列表是用户评价行,长度为电影数量加一

#print(movieId[len(movies)-1])你可以输出看看……

userId=[(i+1) for i in range(610)]

count = 0 #userId initial
userRow[0]=ratings[0][0]
for index in range(len(ratings)):
    if count == ratings[index][0]-1:
        userRow[(movieId.index(ratings[index][6])+1)]=ratings[index][7]
    elif count != ratings[index][0]-1:
        count=count+1
        Q.append(userRow)#添加到评价矩阵 Q 中
        userRow=[0 for i in range(len(movies)+1)]
        userRow[0]=ratings[index][0]
        userRow[(movieId.index(ratings[index][8])+1)]=ratings[index][9]
Q.append(userRow)

我们来分析一下,我当前这个矩阵的大小是 610*9742 那么如果每次打开这个程序都要进行这么一次数据处理,接近六百万次的数据处理能否避免呢?当然可以啦,我直接得到 Q 之后就把它写进一个 Q.csv 的文件里了:

#写入 Q.csv 文件中
with open ("Q.csv","w",newline="") as csvfile:
    writer = csv.writer(csvfile)
    writer.writerows(Q)

然后打开 Q.csv 看看,长这样子呢!

篇幅有限,我就不放出整个数据表了,也放不下,反正代码给了,你们自个儿跑就行。

既然已经做了写入,那么每次只需要读取取用即可:

Q=pd.read_csv("Q.csv",header=None).values.tolist()

为了防止发生读取错误吼,可以试着打个锚点输出一下瞅瞅是不是你想要的数据值即可,这个问题很好解决。值得一提的是,这里写入的是一个无表头的 csv 文件,所以要用 header=None

相似度计算

然后就是利用评价矩阵计算相似度了:

def similar(i,j):
    sum_ij = 0 #求和分子
    mul_i = 0 #i 项的平方和
    mul_j = 0 #j 项的平方和
    for index in range(1,len(i)):
        #if i[index]!=-1 and j[index]!=-1:
        sum_ij=sum_ij+i[index]*j[index]
        mul_i=mul_i+i[index]*i[index]
        mul_j=mul_j+j[index]*j[index]
         #print("i:",i[index],",j:",j[index])
    if mul_i==0 or mul_j==0:
        sim=0
    else:
        sim=sum_ij/(math.sqrt(mul_i)*(math.sqrt(mul_j)))#余弦相似度
    return sim

想看 sim 是啥的可以自己输出一下,反正也就是 print(sim(i,j)) 这里要注意哈,i,j 都是向量,换句话说都是一个 list。

实现标签共现

然后是获取用户的观影类型频数:

def co_tags(userId):
    userGenres=[]#观影矩阵(二维)
    userWatchs=[]#观影类型(一维)
    frequency=[]#类型频率
    userList=[]#定义用户行向量(存放电影编号)
    ListIndex=[]#定义用户电影编号下标
    
    for index in range(1,len(Q[userId-1])):
        if Q[userId-1][index]!=0:
            userList.append(movies[index-1][0])
            ListIndex.append(index-1)#下标默认从 0 开始,所以减一
    #print(len(userList))
    userMovies=[]#二维
    #MovieTags=[]#一维
    #得到用户所有观影的电影类型矩阵
    for item in range(len(ListIndex)):
        MovieGenres=movies[ListIndex[item]][11].split("|")
        userGenres.append(MovieGenres)
        MovieGenres=[]
    #for index in range(len(userGenres)):
        #print(userGenres[index])
    for i in range(len(userGenres)):
        for j in range(len(userGenres[i])):
            if userGenres[i][j] in userWatchs:
                frequency[userWatchs.index(userGenres[i][j])]+=1#自增
            else:
                userWatchs.append(userGenres[i][j])
                frequency.append(1)
    #print(userWatchs)
    #print(frequency)
	...

点击这里阅读全文

以上是关于推荐系统实践:基于数据集MovieLens构造简单推荐系统的主要内容,如果未能解决你的问题,请参考以下文章

第一篇:使用Spark探索经典数据集MovieLens

Spark探索经典数据集MovieLens

ML之KG:基于MovieLens电影评分数据集利用基于知识图谱的推荐算法(networkx+基于路径相似度的方法)实现对用户进行Top电影推荐案例

ML之CF:基于MovieLens电影评分数据集利用基于用户协同过滤算法(余弦相似度)实现对用户进行Top5电影推荐案例

电影数据集

如何使用Movielens数据集进行推荐?