引言 git
以前有段時間研究過推薦算法,倒不是科研須要,是以爲很想弄明白天天淘寶的時候那些猜你喜歡的東西是怎麼冒出來的,還有最近很火的網易雲音樂以及蝦米音樂的推薦算法,這裏很高興的就是網易雲音樂已經被我調教的很棒了,真開心 github
後來瞭解到那些企業作的推薦算法可能是混合推薦,而我只瞭解了基礎的三種,不過相信萬變不離其宗,不少基礎的東西仍是很重要的,因此對這三種推薦算法進行了整理,最後由於以前本身用Python參照着github上一我的的代碼本身也嘗試着實現了一下該推薦系統,主要是基於用戶的推薦,這裏貼出來供你們一塊兒學習 算法
三種推薦算法 數組
推薦算法主要有:User-based Recommender, Item-based Recommender和Slope-One Recommender。 數據結構
1. User-based Recommender app
該算法的主要思想是:與用戶u最類似的用戶喜歡的商品有很大可能也是用戶u喜歡的商品。 oop
1 對於用戶u的每個沒有preference的商品i
2 對於每個對商品i有preference的用戶v
3 計算用戶u與v的類似性s//實際上無需在線計算,能夠事先計算好存儲起來
4 將用戶v對於商品i的preference,乘以s,加到用戶u對商品i的預測preference的平均值
5 返回用戶u的平均值最高的幾個商品做爲推薦商品。 學習
上述算法須要窮舉每一個商品i,致使計算很是很是慢。能夠實現計算好某個用戶u的全部的鄰居(記爲集合n),而後,再爲用戶u作推薦時,只須要考慮用戶u的鄰居的全部有preference的商品便可,而無需考慮全部商品,算法以下: 測試
1 對於每一個其餘的用戶w
2 計算用戶u與用戶w之間的類似度s
3 返回用戶u的類似度最高的幾個用戶,記爲用戶u的鄰居n
4 //上述步驟能夠離線完成,相似於索引
5 對於用戶u的每一個u自己沒有preference,可是u的鄰居n中某個用戶有preference的商品i
6 對於每一個對商品i有preference的用戶u的鄰居v
7 計算用戶u與v的類似度//其實無需在線計算,能夠提早計算好,存儲起來
8 將用戶v對於商品i的preference,乘以s,加到用戶u對商品i的預測preference的平均值 spa
2. Item-based Recommender
該算法的主要思想是:喜歡商品i的用戶u,有很大可能性會喜歡和商品i很類似的其餘商品
1 對於用戶u的每一個沒有preference的商品i
2 對於每一個用戶u有preference的商品j
3 計算商品i與j的類似度s
4 把用戶u對商品j的preference,乘以s,加到用戶u對商品i的preference的平均值
5 返回用戶u的平均值最高的幾個商品做爲推薦商品
3. Slope-One Recommender
首先Slope one是一種基於項目的協同過濾算法(Item-based Recommendation)
首先計算每兩個商品之間preference差值的平均值
1 對於某個商品i
2 對於某個商品j
3 對於每一個對商品i和j都有preference的用戶u
4 將用戶u對商品i和商品j的preference的差值加到全部用戶對商品i和商品j的差值的平均值當中去
推薦
1 對於用戶u的每一個沒有preference的商品i
2 對於用戶u的每一個有preference的商品j
3 取得商品i和商品j的preference差值的平均值diff
4 將diff加上u對商品j的preference的和加入用戶u對商品i的preference的平均值當中去
5 返回用戶u的預測preference最高的幾個商品做爲推薦
推薦算法實戰
__author__ = 'Hadoop'
# -*- coding=utf-8 -*-
import math
import sys
from texttable import Texttable
#
# 使用 |A&B|/sqrt(|A || B |)計算餘弦距離
#
#
#
def calcCosDistSpe(user1,user2):
avg_x=0.0
avg_y=0.0
for key in user1:
avg_x+=key[1]
avg_x=avg_x/len(user1)
for key in user2:
avg_y+=key[1]
avg_y=avg_y/len(user2)
u1_u2=0.0
for key1 in user1:
for key2 in user2:
if key1[1] > avg_x and key2[1]>avg_y and key1[0]==key2[0]:
u1_u2+=1
u1u2=len(user1)*len(user2)*1.0
sx_sy=u1_u2/math.sqrt(u1u2)
return sx_sy
#
# 計算餘弦距離
#
#
def calcCosDist(user1,user2):
sum_x=0.0
sum_y=0.0
sum_xy=0.0
for key1 in user1:
for key2 in user2:
if key1[0]==key2[0] :
sum_xy+=key1[1]*key2[1]
sum_y+=key2[1]*key2[1]
sum_x+=key1[1]*key1[1]
if sum_xy == 0.0 :
return 0
sx_sy=math.sqrt(sum_x*sum_y)
return sum_xy/sx_sy
#
#
# 類似餘弦距離
#
#
#
def calcSimlaryCosDist(user1,user2):
sum_x=0.0
sum_y=0.0
sum_xy=0.0
avg_x=0.0
avg_y=0.0
for key in user1:
avg_x+=key[1]
avg_x=avg_x/len(user1)
for key in user2:
avg_y+=key[1]
avg_y=avg_y/len(user2)
for key1 in user1:
for key2 in user2:
if key1[0]==key2[0] :
sum_xy+=(key1[1]-avg_x)*(key2[1]-avg_y)
sum_y+=(key2[1]-avg_y)*(key2[1]-avg_y)
sum_x+=(key1[1]-avg_x)*(key1[1]-avg_x)
if sum_xy == 0.0 :
return 0
sx_sy=math.sqrt(sum_x*sum_y)
return sum_xy/sx_sy
#
# 讀取文件
#
#
def readFile(file_name):
contents_lines=[]
f=open(file_name,"r")
contents_lines=f.readlines()
f.close()
return contents_lines
#
# 解壓rating信息,格式:用戶id\t硬盤id\t用戶rating\t時間
# 輸入:數據集合 u.data
# 輸出:已經解壓的排名信息 user item rating
#
def getRatingInformation(ratings):
rates=[]
for line in ratings:
rate=line.split("\t")
rates.append([int(rate[0]),int(rate[1]),int(rate[2])])
#print(rates)
return rates
#
# 生成用戶評分的數據結構
#
# 輸入:因此數據 [[2,1,5],[2,4,2]...]
# 輸出:1.用戶打分字典 2.電影字典
# 使用字典,key是用戶id,value是用戶對電影的評價,
# rate_dic[2]=[(1,5),(4,2)].... 表示用戶2對電影1的評分是5,對電影4的評分是2
#
def createUserRankDic(rates):
user_rate_dic={}
item_to_user={}
for i in rates:
user_rank=(i[1],i[2])
if i[0] in user_rate_dic:
user_rate_dic[i[0]].append(user_rank)
else:
user_rate_dic[i[0]]=[user_rank]
if i[1] in item_to_user:
item_to_user[i[1]].append(i[0])
else:
item_to_user[i[1]]=[i[0]]
return user_rate_dic,item_to_user
#
# 計算與指定用戶最相近的鄰居
# 輸入:指定用戶ID,因此用戶數據,因此物品數據
# 輸出:與指定用戶最相鄰的鄰居列表
#
def calcNearestNeighbor(userid,users_dic,item_dic):
neighbors=[]
#neighbors.append(userid)
for item in users_dic[userid]:
for neighbor in item_dic[item[0]]:
if neighbor != userid and neighbor not in neighbors:
neighbors.append(neighbor)
neighbors_dist=[]
for neighbor in neighbors:
dist=calcSimlaryCosDist(users_dic[userid],users_dic[neighbor]) #calcSimlaryCosDist calcCosDist calcCosDistSpe
neighbors_dist.append([dist,neighbor])
neighbors_dist.sort(reverse=True)
#print neighbors_dist
return neighbors_dist
#
# 使用UserFC進行推薦
# 輸入:用戶文件名,用戶ID,鄰居數量
# 輸出:推薦的電影ID,輸入用戶的電影列表,電影對應用戶的反序表,鄰居列表
#
def recommendByUserFC(file_name,userid,k=5):
#讀取文件數據
test_contents=readFile(file_name)
#文件數據格式化成二維數組 List[[用戶id,電影id,電影評分]...]
test_rates=getRatingInformation(test_contents)
#格式化成字典數據
# 1.用戶字典:dic[用戶id]=[(電影id,電影評分)...]
# 2.電影字典:dic[電影id]=[用戶id1,用戶id2...]
test_dic,test_item_to_user=createUserRankDic(test_rates)
#尋找鄰居
neighbors=calcNearestNeighbor(userid,test_dic,test_item_to_user)[:k]
recommend_dic={}
for neighbor in neighbors:
neighbor_user_id=neighbor[1]
movies=test_dic[neighbor_user_id]
for movie in movies:
#print movie
if movie[0] not in recommend_dic:
recommend_dic[movie[0]]=neighbor[0]
else:
recommend_dic[movie[0]]+=neighbor[0]
#print len(recommend_dic)
#創建推薦列表
recommend_list=[]
for key in recommend_dic:
#print key
recommend_list.append([recommend_dic[key],key])
recommend_list.sort(reverse=True)
#print recommend_list
user_movies = [ i[0] for i in test_dic[userid]]
return [i[1] for i in recommend_list],user_movies,test_item_to_user,neighbors
#
# 獲取電影的列表
#
def getMoviesList(file_name):
#print sys.getdefaultencoding()
movies_contents=readFile(file_name)
movies_info={}
for movie in movies_contents:
movie_info=movie.split("|")
movies_info[int(movie_info[0])]=movie_info[1:]
return movies_info
#主程序
#輸入 : 測試數據集合
if __name__ == '__main__':
reload(sys)
sys.setdefaultencoding('utf-8')
movies=getMoviesList("T:/work/reco/ml-100k/u.item")
recommend_list,user_movie,items_movie,neighbors=recommendByUserFC("T:/work/reco/ml-100k/u.data",179,80)
neighbors_id=[ i[1] for i in neighbors]
table = Texttable()
table.set_deco(Texttable.HEADER)
table.set_cols_dtype(['t', # text
't', # float (decimal)
't']) # automatic
table.set_cols_align(["l", "l", "l"])
rows=[]
rows.append([u"movie name",u"release", u"from userid"])
for movie_id in recommend_list[:20]:
from_user=[]
for user_id in items_movie[movie_id]:
if user_id in neighbors_id:
from_user.append(user_id)
rows.append([movies[movie_id][0],movies[movie_id][2],""])
table.add_rows(rows)
print table.draw()