CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

ChristopherAurora 发布于1月前
0 条问题

背景

上一篇文章中主要对CTR预估模型的发展历史与不同模型之间的关系进行了梳理与总结:

天雨粟:CTR预估模型发展过程与关系图谱 ​ zhuanlan.zhihu.com CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

有了上述的理论知识,就来动手实现以下模型代码。本篇文章主要针对DeepFM、Deep&Cross、xDeepFM以及AutoInt四个模型进行实现,代码实现均基于tensorflow-2.0版本,大多数接口都使用的是tf.keras接口,数据集为Criteo中的部分样本,具体如下:

  • 代码版本: Tensorflow-2.0
  • 数据集: Criteo前60万条样本
    • 训练集:50w条样本
    • 验证集:10W条样本

数据集地址:

- 百度网盘:链接: https:// pan.baidu.com/s/1S2vJVg Vmn0juOcggKHWmFg 提取码: ffyd

- 或者自行去criteo官网下载完整数据

注:本文的重点在于如何通过tensorflow2.0实现各类ctr模型,数据的特征工程方面、模型调参方面不作为本文重点,因此仅仅采用了小批量数据集进行算法demo测试;算法的超参方面也都是一些基础的默认设置,不同模型的结果不具备可比性。

目录

代码的实战讲解一共分为4个部分:

  • DeepFM代码实现
  • Deep&Cross代码实现
  • xDeepFM代码实现
  • AutoInt代码实现

代码实战

数据处理

Criteo数据集中共有39个特征,1个label列,其中以I开头的为numerical feature,以C开头的为sparse feature。

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

我们首先对dense和sparse进行处理:

  • dense部分,缺失值填为0
  • sparse部分,缺失值填为-1

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

再次说明,本文重点不在于如何在criteo数据集上取得最优效果,而在于下面算法实现,因此在特征工程处理上都以简单的方式为主。

一. DeepFM

DeepFM模型分为两部分,分别为FM部分与DNN部分,两者分别去提取低阶特征与高阶交叉特征信息,总重对目标进行预估。因此我们的实现部分也会按照FM和DNN部分来进行拆分。

(1)FM部分

FM部分的又分为Linear部分以及Second-order Interaction部分。

在Linear部分我们需要分别对dense feature和sparse feature进行处理。下面我们首先对dense特征处理:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

dense_inputs用来存储所有的dense输入,在得到13个dense特征的列表后,我们将其进行concat,得到batch_size×13的tensor:concat_dense_inputs;紧接着连接一个全连接层即可,这时我们得到的fst_order_dense_layer就是dense部分特征进行线性加和的结果。

接下来对sparse部分进行处理,我们知道sparse特征一般都是进行one-hot以后再转化为embedding特征,在线性部分,我们sparse特征one-hot以后每个特征前面都会有一个 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 ,但实际上由于稀疏性的存在,很多位置取0时,其 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 ,因此我们可以通过embedding lookup的方式对sparse线性部分系数进行处理。

这里举个例子:假设我们的性别特征取值有-1,0,1三种,某个样本的取值为1,则其one-hot以后为[0, 0, 1]向量,我们进行线性回归时会得到 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 ,其实最后只有 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 保留了下来。因此我们可以对性别构造一个3*1的embedding向量,然后通过lookup得到系数。

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

具体在代码中,我们首先构造sparse input(见第一个代码块);紧接着遍历每个input,构造Embedding矩阵,在这里我使用了L2正则,系数为0.5,主要是为了防止过拟合。

线性部分可以通过add函数将dense特征与sparse特征进行加和:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

构造完线性部分后,我们对FM的二阶交叉部分进行构造,同样的我们需要对sparse部分进行k维的embedding,这里我们k=8:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

代码部分与sparse线性部分处理类似,只不过这里的embedding output维度由原先的1变成了8。

得到embedding后,就需要进行二阶交叉系数的计算,FM二阶交叉部分可以通过化简从 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 的时间复杂度降低到 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 时间复杂度,因此在实现上我们也采用这种方法。

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

在每次操作后都标记了tensor的shape,”?”代表batch size。

(2)DNN部分

DeepFM中的DNN部分与FM部分是share embedding layer的,因此直接将FM部分的sparse embedding进行flatten以后接入多个fully-connected layer即可。

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

(3)模型结构

在构造完FM与DNN部分后,我们将FM的线性部分、二阶交叉部分、DNN部分进行加和,再使用sigmoid激活函数得到最终的预估值。

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

可以看到output layer对FM的linear part,FM的二阶交叉项、DNN的logits结果进行了求和后,使用sigmoid激活。

我们可以通过tf.keras.utils中的plot_model将整个模型结构打印出来:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

至此,整个DeepFM模型就搭建完成了,接下来的训练代码比较简单,可以去github中看源代码,这里不再赘述。

这一部分将讲解显式交叉网络三巨头:DCN、xDeepFM、AutoInt。 这三个模型共同点都是通过引入复杂的显式交叉网络来进行高阶交叉特征的学习。考虑到数据处理、tensor定义以及DNN等模块都与DeepFM一致,下文每个模型都仅对其复杂网络部分进行代码实现与讲解,具体来说:

  • DCN:CrossNet
  • xDeepFM:Compressed Interaction Layer
  • AutoInt:Multi-head Self-attention Interacting Layer

二. Deep&Cross Network

DCN网络与DeepFM类似,都采用双路结构,DCN一路是CrossNet,用来捕捉显式高阶交叉特征;一路是DNN用来捕捉隐式交叉特征。DCN的数据处理与tensor定义与DeepFM类似,这里不再赘述。

DCN网络主要是对CrossNet部分进行讲解。我们知道CrossNet的每一层包含了两部分:

  • 显示地进行特征交叉,即下图中Feature Crossing
  • ResNet:即将当前层的输入信息也融入进来,即下面Input部分

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

首先我们先尝试构建一层CrossNet,它的输入有x0与前一层CrossNet输出的embedding结果,代码如下:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

这段代码实现了一层CrossNet,共分为三步:

  • 获取前一层网络embedding的输出大小
  • 初始化本层的网络参数
  • 计算feature crossing以及输出结果

这里的关键代码在于第三步,计算feature crossing部分 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 的时候,先计算了 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 ,此时得到的结果是一个标量 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 ,再用这个标量与 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 交叉得到feature crossing部分。如果我们先计算 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 的话,这时其实是计算这两个向量的外积,会得到一个 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 的矩阵,内存消耗较大。

构建完一层Crossing Layer之后,我们可以再去循环构建多层的crossing layer:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

这里代码部分我们构建了3层的crossing layer,每次将上一层得到的输出 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 进行交叉即可。

下图为构建完成后的Deep&Cross网络图。我们共构建了3层CrossNet,并将最后的CrossNet输出部分与DNN部分concat起来,连接到output层。

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

三. xDeepFM

xDeepFM中提出了压缩交互网络CIN(Compressed Interaction Layer)。顾名思义,压缩交互网络主要分为了两部分:

  • 特征交互部分,对应下图(a)
  • 特征压缩部分,对应下图(b)

如(a)图所示,在交互部分对于第k层的feature maps与原始的feature maps进行交互,得到 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 的tensor,其中 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 是第k层的feature maps的个数, CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 是原始特征数量, CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 是embedding size大小。在交互部分,分为两个步骤:

  • 先将原始两个feature maps按照D所在的维度切开,分别可以得到D列向量
  • 对于左边feature maps的D列向量,以及右边feature maps的D列向量,进行遍历,对应的列计算内积

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

在得到 的tensor后,进入压缩层,这里可以看做对在深度D上的一维卷积操作。具体来说,以(b)图中墨绿色的feature map1为例,图中墨绿色点与三维tensor的第一层全连接进行计算,沿着feature map1虚线框的方向,第二个点是与三维tensor的第二层进行全连接计算;注意这里全连接的参数在D方向上是共享的,如图中墨绿色的线条,对每一层都是一样的。因此压缩层可以看做是对三维 的tensor在深度D上的一维卷积操作。

这里看做一维卷积操作原因是filters只在D维度方向上移动,在 的方向均没有进行移动卷积。

代码实现如下:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

代码中分为三步:

  • 第一步将输入的两个需要交互的feature maps分成D列
  • 第二步计算交互的三维tensor,即在每个维度上进行向外积操作
  • 第三步使用一维的卷积操作得到当前层的输出

这里n_filters是经过该CIN层后,输出的feature map的个数。

有了单层的CIN实现,我们紧接着可以实现多层的CIN网络,这里要注意的是CIN网络的输出是将每一层的feature maps进行sum pooling,然后concat起来,如上图(c)所示。

代码实现如下。经过3层的CIN网络,每一层的filter是12,意味着会产出3个12*k的feature maps,再经过sum-pooling和concat操作后,就得到3*12=36维的向量。

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

xDeepFM的总体结构如下,将linear部分,CIN部分以及DNN部分进行concat以后喂给输出层,得到预估结果。

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

编译模型后得到的结构图:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

四. AutoInt

Automatic Feature Interaction Learning与xDeepFM、DCN一样,都引入了显式地交叉网络。AutoInt中的Interacting Layer引入了NLP中的multi-head self-attention机制,赋予了不同特征交叉以不同的重要性,增加了模型的表达能力。

AutoInt整个模型结构如下,其关键部分就是Interacting Layer中的multi-head self-attention以及ResNet:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

交互层的关键在于self-attention的实现。这里我们给出一个例子:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

上图这个例子为one-head self-attention。假设我们有4个特征,每个特征embedding size为3,我们可以得到一张4*3的feature map;紧接着通过Q,K,V三个矩阵将原始的输入映射到新的空间中,分别得到Query的4*5矩阵、Key的4*5矩阵、Value的4*5矩阵。接着我们需要计算每个特征与其他特征的attention,则需要将Query矩阵与Key矩阵进行矩阵乘法后接入softmax层,得到attention矩阵,attention矩阵为4*4矩阵,其中每个元素 CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解 代表了query_i与key_i之间的重要性,其中每一行的求和为1。以图中浅蓝色原点为例,它代表了Query中的第二个特征与Key中的第三个特征的attention信息。最终将attention矩阵与Valu矩阵进行矩阵乘法,即可得到self-attention的结果。

代码实现如下:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

首先构建多个Q,K,V,代表每个head self-attention的参数,这里我们设置为2-head self-attention。在得到所有的attention结果后,第二步就是将multi-head self-attention的结果concat起来;第三步,将attention结果输入残差网络,得到output。

同样地,我们使用循环来构建多层的attention网络:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

其中每层网络的Q,K,V会将输入embedding映射到6维空间中,每层有2个head的self-attention。

模型编译后的网络结构如下:

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

AutoInt构建完成后我们还可以将Attention结果打印出来,观察交叉特征的重要性。下图打印了一个样本在第一层Interacting Layer的某个self-attention,可以看到I2和I3交叉特征的重要性更高。

CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

总结

本文主要基于tensorflow2.0讲解并实现了DeepFM、xDeepFM、DCN和AutoInt四个模型的主要部分,完整的代码还需参看我的GitHub,如有问题可评论留言。

https://github.com/NELSONZHAO/zhihu/tree/master/ctr_models ​ github.com

转载请注明出处。

查看原文: CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解

  • crazyduck
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。