点击率预估简介
通过上述点击率预估的基本概念,我们会发现其实点击率预估问题就是一个二分类的问题,在机器学习中可以使用逻辑回
归作为模型的输出,其输出的就是一个概率值,我们可以将机器学习输出的这个概率值认为是某个用户点击某个广告的概率。
FM存在的缺点
其缺点在于:当query-item矩阵是稀疏并且是high-rank的时候(比如user有特殊的爱好,或item比较小众),很难非常效率的学习出低维度的表示。这种情况下,
大部分的query-item都没有什么关系。但是dense embedding会导致几乎所有的query-item预测值都是非0的,这就导致了推荐 过度泛化,会推荐一些不那么相关的物品。相反,简单的linear model却可以通过cross-product transformation来记住这些exception rules,
Wide&Deep模型的“记忆能力”与“泛化能力”
Memorization 和 Generalization是推荐系统很常见的两个概念,其中Memorization指的是通过用户与商品的交互信息矩阵学习规则,而Generalization则是泛化规则。我们前面介绍的FM算法就是很好的Generalization的例子,它可以根据交互信息学习到一个比较短的矩阵 ,其中 储存着每个用户特征的压缩表示(embedding),而协同过滤与SVD都是靠记住用户之前与哪些物品发生了交互从而推断出的推荐结果,这两者推荐结果当然存在一些差异,我们的Wide&Deep模型就能够融合这两种推荐结果做出最终的推荐,得到一个比之前的推荐结果都好的模型。可以这么说:Memorization趋向于更加保守,推荐用户之前有过行为的items。相比之下,generalization更加趋向于提高推荐系统的多样性(diversity)。Memorization只需要使用一个线性模型即可实现,而Generalization需要使用DNN实现。
对于wide部分训练时候使用的优化器是带 正则的FTRL算法(Follow-the-regularized-leader),而L1 FTLR是非常注重模型稀疏性质的,也就是说W&D模型采用L1 FTRL是想让Wide部分变得更加的稀疏,即Wide部分的大部分参数都为0,这就大大压缩了模型权重及特征向量的维度。Wide部分模型训练完之后留下来的特征都是非常重要的,那么模 型的“记忆能力”就可以理解为发现"直接的",“暴力的”,“显然的”关联规则的能力。例如Google W&D期望wide部分发现这样的规则:用户安装了应用A,此时曝光应用B,用户安装应用B的概率大。
操作流程
- Retrieval
:利用机器学习模型和一些人为定义的规则,来返回最匹配当前Query的一个小的items集合,这个集合就是
最终的推荐列表的候选集。 - Ranking: a. 收集更细致的用户特征,如:
User features(年龄、性别、语言、民族等)
Contextual features(上下文特征:设备,时间等)
Impression features(展示特征:app age、app的历史统计信息等)
b. 将特征分别传入Wide和Deep一起做训练。在训练的时候,根据最终的loss计算出gradient,反向传播到Wide和
Deep两部分中,分别训练自己的参数(wide组件只需要填补deep组件的不足就行了,所以需要比较少的crossproduct feature transformations,而不是full-size wide Model)
训练方法是用mini-batch stochastic optimization。
Wide组件是用FTRL(Follow-the-regularized-leader) + L1正则化学习。
Deep组件是用AdaGrad来学习。
c. 训练完之后推荐TopN
所以wide&deep模型尽管在模型结构上非常的简单,但是如果想要很好的使用wide&deep模型的话,还是要深入理解 业务,确定wide部分使用哪部分特征,deep部分使用哪些特征,以及wide部分的交叉特征应该如何去选择
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)
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)