推薦系統-Item Based CF代碼實例

前邊我們已經簡單介紹了基於內容的推薦系統CB和基於協同過濾的推薦系統CF,今天我們就來看一個基於協同過濾中的基於物品的 Item Based CF 的一個實際實例來幫助大家更好的來了解和掌握以前的知識。

下面我們來看看我們的元數據,數據很簡單,每一行由userId(用戶ID)、itemId(物品ID)、score(用戶打分)組成,之間用”,“分隔。

推薦系統-Item Based CF代碼實例

我們計算的時候用下邊這個相似度計算公式,這個公式其實本質上和cos相似度計算公式一樣。

推薦系統-Item Based CF代碼實例

其中:

​ Wi,j 表示標號為i和j的兩個item的相似度

​ U(i,j) 表示同時對i和j兩個有評分的用戶的集合

​ ruj 表示用戶u對item i的評分

​ λ 為平滑參數

實際上我們在用的時候可以把分子分母相乘的後半部分當做一個常數捨去,對結果沒有沒有影響。那我們在計算的時候就可以只看前半部分了,通過分析我們就會發現,對於每個用戶來說分母都是相同的,是所有用戶對i的打分的平方和然後乘以所有用戶對j的打分的平方和,而分子就是自己對i和j的乘積,我們分別把分母拆開,就可以得出其實就是自己對i的打分除以所有用戶對i的打分的平方和(相當於歸一化)然後乘以自己對i的打分除以所有用戶對i的打分的平方和。由此我們代碼實現的時候就很簡單了。

我們舉一個簡單的例子來說明這個公式怎麼應用

推薦系統-Item Based CF代碼實例

我們要計算item1和item2的相似度,現在我們已經知道了所有同時對兩個物品打分的用戶A、B、C那麼兩個物品的相似度計算過程,首先把打分進行歸一化,先求得所有用戶對item1的打分的平方和 2^2+1^2+4^2=20​​​ ,然後求得所有用戶對item2的打分的平方和​ 5^2+3^2+2^2=38 然後對所有打分進行歸一化後再分別相乘求和,最後的相似度為

推薦系統-Item Based CF代碼實例

那麼我們現在就有了一個思路,首先把所有打分進行歸一化計算,然後找出所有對i和j打分的集合,然後計算出i和j的相似度。

下面就是按照這個思路的代碼實現,代碼為python寫的MapReduce任務。

  • 歸一化並兩兩取對過程
  • map1.py
#! /usr/bin/env python 
# -*- coding: utf-8 -*-

import sys
import math

item_score_dic = {}
user_item_score_list = []
for line in sys.stdin:
ss = line.strip().split(',')
if len(ss) != 3:
continue
user = ss[0].strip()
item = ss[1].strip()
score = float(ss[2].strip())
user_item_score_list.append((user,item,score))
score = pow(score,2)
if item_score_dic.has_key(item):
item_score_dic[item] += score
else:
item_score_dic[item] = score

for uis in user_item_score_list:
user, item, score = uis
if item_score_dic.has_key(item):
score_sqr = math.sqrt(item_score_dic[item])
print ('\\t'.join([user,item,score/score_sqr]))

reduce1.py

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys

current_user = None
item_score_list = []

for line in sys.stdin:
ss = line.strip().split('\\t')
if len(ss) != 3:
continue
user = ss[0].strip()
item = ss[1].strip()
score = float(ss[2].strip())
if not current_user:
current_user = user
if current_user != user:
for i in range(0, len(item_score_list) - 1):
for j in range(i+1, len(item_score_list)):
item_a, score_a = item_score_list[i]
item_b, score_b = item_score_list[j]
print('\\t'.join([item_a, item_b, score_a * score_b]))
print('\\t'.join([item_b, item_a, score_a * score_b]))
item_score_list = []
current_user = user

item_score_list.append((item, score))

for i in range(0, len(item_score_list) - 1):
for j in range(i + 1, len(item_score_list)):
item_a, score_a = item_score_list[i]
item_b, score_b = item_score_list[j]
print('\\t'.join([item_a, item_b, score_a * score_b]))
print('\\t'.join([item_b, item_a, score_a * score_b]))
  • item1和item2相似對求和階段
  • map2.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys

for line in sys.stdin:
ss = line.strip().split('\\t')
if len(ss) != 3:
continue
item_a = ss[0].strip()
item_b = ss[1].strip()
score = ss[2].strip()
print('%s#%s\\t%s' % item_a, item_b, score)

reduce2.py

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys

current_items = None
sum = 0.0

for line in sys.stdin:
ss = line.strip().split('\\t')
if len(ss) != 2:
continue
item_item = ss[0].strip()
score = float(ss[1].strip())
if not current_items:
current_items = item_item
if current_items != item_item:
item_a, item_b = current_items.split('#')
print('\\t'.join(item_a, item_b, sum))
sum = 0.0
current_items = item_item

sum += score

item_a, item_b = current_items.split('#')
print('\\t'.join(item_a, item_b, sum))

以上就是算法的代碼實現過程,這個算法有一個缺點就是,當數據量非常大的時候,物品兩兩取對的數量會非常大,很容易內存不夠,所以在實際的應用中我們應該隨機取一定量的數據進行計算,而不是把所有的數據都加到計算裡邊來。


如果覺得對你有幫助,可以推薦和分享給你的朋友


分享到:


相關文章: