一、概念
基本概念
1、解决什么问题:信息过载
2、使命:联系用户和信息
3、区别于分类网站(2345、雅虎):覆盖面广、具有个性化
4、区别于搜索引擎(Google):主动、用户不需要提供明确需求
推荐系统的数据来源
1、用户:用户属性、用户标签、社交网络、ip地址、网络设备、位置定位
2、物品:物品属性、物品标签
3、用户 《— 行为 —》物品
推荐系统的评测指标
1、用户满意度:在线指标
2、预测准确性:离线指标
3、覆盖率
4、多样性
5、新颖性
6、惊喜度
7、信任度:是否提供推荐解释
8、实时性
9、健壮性
10、商业目标
二、基础推荐算法
ItemCF基于物品的推荐算法
案例:jim、Tom、Peter本周分别在TM餐厅点选了不同的餐品,吃完后分别做了评价如下
其中5为最满意,1为最不满意,0则是未点选未评价。
- 手撕猪肉和烤牛肉的相似度计算如下
- 手撕猪肉和鳗鱼饭的相似度计算如下
这里使用的欧式距离的计算法,如果使用余弦定理的距离算法
- 手撕猪肉和烤牛肉的相似度如下
- 手撕猪肉和鳗鱼饭的相似度如下:
如果把所有菜品之间的欧式距离相似度计算出来,结果如下:
有了这个物品间的相似度矩阵,我们就能进行推荐了。
以JIM为例,计算他对未评价的日式炸鸡排和寿司饭的潜在评价值
显然,我们知道了,相比于日式炸鸡排,我们更应该推荐给Jim寿司饭。
那如果是基于用户的推荐算法是怎么样的呢?
userCF基于用户的推荐算法
1、首先根据不同用户对相同食品的评价,计算用户之间的相似度,生成用户间的相似度矩阵
2、用户A1对B1食品的评价 = 用户A1和用户A2的相似度 * 用户A2对B1食品的评价 + 用户A1和用户A3的相似度 * 用户A3对B1食品的评价度 + 。。。。。
如下图所示:
什么时候使用itemCF,什么时候使用userCF?
这取决于计算的复杂性。
实际中,为了保证能够快速生成推荐结果,如果使用itemCF,需要预先建立并保存物品间的相似度矩阵。相反,如果使用userCF,需要预先建立并保存用户间的相似度矩阵。
所以,假设你开发了一个小说阅读器APP,在运营的初期,你可能只有1000个用户,但有10000的小说资源。那么如果你使用itemCF进行推荐,所占用的计算和存储资源是userCF的(10000^2/1000^2=100)100倍。
反过来,如果你已经拥有了类似与起点小说这样的用户规模,你的用户已经远远大于你的商品数量。那么你使用itemCF进行推荐将更为合适
Boolean问题
现实中,我们大部分情况下并不能获取到用户对物品的评价指数。通常我们只能得到非是即否的结果,即用户看了还是没看,用户吃了还是没吃,用户买了还是没买。
遇到这样的问题,怎么进行推荐呢?
假设,我们现在为一个视频网站开发推荐系统,我们已经统计了每一个用户是否观看了每一个视频,如下:(实际中,会使用特定的方法保存稀疏矩阵)
a1-a9代表不同的视频,p1-p20代表不同的用户,1代表已经观看了
首先,我们还是从建立物品间(视频)的相似度矩阵开始,计算公式如下:
最终的,相似度矩阵计算结果如下:
我们有了视频间的相似度矩阵,那么计算用户对视频的偏好程度就很简单了:
- 用户A对视频B的偏好程度 = mean(用户已看过的视频分别 * 看过视频和视频B的相似程度)
- 最终结果计算平均数,来避免结果受到用户观看视频结果的影响
- 那么,最后,我们只要将不同品类的视频选择偏好程度最高的几个(且用户没看过的)推荐给用户就可以了。
进阶算法
看完了以上的内容,是否觉得推荐算法原来就是这么简单;那么,哪些研究员们都在研究什么呢?
在现有的基础上不断的把算法升级升级升级!!!
1、升级一:降低热门视频的权重
现实中,太多热门的视频并没有什么推荐的价值,我们总希望把一些不那么热门的视频推荐给真正喜欢它的用户。
所以,当我们在计算视频间的相似度的时候,通过调整计算公式,降低了热门视频的权重:
2、升级二:减少狂热用户的权重
狂热用户就好比当当网上的书店老板,视频网站上的视频爬虫,他们会产生大量的购买和点击操作,导致其他用户和他的相似度都会很高,干扰的算法的最终结果,所以我们要降低他们的权重:
当我们在计算即观看A又观看了B视频的用户数的时候,每一个用户的贡献值不再是1了,如下图;
3、升级三:归一化
如下图显示,计算出来的相似度矩阵,看上去总是趋向于不平衡的分布的,如下图:
如果,我们将每一列的值分布除于每一列的最大值,矩阵看起来就均匀多了,也就会有更好的推荐效果:
实际案例
1、58同城:为招聘者推荐简历
本质上还是计算itemCF,计算物品间的相似度;
区别在于考虑了各种因素的权重,结合到了相似度的计算中
2、YouTube推荐算法:
我们用一个实例来说明这个推荐系统具体是如何运作的:
比如说,小明很喜欢YouTube,他有YouTube账号相关的一切。每天浏览YouTube时,他都会在浏览器登录。一旦登录,YouTube便给小明此次浏览的内容创建三个token:浏览记录、搜索记录以及关于他的统计信息。小明可能压根就不知道这三种数据的存在。
然后轮到候选生成器上场了。YouTube拿这三个token的值跟观看记录类似于小明的用户进行对比,由此筛选出小明可能会喜欢的数百个视频,过滤掉YouTube视频库中数以百万计的其他内容。
接下来,基于视频和小明的相关性,这些视频被排名算法排序。排序时该算法会考虑这样一些问题:小明有多大的可能会打开这个视频?这个视频有没有可能让小明在YouTube上打发更多时间?这个视频的新鲜程度如何?小明最近在YouTube上的活动程度如何?还有数百个其他的问题。
经过YouTube算法的读取、筛选和推荐后,排名最高的视频将被推荐给小明。之后小明看与不看的选择数据都会反馈给神经网络,以供算法后续使用。视频被点开,并吸引小明在YouTube上打发更多时间的目标,则一直持续下去。那些小明没有点开的推荐视频,等他下次登录网站时则有可能通不过候选生成器。
三、代码实现
数据源使用的是经典的ml-100k数据源
# -*- coding: utf-8 -*-
"""
Created on Sat Dec 2 23:29:55 2017
@author: shanesu
"""
import pandas as pd
import numpy as np
from pandas import DataFrame
import math
#读取数据到csv和data
def prepareDataSet():
unames=['user_id','age','gender','occupation','zip']
users=pd.read_table('ml-100k/u.user',sep='|',header=None,names=unames)
mnames = ['movie_id', 'title', 'release_data','video_release_date','URL',\
'unknown','Action','Adventure','Animation','Childrens',\
'Comedy','Crime','Documentary','Drama','Fantasy',\
'Film-Noir','Horror','Musical','Mystery','Romance','Sci_Fi',\
'Thriller','War','Western']
movies = pd.read_table('ml-100k/u.item', sep='|', header=None, names=mnames,encoding='ANSI')
rnames = ['user_id', 'movie_id', 'rating', 'timestamp']
ratings = pd.read_table('ml-100k/u.data', header=None, names=rnames)
all_data = pd.merge(pd.merge(ratings, users), movies)
data = DataFrame(data=all_data,columns=['user_id','movie_id'])
data.to_csv('data.csv')
X=data['user_id']
Y=data['movie_id']
return X, Y, data
def readDataSet():
#读取之前存储的表格并提取我们需要的信息
data=pd.read_csv('data.csv')
X=data['user_id']
Y=data['movie_id']
return X, Y, data
#建立倒排矩阵,x用户,y物品
def ItemSimilarity(X, Y):
#计算用户-物品倒排表
user_item=dict()
for i in range(Y.count()):
user=X.iloc[i]
item=Y.iloc[i]
if user not in user_item:
user_item[user]=set()
user_item[user].add(item)
#对于每个拥挤,将他的物品列表中的物品两两在共现矩阵C中加1
#N(i)为某个物品出现的总数
C={}
N={}
for u,items in user_item.items():
for i in items:
N.setdefault(i,0)
N[i]+=1
C.setdefault(i,{})
for j in items:
if i==j:
continue
C[i].setdefault(j,0)
C[i][j]+=1/math.log(1+len(user_item[u]))
#活跃用户对物品相似度的贡献应该小于不活跃的用户!!
#计算相似度
W=C.copy()
maxw=np.zeros(len(W)+1)
for i,related_items in C.items():
for j,cij in related_items.items():
W[i][j]=cij/math.sqrt(N[i]*N[j])
if W[i][j]>maxw[j]:
maxw[j]=W[i][j]
#公式惩罚了物品j的权重,减轻了热门物品会和很多物品相似的可能性!!
#归一化!!
for i,items in W.items():
for j,cij in items.items():
W[i][j]=W[i][j]/maxw[j]
return W, user_item
def recommend(user,user_item,W,K=10):
#取与用户喜欢的物品集合中各物品,分别最相似的排名前k个物品
rank={}
interacted_items=user_item[user]
for i in interacted_items:
for j, wij in sorted(W[i].items(),key=lambda x:x[1],reverse=True)[0:K]:
if j in interacted_items:
continue
rank.setdefault(j,0)
rank[j]+=wij
return rank
def dict2list(dic:dict):
''' 将字典转化为列表 '''
keys = dic.keys()
vals = dic.values()
lst = [(key, val) for key, val in zip(keys, vals)]
return lst
'''
import itemCF
X, Y, data=itemCF.readDataSet()
W, user_item = itemCF.ItemSimilarity(X,Y)
rank = itemCF.recommend(20,user_item,W,20)
sorted_rank=sorted(itemCF.dict2list(rank), key=lambda x:x[1], reverse=True)[0:10]
'''
推荐参考资料
《推荐算法实践》
https://blog.youkuaiyun.com/Cherrie3/article/details/52757118?locationNum=2&fps=1
http://iyao.ren/2017/02/28/itemcf/
http://geek.youkuaiyun.com/news/detail/134876