第二章 利用用户行为数据
实验设计和算法评测
数据集
-
采用 GroupLens 提供的 MovieLens 数据集介绍和评测各种算法。
- MovieLens 数据集有3个不同的版本,选用中等大小的数据集。
- 该数据集包含6000多用户对4000多部电影的100万条评分。
- 该数据集是一个评分数据集,用户可以给电影评5个不同等级的分数(1~ 5分)。
-
研究隐反馈数据集中的 TopN 推荐问题,忽略数据集中的评分记录。也就是说,TopN推荐的任务是预测用户会不会对某部电影评分,而不是预测用户在准备对某部电影评分的前提下会给电影评多少分。
实验设计
- 协同过滤算法的离线实验一般如下设计。
-
首先,将用户行为数据集按照均匀分布随机分成 M 份(取 M=8),挑选一份作为测试集,将剩下的 M-1 份作为训练集。
-
然后在训练集上建立用户兴趣模型,并在测试集上对用户行为进行预测,统计出相应的评测指标。
-
为了保证评测指标并不是过拟合的结果,需要进行 M 次实验,并且每次都使用不同的测试集。
-
然后将 M 次实验测出的评测指标的平均值作为最终的评测指标。
# 下面的 Python 代码描述了将数据集随机分成训练集和测试集的过程: def SplitData(data, M, k, seed): test = [] train = [] random.seed(seed) for user, item in data: if random.randint(0, M) == k: test.append([user, item]) else: train.append([user, item]) return train, test
- 每次实验选取不同的 k k k( 0 ≤ k ≤ M − 1 0≤ k≤ M-1 0≤k≤M−1)和相同的随机数种子 seed,进行 M 次实验就可以得到 M 个不同的训练集和测试集,然后分别进行实验,用M次实验的平均值作为最后的评测指标。
- 这样做主要是防止某次实验的结果是过拟合的结果(over fitting),但如果数据集够大,模型够简单,为了快速通过离线实验初步地选择算法,也可以只进行一次实验。
-
评测指标
-
对用户 u u u 推荐 N N N 个物品(记为 R ( u ) R(u) R(u) ),令用户 u u u 在测试集上喜欢的物品集合为 T ( u ) T(u) T(u),然后可以通过准确率/召回率评测推荐算法的精度: R e c a l l = ∑ u ∣ R ( u ) ⋂ T ( u ) ∣ ∑ u ∣ T ( u ) ∣ Recall=\frac{\sum_{u}|R(u)\bigcap T(u)|}{\sum_{u}|T(u)|} Recall=∑u∣T(u)∣∑u∣R(u)⋂T(u)∣ P r e c i s i o n = ∑ u ∣ R ( u ) ⋂ T ( u ) ∣ ∑ u ∣ R ( u ) ∣ Precision=\frac{\sum_{u}|R(u)\bigcap T(u)|}{\sum_{u}|R(u)|} Precision=∑u∣R(u)∣∑u∣R(u)⋂T(u)∣
-
召回率描述有多少比例的用户—物品评分记录包含在最终的推荐列表中,而准确率描述最终的推荐列表中有多少比例是发生过的用户—物品评分记录。
# 下面两段代码给出了召回率和准确率的计算方法。 def Recall(train, test, N): hit = 0 all = 0 for user in train.keys(): tu = test[user] rank = GetRecommendation(user, N) for item, pui in rank: if item in tu: hit += 1 all += len(tu) return hit / (all * 1.0) def Precision(train, test, N): hit = 0 all = 0 for user in train.keys(): tu = test[user] rank = GetRecommendation(user, N) for item, pui in rank: if item in tu: hit += 1 all += N return hit / (all * 1.0)
-
算法的覆盖率反映了推荐算法发掘长尾的能力,覆盖率越高,说明推荐算法越能够将长尾中的物品推荐给用户。
-
这里,我们采用最简单的覆盖率定义: C o v e r a g e = ∣ ⋃ u ∈ U R ( u ) ∣ ∣ I ∣ Coverage=\frac{|\bigcup_{u\in U} R(u)|}{|I|} Coverage=∣I∣∣⋃u∈UR(u)∣
-
覆盖率表示最终的推荐列表中包含多大比例的物品。如果所有的物品都被推荐给至少一个用户,那么覆盖率就是100%。
# 如下代码可以用来计算推荐算法的覆盖率: def Coverage(train, test, N): recommend_items = set() all_items = set() for user in train.keys(): for item in train[user].keys(): all_items.add(item) rank = GetRecommendation(user, N) for item, pui in rank: recommend_items.add(item) return len(recommend_items) / (len(all_items) * 1.0)
-
-
我们还需要评测推荐的新颖度,即用推荐列表中物品的平均流行度度量推荐结果的新颖度。
-
如果推荐出的物品都很热门,说明推荐的新颖度较低,否则说明推荐结果比较新颖
-
这里,在计算平均流行度时对每个物品的流行度取对数,这是因为物品的流行度分布满足长尾分布,在取对数后,流行度的平均值更加稳定。
# 如下代码可以用来计算推荐的新颖度: def Popularity(train, test, N): item_popularity = dict() for user, items in train.items(): for item in items.keys() if item not in item_popularity: item_popularity[item] = 0 item_popularity[item] += 1 ret = 0 n = 0 for user in train.keys(): rank = GetRecommendation(user, N) for item, pui in rank: ret += math.log(1 + item_popularity[item]) n += 1 ret /= n * 1.0 return ret
-