标签在我们日常生活中很常见,打标签作为一种重要的用户行为,蕴含了很多用户兴趣信息,因此深入研究和利用用户打标签的行为可以很好地指导我们改进个性化推荐系统的推荐质量。举个例子,下图是酷我音乐的标签,有了标签,用户可以快速找到自己感兴趣的歌,同时酷我也可以通过用户经常使用的标签,更精确的为用户推荐感兴趣的歌曲
原理
当拿到了用户标签行为数据,相信大家都可以想到一个最简单的个性化推荐算法,这里我们称为SimpleTagBased。其描述如下所示:
1. 统计每个用户最常用标签
2. 对于每个标签,统计被打过这个标签次数最多的物品
3. 对于一个用户,找到他常用的标签,从而找到具有这些标签的热门物品进行推荐
一个用户标签行为的数据集一般由一个三元组的集合表示,其中记录 (u,i,b) 表示用户 u 给物品 i 打上了标签 b 。当然,用户的真实标签行为数据远远比三元组表示的要复杂,比如用户打标签的时间、用户的属性数据、物品的属性数据等。所以用户 u 对物品 i 的兴趣公式如下:
案例分析
假设我们拥有一组音乐数据,如下所示:
def load_data(file_path):
records = []
f = open(file_path, "r", encoding="utf-8")
for line in f:
info = line.strip().split("\t")
records.append(info)
return records
# [['A', '一曲相思', '流行'], ['A', '生僻字', '流行'], ['A', '最后的莫西干人', '纯音乐'], ['A', '倩女幽魂', '经典'], ['B', '故乡的原风景', '纯音乐'], ['B', '生僻字', '流行'], ['B', '故乡的原风景', '纯音乐'], ['C', '倩女幽魂', '经典'], ['C', '海阔天空', '经典'], ['D', '海阔天空', '经典'], ['A', '突然好想你', '寂寞'], ['C', '走西口', '民歌'], ['D', '走西口', '民歌'], ['B', '重头再来', '励志'], ['D', '倩女幽魂', '经典'], ['C', '重头再来', '励志'], ['D', '最后的莫西干人', '纯音乐']]
数据处理
数据处理
统计出 user_tags 和 tag_items,其中 user_tags= ,tag_items =
def InitStat(records):
user_tags = dict() # 用户打过标签的次数
tag_items = dict() # 音乐被打过标签的次数,代表歌曲流行度
for user, item, tag in records:
user_tags.setdefault(user, dict())
user_tags[user].setdefault(tag, 0)
user_tags[user][tag] += 1
tag_items.setdefault(tag, dict())
tag_items[tag].setdefault(item, 0)
tag_items[tag][item] += 1
print("用户打过标签的次数: ", user_tags)
print("音乐打过标签的次数: ", tag_items)
return user_tags, tag_items
"""
用户打过标签的次数: {'A': {'流行': 2, '纯音乐': 1, '经典': 1, '寂寞': 1}, 'B': {'纯音乐': 2, '流行': 1, '励志': 1}, 'C': {'经典': 2, '民歌': 1, '励志': 1}, 'D': {'经典': 2, '民歌': 1, '纯音乐': 1}}
音乐打过标签的次数: {'流行': {'一曲相思': 1, '生僻字': 2}, '纯音乐': {'最后的莫西干人': 2, '故乡的原风景': 2}, '经典': {'倩女幽魂': 3, '海阔天空': 2}, '寂寞': {'突然好想你': 1}, '民歌': {'走西口': 2}, '励志': {'重头再来': 2}}
"""
推荐
有了上面获得的数据统计,那么我们就可以根据用户对歌曲的兴趣来为用户推荐歌曲,兴趣度越大,越被优先推荐。
def Recommend(user, K):
recommend_items = dict()
for tag, wut in user_tags[user].items():
for item, wti in tag_items[tag].items():
if item not in recommend_items:
recommend_items[item] = wut * wti # 计算用户对物品兴趣度
else:
recommend_items[item] += wut * wti
rec = sorted(recommend_items.items(),key = lambda x:x[1],reverse = True) # 将推荐歌曲按兴趣度排名
print("用户对歌曲兴趣度: ", rec)
music = []
for i in range(K):
music.append(rec[i][0])
music = "/".join(music)
print("为用户推荐歌曲: ", music)
return music
"""
可以获得为用户推荐的歌单,这里需要去除用户已经感兴趣的歌
用户对歌曲兴趣度: [('生僻字', 4), ('倩女幽魂', 3), ('一曲相思', 2), ('最后的莫西干人', 2), ('故乡的原风景', 2), ('海阔天空', 2), ('突然好想你', 1)]
推荐歌曲: 生僻字/倩女幽魂
"""
优化计算公式:
def InitStat_update(records):
user_tags = dict() # 用户打过标签的次数
tag_items = dict() # 音乐被打过标签的次数,代表歌曲流行度
tag_user = dict() # 标签被用户标记次数
for user, item, tag in records:
user_tags.setdefault(user, dict())
user_tags[user].setdefault(tag, 0)
user_tags[user][tag] += 1
tag_items.setdefault(tag, dict())
tag_items[tag].setdefault(item, 0)
tag_items[tag][item] += 1
tag_user.setdefault(tag, dict())
tag_user[tag].setdefault(user, 0)
tag_user[tag][user] += 1
print("用户打过标签的次数: ", user_tags)
print("音乐打过标签的次数: ", tag_items)
print("标签被用户使用次数: ", tag_user)
return user_tags, tag_items, tag_user
"""
用户打过标签的次数: {'A': {'流行': 2, '纯音乐': 1, '经典': 1, '寂寞': 1}, 'B': {'纯音乐': 2, '流行': 1, '励志': 1}, 'C': {'经典': 2, '民歌': 1, '励志': 1}, 'D': {'经典': 2, '民歌': 1, '纯音乐': 1}}
音乐打过标签的次数: {'流行': {'一曲相思': 1, '生僻字': 2}, '纯音乐': {'最后的莫西干人': 2, '故乡的原风景': 2}, '经典': {'倩女幽魂': 3, '海阔天空': 2}, '寂寞': {'突然好想你': 1}, '民歌': {'走西口': 2}, '励志': {'重头再来': 2}}
标签被用户使用次数: {'流行': {'A': 2, 'B': 1}, '纯音乐': {'A': 1, 'B': 2, 'D': 1}, '经典': {'A': 1, 'C': 2, 'D': 2}, '寂寞': {'A': 1}, '民歌': {'C': 1, 'D': 1}, '励志': {'B': 1, 'C': 1}}
"""
同时推荐的算法,修正为 TagBasedTFIDF
def Recommend_update(user, K):
recommend_items = dict()
for tag, wut in user_tags[user].items():
for item, wti in tag_items[tag].items():
if item not in recommend_items:
recommend_items[item] = wut * wti/log(1+len(tag_user[tag])) # 计算用户对物品兴趣度
else:
recommend_items[item] += wut * wti/log(1+len(tag_user[tag]))
rec = sorted(recommend_items.items(),key = lambda x:x[1],reverse = True) # 将推荐歌曲按兴趣度排名
print("用户对歌曲兴趣度", rec)
music = []
for i in range(K):
music.append(rec[i][0])
music = "/".join(music)
print("为用户推荐歌曲: ", music)
return music
"""
用户对歌曲兴趣度: [('生僻字', 3.6409569065073493), ('倩女幽魂', 2.1640425613334453), ('一曲相思', 1.8204784532536746), ('最后的莫西干人', 1.4426950408889634), ('故乡的原风景', 1.4426950408889634), ('海阔天空', 1.4426950408889634), ('突然好想你', 1.4426950408889634)]
推荐歌曲: 生僻字/倩女幽魂
"""
python 完整代码:
#!/usr/bin/env python
# coding: utf-8
from math import log
from collections import defaultdict
def load_data(file_path):
records = []
f = open(file_path, "r", encoding="utf-8")
for line in f:
info = line.strip().split("\t")
records.append(info)
f.close()
return records
def InitStat(records):
user_tags = dict() # 用户打过标签的次数
tag_items = dict() # 音乐被打过标签的次数,代表歌曲流行度
for user, item, tag in records:
user_tags.setdefault(user, dict())
user_tags[user].setdefault(tag, 0)
user_tags[user][tag] += 1
tag_items.setdefault(tag, dict())
tag_items[tag].setdefault(item, 0)
tag_items[tag][item] += 1
return user_tags, tag_items
def InitStat_update(records):
user_tags = dict() # 用户打过标签的次数
tag_items = dict() # 音乐被打过标签的次数,代表歌曲流行度
tag_user = dict() # 标签被用户标记次数
for user, item, tag in records:
user_tags.setdefault(user, dict())
user_tags[user].setdefault(tag, 0)
user_tags[user][tag] += 1
tag_items.setdefault(tag, dict())
tag_items[tag].setdefault(item, 0)
tag_items[tag][item] += 1
tag_user.setdefault(tag, dict())
tag_user[tag].setdefault(user, 0)
tag_user[tag][user] += 1
return user_tags, tag_items, tag_user
def InitStat_update_2(records):
user_tags = dict() # 用户打过标签的次数
tag_items = dict() # 音乐被打过标签的次数,代表歌曲流行度
tag_user = dict() # 标签被用户标记次数
item_user = dict() # 音乐被不同用户标记次数
for user, item, tag in records:
user_tags.setdefault(user, dict())
user_tags[user].setdefault(tag, 0)
user_tags[user][tag] += 1
tag_items.setdefault(tag, dict())
tag_items[tag].setdefault(item, 0)
tag_items[tag][item] += 1
tag_user.setdefault(tag, dict())
tag_user[tag].setdefault(user, 0)
tag_user[tag][user] += 1
item_user.setdefault(item, dict())
item_user[item].setdefault(user, 0)
item_user[item][user] += 1
return user_tags, tag_items, tag_user, item_user
def Recommend(user, K):
recommend_items = dict()
for tag, wut in user_tags[user].items():
for item, wti in tag_items[tag].items():
if item not in recommend_items:
recommend_items[item] = wut * wti # 计算用户对物品兴趣度
else:
recommend_items[item] += wut * wti
rec = sorted(recommend_items.items(), key=lambda x: x[1], reverse=True) # 将推荐歌曲按兴趣度排名
print(">>", rec)
music = []
for i in range(K):
music.append(rec[i][0])
music = "/".join(music)
return music
def Recommend_update(user, K):
recommend_items = dict()
for tag, wut in user_tags[user].items():
for item, wti in tag_items[tag].items():
if item not in recommend_items:
recommend_items[item] = wut * wti / log(1 + len(tag_user[tag])) # 计算用户对物品兴趣度
else:
recommend_items[item] += wut * wti / log(1 + len(tag_user[tag]))
rec = sorted(recommend_items.items(), key=lambda x: x[1], reverse=True) # 将推荐歌曲按兴趣度排名
print(">>>>>>", rec)
music = []
for i in range(K):
music.append(rec[i][0])
music = "/".join(music)
return music
def Recommend_update_2(user, K):
recommend_items = dict()
for tag, wut in user_tags[user].items():
for item, wti in tag_items[tag].items():
if item not in recommend_items:
recommend_items[item] = (wut / log(1 + len(tag_user[tag]))) * (
wti / log(1 + len(item_user[item]))) # 计算用户对物品兴趣度
else:
recommend_items[item] += (wut / log(1 + len(tag_user[tag]))) * (wti / log(1 + len(item_user[item])))
rec = sorted(recommend_items.items(), key=lambda x: x[1], reverse=True) # 将推荐歌曲按兴趣度排名
print(">>>>>>", rec)
music = []
for i in range(K):
music.append(rec[i][0])
music = "/".join(music)
return music
if __name__ == '__main__':
file_path = u"./data/标签的推荐数据.txt"
records = load_data(file_path)
# print(records)
# user_tags, tag_items = InitStat(records)
# user_tags, tag_items, tag_user = InitStat_update(records)
user_tags, tag_items, tag_user, item_user = InitStat_update_2(records)
print("用户打过标签的次数: ", user_tags)
print("音乐打过标签的次数: ", tag_items)
print("标签被用户使用次数: ", tag_user)
print("音乐被用户标记次数: ", item_user)
# rec = Recommend("A", 2)
# rec = Recommend_update("A", 2)
rec = Recommend_update_2("A", 2)
print("推荐歌曲: ", rec)