[猜你喜欢]冠军“yes,boy!”分享,含竞赛源代
DataCastle运营 发表于 2016-7-20 17:31:52
我是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微信公众号
[猜你喜欢]冠军“yes,boy!”分享,含竞赛源代的更多相关文章
- “猜你喜欢”是怎么猜中你心思的?
文/Joseph A. Konstan & John Riedl)如今,到网上购物的人已经习惯了收到系统为他们做出的个性化推荐.Netflix 会推荐你可能会喜欢看的视频.TiVo 会自动把节 ...
- 图像显示 imshow()[OpenCV 笔记5]
void imshow(const string& winname InputArray mat); winname 窗口表识名称 mat 需要显示的图像.InputArray类型,声明如下 ...
- 仿即刻app"猜你喜欢"切换控件
最近在即刻里看到即刻的"猜你喜欢"的板块,觉得效果很赞. 当点击"换一换"时,上面三个条目程序切换效果,并且三个条目的切换以不同的速度进行. 于是开始想办法撸出 ...
- “猜你喜欢”的背后揭秘--10分钟教你用Python打造推荐系统
欲直接下载代码文件,关注我们的公众号哦!查看历史消息即可! 话说,最近的瓜实在有点多,从我科校友李雨桐怒锤某男.陈羽凡吸毒被捕.蒋劲夫家暴的三连瓜,到不知知网翟博士,再到邓紫棋解约蜂鸟.王思聪花千芳隔 ...
- [saiku] 源码整合[普通WEB项目]
saiku源码的整合分为[普通web项目整合]和[maven整合]两种 本节主要是讲解如何整合为普通的web项目 转载自:http://blog.csdn.net/gsying1474/article ...
- [网站性能1]对.net系统架构改造的一点经验和教训
文章来源:http://www.admin10000.com/document/2111.html 在互联网行业,基于Unix/Linux的网站系统架构毫无疑问是当今主流的架构解决方案,这不仅仅是因为 ...
- Linux rpm 命令参数使用详解[介绍和应用]
RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序” rpm 执行安装包 二进制包(Binary)以及源代码包(Source)两 ...
- Linux rpm 命令参数使用详解[介绍和应用](转)
RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序” rpm 执行安装包二进制包(Binary)以及源代码包(Source)两种 ...
- Linux rpm 命令参数使用详解[介绍和应用](转)
RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序” rpm 执行安装包二进制包(Binary)以及源代码包(Source)两种 ...
随机推荐
- hdu1937 二维尺取
/* 二维上的尺取,外层循环枚举j轴上的可能,内层在i轴上尺取即可 O(N^3) */ #include<iostream> #include<cstdio> #include ...
- python 全栈开发,Day43(引子,协程介绍,Greenlet模块,Gevent模块,Gevent之同步与异步)
昨日内容回顾 I/O模型,面试会问到I/O操作,不占用CPU.它内部有一个专门的处理I/O模块.print和写log 属于I/O操作,它不占用CPU 线程GIL保证一个进程中的多个线程在同一时刻只有一 ...
- 【C++ Primer 第13章】6.对象移动
右值引用 左值和右值 (1)两者区别: ①左值:能对表达式取地址.或具名对象/变量.一般指表达式结束后依然存在的持久对象. ②右值:不能对表达式取地址,或匿名对象.一般指表达式结束就不再存在的临时对象 ...
- python学习之条件语句(if循环)
Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块.可以通过下图来简单了解条件语句的执行过程: Python程序语言指定任何非0和非空(null)值为tru ...
- python全栈开发day16-正则表达式和re模块
1.昨日内容回顾 2.正则表达式(re模块是python中和正则表达式相关的模块) 1.作用 1).输入字符串是否符合匹配条件 2).从大段文字中匹配出符合条件的内容 2.字符组 [0-9a-zA-Z ...
- User Agent 设置
感谢版主回复,版主贴的方法网上到处都是,我试了很多次都是不行的,有用的方法都几乎这个到处转贴的信息淹没了. 今天我总算在一个博客找了到可行的方法,转过来和大家分享 Windows Registry E ...
- python中的协程:greenlet和gevent
python中的协程:greenlet和gevent 协程是一中多任务实现方式,它不需要多个进程或线程就可以实现多任务. 1.通过yield实现协程: 代码: import time def A(): ...
- java实现判断一个经纬度坐标是否在一个多边形内(经自己亲测)
1.在高德地图上绘制的多边形:经纬度逗号分隔格式:上面是用来方便存坐标的对象:下面是方法测试:直接复制代码即可运行 public class Point { private Double x; pri ...
- Color the ball HDU1556
这题整整debug了两个小时 不同组居然要初始化 本以为built函数里面已经初始化好了!!!!! 其他无需注意 #include<cstdio> #include<cstring ...
- 用mybatis的代码自动生成工具,炒鸡好用,推荐一下别人的操作
http://www.cnblogs.com/smileberry/p/4145872.html