推荐系统实践(四)----基于标签的推荐算法

  标签在我们日常生活中很常见,打标签作为一种重要的用户行为,蕴含了很多用户兴趣信息,因此深入研究和利用用户打标签的行为可以很好地指导我们改进个性化推荐系统的推荐质量。举个例子,下图是酷我音乐的标签,有了标签,用户可以快速找到自己感兴趣的歌,同时酷我也可以通过用户经常使用的标签,更精确的为用户推荐感兴趣的歌曲。

1. S i m p l e T a g B a s e d SimpleTagBased SimpleTagBased

  • 原理

  当拿到了用户标签行为数据,相信大家都可以想到一个最简单的个性化推荐算法,这里我们称为 S i m p l e T a g B a s e d SimpleTagBased SimpleTagBased。其描述如下所示:
  1. 统计每个用户最常用标签
  2. 对于每个标签,统计被打过这个标签次数最多的物品
  3. 对于一个用户,找到他常用的标签,从而找到具有这些标签的热门物品进行推荐

  一个用户标签行为的数据集一般由一个三元组的集合表示,其中记录 ( u , i , b ) (u, i, b) (u,i,b) 表示用户 u u u 给物品 i i i 打上了标签 b b b。当然,用户的真实标签行为数据远远比三元组表示的要复杂,比如用户打标签的时间、用户的属性数据、物品的属性数据等。所以用户 u u u 对物品 i i i 的兴趣公式如下:
P ( u , i ) = ∑ b n u , b n b , i P(u,i) = \sum\limits_b {{n_{u,b}}{n_{b,i}}} P(u,i)=bnu,bnb,i
   n u , b {n_{u,b}} nu,b是用户 u u u 打过标签 b b b 的次数, n b , i {n_{b,i}} nb,i是物品 i i i 被打过标签 b b b 的次数。

  • 案例分析
      假设我们拥有一组音乐数据,如下所示:
  • 数据处理
    利用三元组集合表示数据集,因为数据是自己编的,所以本来就是三元组,这里只需要简单的处理一下就好,使用 P y t h o n Python Python实现,用 r e c o r d s records records 存储标签数据的三元组,其中 r e c o r d s [ i ] = [ u s e r , i t e m , t a g ] records[i] = [user, item, tag] records[i]=[user,item,tag]
	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', '最后的莫西干人', '纯音乐']]
  • 数据处理
    统计出 u s e r _ t a g s user\_tags user_tags t a g _ i t e m s tag\_items tag_items,其中 u s e r _ t a g s = n u , b user\_tags = {n_{u,b}} user_tags=nu,b,$tag_items = n b , i {n_{b,i}} nb,i
	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)]
	推荐歌曲:  生僻字/倩女幽魂

  

2. T a g B a s e d T F I D F TagBasedTFIDF TagBasedTFIDF
  推荐完成后,我们再仔细回顾一下,发现一个问题,如果新出的一首流行歌,举个例子,比如说《生僻字》,这首歌可能很多人都听过,也都打了标签,那么 n b , i {n_{b,i}} nb,i 就非常大,那么即使 n u , b {n_{u,b}} nu,b 很小,用户对《生僻字》的兴趣度也会很大,就很有可能发生推荐错误的情况。
   所以上面这种推荐算法给热门标签对应的热门物品很大的权重,因此会造成推荐热门的物品给用户,从而降低推荐结果的新颖性。另外,这个公式利用用户的标签向量对用户兴趣建模,其中每个标签都是用户使用过的标签,而标签的权重是用户使用该标签的次数。这种建模方法的缺点是给热门标签过大的权重,从而不能反应用户个性化的兴趣。这里我们可以借鉴TF-IDF的思想,对这一公式进行改进,提出 T a g B a s e d T F I D F TagBasedTFIDF TagBasedTFIDF算法:
P ( u , i ) = ∑ b n u , b log ⁡ ( 1 + n b ( u ) ) n b , i P(u,i) = \sum\limits_b {\frac{{{n_{u,b}}}}{{\log (1 + n_b^{(u)})}}{n_{b,i}}} P(u,i)=blog(1+nb(u))nu,bnb,i
      n b ( u ) n_b^{(u)} nb(u)记录了标签 b b b 被多少个不同的用户使用过

   总体的实现跟 S i m p l e T a g B a s e d SimpleTagBased SimpleTagBased 没有多大区别,只是在数据处理这个步骤时,需要统计 n b ( u ) n_b^{(u)} nb(u)

	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}}

   同时推荐的算法,修正为 T a g B a s e d T F I D F TagBasedTFIDF 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)]
	推荐歌曲:  生僻字/倩女幽魂

   因为我们这里数据量比较少,所以看不出来效果,如果各位看官感兴趣,可以自己去网上获取数据,来实践一下效果。
  
  
3. T a g B a s e d T F I D F + + TagBasedTFIDF++ TagBasedTFIDF++
   同理,我们也可以借鉴 T F − I D F TF-IDF TFIDF 的思想对热门物品进行惩罚,从而得到 T a g B a s e d T F I D F + + TagBasedTFIDF++ TagBasedTFIDF++ 算法:
P ( u , i ) = ∑ b n u , b log ⁡ ( 1 + n b ( u ) ) n b , i log ⁡ ( 1 + n i ( u ) ) P(u,i) = \sum\limits_b {\frac{{{n_{u,b}}}}{{\log (1 + n_b^{(u)})}}\frac{{{n_{b,i}}}}{{\log (1 + n_i^{(u)})}}} P(u,i)=blog(1+nb(u))nu,blog(1+ni(u))nb,i
      n i ( u ) n_i^{(u)} ni(u) 记录了物品i被多少个不同的用户打过标签

   因为代码实现方式和 T a g B a s e d T F I D F TagBasedTFIDF TagBasedTFIDF 区别不大,这里我就不实现了,有兴趣的童鞋可以自己动手试试。

4. 分析
基于标签的音乐推荐系统

音乐推荐算法的Python实现可以使用基于用户的协同过滤推荐算法和基于项目的协同过滤推荐算法。在Python中,可以使用一些常用的库来实现这些算法,例如numpy、pandas和scikit-learn。 基于用户的协同过滤推荐算法可以通过计算用户之间的相似度来推荐相似用户喜欢的音乐。可以使用用户对音乐的评分数据来计算用户之间的相似度,然后根据相似用户的喜好推荐音乐给目标用户。 基于项目的协同过滤推荐算法可以通过计算音乐之间的相似度来推荐相似的音乐给用户。可以使用音乐的特征数据(如歌手、风格、时长等)来计算音乐之间的相似度,然后根据相似音乐推荐给用户。 除了协同过滤算法,还可以使用其他推荐算法,例如基于内容的推荐算法(通过分析音乐的内容特征来推荐相似的音乐)和深度学习算法(使用神经网络来学习用户和音乐之间的关系)。 在项目中,可以使用Python的Django框架和MySQL数据库来实现在线音乐推荐系统。前台用户可以注册、登录、浏览音乐、搜索音乐、评分音乐、收藏音乐、评论音乐等功能。后台管理员可以管理用户、音乐、音乐类型、评分、收藏、评论、歌单、兴趣标签、播放记录、权限等。 个性化推荐功能可以根据用户的喜好和行为来推荐音乐。无论是否登录,可以在前台首页展示热点推荐音乐。登录用户可以在前台首页展示个性化推荐音乐,通过协同过滤推荐算法和评分数据来推荐音乐。如果没有推荐结果,还可以进行喜好标签推荐,随机查找喜好标签下的音乐。 整个项目可以使用Python 3.8、Django 3、MySQL 8以及一些前端工具和库(如HTML页面、JavaScript脚本、jQuery脚本、Bootstrap前端框架、layer弹窗组件、webuploader文件上传组件等)来完成。 希望以上信息对您有所帮助。如果还有其他问题,请随时提问。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值