zoukankan      html  css  js  c++  java
  • 基于简单的协同过滤和网状推荐系统的实现(python和moviellens数据集)

      本文的理论基础是上篇我看的《个性化推荐系统的研究进展》(周涛等,2009),这是一篇计较老的综述了,所以里面的方法也比较老:),但是练手不错,最起码实现没那么头疼,主要是电脑能相对的运算xD。原理在我的另一个思维导图里已经有了,在此不再赘述,直接上代码:(本代码力求实现,所以还没有优化,目前采用串行计算,有时间的话会优化):

      结果:

      a.推荐数为100时,基于网络结构的r因子为0.4左右,实际上是非常大的,但是不是说明模型的效果不是特别好,因为:;

        (1):我将用户的度或者电影的度为零时依然参与了构建,冷启动拉低了效率

        (2):度大的用户或者电影的影响力过于巨大;

        (3):与协同过滤一样的问题,关联矩阵稀疏

      b..按照我的误差函数,最后协同过滤结果为-2.6,也就是说推荐系统推测出来的评分值与用户本身评分差距平均值为2.8分(满分5分),误差看来很大,实则因为:

        (1):存在孤立点,或者说是新用户、新电影,这种冷启动是协同过滤无法处理的;

        (2):关联矩阵十分稀疏,而且数据集中的打分多是4、5分,在拉低后差距愈发明显。

      0,提示:

      (1)虽然没有用并行算法,但是可以考虑用jit加速。

      (2)而且很重要的一点是,关联矩阵非常稀疏,要考虑度为零,相似性为零等情况,不然老会得不到结果。

      (3)本实例中用的是6040x3952(用户,电影)的数据包。

      (4)基于网络模型采用综述里的评价指标,协同过滤我用的是推荐打分值与原有打分值的差值平均值(原来没有打分的差值为0)

      1,用户与产品的关联矩阵、度、评分矩阵的写入:

    def GetData():
        user_vec = np.zeros((6040,3952))
        user_rating = np.zeros((6040,3952))
        degree_user = np.zeros((1,6040))
        degree_mov = np.zeros((1,3952))
    
        rating = np.loadtxt("rating.txt",dtype = np.str,delimiter= "::")
        #print(rating[:2,:3])
        data = np.array(rating[0:,:3],dtype=np.int)
    
    
        #写入用户向量和用户、电影的度
        for i in range(0,1000209):
            if data[i][2] > 3:
                user_vec[int(data[i][0])-1][int(data[i][1])-1] = 1
                degree_user[0][data[i][0] - 1] = int(degree_user[0][data[i][0] - 1] +1)
                degree_mov[0][data[i][1] - 1] = int(degree_mov[0][data[i][1] - 1] + 1)
            if data[i][2]:
                user_rating[int(data[i][0]) - 1][int(data[i][1]) - 1] = int(data[i][2])
        return user_vec,user_rating,degree_user,degree_mov

      2,相似度计算:

    def SimCom(user_vec):
        #相似度矩阵
        user_sim = np.zeros((6040, 6040))
        for i in range(0,6040):
            for j in range(i+1,6039):
                user_sim[i][j] = float(np.dot(user_vec[i], user_vec[j]) / (np.linalg.norm(user_vec[i]) * np.linalg.norm(user_vec[i])))
                user_sim[i][j] = round(user_sim[i][j],3)
            user_sim[i][i] = 1
            if i %10 ==0 :
                print(i)
    
        for i in range(1,6040):
            for j in range(0,i):
                user_sim[i][j] =user_sim[j][i]
        return user_sim

      3,新的评分矩阵生成:

    #新的评分矩阵
    '''
    在此不考虑新打分矩阵出现后需要迭代运算
    '''
    @jit
    def RecS_rating(user_sim,user_rating,user_vec):
        user_NewRating = np.zeros((6040,3952))
        for i in range(0,6040):
            all_sim = np.sum(user_sim[i])
            degree = np.sum(user_vec[i])
            if degree != 0:
                rating_avg= np.sum(user_rating[i]) / degree
            else:
                rating_avg= 0
            for j in range(0,3952):
                for k in range(0,6040):
                    user_NewRating[i][j] = user_NewRating[i][j] + user_sim[i][j] * (user_rating[k][j]-user_rating[i][j])
                if all_sim != 0:
                    user_NewRating[i][j] =int(user_NewRating[i][j]/all_sim+ rating_avg)
                else:
                    user_NewRating [i][j] = 5
                if user_NewRating[i][j] > 5:
                    user_NewRating[i][j] = 5
                if user_NewRating[i][j] <0:
                    user_NewRating[i][j] = 0
                    # 显示计算到哪
            if i % 100 == 0:
                print(i)
        return  user_NewRating

      4,资源分配矩阵生成:

    #资源配额矩阵
    def Wight_mov(degree_user,degree_mov,user_vec):
        w_mov = np.zeros((3952, 3952))
        for i in range(0,3952):
            for j in range(0,3952):
                w_mov[i][j] = 0
                for k in range(0,6040):
                    if degree_user[0][k] == 0:
                        continue
                    w_mov[i][j] = w_mov[i][j] + (user_vec[k][i]*user_vec[k][j])/degree_user[0][k]
                if degree_mov[0][j] == 0:
                    w_mov[i][j] = 0
                    continue
                w_mov[i][j] = round(w_mov[i][j] / degree_mov[0][j],4)
                #print(j)
            #显示计算到哪
            if i %100 ==0 :
                print(i)
        return w_mov

      5,Top-K(用于选择推荐值或者评分最高的K个值)矩阵:

    
    
    #top-K排序
    def partition_arg_topK(matrix1, K):
        matrix = 1.- matrix1
        a_part = np.argpartition(matrix, K, axis=1)
        column_index = np.arange(matrix.shape[0])[:, None]
        a_sec_argsort_K = np.argsort(matrix[column_index, a_part[:, 0:K]], axis=1)
        return a_part[:, 0:K][column_index, a_sec_argsort_K]

      6,输出参数(以协同过滤为例子:评判标准定为用户已选产品新老评分之差,再取平均)而网状则不同,方法详见另一个思维导图:

       6.1 协同过滤

    def Result_corr(user_vec,user_rating,new_rating):
    correct_corr = np.zeros((1,6040))
    sum_valid = 0
    for i in range(0,6040):
    degree = np.sum(user_vec[i])
    for k in range(0, 3952):
    if user_vec[i][k] == 1:
    correct_corr[0][i] =correct_corr[0][i]+ (new_rating[i][k] - user_rating[i][k])
    if degree != 0:
    correct_corr[0][i] = correct_corr[0][i] / degree
    sum_valid = sum_valid+1
    else:
    correct_corr[0][i]= 0
    #print(degree)
    #print(sum_valid)
    #print(np.sum(correct_corr) / sum_valid)
    return correct_corr

       6.2基于网络

    def Result_net(topK,user_vec):
        correct_net= np.zeros(6040)
        for i in range(0,6040):
            find_in = 0
            for k in range(0,3952):
                #计算推荐指数
                for j in range(0,50):
                    if user_vec[i][k] == 1 and (k in topK[i]) and topK[i][j] == k:
                         correct_net[i] = correct_net[i]+((j+1)/50)
                         find_in = find_in + 1
                         break
            if find_in !=0:
                correct_net[i] = round(correct_net[i] / find_in,3)
            else:
                correct_net[i] = 1
            print(i)
        return correct_net

      7,一些中间数据结果:

    资源分配矩阵:

         算了,数据多,不在此展示了:)

  • 相关阅读:
    JavaScript的离线存储——localStorage、sessionStorage以及cookie
    for循环中的异步处理(异步变同步)
    pc网站随鼠标滚动动态出现效果
    layui tab选项卡Hash地址的定位和跳转到指定tab栏
    scroll滚动监听实现animate返回顶部(有坑)
    Vue之使用elementUI的upload上传组件导入csv文件
    element+sortablejs插件实现拖拽排序效果
    超简单的jq图片上传
    取字符串中的汉字的俩种方式
    js获得url地址携带参数
  • 原文地址:https://www.cnblogs.com/panern/p/12427831.html
Copyright © 2011-2022 走看看