算法工程师修仙之路:推荐系统实践(四)

第二章 利用用户行为数据

实验设计和算法评测


数据集

  • 采用 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 0kM1)和相同的随机数种子 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=uT(u)uR(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=uR(u)uR(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=IuUR(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
      

你可能感兴趣的:(实战,应用)