對于中小型的公司,使用者的資料量及公司産品的個數都是較小規模的,需要提供給使用者的推薦系統實作的重心也從人性化變成了實作成本,協同推薦就是非常常見、有效且可以快速實作的方法,也是本文想介紹的。
正常的快速簡單推薦系統實作方法不排除以下幾種:
-
熱門推薦
所有人打開浏覽的内容都一緻,驚喜性會有所缺失,但是實作特别簡單,稍加邏輯帶給使用者的體驗感滿足了基本需求。
-
SVD+推薦
之前也讨論過實作方法了,附上連結:SVD及擴充的矩陣分解方法
-
基于模型推薦
這個比較偏向業務場景,可以說是經典的場景化模型,之前寫過一篇基于使用者特征的偏好推薦,可以參考一下:蘇甯易購的使用者交叉推薦
-
協同推薦
這個也是幾乎每個公司都會用的,也是非常非常常見有效的算法之一
協同推薦介紹
首先,我們先來了解一下什麼叫做協同推薦。
基于使用者的協同過濾推薦算法是最早誕生的,1992年提出并用于郵件過濾系統,兩年後1994年被 GroupLens 用于新聞過濾。一直到2000年左右,該算法都是推薦系統領域最著名的算法。算是非常古董級别的算法之一了,但是古董歸古董,它的效果以及實作的成本卻奠定了它在每個公司不可取代的地位。
基于使用者的協同推薦
使用者u1喜歡的電影是A,B,C
使用者u2喜歡的電影是A, C, E, F
使用者u3喜歡的電影是B,D
複制
假設u1、u2、u3使用者喜歡的電影分布如上,基于使用者的協同推薦幹了這麼一件事情,它根據每個使用者看的電影(A、B、C、...)相似程度,來計算使用者之間的相似程度,将高相似的使用者看過但是目标使用者還沒有看過的電影推薦給目标使用者。
基于商品的協同推薦
電影A被u1,u2看過
電影B被u1,u3看過
電影C被u1,u2看過
電影D被u3看過
電影E被u2看過
電影F被u2看過
複制
假設A~F電影被使用者觀影的分布如上,基于商品的協同推薦幹了這麼一件事情,它根據電影(A、B、C、...)被不同使用者觀看相似程度,來計算電影之間的相似程度,根據目标使用者看過的電影的高相似度的電影推薦給目标使用者。
看起來以上的邏輯是非常簡單的,其實本來也是非常簡單的,我看了下,網上關于以上的代碼實作還是比較林散和有問題的,優化了python版本的code,并詳細解釋了每一步,希望,對初學者有所幫助。
#time 2017-09-17
#author:shataowei
#based-item
#所要的基礎包比較簡單
from collections import defaultdict
import math
import time
startTime = time.time()
#讀取資料的過程
#/Users/slade/Desktop/machine learning/data/recommender/u1.base
def readdata(location):
list2item = {} #商品對應的使用者清單(1:[[1,2],[2,3]]代表商品1對應使用者1的行為程度為2,商品1對應的使用者2的行為程度為3)
list2user = {} #使用者對應的商品清單(1:[[1,2],[2,3]]代表使用者1對應商品1的行為程度為2,使用者1對應的商品2的行為程度為3)
f = open(location,'r')
data = f.readlines()
data = [x.split('\t') for x in data]
f.close()
for i in data:
if int(i[1]) not in list2item.keys():
list2item[int(i[1])] = [[int(i[0]),int(i[2])]]
else:
list2item[int(i[1])].append([int(i[0]),int(i[2])])
if int(i[0]) not in list2user.keys():
list2user[int(i[0])] = [[int(i[1]),int(i[2])]]
else:
list2user[int(i[0])].append([int(i[1]),int(i[2])])
return list2item,list2user
#list2item,list2user=readdata('/Users/slade/Desktop/machine learning/data/recommender/u1.base')
#基于item的協同推薦
#0.将使用者行為程度離散化:浏覽:1,搜尋:2,收藏:3,加車:4,下單未支付5
#1.計算item之間的相似度:item共同觀看次數/單item次數連乘
#2.尋找目标使用者觀看過的item相關的其他item清單
#3.計算其他item的得分:相似度*使用者行為程度,求和
#0 hive操作
#1.1統計各商品出現次數
def itemcf_itemall(userlist = list2user):
I={}
for key in userlist:
for item in userlist[key]:
if item[0] not in I.keys():
I[item[0]] = 0
I[item[0]] = I[item[0]] + 1
return I
#1.2計算相似矩陣
def itemcf_matrix(userlist = list2user):
C=defaultdict(defaultdict)
W=defaultdict(defaultdict)
#根據使用者的已購商品來形成對應相似度矩陣
for key in userlist:
for item1 in userlist[key]:
for item2 in userlist[key]:
if item1[0] == item2[0]:
continue
if item2 not in C[item1[0]].keys():
C[item1[0]][item2[0]] = 0
C[item1[0]][item2[0]] = C[item1[0]][item2[0]] + 1
#計算相似度,并填充上面對應的相似度矩陣
for i , j in C.items():
for z , k in j.items():
W[i][z] = k/math.sqrt(I[i]*I[z])
#k/math.sqrt(I[i]*I[z])計算相似度,其中k為不同商品交集,sqrt(I[i]*I[z])用來壓縮那些熱門商品必然有高交集的問題
return W
#2.尋找使用者觀看的其他item
def recommendation(userid,k):
score_final = defaultdict(int)
useriditem = []
for item,score in list2user[userid]:
#3.計算使用者的item得分,k來控制用多少個相似商品來計算最後的推薦商品
for i , smimilarity in sorted(W[item].items() , key = lambda x:x[1] ,reverse =True)[0:k]:
for j in list2user[userid]:
useriditem.append(j[0])
if i not in useriditem:
score_final[i] = score_final[i] + smimilarity * score
#累加每一個商品使用者的評分與其它商品的相似度積的和作為衡量
#最後的10控制輸出多少個推薦商品
l = sorted(score_final.items() , key = lambda x : x[1] , reverse = True)[0:10]
return l
#I = itemcf_itemall()
#W = itemcf_matrix()
#result_userid = recommendation(2,k=20)
endTime = time.time()
print endTime-startTime
複制
python來實作基于item的協同推薦就完成了,核心的相似度計算可以根據實際問題進行修改,整體流程同上即可,當然資料量大的時候分布式去寫也是可以的。
#time 2017-09-17
#author:shataowei
#based-user
from collections import defaultdict
import math
import time
startTime = time.time()
#讀取資料
#/Users/slade/Desktop/machine learning/data/recommender/u1.base
def readdata(location):
list2item = {} #商品對應的使用者清單
list2user = {} #使用者對應的商品清單
f = open(location,'r')
data = f.readlines()
data = [x.split('\t') for x in data]
f.close()
for i in data:
if int(i[1]) not in list2item.keys():
list2item[int(i[1])] = [[int(i[0]),int(i[2])]]
else:
list2item[int(i[1])].append([int(i[0]),int(i[2])])
if int(i[0]) not in list2user.keys():
list2user[int(i[0])] = [[int(i[1]),int(i[2])]]
else:
list2user[int(i[0])].append([int(i[1]),int(i[2])])
return list2item,list2user
#list2item,list2user=readdata('/Users/slade/Desktop/machine learning/data/recommender/u1.base')
#基于使用者的協同推薦
#0.先通過hive求出近一段時間(根據業務頻率定義),使用者商品的對應表
#1.求出目标使用者的鄰居,并計算目标使用者與鄰居之間的相似度
#2.列出鄰居是以購買的商品清單
#3.針對第二步求出了商品清單,累加所對應的使用者相似度,并排序求top
#0.hive操作
#1.1求出目标使用者的鄰居,及對應的相關程度
def neighbour(userid,user_group = list2user,item_group = list2item):
neighbours = {}
for item in list2user[userid]:
for user in list2item[item[0]]:
if user[0] not in neighbours.keys():
neighbours[user[0]] = 0
neighbours[user[0]] = neighbours[user[0]] + 1
return neighbors
#通常來說,基于item的推薦對于商品量較大的業務會構成一個巨大的商品矩陣,這時候如果使用者人均購買量較低的時候,可以考慮使用基于user的推薦,它在每次計算的時候會隻考慮相關使用者,也就是這邊的neighbours(有點支援向量基的意思),大大的降低了計算量。
#neighbours = neighbour(userid=2)
#1.2就算使用者直接的相似程度,這邊用的餘弦相似度:點積/模的連乘
def similarity(user1,user2):
x=0
y=0
z=0
for item1 in list2user[user1]:
for item2 in list2user[user2]:
if item1[0]==item2[0]:
x1 = item1[1]*item1[1]
y1 = item2[1]*item2[1]
z1 = item1[1]*item2[1]
x = x + x1
y = y + y1
z = z + z1
#避免分母為0
if x * y == 0 :
simi = 0
else:
simi = z / math.sqrt(x * y)
return simi
#1.3計算目标使用者與鄰居之間的相似度:
def N_neighbour(userid,neighbours,k):
neighbour = neighbours.keys()
M = []
for user in neighbour:
simi = similarity(userid,user)
M.append((user,simi))
M = sorted(M,key = lambda x:x[1] ,reverse = True)[0:k]
return M
#M = N_neighbour(userid,neighbours,k=200)
#2.列出鄰居所購買過的商品并計算商品對應的推薦指數
def neighbour_item(M=M):
R = {}
M1 = dict(M)
for neighbour in M1:
for item in list2user[neighbour]:
if item[0] not in R.keys():
R[item[0]] = M1[neighbour] * item[1]
else:
R[item[0]] = R[item[0]] + M1[neighbour] * item[1]
#根據鄰居買過什麼及與鄰居的相似度,計算鄰居買過商品的推薦度
return R
# R = neighbour_item(M)
#3.排序得到推薦商品
Rank = sorted(R.items(),key=lambda x:x[1],reverse = True)[0:50]
endTime = time.time()
print endTime-startTime
複制
python來實作基于user的協同推薦就完成了,核心的相似度計算可以根據實際問題進行修改,基于user的實作過程中,用了鄰居這個概念,大大降低了計算量,我用了大概20萬使用者,2千的商品數,基于user的推薦實作速度大概為基于商品的10分之一,效果差異卻相差不大。
協同推薦是非常簡單的推薦入門算法之一,也是必須要手動快速代碼實作的算法之一,希望能給大家一些幫助。