欲直接下载代码文件,关注咱们的公众号哦!查看历史消息便可!python
话说,最近的瓜实在有点多,从我科校友李雨桐怒锤某男、陈羽凡吸毒被捕、蒋劲夫家暴的三连瓜,到不知知网翟博士,再到邓紫棋解约蜂鸟、王思聪花千芳隔空互怼。算法
而最近的胜利夜店、张紫妍巨瓜案、最强大脑选手做弊丑闻,更是让吃瓜群众直呼忙不过来:瓜来的太快就像龙卷风,扶我起来,我还能吃!网络
说到底,这实际上是一个信息过载的时代:公众号天天数十条的推送、朋友圈的晒娃晒旅游、各类新闻报道扑面而来使人眼花缭乱、应接不暇……app
那么问题来了,怎么找到本身的关注点呢?俗话说得好,有问题,找度娘,输入关键词一回车就完事儿了。dom
然而,懒是人的天性,而有的人(好比小编)则连关键词都懒得搜,但愿计算机能自动挖掘咱们的兴趣点,并为咱们推荐感兴趣的内容,因此咱们就迫切地须要推荐系统来帮助咱们了。那么如今咱们就来说讲推荐系统吧~ide
推荐系统相信你们并不陌生,从“我有歌也有热评”的云村里的每日歌曲推荐,淘宝的猜你喜欢,再到外卖APP和视频网站的推送,推荐系统彷佛成了各类APP的宠儿(请忽略小编的老年人口味)。函数
热门app的推荐系统学习
简单来讲,推荐系统就是根据用户的各类数据(历史行为数据、社交关系数据、关注点、上下文环境等)在海量数据中判断用户感兴趣的item并推荐给用户的系统。测试
“哇这个东西就是我想要的。”优化
“诶,这首歌还真好听。”
“emm这部电影还挺对我胃口的”
推荐系统对用户而言,可以简化搜寻过程、发现新鲜好玩使人惊喜的东西;而对商家而言,则是可以提供个性化服务,提升用户的信任度、粘度以及活跃度,从而提升营收。
一个完整的推荐系统每每是十分复杂的。既然如此,仅仅经过准确率一个标准推荐系统做评测是远远不够的,为此咱们须要定义多个标准,从多维度评价一个推荐系统的好坏。
notation
1.准确度
对打分系统(好比说淘宝的评论一到五星打分)而言,一说到评判标准,最早想到的确定就是均方根偏差RMSE和平均绝对偏差MAE啦:
RMSE和MAE计算公式
对TOP N推荐(生成一个TOP N推荐列表)而言,Precision和Recall则是咱们的关注点:
Precision和Recall计算公式
说人话版本:Precision就是指系统推荐的东西中用户感兴趣的有多少,Recall就是用户感兴趣的东西中你推荐了多少
举个栗子:
2.覆盖率
表示推荐系统对item长尾的发掘能力
科普知识
马太效应:强者愈强,弱者愈弱
长尾效应:大多数的需求会集中在头部(爆款商品),而分布在尾部的需求是个性化的、零散的、小量的需求(冷门商品)。但这部分差别化的、少许的需求会在需求曲线上面造成一条长长的“尾巴”。若是将全部非流行的市场累加起来就会造成一个比流行市场还大的市场。
由长尾效应可知,知足用户个性化的需求十分重要,因此推荐系统要尽量地挖掘出合用户口味的冷门商品;若是推荐系统严重偏向推荐热门商品,那么只会形成热门商品越热门,冷门商品越冷门,对商家的营收十分不利。
因此这就要求推荐系统的覆盖率要高(推荐系统对全部用户推荐的item占item总数的比例):
覆盖率还有另外一种计算方式:信息熵
其中,p(i)=第i件item被推荐次数/全部item总被推荐次数
科普时间:
“信息是用来消除随机不肯定性的东西。”由高中化学知道,熵是用来衡量一个系统的混乱程度的,熵越大,混乱程度越高。而一样的,信息熵越大,对一件事情的不肯定性就越大。
32支球队打世界杯,只有一队胜利,可是咱们不知道关于比赛、关于球队的任何信息,也就是说每支球队获胜的几率为1/32。若是咱们想要知道哪支队伍胜利,咱们只能无任何根据地瞎猜,那么最要猜几回呢?经过折半查找法咱们能够发现,咱们顶多五次就能找到胜利的球队。因此说,这个问题的最大信息熵就是5bit(信息熵在p(i)所有相等时最大)。
若是这时候,咱们知道了更多的信息,好比说是哪国球队(德国队和中国队你选哪一个?)、球队的既往胜率,那么这时候,各个球队获胜的几率就发生了变更,而此时信息熵也获得了下降。
回到咱们的推荐系统中来,信息熵越大,代表item之间的p(i)越接近,也就是说每一个item被推荐的次数越接近, 即覆盖率越大。
3.多样性
表示推荐列表中物品之间的不类似性
Q:为何须要多样性?
A:试想,一个喜欢裙子的用户,若是你给她推荐的十件商品都是裙子,那她可能也只会买一条最合心意的裙子,倒不如把一部分的推荐名额给其余种类的商品;另外,一位用户买了一台计算机,你还给他推荐另外的计算机吗?从商家的角度看,推荐鼠标、键盘等是最好的选择。
因此咱们的推荐列表须要尽量地拓宽种类,增长用户的购买欲望。
4.还有其余的评判标准
新颖度:新颖的推荐是指给用户推荐那些他们之前没有据说过的物品。
惊喜度:推荐结果和用户的历史兴趣不类似,但却让用户以为满意。(而新颖性仅仅取决于用户是否据说过这个推荐结果。)
信任度:推荐系统给你推荐的依据是什么(“你的朋友也喜欢这首歌”比起“喜欢那首歌的人也喜欢这首歌”更能让用户信任)
1. 协同过滤(collaborative filtering)
回想一下,当你遇到剧荒、歌荒时,会怎么作?
“诶,小陈,最近有啥好看的电视剧,给我安利安利呗。”
相信大多数人首先想到的都是找一个趣味相投的朋友,问一下他们有啥好推荐的。
协同过滤也就是基于这样的思想。而协同过滤分为两种:user-based(基于用户,找到最类似的user)和item-based(基于商品,找到最类似的item)。
那么,协同过滤须要解决的核心问题是:
如何找到最类似的user/item?
所以咱们须要衡量类似性的指标:
其实Pearson类似度是考虑了user/item之间的差别而来。
Q:为何要减去mean?
A:好比,user1打分十分苛刻,3分已是至关好的商品;而user2是一位佛系用户,不管商品有多差,打分时3分起步。那么咱们能够说user1的3分和user2的5分是等价的。而Pearson类似度就是经过减去mean来进行相对地归一化。
user-based/item-based算法流程:
1. 计算目标user/item和其他user/item的类似度
2. 选择与目标user/item有正类似度的user/item
3. 加权打分
举个栗子(使用Pearson类似度和user-based):
咱们有这样一个打分表,想要预测USER2对ITEM3的打分
2. 隐因子模型(Latent Factors Model)
咱们有一个user对item的打分矩阵,可是有一些位置是空着的(咱们候选推荐的item),因此咱们要作的,就是把这些空位一网打尽,一次性填满。
隐因子模型的基本思想就是:
打分矩阵R只有两个维度:user和item,那么可否引入影响用户打分的隐藏因素(即隐因子,不必定是人能够理解的,如同神经网络同样的黑箱)在user和item之间搭一座桥(分解为多个矩阵,使得这些矩阵的乘积近似于R)
举个例子:
隐因子模型举例
仔细观察,在非0的部位上R和R’十分接近,而在0的部位上,R’获得了填充,而这能够做为咱们推荐的依据。
Q:如何分解矩阵?
A:说到矩阵分解,首先想到的就是SVD了(Python AI 教学|SVD(Singular Value Decomposition)算法及应用)。
然而,SVD的时间复杂度为O(n3),在这里小编推荐另外一种实现:梯度降低
算法流程:
隐因子模型的梯度降低实现算法流程
算法改进:
前面提到了用户之间评分标准的差别性(某些用户比较严格),而咱们经过引入正则化项和误差项(user bias和item bias)来进行优化。因为篇幅缘由,本文不做具体解释,感兴趣的朋友麻烦自行百度啦~
3. 算法优缺点比较:
冷启动问题:
对于冷启动问题,通常分为三类:
用户冷启动:如何对新用户作个性化推荐。
物品冷启动:如何将新加进来的物品推荐给对它感兴趣的用户。
系统冷启动:新开发的网站如何设计个性化推荐系统。
在user-based和item-based之间,通常使用item-based,由于item-based稳定性高(user的打分标准、喜爱等飘忽不定,有很大的不肯定性),并且user太多时计算量大。
都说女人心海底针,还在为选什么电影才能打动妹子烦恼吗?还在担忧没法彰显本身的品味吗?相信下面的电影推荐系统代码必定可以回答你关于如何选择电影的疑惑。(捂脸,逃)
1. 数据源
小编经过问卷调查获取了朋友圈对15部电影的评分(1表明第一个选项即没看过,2~6表示1~5星)
2.代码
注:python有许多方便计算的函数,如norm()计算向量的模和corrcoef()计算pearson类似度,不过为了广大朋友们记忆深入,小编这里本身来实现这些计算~
1# -*- coding: utf-8 -*- 2 3""" 4Created on Thu Mar 21 19:29:54 2019 5 6@author: o 7""" 8import pandas as pd 9import numpy as np 10from math import sqrt 11import copy 12 13def load_data(path): 14 df = pd.read_excel(path) 15 #去掉不须要的列 16 df = df[df.columns[5:]] 17 #1表示没看过,2~6表示1~5星 18 df.replace([1,2,3,4,5,6],[0,1,2,3,4,5],inplace = True) 19 columns = df.columns 20 df = np.array(df) 21 #测试过程当中发现有nan存在,原来是由于有人恶做剧,全填了没看过,致使分母为0 22 #此处要删除全为0的行 23 delete = [] 24 for i in range(df.shape[0]): 25 all_0 = (df[i] == [0]*15) 26 flag = False 27 for k in range(15): 28 if all_0[k] == False: 29 flag = True 30 break 31 if flag == False: 32 delete.append(i) 33 print(i) 34 df = np.delete(df,delete,0) 35 return df,columns 36 37 38#定义几种衡量类似度的标准 39#余弦类似度 40def cos(score,your_score): 41 cos = [] 42 len1 = 0 43 for i in range(15): 44 len1 += pow(your_score[i],2) 45 len1 = sqrt(len1) 46 for i in range(score.shape[0]): 47 len2 = 0 48 for k in range(15): 49 len2 += pow(score[i][k],2) 50 len2 = sqrt(len2) 51 cos.append(np.dot(your_score,score[i])/(len1*len2)) 52 return cos 53 54#欧氏距离 55def euclidean(score,your_score): 56 euclidean = [] 57 for i in range(score.shape[0]): 58 dist = 0 59 for k in range(score.shape[1]): 60 dist += pow((score[i][k]-your_score[k]),2) 61 dist = sqrt(dist) 62 euclidean.append(dist) 63 return euclidean 64 65 66#pearson类似度 67#Python有内置函数corrcoef()能够直接计算,不过这里仍是手写巩固一下吧~ 68def pearson(score,your_score): 69 pearson = [] 70 n = score.shape[1] 71 sum_y = 0 72 count = 0 73 #计算目标用户打分的均值 74 for i in range(n): 75 if your_score[i]!=0: 76 count += 1 77 sum_y += your_score[i] 78 mean_y = sum_y/count 79 print('\n') 80 print('你的平均打分为:',mean_y) 81 print('\n') 82 #计算目标用户打分向量的长度 83 len1 = 0 84 for i in range(n): 85 if your_score[i]!=0: 86 your_score[i] -= mean_y 87 len1 += pow(your_score[i],2) 88 len1 = sqrt(len1) 89 #print(len1,mean_y,your_score) 90 91 for i in range(score.shape[0]): 92 #计算其余用户打分的均值 93 # print(i,score[i]) 94 count = 0 95 sum_x = 0 96 for k in range(n): 97 if score[i][k]!=0: 98 count += 1 99 sum_x += score[i][k] 100 mean_x = sum_x/count 101 #计算其余用户打分向量的长度 102 len2 = 0 103 for k in range(n): 104 if score[i][k]!=0: 105 score[i][k] -= mean_x 106 len2 += pow(score[i][k],2) 107 len2 = sqrt(len2) 108 #print(len2,mean_x,score[i],'\n','\n') 109 #分母不可为零,否则会产生nan 110 if len2 == 0: 111 pearson.append(0) 112 else: 113 pearson.append(np.dot(your_score,score[i])/len1/len2) 114 return pearson,mean_y 115 116 117#找到类似度最高的用户 118def find_nearest(sim): 119 index = [i for i in range(len(sim))] 120 #index和sim的元组列表 121 sorted_value = list(zip(index,sim)) 122 #降序排序 123 sorted_value = sorted(sorted_value,key = lambda x : x[1],reverse = True) 124 return sorted_value 125 126 127#user-based collaborative_filtering 128def collaborative_filtering(score,your_score,movies): 129 #目标用户对15部电影有无看过的bool列表 130 seen1 = np.array([bool(i) for i in your_score]) 131 #使用pearson过程当中会改变score矩阵的值,须要用deepcopy复制一份 132 score1 = copy.deepcopy(score) 133 #几种类似度的衡量 134 #sim = cos(score,your_score) 135 #sim = euclidean(score,your_score) 136 sim, mean_target= pearson(score1,your_score) 137 #找到最类似的用户 138 sorted_value = find_nearest(sim) 139 140 #找到类似度>0的用户数量 141 count = 0 142 for i in range(score.shape[0]): 143 if sorted_value[i][1]<=0: 144 break 145 else: 146 count += 1 147 #取根值,去掉正类似度中偏低的user 148 count = int(sqrt(count)) 149 150 #加权打分 进行推荐 151 print('使用user-based协同过滤进行加权预测打分:') 152 for i in range(score.shape[1]): 153 #若是目标用户没看过 154 if not seen1[i]: 155 #初始化分子分母 156 numerator = denominator = 0 157 for k in range(count): 158 index = sorted_value[k][0] 159 if score[index][i] != 0: 160 numerator += score[index][i]*sorted_value[k][1] 161 denominator += sorted_value[k][1] 162 if not denominator: 163 print(movies[i],'无相关度高的人看过,没法预测得分') 164 elif numerator/denominator > mean_target: 165 print(movies[i],':',numerator/denominator,'推荐观看') 166 else: 167 print(movies[i],":",numerator/denominator,'不推荐观看') 168 return None 169 170 171#梯度降低+隐因子模型 172def latent_factors(score,your_score,movies): 173 #目标用户的打分向量整合进打分矩阵 174 score1 = np.vstack([your_score,score]) 175 #打分矩阵的维度 176 n,m = score1.shape[0],score1.shape[1] 177 #隐因子数量设为K 178 K = 15 179 #最大迭代次数 180 max_iteration = 1000 181 #学习速率和正则化因子 182 alpha = 0.01 183 beta = 0.01 184 #LossFunction改变值小于threshold就结束 185 threshold = 0.7 186 #迭代次数 187 count = 0 188 #初始化分解后的矩阵P、Q 189 p = np.random.random([n,K]) 190 q = np.random.random([m,K]) 191 #全体用户对15部电影有无看过的bool矩阵 192 bool_matrix = [[bool(k) for k in i] for i in score1] 193 194 while True: 195 count += 1 196 #更新P Q矩阵 197 for i in range(n): 198 for j in range(m): 199 if bool_matrix[i][j]: 200 eij = score1[i][j] - np.dot(p[i],q[j]) 201 for k in range(K): 202 #同时更新pik和qjk 203 diff=[0,0] 204 diff[0] = p[i][k] + alpha*(2*eij*q[j][k]-beta*p[i][k]) 205 diff[1] = q[j][k] + alpha*(2*eij*p[i][k]-beta*q[j][k]) 206 p[i][k] = diff[0] 207 q[j][k] = diff[1] 208 #计算偏差 209 error = 0 210 for i in range(n): 211 for j in range(m): 212 if bool_matrix[i][j]: 213 error += pow((score1[i][j]-np.dot(p[i],q[j])),2) 214 for k in range(K): 215 error += beta/2*(pow(p[i][k],2)+pow(q[j][k],2)) 216 RMSE = sqrt(error/n) 217 print(count,'root_mean_square_error:',RMSE) 218 if RMSE<threshold or count>max_iteration: 219 break 220 221 #打印目标用户没看过的电影 222 seen1 = np.array([bool(i) for i in your_score]) 223 print('你没看过的影片有:') 224 for i in range(m): 225 if not seen1[i]: 226 print(movies[i]) 227 print('\n') 228 229 #输出梯度降低预测分数 230 predict = np.dot(p[0],q.T) 231 print('使用梯度降低+隐因子模型进行预测打分:') 232 for i in range(m): 233 if not seen1[i]: 234 print(movies[i],':',predict[i]) 235 return None 236 237 238def recommend(): 239 #请自行修改路径 240 path = r'C:\Users\o\Desktop\请给15部电影打分吧.xls' 241 #原始打分矩阵,电影名称列表 242 score, movies = load_data(path) 243 #必须转换成Float类型,否则会出现分母为0的状况 244 score = score.astype(float) 245 your_score = [] 246 #构造目标用户打分向量 247 print('请依次输入你对15部电影的打分(0表示没看过,1~5表示1~5星,以空格分隔):') 248 print(movies) 249 str = input() 250 your_score = np.array([int(i) for i in str.split(' ')]).astype(float) 251 #进行预测 252 latent_factors(score,your_score,movies) 253 collaborative_filtering(score,your_score,movies) 254 return None 255 256 257if __name__=='__main__': 258 recommend()