推荐算法(3):利用用户标签数据

第一种CB
第二种 UCF
第三种 用户标签行为数据。就是人为的添加的标签,比如用户对看的一部电影打标签,写博客时作者给博客打的标签。标签分两种:一种是作者或者专家给商品打标签,一种是普通用户对商品打标签。后者被称为UGC。
商品的内容信息和标签信息,都是特征。基于CF的推荐算法不用特征,只用用户行为。

1.UGC代表应用:
UGC标签系统的鼻祖Delicious,论文书签网站CiteULike,音乐网站Last.fm,视频网站Hulu,书和电影评分网站豆瓣等。

2.几个问题:
如何利用用户打标签的行为为其推荐物品(基于标签的推荐)?
如何在用户给物品打标签时为其推荐适合该物品的标签(标签推荐)?

用户为什么要打标签?
社会维度:有些标签是为上传者能够更好的组织自己得信息,有些标签是为了其他广大用户找到想要的信息;功能维度:有些标注是为了更好的组织内容,方便用户将来的查找,有些标签是为了传达某些信息,比如照片拍摄的时间和地点。

用户怎么打标签?
横坐标表示标签的流行度x,纵坐标表示该流行度的所有商品的数量f(x),服从长尾分布。

用户打什么样的标签?
希望用户为物品打的标签能够准确的描述物品内容属性,但用户往往不会按照我们预期的想法操作,而是会给物品打上各种奇奇怪怪的标签。,分为以下几类:表面物品是什么,表明物品的种类,表明谁拥有该物品,表示用户的观点(搞笑),用户相关的标签(我的最爱),用户的任务(找工作)。

3.基于标签的推荐系统
一个标签是用户对物品的一个描述,比如关键词“搞笑”。但是在进行推荐时,用户标签行为数据不仅包括**(user_id,item_id,tags)**。还包括用户的内容信息和商品的内容信息。(基于CF的推荐,不需要用户和商品的内容信息

实验设置:实验数据是Delicious数据集和CiteULIke数据集;将数据分为10份,9份用来训练,1份用来测试;评价指标是准确率,召回率,多样性;

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

一个用户标签行为的数据集一般由一个三元组的集合表示,其中记录 (u,i,b) 表示用户 u 给物品 i打上了标签 b。当然,用户的真实标签行为数据远远比三元组表示的要复杂,比如用户打标签的时间、用户的属性数据、物品的属性数据等。所以用户 u 对物品 i 的兴趣公式如下:
  

2.TagBasedTFIDF
问题:如果一个item很热门,则会导致nb,i很大,那么即使nu,b小,最后的结果也会很大。
推荐完成后,我们再仔细回顾一下,发现一个问题,如果新出的一首流行歌,举个例子,比如说《生僻字》,这首歌可能很多人都听过,也都打了标签,那么 nb,i 就非常大,那么即使 nu,b 很小,用户对《生僻字》的兴趣度也会很大,就很有可能发生推荐错误的情况。*

所以上面这种推荐算法给热门标签对应的热门物品很大的权重,因此会造成推荐热门的物品给用户,从而降低推荐结果的新颖性。另外,这个公式利用用户的标签向量对用户兴趣建模,其中每个标签都是用户使用过的标签,而标签的权重是用户使用该标签的次数。这种建模方法的缺点是给热门标签过大的权重,从而不能反应用户个性化的兴趣。这里我们可以借鉴TF-IDF的思想,对这一公式进行改进,提出TagBasedTFIDF算法:

总体的实现跟 SimpleTagBased SimpleTagBasedSimpleTagBased 没有多大区别,只是在数据处理这个步骤时,需要统计n(u)b

3.TagBasedTFIDF++
同理,我们也可以借鉴 TF−IDF的思想对热门物品进行惩罚,从而得到 TagBasedTFIDF++

4.基于图的推荐算法(三元)
基于用户动作行为的数据,可以将用户行为表示在图上,通过随机游走获取概率值,从而进行推荐。现在是基于用户标签行为的数据,由原来的二元组(user,item)变为三元组(user,item,tag)。有两种方法:第一种是对每一个(user,item,tag),添加三条无向边user_item,user_tag,item_tag。第二种方法是将节点分为三列,分别为用户标签商品,每次添加两条有向边user->tag以及tag->item。在图上表示了用户标签数据后,利用随机游走算法获得概率值,然后进行推荐。

二、数据的稀疏性
在前边的算法中,用户兴趣和物品的联系是通过B(u) B(i)交集得到的,但是对于新用户,这个交集的结果将会非常小,为了提高推荐结果的可靠性,这里我们要对标签进行扩展,比如若用户曾经用过“推荐系统”这个标签,我们可以将这个标签的相似标签也加入到用户标签集合中,比如“个性化”、“协同过滤”等标签。
进行标签扩展的方法有很多,比如说话题模型,这里遵循简单原则介绍一种基于邻域的方法
**标签扩展的本质是找到与他相似的标签,也就是计算标签之间的相似度。最简单的相似度可以是同义词。**如果有一个同义词词典,就可以根据这个词典进行标签扩展。如果没有这个词典,我们可以从数据中统计出标签的相似度。
如果认为同一个物品上的不同标签具有某种相似度,那么当两个标签同时出现在很多物品的标签集合中时,我们就可以认为这两个标签具有较大的相似度。对于标签b,令N(b)为有标签b的物品的集合,n_{b,i}为给物品i打上标签b的用户数,我们可以通过如下余弦相似度公式计算标签b和标签b’的相似度:

N(b)表示有标记b的物品集合
nb,i表示给物品i标记b的用户数

三、标签清理
不是所有标签都能反应用户的兴趣。比如,在一个视频网站中,用户可能对一个视频打了一个表示情绪的标签,比如“不好笑”,但我们不能因此认为用户对“不好笑”有兴趣,并且给用户推荐其他具有“不好笑”这个标签的视频。相反,如果用户对视频打过“成龙”这个标签,我们可以据此认为用户对成龙的电影感兴趣,从而给用户推荐成龙其他的电影。同时,标签系统里经常出现词形不同、词义相同的标签,比如recommender system和recommendation engine就是两个同义词。
标签清理的另一个重要意义在于将标签作为推荐解释。如果我们要把标签呈现给用户,将其作为给用户推荐某一个物品的解释,对标签的质量要求就很高。首先,这些标签不能包含没有意义的停止词或者表示情绪的词,其次这些推荐解释里不能包含很多意义相同的词语。
一般来说有如下标签清理方法:
 去除词频很高的停止词;
 去除因词根不同造成的同义词,比如 recommender system和recommendation system;
 去除因分隔符造成的同义词,比如 collaborative_filtering和collaborative-filtering。
为了控制标签的质量,很多网站也采用了让用户进行反馈的思想,即让用户告诉系统某个标签是否合适。

四、标签推荐
当用户浏览某个物品时,标签系统非常希望用户能够给这个物品打上高质量的标签,这样才能促进标签系统的良性循环。因此,很多标签系统都设计了标签推荐模块给用户推荐标签。

标签推荐的四种简单算法:
a:给用户u推荐整个系统里最热门的标签(这里将这个算法称为PopularTags)令tags[b]为标签b的热门程度,那么这个算法的实现如下:

def RecommendPopularTags(user,item, tags, N): return sorted(tags.items(), key=itemgetter(1), reverse=True)[0:N]

  • 1

  • 2

  • 3

  • 1

  • 2

  • 3

b:给用户u推荐物品i上最热门的标签(这里将这个算法称为ItemPopularTags)。令item_tags[i][b]为物品i被打上标签b的次数

def RecommendItemPopularTags(user,item, item_tags, N):    return sorted(item_tags[item].items(), key=itemgetter(1), reverse=True)[0:N]1212

c:是给用户u推荐他自己经常使用的标签(这里将这个算法称为UserPopularTags)。令user_tags[u][b]为用户u使用标签b的次数

def RecommendUserPopularTags(user,item, user_tags, N): return sorted(user_tags[user].items(), key=itemgetter(1), reverse=True)[0:N]

  • 1

  • 2

  • 3

  • 1

  • 2

  • 3

d:前面两种的融合(这里记为HybridPopularTags),该方法通过一个系数将上面的推荐结果线性加权,然后生成最终的推荐结果。(注意在上面的实现中,我们在将两个列表线性相加时都将两个列表按最大值做了归一化,这样的好处是便于控制两个列表对最终结果的影响,而不至于因为物品非常热门而淹没用户对推荐结果的影响,或者因为用户非常活跃而淹没物品对推荐结果的影响。)

def RecommendHybridPopularTags(user,item, user_tags, item_tags, alpha, N):    max_user_tag_weight = max(user_tags[user].values())    for tag, weight in user_tags[user].items():        ret[tag] = (1 – alpha) * weight / max_user_tag_weight    max_item_tag_weight = max(item_tags[item].values())    for tag, weight in item_tags[item].items():        if tag not in ret:            ret[tag] = alpha * weight / max_item_tag_weight        else:            ret[tag] += alpha * weight / max_item_tag_weight    return sorted(ret[user].items(), key=itemgetter(1), reverse=True)[0:N]`123456789101112123456789101112
(0)

相关推荐