zoukankan      html  css  js  c++  java
  • [猜你喜欢]冠军“yes,boy!”分享,含竞赛源代

    [猜你喜欢]冠军“yes,boy!”分享,含竞赛源代码 

    DataCastle运营 发表于 2016-7-20 17:31:52

     
     
     844  3  5

    我是Yes,boy! ,来自东北大学计算机学院。在猜你喜欢推荐系统竞赛中,很幸运取得第一名的成绩,下面我简单介绍下我的思路。

    本次比赛的赛题背景是给出了约3400万条数据,包含一个商品网站站内顾客在某一时刻对某一个商品的打分值,分值范围为1至5分。目的是通过对这些数据的学习和训练,准确预测某时刻某个用户对某个未评分商品的评分。

    通过背景可知这是一个关于推荐系统的研究问题。而推荐系统在预测准确度上有不同的研究方向,一种是基于TopN的研究,即主要是给用户一个个性化的推荐列表,一般通过准确率度量推荐的优劣;一种是基于评分预测的研究, 它的度量方式一般是RMSE或者MAE 。在本次比赛就是通过RMSE来评价预测的好坏。那么我们接下来要使用的方法就集中在优化评分预测的RMSE上。

    在具体做的过程中,我觉得有几点需要注意的:

    1. 分析数据,了解数据的大致规律

    2. 方法先尝试简单方法,再尝试复杂方法;对复杂方法,要一点点的调整

    基于此,我由简及繁使用了三类模型:

    1. 基于聚类的推荐

    2. 基于协同过滤的推荐

    3. 基于模型学习的推荐

    在这三类中,每一类又包含很多方法,不能绝对的说哪一类模型最好,依照具体的数据形式、数据内容而定。

    第一类的模型我大概使用到的方法:

    1. 全局均值

    2. 物品均值

    3. 用户均值

    4. 用户分类-物品均值

    5. 物品分类-用户均值

    6. 用户活跃度

    7. 物品活跃度

    8. 改进的用户活跃度

    9. 改进的物品活跃度

    这类模型的共同特征是通过设计聚类方法来对用户和物品分类,利用同类用户对同类物品的评分均值来预测用户对物品的评分。另外通过该模型的实现对用户和商品的特征有一个基本的了解。

    下面是其中一种方法(用户分类-物品均值)的代码:

    import pandas as pd
    
    import numpy as np
    
    
    
    train = pd.read_csv('data/train.csv')
    
    test = pd.read_csv('data/test.csv')
    
    
    
    rate_rank = train.groupby('uid').mean().loc[:,['score']].iloc[:,-1]
    
    rate_rank=pd.DataFrame(np.int32((rate_rank*2).values),index=rate_rank.index,columns=['group'])
    
    rate_rank_des = rate_rank.reset_index()
    
    
    
    train_plus = pd.merge(train,rate_rank_des,how='left',on='uid')
    
    test_plus = pd.merge(test,rate_rank_des,how='left',on='uid')
    
    res = train_plus.groupby(['iid','group']).mean().reset_index().loc[:,['iid','group','score']]
    
    result6 = pd.merge(test_plus,res,how='left',on=['iid','group']).filllna(3.0)
    
    

    第二类的模型我主要使用的方法是基于物品的协同过滤,它的核心思想是当预测用户对一个物品评分时,主要考虑与该物品最相似且用户已打过分的若干物品。所以在这其中相似度的度量方法尤其重要,包括欧氏距离、皮尔逊相似度度量、余弦相似度度量、改进的余弦相似度度量。(之所以不使用基于用户的协同过滤,是由于建立用户-用户的相似度矩阵过于巨大)

    实现代码见  similarity.py 和Model4.py 

     similarity.py

    # -*- coding: utf-8 -*-
    """
    Created on Sun Jul 03 15:14:55 2016
    
    @author: Yes,boy!
    """
    
    import pandas as pd
    import numpy as np
    
    
    
    rate_mat = pd.read_csv('data/rate_mat.csv',index_col=0).values
    
    
    def cosine_sim(i,j):
        a = rate_mat[:,i]
        b = rate_mat[:,j]
        m = np.dot(a,b)
        n = np.sqrt(np.dot(a,a)*np.dot(b,b))
        return  m/float(n)
    
    def cosine_sim_s(i,j):
        a = rate_mat[:,i]
        b = rate_mat[:,j]    
        intersection = a*b
        if intersection[intersection!=0].size==0:
            return 0.0
        
        c = a[a!=0]#评价物品i的所有用户评分
        d = b[b!=0]    
        p = np.mean(c)#物品i的所有用户评分均值
        q = np.mean(d)
        
        m = np.dot(a[intersection!=0]-p,b[intersection!=0]-q)
        n = np.sqrt(np.dot(c-p,c-p)*np.dot(d-q,d-q))
        if n==0:
            return 0.0
        return m/float(n)
    
    def pearson(i,j):
        a = rate_mat[:,i]
        b = rate_mat[:,j]    
        intersection = a*b
        if intersection[intersection!=0].size==0:
            return 0.0
        
        c = a[intersection!=0]#评价物品i的公共用户评分
        d = b[intersection!=0]  
        p = np.mean(a[a!=0])#物品i的所有用户评分均值
        q = np.mean(b[b!=0])
        
        m = np.dot(c-p,d-q)
        n = np.sqrt(np.dot(c-p,c-p)*np.dot(d-q,d-q))
        if n==0:
            return 0.0
        return m/float(n)
    
    
    rate_cos = np.zeros((14620,14620))
    rate_cos_s = np.zeros((14620,14620))
    rate_pearson = np.zeros((14620,14620))
    
    
    for i in range(14620):
        for j in range(14620):
            if i==j:
                rate_cos[i,j]=1
            elif rate_cos[j,i]!=0:
                rate_cos[i,j]=rate_cos[j,i]
            else:
                rate_cos[i,j]=cosine_sim(i,j)
    
    for i in range(14620):
        for j in range(14620):
            if i==j:
                rate_cos_s[i,j]=1
            elif rate_cos_s[j,i]!=0:
                rate_cos_s[i,j]=rate_cos_s[j,i]
            else:
                rate_cos_s[i,j]=cosine_sim_s(i,j)
                
    for i in range(14620):
        for j in range(14620):
            if i==j:
                rate_pearson[i,j]=1
            elif rate_pearson[j,i]!=0:
                rate_pearson[i,j]=rate_pearson[j,i]
            else:
                rate_pearson[i,j]=pearson(i,j)
    
    iid_index = pd.read_csv('data/rate_mat.csv',index_col=0).columns
    pd.DataFrame(rate_cos,index=iid_index,columns=iid_index).to_csv('data/rate_cos.csv')
    pd.DataFrame(rate_cos_s,index=iid_index,columns=iid_index).to_csv('data/rate_cos_s.csv')
    pd.DataFrame(rate_pearson,index=iid_index,columns=iid_index).to_csv('data/rate_pearson.csv')
       

    Model4.py 

    # -*- coding: utf-8 -*-
    """
    Created on Tue Jul 19 20:16:24 2016
    
    @author: Yes,boy!
    """
    
    import pandas as pd
    import numpy as np
    
    test = pd.read_csv('data/test.csv')
    test_arr = test.values.copy()
    
    rate_mat = pd.read_csv('data/rate_mat.csv',index_col=0)        #用户-物品评分矩阵 
    rate_cos = pd.read_csv('data/rate_cos.csv',index_col=0)        #基于余弦相似度的物品评分矩阵 
    rate_cos_s = pd.read_csv('data/rate_cos_s.csv',index_col=0)    #基于改进余弦相似度的物品评分矩阵
    rate_pearson = pd.read_csv('data/rate_pearson.csv',index_col=0)#基于皮尔逊相关系数的物品评分矩阵
    
    rate_mat = rate_mat.rename(columns=dict(zip(rate_mat.columns,[int(i) for i in rate_mat.columns])))
    rate_cos = rate_cos.rename(columns=dict(zip(rate_cos.columns,[int(i) for i in rate_cos.columns])))
    rate_cos_s = rate_cos_s.rename(columns=dict(zip(rate_cos_s.columns,[int(i) for i in rate_cos_s.columns])))
    rate_pearson = rate_pearson.rename(columns=dict(zip(rate_pearson.columns,[int(i) for i in rate_pearson.columns])))
    
    iid_index = rate_mat.columns
    
    def Recommendation_s(uid,iid,k=10,iid_iid_sim=rate_cos_s):
        score = 0
        weight = 0
        iid_sim = iid_iid_sim.loc[iid,:].values      #商品iid 对所有商品的相似度    
        uid_action = rate_mat.loc[uid,:].values      #用户uid 对所有商品的行为评分  
        iid_action = rate_mat.loc[:,iid].values      #物品iid 得到的所有用户评分   
        sim_indexs = np.argsort(iid_sim)[-(k+1):-1]  #最相似的k个物品的index
        
        
        iid_i_mean = np.sum(iid_action)/iid_action[iid_action!=0].size
        for j in sim_indexs :
            if uid_action[j]!=0:
                iid_j_action = rate_mat.values[:,j]
                iid_j_mean = np.sum(iid_j_action)/iid_j_action[iid_j_action!=0].size
                score += iid_sim[j]*(uid_action[j]-iid_j_mean)
                weight += abs(iid_sim[j])
                
        if weight==0:  
            return iid_i_mean #可以再改进!
        else:
            return iid_i_mean + score/float(weight)
    
    def Recommendation(uid,iid,k=10,iid_iid_sim=rate_cos_s):
        score = 0
        weight = 0
        iid_sim = iid_iid_sim.loc[iid,:].values      #商品iid 对所有商品的相似度
        uid_action = rate_mat.loc[uid,:].values      #用户uid 对所有商品的行为评分
        iid_action = rate_mat.loc[:,iid].values      #物品iid 得到的所有用户评分
        sim_indexs = np.argsort(iid_sim)[-(k+1):-1]  #最相似的k个物品的index
        
        
        iid_mean = np.sum(iid_action)/iid_action[iid_action!=0].size
        for j in sim_indexs :
            if uid_action[j]!=0:
                score += iid_sim[j]*(uid_action[j]-iid_mean)
                weight += abs(iid_sim[j])
                
        if weight==0:  
            return iid_mean #可以再改进!
        else:
            return iid_mean + score/float(weight)
    
    Num = len(test)
    result = np.zeros(Num)
    def pred(k,iid_iid_sim):
        count =0
        for i in range(Num):
            a=test_arr[i,0]
            b=test_arr[i,1]
            if b not in iid_index:
                result[i]=3
                count = count +1
            else:
                result[i] = Recommendation_s(a,b,k,iid_iid_sim)
        print 'count:',count
    
    

    第三类的模型使用的方法有:

    1. SVD

    2. NMF

    3. RSVD

    4. SVD++

    5. SVDfeature

    6. Libmf

    7. Libfm

    这一类模型的共同特点是矩阵分解。即对用户-物品评分矩阵分解成若干个小矩阵,目的是分解之后的矩阵乘积接近原始矩阵,于是也实现了对原始矩阵为空的值的预测。在这些方法中,比较重要的几个参数有:隐特征个数,随机梯度下降中的学习率,正则化参数,总迭代次数等。具体在每个方法中这些参数的最优值也不尽相同。

    具体介绍其中两个在本赛题上表现最好的模型:svdfeature 和 libfm 

    Svdfeature 是一个feature-based协同过滤和排序工具,由陈天启所在的上海交大Apex实验室开发,大名鼎鼎的xgboost 同样来自于他们。里面能够方便实现svd ,svd++ 等方法。在使用过程中,步骤如下:

    1. 数据预处理:用户和物品的id 不是连续的,需要进行重新的映射,转换为从1至用户/物品个数这样的连续取值。

    2. 数据格式转换:要转换为模型要求的格式

    3. 为了存储空间和计算速度,最好再转换为二进制形式

    4. 设置各类参数。

    5. 预测

    在主要参数如下设置的情况下,线上得分能达到7.86

    base_score = 3 全局偏置

    learning_rate = 0.005 学习率

    wd_item ,wd_user =0.004 正则化参数

    num_factor =4000 隐含特征个数

    LibFM是专门用于矩阵分解的利器,尤其是其中实现了MCMC(Markov Chain Monte Carlo)优化算法,比常见的SGD优化方法精度要高,但运算速度要慢一些。LibFM中还实现了SGD、SGDA(Adaptive SGD)、ALS(Alternating Least Squares)等算法。

    在这里面,也有很多参数和方法可以灵活设置,比如-dim 维度,-iter 迭代次数,-learning_rate 学习率,-method 优化方法,-task 任务类型,-validation验证集,-regular 正则化参数。

    数据处理方式和上面类似,主要设置参数如下,这个方法的最优线上结果是7.88

    -iter 100 –dim 1,1,64 –method MCMC –task –r 

    除此以外, libmf 模型效果也不错,经过优化之后结果能达到7.85 。而基于scipy 的svd 和基于sklearn的NMF在小数据集上效果很好,数据量特别大的情况下效果不理想,也可能是我调参和优化不够好的问题。

    关于融合

    一种是采用联级融合,即使一种模型的预测结果作为下一个模型的输入,不过要同时调整下一个模型的目标函数。另外一种方法是模型加权融合,最简单的是线性融合,通过各个模型在验证集的结果和超参数优化方法Hyperopt 找到最佳的融合系数,然后在线上使用这些融合系数进行融合。

    关于改进

    1)时间因素

    时间的因素我一直没有使用,后来我读到过有在svd++ 中加入时间因素的资料,预计加入后能够提升模型效果。

    2)预测值取整

    现在我的模型对每个用户的预测值绝大部分不是整数,而真实值却是整数,将预测之后的值转换成整型对结果会有提升。不过会存在转换正确和错误的问题,我之前方法比较简单,提升的幅度非常小。 

    由于比赛期间没有把思路整理成文档,赛后才开始总结自己的思路,有写的不明白的地方大家都可以提出来,然后在讨论中相互启发。

    了解更多竞赛即时信息,关注DC微信公众号

  • 相关阅读:
    LeetCode 1275. 找出井字棋的获胜者 Find Winner on a Tic Tac Toe Game
    LeetCode 307. 区域和检索
    LeetCode 1271 十六进制魔术数字 Hexspeak
    秋实大哥与花 线段树模板
    AcWing 835. Trie字符串统计
    Leetcode 216. 组合总和 III
    Mybatis 示例之 复杂(complex)属性(property)
    Mybatis 示例之 复杂(complex)属性(property)
    Mybatis 高级结果映射 ResultMap Association Collection
    Mybatis 高级结果映射 ResultMap Association Collection
  • 原文地址:https://www.cnblogs.com/zhizhan/p/5941480.html
Copyright © 2011-2022 走看看