社會化推薦之所以受到很多網站的重視,是緣於如下優點:
1. 好友推薦可以增加推薦的信任度 好友往往是用戶最信任的。用戶往往不一定信任計算機的智能,但會信任好朋友的推薦。同樣是給用戶推薦《天龍八部》,前面提到的基於物品的協同過濾算法會說是因爲用戶之前看過《射鵰英雄傳》,而好友推薦會說是因爲用戶有8個好友都非常喜歡《天龍八部》。對比這兩種解釋,第二種解釋一般能讓用戶更加心動,從而購買或者觀看《天龍八部》。
2. 社交網絡可以解決冷啓動問題 當一個新用戶通過微博或者抖音賬號登錄網站時,我們可以從社交網站中獲取用戶的好友列表,然後給用戶推薦好友在網站上喜歡的物品。從而我們可以在沒有用戶行爲記錄時就給用戶提供較高質量的推薦結果,部分解決了推薦系統的冷啓動問題。
當然,社會化推薦也有一些缺點,其中最主要的就是很多時候並不一定能提高推薦算法的離線精度(準確率和召回率)。特別是在基於社交圖譜數據的推薦系統中,因爲用戶的好友關係不是基於共同興趣產生的,所以用戶好友的興趣往往和用戶的興趣並不一致。
原理
下面兩個表格分別爲作者編的用戶好友數據與用戶興趣數據:
用戶 | 好友 |
---|---|
A | B,C,D |
B | A,C |
C | A,B,D |
D | A,C,E |
E | D |
用戶 | 興趣 |
---|---|
A | 籃球,乒乓球 |
B | 羽毛球,籃球,足球 |
C | 乒乓球,桌球,足球 |
D | 足球,羽毛球,籃球 |
E | 乒乓球,桌球,籃球,棒球 |
數據處理:
def load_data(friend_file, interest_file): # 規範數據集
fri_f = open(friend_file, "r", encoding="utf-8")
int_f = open(interest_file, "r", encoding="utf-8")
friends_data = dict()
for line in fri_f:
data = line.strip().split("\t")
friends_data[data[0]] = data[1].split(",")
interests_data = dict()
for line in int_f:
data = line.strip().split("\t")
interests_data[data[0]] = data[1].split(",")
fri_f.close()
int_f.close()
print("好友數據集: ", friends_data)
print("興趣數據集: ", interests_data)
return friends_data, interests_data
"""
好友數據集: {'A': ['B', 'C', 'D'], 'B': ['A', 'C'], 'C': ['A', 'B', 'D'], 'D': ['A', 'C', 'E'], 'E': ['D']}
興趣數據集: {'A': ['籃球', '乒乓球'], 'B': ['羽毛球', '籃球', '足球'], 'C': ['乒乓球', '桌球', '足球'], 'D': ['足球', '羽毛球', '籃球'], 'E': ['乒乓球', '桌球', '籃球', '棒球']}
"""
計算倒排表
def user_friend_interest(friends_data, interests_data): # 構建倒排列表
friends_dic = dict()
for user, friends in friends_data.items():
for friend in friends:
if friend not in friends_dic:
friends_dic[friend] = set()
friends_dic[friend].add(user)
interests_dic = dict()
for user, interests in interests_data.items():
for interest in interests:
if interest not in interests_dic:
interests_dic[interest] = set()
interests_dic[interest].add(user)
print("好友數據倒排列表: ", friends_dic)
print("興趣數據倒排列表: ", interests_dic)
return friends_dic, interests_dic
"""
好友數據倒排列表: {'B': {'C', 'A'}, 'C': {'D', 'B', 'A'}, 'D': {'E', 'C', 'A'}, 'A': {'B', 'C', 'D'}, 'E': {'D'}}
興趣數據倒排列表: {'籃球': {'D', 'E', 'B', 'A'}, '乒乓球': {'E', 'C', 'A'}, '羽毛球': {'B', 'D'}, '足球': {'B', 'C', 'D'}, '桌球': {'E', 'C'}, '棒球': {'E'}}
"""
相似度計算
有了倒排列表我們就可以計算用戶的好友熟悉度與用戶興趣相似度,兩者計算方法相同:
def similarity(data): # 好友熟悉度 相似度計算
C = dict()
N = dict()
for user, friends in data.items():
for u in friends:
N.setdefault(u, 0)
N[u] += 1 # 計算每個用戶好友數量
for v in friends:
if u == v:
continue
C.setdefault(u, {})
C[u].setdefault(v, 0)
C[u][v] += 1 # 計算共同好友數量
W = dict()
for u, related_users in C.items():
for v, cuv in related_users.items():
W.setdefault(u, {})
W[u].setdefault(v, 0)
W[u][v] = cuv / math.sqrt(N[u] * N[v])
return W
"""
用戶-好友熟悉度: {'C': {'A': 0.6666666666666666, 'E': 0.5773502691896258, 'B': 0.4082482904638631, 'D': 0.3333333333333333}, 'A': {'C': 0.6666666666666666, 'B': 0.4082482904638631, 'D': 0.3333333333333333, 'E': 0.5773502691896258}, 'B': {'D': 0.8164965809277261, 'A': 0.4082482904638631, 'C': 0.4082482904638631}, 'D': {'B': 0.8164965809277261, 'A': 0.3333333333333333, 'C': 0.3333333333333333}, 'E': {'C': 0.5773502691896258, 'A': 0.5773502691896258}}
用戶-興趣相似度: {'B': {'D': 1.0, 'E': 0.2886751345948129, 'A': 0.4082482904638631, 'C': 0.3333333333333333}, 'D': {'B': 1.0, 'E': 0.2886751345948129, 'A': 0.4082482904638631, 'C': 0.3333333333333333}, 'E': {'B': 0.2886751345948129, 'D': 0.2886751345948129, 'A': 0.7071067811865475, 'C': 0.5773502691896258}, 'A': {'B': 0.4082482904638631, 'D': 0.4082482904638631, 'E': 0.7071067811865475, 'C': 0.4082482904638631}, 'C': {'E': 0.5773502691896258, 'A': 0.4082482904638631, 'B': 0.3333333333333333, 'D': 0.3333333333333333}}
"""
推薦:
由上面的公式,可以利用python實現如下邏輯
def Recommend(user, familiarity, similarity, train): # 假設對每個物品的喜歡程度都爲1
pw = 1
rank = dict()
interacted_items = train[user] # 獲取user感興趣的物品
rank = dict()
for fid, fw in familiarity[user].items():
for item in train[fid]:
if item in interacted_items:
continue
rank.setdefault(item, 0)
rank[item] = fw * pw
for vid, sw in similarity[user].items():
for item in train[vid]:
if item in interacted_items:
continue
rank.setdefault(item, 0)
rank[item] = sw * pw
rank = sorted(rank.items(),key = lambda x:x[1],reverse = True) # 按興趣度排序
return rank
"""
爲用戶A推薦興趣列表: [('棒球', 0.7071067811865475), ('桌球', 0.4082482904638631), ('足球', 0.4082482904638631), ('羽毛球', 0.4082482904638631)]
"""