欲直接下載代碼文件,關注咱們的公衆號哦!查看歷史消息便可!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()