CTR 模型之 Deep & Cross (DCN) 与 xDeepFM 解读

本篇文章讲解 CTR 经典模型 Deep & Cross (DCN) 与 xDeepFM,之所以把这两个模型放一起讲是因为它们有很近的“血缘关系”。理解了 DCN 的思想,再去理解 xDeepFM 就不觉得困难了。

以下文章对这两个模型的讲解很到位:

首先了解揭秘 Deep & Cross : 如何自动构造高阶交叉特征

推荐系统遇上深度学习(二十二)–DeepFM升级版XDeepFM模型强势来袭!

xDeepFM:名副其实的 ”Deep” Factorization Machine

因此,本文主要是对上述文章内容的梳理。

Deep & Cross (DCN)

首先来看看网络结构:

CTR 模型之 Deep & Cross (DCN) 与 xDeepFM 解读

首先来看看网络的输入部分。元素特征需要做如下处理:

  1. 对sparse特征进行embedding,对于multi-hot的sparse特征,embedding之后再做一个简单的average pooling;

  2. 对dense特征归一化,然后和embedding特征拼接,作为随后Cross层与Deep层的共同输入:
    x0=[xembed,1T,xembed,2T,...xembed,kT,xdenseT]T x_0 = [x^T_{embed, 1},x^T_{embed, 2},...x^T_{embed, k},x^T_{dense}]^T

接下来看看 Cross 部分。

上图中模型的左半部分是包含了许多层的 Cross network,目标是以显式、可控、高效的方式自动构建有限高阶交叉特征。其中第 l+1l + 1 层的计算过程为:

CTR 模型之 Deep & Cross (DCN) 与 xDeepFM 解读

xl+1=f(xl,wl,bl)+xl=x0xlTwl+bl+xl x_{l+1} = f(x_l, w_l, b_l)+x_l =x_0x^T_lw_l + b_l + x_l
其中,xl+1,xl,x0x_{l+1}, x_l, x_0 是 d 维向量。计算过程有两个特点:

  1. 输出神经元个数与输入网络的 x0x_0 维度相同;
  2. 每一层的 ff 都是在拟合 xl+1xlx_{l+1} - x_l 的残差,这样可以减缓梯度消失问题,使得网络更深。

为什么这么设计呢?先看一些计算的例子:

假设 Cross 网络有两层,x0=[x0,1x0,2]Tx_0 = [x_{0, 1} x_{0, 2}]^T,为方便讨论令 bi=0b_i = 0,则有:
x1=x0x0Tw0+x0=[x0,1x0,2][x0,1,x0,2][w0,1w0,2]+[x0,1x0,2]=[w0,1x0,12+w0,2x0,1x0,2+x0,1w0,1x0,2x0,1+w0,2x0,22+x0,2] \begin{aligned} \boldsymbol{x}_{1} &=\boldsymbol{x}_{0} \boldsymbol{x}_{0}^{T} \boldsymbol{w}_{0}+\boldsymbol{x}_{0}=\left[\begin{array}{c} x_{0,1} \\ x_{0,2} \end{array}\right]\left[x_{0,1}, x_{0,2}\right]\left[\begin{array}{c} w_{0,1} \\ w_{0,2} \end{array}\right]+\left[\begin{array}{c} x_{0,1} \\ x_{0,2} \end{array}\right]=\left[\begin{array}{c} w_{0,1} x_{0,1}^{2}+w_{0,2} x_{0,1} x_{0,2}+x_{0,1} \\ w_{0,1} x_{0,2} x_{0,1}+w_{0,2} x_{0,2}^{2}+x_{0,2} \end{array}\right] \\ \end{aligned}
进而有:
x2=x0x1Tw1+x1=[w1,1x0,1x1,1+w1,2x0,1x1,2+x1,1w1,1x0.2x1,1+w1,2x0,2x1,2+x1,2]=[w0,1w1,1z0,13+(w0,2w1,1+w0,1w1,2)x0,12x0,2+w0,2w1,2x0,1x0,22+(w0,1+w1,1)x0,12+(w0,2+w1,2)x0,1z0,2+x0,1] \begin{aligned} x_{2}=& x_{0} x_{1}^{T} w_{1}+x_{1} \\ =&\left[\begin{array}{c} w_{1,1} x_{0,1} x_{1,1}+w_{1,2} x_{0,1} x_{1,2}+x_{1,1} \\ w_{1,1} x_{0.2} x_{1,1}+w_{1,2} x_{0,2} x_{1,2}+x_{1,2} \\ \end{array}\right] \\ =& \left[\begin{array}{c} {w_{0,1} w_{1,1} z_{0,1}^{3}+\left(w_{0,2} w_{1,1}+w_{0,1} w_{1,2}\right) x_{0,1}^{2} x_{0,2}+w_{0,2} w_{1,2} x_{0,1} x_{0,2}^{2}+\left(w_{0,1}+w_{1,1}\right) x_{0,1}^{2}+\left(\operatorname{w}_{0,2}+w_{1,2}\right) x_{0,1} z_{0,2}+x_{0,1}} \\ \cdot \cdot \cdot \cdot \cdot \cdot \cdot \end{array}\right] \end{aligned}
可以看到 x1x_1 包含了从一阶到二阶所有的可能交叉组合,而 x2x_2 包含了从一阶到三阶的所有可能交叉组合,并且有下面的特点:

  1. 有限高阶:交叉的阶数由层数决定,深度 LcL_c 具最高有 Lc+1L_c + 1 阶的交叉特征;
  2. 参数线性增长:模型参数数量与输入维度成线性增长关系:2×d×Lc2\times d \times L_c
  3. 参数共享:并不是每个交叉组合都具有独立的权重参数,而是共享了 2×d×Lc2\times d \times L_c 个权重参数。参数共享机制使得模型具有更强的泛化性和鲁棒性。

这里对第三点加以解释:如果每个交叉特征有独立的参数,那么当训练样本中某个交叉特征 xixjx_i x_j 没有出现,那么此交叉特征的权重参数就是 0 。而参数共享的情况下,权重几乎不可能是 0,并且对于一些噪声数据也可以由大部分正常样本来纠正参数的学习。

要注意的是,输入的 x0x_0 是在 embedding 之后得到的,后续的交叉也是对 embedding 后的数值进行交叉。直接将 sparse 特征输入网络进行交叉才是最合理的做法,但是这么做的话参数过多,所以先进行一次 embedding 操作,这实际上也是一种 trade-off。

其他的 DNN 部分和输出的部分都比较好理解,这里就不多做叙述了。

xDeepFM

我第一次看到 xDeepFM 以为是 DeepFM 的改进版本,后来才发现他更像是在 DCN 的基础上改进的。

在正式讲解之前,先了解下什么是 bit-wise 什么是vector-wise

假设隐向量的维度为3维,如果两个特征(对应的向量分别为(a1,b1,c1)和(a2,b2,c2)的话)在进行交互时,交互的形式类似于f(w1 * a1 * a2,w2 * b1 * b2 ,w3 * c1 * c2)的话,此时我们认为特征交互是发生在元素级(bit-wise)上。如果特征交互形式类似于 f(w * (a1 * a2 ,b1 * b2,c1 * c2))的话,那么我们认为特征交互是发生在特征向量级(vector-wise)

对于包括 DCN 在内的许多模型在构造交叉特征时,都是以 bit-wise 的方式进行特征组合,而FM是以向量为最细粒度学习相关性,即vector-wise。xDeepFM的动机,正是将 FM 的 vector-wise 思想引入Cross部分。

下图是 xDeepFM 的整体结构:

CTR 模型之 Deep & Cross (DCN) 与 xDeepFM 解读

模型的输入部分与 DCN 类似,本节重点讲解 CIN 部分,如下图所示:

CTR 模型之 Deep & Cross (DCN) 与 xDeepFM 解读

CIN 的输入来自 embedding 层,假设有 m 个 field,每个 field 的 embedding 维度为 D ,则输入可以表示为矩阵 X0RmDX^0 \in \mathbb R^{m * D}

CIN 内部有 k 层,每一层都会输出一个矩阵 XkRHkDX^k \in \mathbb R^{H_k * D} ,k 表示第 k 层的输出, HkH_k 表示第 k 层有 HkH_k 个维度为 D 的向量。要得到 XkX^{k} ,需要接收两个矩阵作为输入,一个是 Xk1X^{k-1} ,另一个是 X0X^0 ,具体的计算公式如下:
Xh,k=i=1Hk1j=1mWijk,h(Xi,k1Xj,0)R1D, where 1hHk \boldsymbol{X}_{h, *}^{k}=\sum_{i=1}^{H_{k-1}} \sum_{j=1}^{m} \boldsymbol{W}_{i j}^{k, h}\left(\boldsymbol{X}_{i, *}^{k-1} \circ \boldsymbol{X}_{j, *}^{0}\right) \in \mathbb{R}^{1 * D}, \quad \text { where } 1 \leq h \leq H_{k}
其中 Wk,hRHk1mW^{k, h} \in \mathbb R^{H_{k-1} * m},表示要得到第 k 层第 h 个向量所需要的权重矩阵, Hk1H_{k-1} 表示第 k1k-1 层的输出矩阵 Xk1X^{k-1}Hk1H_{k-1} 个维度为 D 的向量组成。\circ 表示Hadamard乘积,即逐元素乘,例如:
<a1,b1,c1><a2,b2,c2>=<a1b1,a2b2,a3b3> <a_1, b_1, c_1> \circ <a_2, b_2, c_2> = <a_1b_1, a_2b_2, a_3b_3>
式子中 Xi,k1Xj,0\boldsymbol{X}_{i, *}^{k-1} \circ \boldsymbol{X}_{j, *}^{0} 是表示取出 Xk1X^{k-1} 的第 ii 个向量与输入层 X0X^{0} 中第 jj 个向量进行 Hadamard 乘积运算。整个公式的计算过程可以用下图表示:

CTR 模型之 Deep & Cross (DCN) 与 xDeepFM 解读

上面的公式的计算结果只得到了第 h 个向量 Xh,k\boldsymbol{X}_{h, *}^{k} ,实际上我们会使用 HkH_k 个不同的权重矩阵 Wk,hW^{k,h} 来获得不同的向量,最后这些向量拼接成输出 XkRHkDX^{k} \in \mathbb R^{H_k * D}

需要注意的是,在第 ll 层实际上只包含了第 l+1l+1 阶的交叉特征,为了得到不同阶的输出,需要在每一层得到计算结果后通过 sum pooling 进行输出。至于一阶特征,则单独由模型的结构图最左侧的 Linear 模块进行加权求和。最右侧的 DNN 模块比较好理解,就不做解释了。

从计算过程就可以看出此模型的复杂度比较高,需要的参数也比较多,因此训练的时候可能出现过拟合现象,为此可以配合 Droupout 或者其他防止过拟合的方法使用。