協同過濾,即Collaborative Filtering,簡稱CF。
主要內容:
1. 協同過濾的簡介
2. 協同過濾的核心
3. 協同過濾的實現
4. 協同過濾的應用
--------------------------------------------------------------------------
1. 協同過濾的簡介
關於協同過濾的一個最經典的例子就是看電影,有時候不知道哪一部電影是我們喜歡的或者評分比較高的,那麼通常的做法就是問問周圍的朋友,看看最近有什麼好的電影推薦。在問的時候,都習慣於問跟自己口味差不多的朋友,這就是協同過濾的核心思想。
協同過濾是在海量數據中挖掘出小部分與你品味類似的用戶,在協同過濾中,這些用戶成爲鄰居,然後根據他們喜歡的東西組織成一個排序的目錄推薦給你。所以就有如下兩個核心問題
(1)如何確定一個用戶是否與你有相似的品味?
(2)如何將鄰居們的喜好組織成一個排序目錄?
協同過濾算法的出現標誌着推薦系統的產生,協同過濾算法包括基於用戶和基於物品的協同過濾算法。
2. 協同過濾的核心
要實現協同過濾,需要進行如下幾個步驟:
(1)收集用戶偏好
(2)找到相似的用戶或者物品
(3)計算並推薦
(1)收集用戶偏好
從用戶的行爲和偏好中發現規律,並基於此進行推薦,所以如何收集用戶的偏好信息成爲系統推薦效果最基礎的決定因素。用戶有很多種方式向系統提供自己的偏好信息,比如:評分,投票,轉發,保存書籤,購買,點擊流,頁面停留時間等等。
以上的用戶行爲都是通用的,在實際推薦引擎設計中可以自己多添加一些特定的用戶行爲,並用它們表示用戶對物品的喜好程度。通常情況下,在一個推薦系統中,用戶行爲都會多於一種,那麼如何組合這些不同的用戶行爲呢 ?基本上有如下兩種方式:
(1)將不同的行爲分組
一般可以分爲查看和購買,然後基於不同的用戶行爲,計算不同用戶或者物品的相似度。類似與噹噹網或者亞馬遜給出的“購買了該書的人還購買了”,“查看了該書的人還查看了”等等。
(2)不同行爲產生的用戶喜好對它們進行加權對不同行爲產生的用戶喜好進行加權,然後求出用戶對物品的總體喜好。
好了,當我們收集好用戶的行爲數據後,還要對數據進行預處理,最核心的工作就是減噪和歸一化。
減噪: 因爲用戶數據在使用過程中可能存在大量噪音和誤操作,所以需要過濾掉這些噪音。
歸一化:不同行爲數據的取值相差可能很好,例如用戶的查看數據肯定比購買數據大得多。通過歸一化,才能使數據更加準確。
通過上述步驟的處理,就得到了一張二維表,其中一維是用戶列表,另一維是商品列表,值是用戶對商品的喜
好。還是以電影推薦爲例,如下表:
找到相似的用戶或物品
對用戶的行爲分析得到用戶的喜好後,可以根據用戶的喜好計算相似用戶和物品,然後可以基於相似用戶或物品進行推薦。這就是協同過濾中的兩個分支了,基於用戶的和基於物品的協同過濾。
關於相似度的計算有很多種方法,比如常用的餘弦夾角,歐幾里德距離度量,皮爾遜相關係數等等。而如果採用歐幾里德度量,那麼可以用如下公式來表示相似度
在計算用戶之間的相似度時,是將一個用戶對所有物品的偏好作爲一個向量,而在計算物品之間的相似度時,是將所有用戶對某個物品的偏好作爲一個向量。求出相似度後,接下來可以求相似鄰居了。
計算並推薦
在上面,我們求出了相鄰用戶和相鄰物品,接下來就應該進行推薦了。當然從這一步開始,分爲兩方面,分別是基於用戶的協同過濾和基於物品的協同過濾。我會分別介紹它們的原理
(1)基於用戶的協同過濾算法
在上面求相似鄰居的時候,通常是求出TOP K鄰居,然後根據鄰居的相似度權重以及它們對物品的偏好,預測當前用戶沒有偏好的未涉及物品,計算得到一個排序的物品列表進行推薦。
(2)基於物品的協同過濾算法
跟上述的基於用戶的協同過濾算法類似,但它從物品本身,而不是用戶角度。比如喜歡物品A的用戶都喜歡物品C,那麼可以知道物品A與物品C的相似度很高,而用戶C喜歡物品A,那麼可以推斷出用戶C也可能喜歡物品C。如下圖:
上面的相似度權重有時候需要加入懲罰因子,舉個例子,在日常生活中,我們每個人購買衛生紙的的頻率比較高,但是不能說明這些用戶的興趣點相似,但是如果它們都買了照相機,那麼就可以大致推出它們都是攝影愛好者。所以像衛生紙這樣的物品在計算時,相似度權重需要加上懲罰因子或者乾脆直接去掉這類數據。
適用場景
對於一個在線網站,用戶的數量往往超過物品的數量,同時物品數據相對穩定,因此計算物品的相似度不但計算量小,同時不必頻繁更新。但是這種情況只適用於電子商務類型的網站,像新聞類,博客等這類網站的系統推薦,情況往往是相反的,物品數量是海量的,而且頻繁更新。所以從算法複雜度角度來說,兩種算法各有優勢。關於協同過濾的文章,可以參考這裏:http://www.tuicool.com/articles/6vqyYfR
3. 協同過濾的實現
上面已經介紹了協同過濾的核心思想,現在就來實戰一下吧! 採用數據集如下
鏈接:http://grouplens.org/datasets/movielens/
這個數據集是很多用戶對各種電影的評分。接下來先採用Python實現基於用戶的協同過濾算法。
首先,我們需要以表格形式讀取數據,需要用到Texttable第三方包。安裝包如下鏈接
鏈接:https://pypi.python.org/pypi/texttable/
大致用法如下:
更多方法的使用需要參考Textdtable的源文件texttable.py。接下來可以實現協同過濾算法了。
代碼:
# -*-coding=utf-8 -*-
import sys
import math
from texttable import Texttable
#計算餘弦距離
def getCosDist(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_x += key1[1] * key1[1]
sum_y += key2[1] * key2[1]
sum_xy += key1[1] * key2[1]
if sum_xy == 0.0:
return 0
demo = math.sqrt(sum_x * sum_y)
return sum_xy / demo
#讀取文件,讀取以行爲單位,每一行是列表裏的一個元素
def readFile(filename):
contents = []
f = open(filename, "r")
contents = f.readlines()
f.close()
return contents
#數據格式化爲二維數組
def getRatingInfo(ratings):
rates = []
for line in ratings:
rate = line.split("\t")
rates.append([int(rate[0]), int(rate[1]), int(rate[2])])
return rates
#生成用戶評分數據結構
def getUserScoreDataStructure(rates):
#userDict[2]=[(1,5),(4,2)].... 表示用戶2對電影1的評分是5,對電影4的評分是2
userDict = {}
itemUser = {}
for k in rates:
user_rank = (k[1], k[2])
if k[0] in userDict:
userDict[k[0]].append(user_rank)
else:
userDict[k[0]] = [user_rank]
if k[1] in itemUser:
itemUser[k[1]].append(k[0])
else:
itemUser[k[1]] = [k[0]]
return userDict, itemUser
#計算與指定用戶最相近的鄰居
def getNearestNeighbor(userId, userDict, itemUser):
neighbors = []
for item in userDict[userId]:
for neighbor in itemUser[item[0]]:
if neighbor != userId and neighbor not in neighbors:
neighbors.append(neighbor)
neighbors_dist = []
for neighbor in neighbors:
dist = getCosDist(userDict[userId], userDict[neighbor])
neighbors_dist.append([dist, neighbor])
neighbors_dist.sort(reverse = True)
return neighbors_dist
#使用UserFC進行推薦,輸入:文件名,用戶ID,鄰居數量
def recommendByUserFC(filename, userId, k = 5):
#讀取文件
contents = readFile(filename)
#文件格式數據轉化爲二維數組
rates = getRatingInfo(contents)
#格式化成字典數據
userDict, itemUser = getUserScoreDataStructure(rates)
#找鄰居
neighbors = getNearestNeighbor(userId, userDict, itemUser)[:5]
#建立推薦字典
recommand_dict = {}
for neighbor in neighbors:
neighbor_user_id = neighbor[1]
movies = userDict[neighbor_user_id]
for movie in movies:
if movie[0] not in recommand_dict:
recommand_dict[movie[0]] = neighbor[0]
else:
recommand_dict[movie[0]] += neighbor[0]
#建立推薦列表
recommand_list = []
for key in recommand_dict:
recommand_list.append([recommand_dict[key], key])
recommand_list.sort(reverse = True)
user_movies = [k[0] for k in userDict[userId]]
return [k[1] for k in recommand_list], user_movies, itemUser, neighbors
#獲取電影的列表
def getMovieList(filename):
contents = readFile(filename)
movies_info = {}
for movie in contents:
single_info = movie.split("|")
movies_info[int(single_info[0])] = single_info[1:]
return movies_info
#從這裏開始運行
if __name__ == '__main__':
reload(sys)
sys.setdefaultencoding('utf-8')
#獲取所有電影的列表
movies = getMovieList("u.item")
recommend_list, user_movie, items_movie, neighbors = recommendByUserFC("u.data", 50, 80)
neighbors_id=[ i[1] for i in neighbors]
table = Texttable()
table.set_deco(Texttable.HEADER)
table.set_cols_dtype(['t', 't', 't'])
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][1],""])
table.add_rows(rows)
print table.draw()
推薦結果如下:
接下來再來看一個題目,這個題目是2014年阿里的大數據競賽題目,描述可以參考如下鏈接
題目:http://102.alibaba.com/competition/addDiscovery/gameIntroduce.htm
題意:根據用戶在天貓的4個月的行爲日誌,建立用戶的品牌偏好,並預測他們在接下來的一個月內對品牌商品的
購買行爲。開放的字段類型如下
解析:http://www.tuicool.com/articles/AN7Rf2
上面的是建立的簡單的模型,實際上在天貓,有基於行爲簇的用戶偏好分析。
協同過濾資料