基于SVD算法的电影推荐系统实验

推荐系统发展简单思维导图

推荐系统思维导图

电影推荐系统最初思路

基于协同过滤算法的推荐系统设计
原本是想实现一些基本的基于协同过滤思想的推荐系统设计,写完代码之后发现效果不理想。因为是要交的实验报告,所以转而使用其他算法做准确率和召回率。

程序实现的功能

  1. 基于关键字搜索的搜索推荐模块
  2. 基于新用户的推荐模块
  3. 基于老用户的推荐模块
  4. 相关数据的可视化
  • 基于SVD算法的推荐系统使用的是surprise第三方推荐系统库,安装surprise需要virtual C++ 2015构建工具,可从官网下载构建工具而不用安装庞大的vs C++ 软件
  • 电影数据集来自movielens中的ml-1m.zip数据集

程序整体框架

程序整体流程框架如图所示程序整体框架

基于关键字搜索推荐模块

设计思路如图所示:基于关键字搜索推荐模块流程图

搜索推荐模块代码

def search_name(movies_data  , name , ru_mat):
    title_flag = input("search by title:")

    # #矩阵中用户总数  矩阵列 、行
    sum_users = np.size(ru_mat,1)
    sum_movies = np.size(ru_mat ,0)
    #保存平均评分
    aver_lis = []
    for i in range(sum_movies):
        movie_averpoint = sum(ru_mat[i])/sum_users
        aver_lis.append(movie_averpoint)
    #movies_data增加一列,最终输出 电影ID 、 电影标题 、 电影类别 、 电影平均评分
    movies_data['averpoint'] = aver_lis[:3883]
    #根据movies_data查找具有该类别的电影,再根据平均评分排序截取前十数据返回
    #先查找电影名称、再查找电影类型,均无则返回错误告知
    if title_flag == 'y'  or title_flag=='Y':
        reco_movies = movies_data[movies_data['title'].str.contains(pat=name)]
        
    else :
        reco_movies = movies_data[movies_data['genres'].str.contains(pat=name)]
        
    reco_movies=reco_movies.sort_values(by = 'averpoint' , ascending =False)[:10]
    return reco_movies

基于电影名称关键字的推荐模块

推荐效果如图所示:基于电影名称关键字的推荐模块效果展示

基于电影类型搜索的推荐模块

推荐效果如图所示:电影类型推荐流程图

新用户电影推荐模块

设计思路如图所示:新用户推荐模块

新用户推荐模块代码

def new_userreco(users_data , loginID):
    gender_map = {'F':0 , 'M':1}
    users_data['gender'] = users_data['gender'].map(gender_map)
    users_data = users_data.drop(['zip-code'],axis=1)
    users_mat = users_data.iloc[:,:].values
    users_mat=preprocessing.scale(users_mat)
    #构造相似度矩阵
    newuser_similar = cosine_similarity(users_mat , dense_output=True)   
    NU_silimar_df = pd.DataFrame(newuser_similar)
    #截取该用户与其他用户的相似度
    user_similar=NU_silimar_df.iloc[loginID-1]
    #user_similar中的列名称即为用户ID-1 , 先获取用户ID-1
    user_similar_dict = user_similar.to_dict()
    #与logID用户相似的用户存放字典中,再根据字典中的value值排序取其前三
    user_similar_dict = sorted(user_similar_dict.items() , key=lambda x: x[1] , reverse=True )[1:4]
    #记录当下推荐的电影数
    sum_recmovies = 0
    #新建推荐电影ID空字典
    rec_moviesID  = []
    for x in range(3):
        #获取的是相似用户ID-1
        similar_userID = user_similar_dict[x][0]
        #当下第i个相似用户的相似度
        simila = user_similar_dict[x][1]
        #在用户评分矩阵中,该相似用户所在的行就是相似用户ID-1=similar_userID
        similuser_rating = UR_mat[similar_userID]
        #推荐10部电影,从当前第i个相似用户的用户评分表中找到看过的电影,并计算电影推荐得分,公式:用户相似度*0.7+电影评分*0.3
        for i in range(len(similuser_rating)):
            if sum_recmovies < 10:
                #矩阵是从0开始,真实movieID要加1
                if similuser_rating[i] != 0:
                    score = simila*0.7+0.3*similuser_rating[i]
                    rec_moviesID.append(([i+1] , score))
                    sum_recmovies +=1
                    #每次加入新推荐电影,就对字典排序,为推荐电影满10个时插入更高评分的电影准备
                    rec_moviesID =  sorted(rec_moviesID, key=lambda x: x[1] , reverse=True )
            else:
                if similuser_rating[i] != 0:
                    score = simila*0.7+0.3*similuser_rating[i]
                    if rec_moviesID[9][1] < score:
                        #删除字典中电影得分最少的元组,加入评分更高的元组,使推荐电影的评分在所有的电影中都是最高的
                        rec_moviesID[9]=([i+1] , score)
                        rec_moviesID = sorted(rec_moviesID , key=lambda x: x[1] , reverse=True )
    #以上推荐的10部电影ID获取完成
    #根据推荐电影ID获取电影详细信息存储,并输出
    rec_movies = []
    for i in range(len(rec_moviesID)):
        moviesID = rec_moviesID[i][0]
        moviesID =moviesID[0]-1
        rec_movie = list(movies_dat.iloc[moviesID]) 
        rec_movies.append(rec_movie)     # 电影信息
        #添加电影评分
        col = UR_mat[[moviesID]]
        rec_movie.append( col[(0<col)].mean() )
    rec_movies=rec_movies[:10]
    rec_movies = sorted(rec_movies , key=lambda x: x[3] , reverse=True )
    return   rec_movies

新用户推荐效果

新用户电影推荐效果图

老用户电影推荐模块

最初基于协同过滤思想设计思路如图所示
老用户电影推荐模块

基于SVD算法的程序流程图
基于SVD算法的程序流程图

老用户推荐模块代码

这里是自己写的协同过滤的算法,推荐效果不高,只建议参考不建议使用。

def login_reco(UR_mat,movies_dat ,loginID):
    UR_mat = preprocessing.scale(UR_mat)
    #构造相似度矩阵
    similar_mat = cosine_similarity(UR_mat , dense_output=True)
    # print(similar_mat)
    silimar_df = pd.DataFrame(similar_mat)
    #截取该用户与其他用户的相似度
    user_similar=silimar_df.iloc[loginID-1]
    #user_similar中的列名称即为用户ID-1 , 先获取用户ID-1
    user_similar_dict = user_similar.to_dict()
    #与logID用户相似的用户存放字典中,再根据字典中的value值排序取其前三
    user_similar_dict = sorted(user_similar_dict.items() , key=lambda x: x[1] , reverse=True )[1:2] 
    simlar_userID = [user[0]   for user in user_similar_dict  ] #这里获取的只是用户所在行数,真实用户ID还要加一  
    alluser_watch = []
    reco_movies = [ ]
    for ID in simlar_userID:
        user_line=UR_mat[ID]
        for i in range(len(user_line)):
            if user_line[i] != 0:
                alluser_watch.append(i)#这里获取只是电影所在列,真实电影ID还要加一     
    result = Counter(alluser_watch).most_common(10) #[:10]
    rec_moviesID = [x[0]+1 for x in result]
    for id in rec_moviesID:
        reco_movies.append(list(movies_dat.iloc[id]))
    return rec_moviesID ,reco_movies #返回的是真实的电影ID

这是我参考surprise官网写的代码,推荐效果在文章末尾数据可视化中可见。

def SVD_rec( ):
    # 指定文件所在路径
    file_path = os.path.expanduser('ratings.csv')
    # 告诉文本阅读器,文本的格式是怎么样的
    reader = Reader(line_format='user item rating', sep=',')
    # 加载数据
    data = Dataset.load_from_file(file_path, reader=reader)
    #十折交叉验证,n_splits=n 就是n折
    kf = KFold(n_splits=10)
    algo = SVD()
    #十组测试集中每组的平均准确率
    accuracy = []
    recall = []
    #将原始数据分成10等分,进行十折交叉验证
    for trainset, testset in kf.split(data):
        print('----------------------')
        #对训练集使用SVD算法进行训练
        algo.fit(trainset)
        #使用训练好的SVD算法获得测试集的预测值
        predictions = algo.test(testset)
        #计算该组的所有测试集用户的准确率和召回率
        precisions, recalls = precision_recall_at_k(predictions, k=5, threshold=4)
        # 计算测试集的平均准确率和召回率
        accuracy.append( sum(prec for prec in precisions.values()) / len(precisions) )
        recall.append( sum(rec for rec in recalls.values()) / len(recalls) )
    return accuracy ,recall

def precision_recall_at_k(predictions, k=10, threshold=3.5):
    '''返回为每个用户推荐k个物品时的准确率和召回率。'''
    # 首先将预测值映射至每个用户
    user_est_true = defaultdict(list)
    for uid, _, true_r, est, _ in predictions:
        user_est_true[uid].append((est, true_r))
    precisions = dict()
    recalls = dict()
    for uid, user_ratings in user_est_true.items():
        # Sort user ratings by estimated value
        user_ratings.sort(key=lambda x: x[0], reverse=True)
        # Number of relevant items
        n_rel = sum((true_r >= threshold) for (_, true_r) in user_ratings)
        # Number of recommended items in top k
        n_rec_k = sum((est >= threshold) for (est, _) in user_ratings[:k])
        # Number of relevant and recommended items in top k
        n_rel_and_rec_k = sum(((true_r >= threshold) and (est >= threshold))
                              for (est, true_r) in user_ratings[:k])
        # Precision@K: Proportion of recommended items that are relevant
        precisions[uid] = n_rel_and_rec_k / n_rec_k if n_rec_k != 0 else 1
        # Recall@K: Proportion of relevant items that are recommended
        recalls[uid] = n_rel_and_rec_k / n_rel if n_rel != 0 else 1
    return precisions, recalls


老用户推荐效果

推荐效果如图所示:
老用户电影推荐效果

数据可视化

用户评分相似度矩阵可视化

用户评分相似度矩阵可视化
相似矩阵可视化代码

        fig , ax = plt.subplots(1,1)
        ax.set_title('相似度矩阵可视化' , fontsize = 12)
        ax.matshow(UR_mat)
        plt.show()

准确率与召回率可视化(SVD)

十折交叉验证准确率召回率
召回率可视化代码

#画图
def show_acc_recall(accuracy ,  recall):
    #准确率和召回率折线图
    plt.plot(accuracy, marker = 'o', label = '准确率')
    plt.plot(recall, marker = 'x', label = '召回率')
    plt.xlabel('测试集')
    plt.ylabel('百分比')
    plt.title('准确率和召回率折线图')
    plt.legend()
    plt.show()
    #十折交叉验证的平均准确率
    name_list = ['平均准确率' , '平均召回率']  
    num_list = [sum(accuracy)/len(accuracy) , sum(recall)/len(recall)]  
    plt.bar(range(len(num_list)), num_list,color='rgb',tick_label=name_list)  
    plt.show()  

accuracy , recall =SVD_rec()
show_acc_recall(accuracy , recall)
平均准确率与召回率(SVD)

平均准确率与召回率

总结

你又在写bug啊???

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值