提供推薦
協同過濾算法通常的做法是對一大羣人進行搜索,並從中找到與我們品味相近的一小羣人,算法會對這些人的偏好的其他內容進行考查,並將它們組合起來構造出一個經過排名的推薦列表。
-
收集偏好
這裏如果數據量不大的時候可以直接放入內存中,但是如果數據量過大的時候要放入數據庫
-
尋找相近的用戶
收集數據完成後,需要計算他們的相似度評價值,常見的兩種的方式爲:歐幾里得距離和皮爾遜相關度
-
關於對於選擇哪一種相似度量方法
其實還有Jaccard係數和曼哈頓距離等,其實各種的相似度量的方法對結果影響是很小的。
-
爲評分者打分
如果是基於用戶的協同過濾的話,這裏求的是用戶與目標用戶的之間的相似度,並按相似度高低排序
如果是基於物品的協同過濾的話,這裏求的是物品與目標物品的相似度,並按相似度的高低排序
-
推薦物品
-
考慮到評論者還沒對某些影片做評論,而這些影片正是我們喜歡的影片,還有可能就是匹配到某個熱衷於某個影片的古怪者,但是所有的評論者都不看好這部影片:解決方案爲 可以採用加權得方式對結果進行修正,通過相似度在乘上他們影片得分數值
-
我們可以採用計算總值得方式進行排序得出結果,考慮到一部受更多人評論的影片對結果產生更大的影響,爲了修正這個問題,我們需要除以表中所有對這部電影的有關評論的評論者的相似度和
-
-
匹配商品
其實這裏是基於物品的協同過濾算法,通過求物品的相似度來基於物品進進行推薦
下面介紹兩種協同過濾算法
基於用戶的協同過濾算法
收集數據的偏好 — 數據的收集問題
-
尋找相近的用戶,需要一種度量方式來確定人們在品味方面的相似度,一般稱爲相似度評價值。常用的方法有歐幾里德距離和皮爾遜相關度。
-
歐幾里德距離評價:經過人們一致評價的物品爲座標軸,然後將參與評價的人繪製到座標軸上,並考察他們的距離。
from math import sqrt # 返回一個關於person1與person2的基於距離的相似度評價 def sim_distance(prefs, person1, person2): si = {} for item in prefs[person1]: if item in prefs[person2]: si[item] = 1 if len(si) == 0: return 0 sum_of_squares = sum( [pow(prefs[person1][item] - prefs[person2][item], 2) for item in prefs[person1] if item in prefs[person2]]) # 新函數返回總是介於0-1的之間的值,返回1表示兩個人具有相同的愛好,這裏分母上+1的防止分母爲0 return 1 / (1 + sqrt(sum_of_squares))
-
皮爾遜相關度評價:計算公式較爲複雜,但是他在數據不是很規範的時候(比如偏差很大的時候),會傾向給出好的結果。採用皮爾遜方法評價時候,它可以修正 “誇大分值”,雖然一個人總是比另一個人的評分高,但是使用皮爾遜相關係數依然可以存在相關性,但是如使用歐幾里得算法,得出兩個人的不相近的結論。
def sim_pearson(prefs,p1,p2): # Get the list of mutually rated items si={} for item in prefs[p1]: if item in prefs[p2]: si[item]=1 # if they are no ratings in common, return 0 if len(si)==0: return 0 # Sum calculations n=len(si) # Sums of all the preferences sum1=sum([prefs[p1][it] for it in si]) sum2=sum([prefs[p2][it] for it in si]) # Sums of the squares sum1Sq=sum([pow(prefs[p1][it],2) for it in si]) sum2Sq=sum([pow(prefs[p2][it],2) for it in si]) # Sum of the products pSum=sum([prefs[p1][it]*prefs[p2][it] for it in si]) # Calculate r (Pearson score) num=pSum-(sum1*sum2/n) den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n)) if den==0: return 0 r=num/den return r
-
-
爲評論者打分
# 從反應偏好的字典種返回最爲匹配的者 # 返回的結果的個數和相似度函數均爲可選參數 def topMatches(prefs, person, n=5, similarity=sim_pearson): scores = [(similarity(prefs, person, other), other) for other in prefs if other != person] scores.sort() scores.reverse() return scores[0:n]
-
推薦物品
def getRecommendations(prefs, person, similarity=sim_pearson): totals = {} simSums = {} for other in prefs: if other == person: continue sim = similarity(prefs, person, other) if sim <= 0: continue for item in prefs[other]: if item not in prefs[person] or prefs[person][item] == 0: totals.setdefault(item, 0) totals[item] += prefs[other][item] * sim simSums.setdefault(item, 0) simSums[item] += sim rankings = [(total / simSums[item], item) for item, total in totals.items()] rankings.sort() rankings.reverse() return rankings
基於物品的協同過濾算法
基於用戶的協同過濾算法對於數量以千的用戶數量或者物品規模是可以的,但是對於大型的數據量,其速度會非常慢。
同時,一個銷售量爲數百萬的網站,也許在用戶偏好方面上會有很少的重疊,可以讓用用戶的相似性判斷非常的難,在擁有大量的數據的時候,基於物品的協同過濾算法能夠得到更好的結果,而且它允許我們將大量的計算任務預先執行,
-
構造物品比較數據集 當用戶的基數和評分的數量不是很大的時候,需要頻繁的執行該函數,才能使相似度不至於過期,隨着用戶的數量不斷地增長地時候,物品之間地相似度評價值通常會變得很穩定。
def transformPrefs(prefs): result = {} for person in prefs: for item in prefs[person]: result.setdefault(item, {}) # 將物品和人員對調 result[item][person] = prefs[person][item] return result # movies = transformPrefs(critics) def calculateSimilarItems(prefs, n=10): result = {} itemPrefs = transformPrefs(prefs) c = 0 for item in itemPrefs: c += 1 if c % 100 == 0: print("{} / {}".format(c, len(itemPrefs))) scores = topMatches(itemPrefs, item, n=n, similarity=sim_distance) result[item] = scores return result itemsim = calculateSimilarItems(critics)
-
獲得推薦
def getRecommendedItems(prefs, itemMatch, user): userRatings = prefs[user] scores = {} totalSim = {} for (item, rating) in userRatings.items(): for (similarity, item2) in itemMatch[item]: if item2 in userRatings: continue scores.setdefault(item2, 0) scores[item2] += similarity * rating totalSim.setdefault(item2, 0) totalSim[item2] += similarity rankings = [(score / totalSim[item], it em) for item, score in scores.items()] rankings.sort() rankings.reverse() return rankings print(getRecommendedItems(critics,itemsim,'Toby'))
使用數據集對算法進行測試
def loadMovieLens(path='/data/movielens'):
movies = {}
for line in open(path + "/u.item"):
(ids, title) = line.split('|')[0:2]
movies[ids] = title
prefs = {}
for line in open(path, '/u.data'):
(user, movieid, rating, ts) = line.split('\t')
prefs.setdefault(user, {})
prefs[user][movies[movieid]] = float(rating)
return prefs
prefs = loadMovieLens()
getRecommendations(prefs,'87')[0:30]
itemsim = calculateSimilarItems(prefs,n=50)
getRecommendedItems(prefs,itemsim,'87')[0:30]
是基於用戶進行協同過濾還是基於物品進行協同過濾
針對大數據集地時候,基於物品進行過濾地時候比基於用戶進行過濾的更快,不過它要維護物品相似度表的額外開銷,
對於稀疏矩陣來說,基於物品的協同過濾要好於基於用戶的的,對於密集性的數據集,兩者的效果幾乎一樣。
基於用戶的協同過濾算法更加易於實現,通常適合於規模較小的變換很頻繁的內存數據集。