《推荐系统笔记(十五)》流行度以及基于流行度的推荐

基于流行度的推荐是围绕着流行度的计算给出的推荐。那么,如何定义流行度呢?

流行度有很多定义或者计算方法,比如,

  • 简单统计一段时间内的物品的购买次数,
  • 或者更加复杂的基于概率论的计算方法

无论流行度计算方式如何,影响流行度的两个因素,大概是

  • 时间因素,比如,不同时间段内的新闻播报的热度不同,今年和去年流行的衣服不同等等
  • 空间因素,比如,位于网站首页的物品和位于多级子页面下的物品流行度不同,不同国家的明星在国内外的关注度也不同

我们将采用movielens数据集,采用最简单的流行度统计方式,即电影的观看总人数作为流行度。

movielens数据集已经上传,各位可以免费下载。

我们的思路是这样的,根据电影流行度定义用户相似度,再让最相似的几个用户对电影投票,按照票数排名,推荐给用户前几名电影。

具体的,

  • 定义电影 i i i的流行度 p o p u l a r i t y ( i ) popularity(i) popularity(i)为电影 i i i的观看人数
  • 相比于热门电影,如果两个用户同时看了很多冷门电影,那么更能说明这两个用户兴趣相似
  • 我们给予流行度低的物品更大的权重,我们可以给出电影 i i i的权重 w e i g h t ( i ) weight(i) weight(i) w e i g h t ( i ) = 平 均 流 行 度 p o p u l a r i t y ( i ) weight(i)=\frac{平均流行度}{popularity(i)} weight(i)=popularity(i)
    这里,我们认为低于平均流行度的物品为流行度低,高于平均流行度的为高流行度物品,对于低流行度物品我们给予大于1的权重,对于高流行度的物品我们给予小于1的权重
  • 根据电影权重,计算用户 u u u和用户 v v v的相似度 s i m i l a r i t y ( u , v ) similarity(u, v) similarity(u,v),这里,我们说共同看过的电影权重和越大,越说明看过的冷门电影越多,越说明两个用户相似,相似度公式 s i m i l a r i t y ( u , v ) = ∑ i ∈ N ( u ) ∩ N ( v ) w e i g h t ( i ) ∑ i ∈ N ( u ) ∪ N ( v ) w e i g h t ( i ) similarity(u, v)=\frac{\sum_{i\in N(u)\cap N(v)}weight(i)}{\sum_{i\in N(u)\cup N(v)}weight(i)} similarity(u,v)=iN(u)N(v)weight(i)iN(u)N(v)weight(i)
  • 根据用户相似度,找出用户 u u u最相似的10名用户
  • 让这些用户对所有的电影进行投票,如果看过就投1票,如果没有看过,就不投票,记每个电影的投票结果为 n u m s ( i ) nums(i) nums(i)
  • 显然,热门电影的投票结果显然比较高,但是如果热门电影用户没有看过,说明用户可能根本不喜欢热门电影,因此,在推荐结果上,我们还需要考虑流行度的影响,即电影 i i i的得分 s c o r e ( i ) score(i) score(i) s c o r e ( i ) = n u m s ( i ) l o g ( 1 + p o p u l a r i t y ( i ) ) score(i)=\frac{nums(i)}{log(1+popularity(i))} score(i)=log(1+popularity(i))nums(i)
  • 根据最终得分,取得得分最多的 k k k个电影作为推荐
# 第三方库
import numpy as np
import pandas as pd
# 数据载入
data = pd.read_csv(r'D:\myfile\开课吧\推荐系统\第六节\movielens\ratings.csv', )
data.head()
# 去掉timestamp
data.drop('timestamp', axis=1, inplace=True)
data.head()
# 统计电影流行度popularity
popularity = data.groupby('movieId')['userId'].count()
popularity.head()
# 计算电影权重weight
weight = popularity.apply(lambda x: popularity.mean()/x)
weight.head()
# 所有用户的集合
users = list(data['userId'].unique())

# user_items字典,记录用户看过的电影集合
user_items = {}
for user, group in data.groupby('userId'):
    user_items[user] = set(group['movieId'].tolist())

# 计算用户u和用户v的相似度similarity
# 我们仅需要计算 u<v的部分,因为u>v是一样的
similarity = {}
for u in users:
    similarity.setdefault(u, {})
    
    for v in users:
        if u < v:
            total_similarity, intersect_similarity = 0, 0
            # 用户u和用户v看过的所有电影集合
            total = user_items[u].union(user_items[v])
            # 用户u和用户v看过电影集合的交集
            intersect = user_items[u].intersection(user_items[v])
            
            for i in total:
                similarity_i = weight[i]
                total_similarity + similarity_i
                if i in intersect:
                    intersect_similarity += similarity_i
            
            if total_similarity < 1e-6:
                similarity[u][v] = 0
            else:
                similarity[u][v] = intersect_similarity / total_similarity
# 对于用户u,给出最相似的10个用户
def get_tenNeighbors(u):
    return list(dic(sorted(similarity[u].items(), key=lambda x: x[1], reverse=True)[:10]).keys())

get_tenNeighbors(1) 
# 所有物品集合items
items = list(popularity.index)

# 对于这些用户,计算所有物品的观看次数
def get_nums(u):
    item_nums = {}
    for v in get_tenNeighbors(u):
        for i in items:
            item_nums.setdefault(i, 0)
            
            if i in user_items[v]:
                item_nums[i] += 1
    return item_nums

# 计算所有物品的score
def get_scores(u):
    item_nums = get_nums(u)
    scores = {}
    
    for i, nums in item_nums.items():
        scores[i] = nums / np.log(1 + popularity[i])
    
    return scores

# 对scores进行排序,进行topN排序
def topN(u, N=4):
    scores = get_scores(u)
    return sorted(scores.items(), key=lambda x: x[1], reverse=True)[:N]
# 测试
topN(1)

由于计算用户之间的相似度非常耗费时间,实际上,这里的复杂度计算也是很高的,复杂度有 O ( n m 2 ) O(nm^2) O(nm2),这里, m m m为用户数, n n n为物品数。

实际上,这里我运行了二十分钟,还是没有跑出结果,原因就是复杂度高了。可能的解决方式是,先将数据量减小,在小的数据集上跑一跑,然后再在原本数据集上跑。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值