1. 点击率预估简介
点击率预估是用来解决什么问题?
点击率预估是对每次广告点击情况作出预测,可以输出点击或者不点击的概率 —— PClick.
点击率模型需要做什么?
- 其实点击率模型预估问题就是一个二分类问题
- 逻辑回归输出的就是[0,1]的概率值
- 因此可以使用LR的方式来学习建立模型
点击率预估与推荐算法有什么不同?
- 点击率预估: 需要得到某个用户对广告的点击率,然后结合报价信息用于排序(常用于AD业务)
- 推荐算法: 常常是TopN推荐的问题,很多情况下只需要得到一个最优的推荐次序,当然,广告的点击率也是可以使用的
2. FM与Wide&Deep的相较之下的区别
- FM的缺点
- 当矩阵过于稀疏并且是high-rank的时候(比如user有特殊的爱好,或item比较小众),很难非常效率的学习出低维度的表示
- 当矩阵过于稠密(dense embedding)的时候,会导致几乎所有的query-item的预测值都为0,这就导致了推荐过渡泛化,会推荐一些不再那么相关的物品。
- 一些异常规则(exception rules), 可以通过linear model来记住这些,具体方法:cross-product transformation(交叉产品转化法,下面再细讲)。
3. Wide & Deep模型的“记忆能力”与“泛化能力”
3.1 简介“记忆能力”与“泛化能力”
- 记忆能力:通过用户与商品的交互信息矩阵学习规则 (更加保守,会更多推荐之前积累的经验和规则,线性模型就可以实现)
- FM算法就是很好的泛化例子,他通过交互信息学习到一个比较短的矩阵V,其中Vi存储着每个用户特征的压缩表示(embedding:实物映射为向量),
- 而协同过滤与SVD都是靠记住用户之前与那些物品交互,进而(加权、平均、相近)推测判断出推荐结果的
- 我们的Wide&Deep模型就是能够融合这两种推荐结果而做出最终的推荐,得出一个比之前的推荐结果逗号的模型。
- 泛化能力:(机器学习算法对新鲜样本的适应能力,在Rs中指的是更趋于多样化,便于提高推荐系统的多样性,多使用DNN来实现)
- 泛化规则(又快又稳又好)
- 组合后需要注意的点:用户需要根据自己的场景去选择哪些特征放在Wide部分。
3.2 如何理解Wide部分有利于增强模型的“记忆能力”,Deep部分有利于增强模型的“泛化能力”?
-
Wide部分
- 总体是一个广义的线性模型
- 输入特征包括两个部分:原始的部分特征+原始特征的交互特征((cross-product transformation),对于交互特征可以定义为:(当两个特征同时都为1的时候,这个新的特征才能为1,否则就是0,说白了就是一个AND组合特征)
[phi_{k}(x)=prod_{i=1}^d x_i^{c_{ki}}, c_{ki}in {0,1} ]- 优化器: Wide模型使用待L1正则化的FTRL算法(跟随正规化-领头算法),而L1 FTLR是非常注重模型稀疏性质的; W&D模型采用的是想让Wide部分变得更加稀疏(什么规则???), 即Wide部分的大部分参数都为0,这就大大压缩了模型权重以及特征向量的维度.
- Wide部分模型训练完之后留下来的矩阵都是非常重要的, 这是模型的"记忆能力"就凸显了出来,得到留下的特征大多都是直接的、暴力的、显然的关联规则的能力。
- 举个例子,Google W&D期望wide部分发现这样的规则:用户安装了应用A,此时曝光应用B,用户安装应用B的概率大。
-
DEEP部分
- Deep部分是一个DNN模型,输入的特征主要分为两大类,一类是数值特征(可以直接输入DNN),一类是特别的特征(需要经过Embrdding映射转换之后才可以输入到DNN中),Deep部分的数学形式如下:
[a^{(l+1)} = f(W^{l}a^{(l)} + b^{l}) ]- 我们知道DNN模型随着层数的增加,中间的特征就越抽象,也就提高了模型的泛化能力。
- 对于Deep部分的DNN模型作者使用了深度学习常用的优化器AdaGrad,这也是为了使得模型可以得到更精确的解。
-
Wide部分与Deep部分的结合
- W&D模型是将两部分输出的结果结合起来联合训练,将deep和wide部分的输出重新使用一个逻辑回归模型做最终的预测,输出概率值。联合训练的数学形式如下:
[P(Y=1|x)=delta(w_{wide}^T[x,phi(x)] + w_{deep}^T a^{(lf)} + b) ]
4. 操作流程
- Retrieval(检索) :利用机器学习模型和一些人为定义的规则,来返回最匹配当前Query的一个小的items集合,这个集合就是最终的推荐列表的候选集。
- Ranking(排名推荐TopN):
- 收集更细致的用户特征,如:
- 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
所以wide&deep模型尽管在模型结构上非常的简单,但是如果想要很好的使用wide&deep模型的话,还是要深入理解业务,确定wide部分使用哪部分特征,deep部分使用哪些特征,以及wide部分的交叉特征应该如何去选择
- 收集更细致的用户特征,如:
5. tensorflow的安装
今天网速还OK,从新开了一个conda环境,照着教程完美安装完成;刚开始在老的环境上pip倒是可以成功,但是还有些版本啥的没有对应;conda就像是docker,来个新的,照着别人的教程,一般不会再出新的环境问题。
参考链接:
- Windows下安装Anaconda3(附带python3.8)以及TensorFlow
https://blog.csdn.net/weixin_42412254/article/details/107569830
6. 代码实战
# -*- coding: utf-8 -*-
# 第一步安装,调包
from pyfm import pylibfm
from sklearn.feature_extraction import DictVectorizer
import numpy as np
import tensorflow as tf
from sklearn.metrics import mean_squared_error
from tensorflow_core.python.keras.premade.linear import LinearModel
from tensorflow_core.python.keras.premade.wide_deep import WideDeepModel
from tensorflow import keras
if __name__ == '__main__':
# 第二步:创建训练集并转换成one-hot编码的特征形式
train = [
{"user": "1", "item": "5", "age": 19},
{"user": "2", "item": "43", "age": 33},
{"user": "3", "item": "20", "age": 55},
{"user": "4", "item": "10", "age": 20},
]
# DictVectorizer() Transforms lists of feature-value mappings to vectors.
dv = DictVectorizer()
X_train = dv.fit_transform(train)
print(X_train.toarray())
# 第三步:创建标签, 这里简单创建了一个全1的标签:
y_train = np.repeat(1.0, X_train.shape[0])
# 第四步:训练并预测, 就和调用sklearn的包是一样的用法:
fm = pylibfm.FM()
fm.fit(X_train, y_train)
X_test = dv.transform({"user": "1", "item": "10", "age": 24})
y_test = fm.predict(X_test)
print(y_test)
print('#' * 20)
# 查看tensorflow版本
print('tensorflow版本:', tf.__version__)
# 第一部分是使用tensorflow中已经封装好的wide&deep模型
# Tensorflow内置的WideDeepModel
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'], loss='mse', metrics=['mse'])
# define dnn_inputs and linear_inputs as separate numpy arrays or
# a single numpy array if dnn_inputs is same as linear_inputs.
# 格式转换
# [[19. 0. 0. 0. 1. 1. 0. 0. 0.]
# [33. 0. 0. 1. 0. 0. 1. 0. 0.]
# [55. 0. 1. 0. 0. 0. 0. 1. 0.]
# [20. 1. 0. 0. 0. 0. 0. 0. 1.]]
# [0.99806595]
linear_inputs = X_train.toarray()
dnn_inputs = linear_inputs
epochs = 100
# y_train = y_train
# or define a single `tf.data.Dataset` that contains a single tensor or
# separate tensors for dnn_inputs and linear_inputs.
# train_dataset = tf.data.Dataset.from_tensors(())
combined_model.fit([linear_inputs, dnn_inputs], y_train, epochs)
# 模型验证
# Returns the loss value & metrics values for the model in test mode.
loss_mse = combined_model.evaluate([linear_inputs, dnn_inputs], y_train, epochs)
print('loss,mse', loss_mse)
# 预测
linear_test_inputs= X_test.toarray()
dnn_test_inputs = linear_test_inputs
y_test = [1]
pre = combined_model.predict([linear_test_inputs, dnn_test_inputs])
print(pre)
简易输出:(前半部分是FM的代码,tensorflow还需要再深度学习)
[[19. 0. 0. 0. 1. 1. 0. 0. 0.]
[33. 0. 0. 1. 0. 0. 1. 0. 0.]
[55. 0. 1. 0. 0. 0. 0. 1. 0.]
[20. 1. 0. 0. 0. 0. 0. 0. 1.]]
Creating validation dataset of 0.01 of training for adaptive regularization
-- Epoch 1
Training log loss: 0.27579
[0.98898911]
####################
tensorflow版本: 2.0.0
2020-11-22 21:42:27.116396: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
Train on 4 samples
4/4 [==============================] - 1s 217ms/sample - loss: 17.6376 - mse: 17.6376
4/1 [========================================================================================================================] - 0s 24ms/sample - loss: 469.0519 - mse: 469.0519
[469.0518798828125, 469.05188]
[[-13.908051]]
import sys; print('Python %s on %s' % (sys.version, sys.platform))
Python 3.7.9 (default, Aug 31 2020, 17:10:11) [MSC v.1916 64 bit (AMD64)] on win32