[猜你喜欢]冠军 yes,boy! 分享 | 推荐系统也可以很简单 做个记录 以后方便学习

DC

关于分享

无论是充满“魔性”的“yes,boy!”这个昵称,还是诸神无法超越的7.89645分,都让这次[猜你喜欢]冠军充满了神秘色彩,最关键的是,“yes,boy!”从未在技术群的讨论中露面。他是用怎样的方法把竞赛的成绩做到最好,这其中有哪些不为人知的细节?“yes,boy!”亲自操刀,分享他对推荐系统的理解。


01

关于竞赛


DC的小伙伴们大家好,我是Yes,boy! ,来自东北大学计算机学院。在猜你喜欢推荐系统竞赛中,很幸运以7.89465的得分取得第一名,看到大家在群里对推荐系统的热情很高,所以在这里我简单介绍下竞赛中我的思路。


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


通过背景可知这是一个关于推荐系统的研究问题。而推荐系统在预测准确度上有不同的研究方向,一种是基于TopN的研究,即主要是给用户一个个性化的推荐列表,一般通过准确率度量推荐的优劣;一种是基于评分预测的研究,它的度量方式一般是RMSE或者MAE 


在本次比赛就是通过RMSE来评价预测的好坏。那么我们接下来要使用的方法就集中在优化评分预测的RMSE上。




02

竞赛思路


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

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

  2. 方法先尝试简单方法,再尝试复杂方法;

  3. 对复杂方法,要一点点的调整。


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

  1. 基于聚类的推荐

  2. 基于协同过滤的推荐

  3. 基于模型学习的推荐


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


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

  1.   全局均值

  2.   物品均值

  3.   用户均值

  4.   用户分类-物品均值

  5.   物品分类-用户均值

  6.   用户活跃

  7.   物品活跃度

  8.   改进的用户活跃度

  9.   改进的物品活跃度

  ………


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




03

代码实现


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


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 (附在文尾)

 


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

  1. SVD

  2. NMF

  3. RSVD

  4. SVD++

  5. SVDfeature

  6. Libmf

  7. Libfm


这一类模型的共同特点是矩阵分解。即对用户-物品评分矩阵分解成若干个小矩阵,目的是分解之后的矩阵乘积接近原始矩阵,于是也实现了对原始矩阵为空的值的预测。


在这些方法中,比较重要的几个参数有:隐特征个数,随机梯度下降中的学习率,正则化参数,总迭代次数等。具体在每个方法中这些参数的最优值也不尽相同。




04

优秀模型


具体介绍其中两个在本赛题上表现最好的模型: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是专门用于矩阵分解的利器,尤其是其中实现了MCMCMarkov Chain Monte Carlo)优化算法,比常见的SGD优化方法精度要高,但运算速度要慢一些。LibFM中还实现了SGDSGDAAdaptive SGD)、ALSAlternating 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 基于sklearnNMF在小数据集上效果很好,数据量特别大的情况下效果不理想,也可能是我调参和优化不够好的问题。

 



05

关于融合


一种是采用联级融合,即使一种模型的预测结果作为下一个模型的输入,不过要同时调整下一个模型的目标函数。


另外一种方法是模型加权融合,最简单的是线性融合,通过各个模型在验证集的结果和超参数优化方法Hyperopt 找到最佳的融合系数,然后在线上使用这些融合系数进行融合。

 



06

关于改进


1时间因素

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


2预测值取整

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

 

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




07

代码附录

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




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')




[猜你喜欢]冠军分享

- DataCastle -


原文链接 http://mp.weixin.qq.com/s?__biz=MzIwMTgwNjgyOQ==&mid=2247484128&idx=1&sn=7e7b64aa86387bba1bee69c5ef7ffd37#rd


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值