初学推荐系统-05-Wide&Deep [附tensorflow的WideDeepModel代码简单实践]

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 简介“记忆能力”与“泛化能力”

image-20200910214310877
  • 记忆能力:通过用户与商品的交互信息矩阵学习规则 (更加保守,会更多推荐之前积累的经验和规则,线性模型就可以实现)
    • 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,来个新的,照着别人的教程,一般不会再出新的环境问题。
参考链接:

  1. 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

你不逼自己一把,你永远都不知道自己有多优秀!只有经历了一些事,你才会懂得好好珍惜眼前的时光!
原文地址:https://www.cnblogs.com/zhazhaacmer/p/13888400.html