每日论文(2)—— Wide & Deep Learning for Recommender Systems

今天的论文是《Wide & Deep Learning for Recommender Systems》,这篇论文是谷歌在2016年提出的算法。

在开始之前我们提几个要点:

  • 这个模型与FM不一样,应用前提是已经对大量的购买(点击)信息做了召回(压缩了数据集)然后才做的推荐
  • Wide&Deep模型需要手动做特征工程
  • 论文很容易读懂,篇幅很短内容不难建议直接看一遍!

论文要点

FM它不香吗

之前我们已经学了FM模型,不是已经很好了吗,为啥还要整这个Wide&Deep呢?其缺点在于:当query-item矩阵是稀疏并且是high-rank的时候(比如user有特殊的爱好,或item比较小众),很难非常效率的学习出低维度的表示。这种情况下,大部分的query-item都没有什么关系。但是dense embedding会导致几乎所有的query-item预测值都是非0的,这就导致了推荐过度泛化,会推荐一些不那么相关的物品。相反,简单的linear model却可以通过cross-product transformation来记住这些exception rules,cross-product transformation是什么意思后面再提。

Memorization 和 Generalization

Memorization 和 Generalization是推荐系统很常见的两个概念,其中Memorization指的是通过用户与商品的交互信息矩阵学习规则,而Generalization则是泛化规则。我们前面介绍的FM算法就是很好的Generalization的例子,它可以根据交互信息学习到一个比较短的矩阵$V$,其中$v_{i}$储存着每个用户特征的压缩表示(embedding),而协同过滤与SVD都是靠记住用户之前与哪些物品发生了交互从而推断出的推荐结果,这两者推荐结果当然存在一些差异,我们的Wide&Deep模型就能够融合这两种推荐结果做出最终的推荐,得到一个比之前的推荐结果都好的模型。

可以这么说:Memorization趋向于更加保守,推荐用户之前有过行为的items。相比之下,generalization更加趋向于提高推荐系统的多样性(diversity)。Memorization只需要使用一个线性模型即可实现,而Generalization需要使用DNN实现。

Cross-product transformation

Wide中使用Cross-product transformation来生成组合特征,也就是手动的二阶特征提取,作用是学习组合特征并为模型增加非线性性,说的文邹邹一点:

这个式子什么意思读者可以自行找原论文看看,大体意思就是两个特征都同时为1这个新的特征才能为1,否则就是0,说白了就是一个特征组合。用原论文的例子举例:

AND(user_installed_app=QQ, impression_app=WeChat),当特征user_installed_app=QQ,和特征impression_app=WeChat取值都为1的时候,组合特征AND(user_installed_app=QQ, impression_app=WeChat)的取值才为1,否则为0。

操作流程

  • Retrieval :利用机器学习模型和一些人为定义的规则,来返回最匹配当前Query的一个小的items集合,这个集合就是最终的推荐列表的候选集。

  • Ranking

    • 收集更细致的用户特征,如:

      • User features(年龄、性别、语言、民族等)
      • Contextual features(上下文特征:设备,时间等)
      • Impression features(展示特征:app age、app的历史统计信息等)
    • 将特征分别传入Wide和Deep一起做训练。在训练的时候,根据最终的loss计算出gradient,反向传播到Wide和Deep两部分中,分别训练自己的参数(wide组件只需要填补deep组件的不足就行了,所以需要比较少的cross-product feature transformations,而不是full-size wide Model)

      • 训练方法是用mini-batch stochastic optimization。
      • Wide组件是用FTRL(Follow-the-regularized-leader) + L1正则化学习。
      • Deep组件是用AdaGrad来学习。
    • 训练完之后推荐TopN

上代码

在Tensorflow的库中是已经内置了Wide-Deep model的,想要查看源代码了解具体实现过程可以看这里。下面参考Tensorflow官网的示例代码进行讲解。我们用到的数据集下载链接戳这里

首先看全局实现:

1
2
3
tf.keras.experimental.WideDeepModel(
linear_model, dnn_model, activation=None, **kwargs
)

这一步很容易看出来就是将linear_model与dnn_model拼接在了一起,对应于Wide-Deep FM中的最后一步。比如我们可以将linear_model与dnn_model做一个最简单的实现:

1
2
3
4
5
6
7
8
9
10
11
12
linear_model = LinearModel()
dnn_model = keras.Sequential([keras.layers.Dense(units=64),
keras.layers.Dense(units=1)])
combined_model = WideDeepModel(linear_model, dnn_model)
combined_model.compile(optimizer=['sgd', 'adam'], 'mse', ['mse'])
# define dnn_inputs and linear_inputs as separate numpy arrays or
# a single numpy array if dnn_inputs is same as linear_inputs.
combined_model.fit([linear_inputs, dnn_inputs], y, epochs)
# or define a single `tf.data.Dataset` that contains a single tensor or
# separate tensors for dnn_inputs and linear_inputs.
dataset = tf.data.Dataset.from_tensors(([linear_inputs, dnn_inputs], y))
combined_model.fit(dataset, epochs)

这里第一步就是直接调用一个keras.experimental中的linear_model,第二步简单实现了一个全连接神经网络,第三步使用WideDeepModel将前两步产生的两个model拼接在一起,形成最终的combined_model,接着就是常规的compile和fit了。

除此之外线性模型与DNN模型在联合训练之前均可进行分别训练:

1
2
3
4
5
6
7
8
9
linear_model = LinearModel()
linear_model.compile('adagrad', 'mse')
linear_model.fit(linear_inputs, y, epochs)
dnn_model = keras.Sequential([keras.layers.Dense(units=1)])
dnn_model.compile('rmsprop', 'mse')
dnn_model.fit(dnn_inputs, y, epochs)
combined_model = WideDeepModel(linear_model, dnn_model)
combined_model.compile(optimizer=['sgd', 'adam'], 'mse', ['mse'])
combined_model.fit([linear_inputs, dnn_inputs], y, epochs)

这里前三行代码训练了一个线性模型,中间三行代码训练了一个DNN模型,最后三行代码则将两个模型联合训练,以上就完成了对Tensorflow的WideDeepModel的调用,其中每个函数有一些其他参数我们这里不详细说明,读者若有需要可自行在tensorflow官网查询,另外该部分的源代码在Tensorflow的Github上有展示,链接在这

参考链接:

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2020-2021 chenk
  • 由 帅气的CK本尊 强力驱动
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信