本篇文章讲解 CTR 经典模型 Deep & Cross (DCN) 与 xDeepFM,之所以把这两个模型放一起讲是因为它们有很近的“血缘关系”。理解了 DCN 的思想,再去理解 xDeepFM 就不觉得困难了。
以下文章对这两个模型的讲解很到位:
首先了解揭秘 Deep & Cross : 如何自动构造高阶交叉特征
推荐系统遇上深度学习(二十二)–DeepFM升级版XDeepFM模型强势来袭!
xDeepFM:名副其实的 ”Deep” Factorization Machine
因此,本文主要是对上述文章内容的梳理。
Deep & Cross (DCN)
首先来看看网络结构:
首先来看看网络的输入部分。元素特征需要做如下处理:
-
对sparse特征进行embedding,对于multi-hot的sparse特征,embedding之后再做一个简单的average pooling;
-
对dense特征归一化,然后和embedding特征拼接,作为随后Cross层与Deep层的共同输入:
x0=[xembed,1T,xembed,2T,...xembed,kT,xdenseT]T
接下来看看 Cross 部分。
上图中模型的左半部分是包含了许多层的 Cross network,目标是以显式、可控、高效的方式自动构建有限高阶交叉特征。其中第 l+1 层的计算过程为:
xl+1=f(xl,wl,bl)+xl=x0xlTwl+bl+xl
其中,xl+1,xl,x0 是 d 维向量。计算过程有两个特点:
- 输出神经元个数与输入网络的 x0 维度相同;
- 每一层的 f 都是在拟合 xl+1−xl 的残差,这样可以减缓梯度消失问题,使得网络更深。
为什么这么设计呢?先看一些计算的例子:
假设 Cross 网络有两层,x0=[x0,1x0,2]T,为方便讨论令 bi=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]
进而有:
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⋅⋅⋅⋅⋅⋅⋅]
可以看到 x1 包含了从一阶到二阶所有的可能交叉组合,而 x2 包含了从一阶到三阶的所有可能交叉组合,并且有下面的特点:
- 有限高阶:交叉的阶数由层数决定,深度 Lc 具最高有 Lc+1 阶的交叉特征;
- 参数线性增长:模型参数数量与输入维度成线性增长关系:2×d×Lc;
- 参数共享:并不是每个交叉组合都具有独立的权重参数,而是共享了 2×d×Lc 个权重参数。参数共享机制使得模型具有更强的泛化性和鲁棒性。
这里对第三点加以解释:如果每个交叉特征有独立的参数,那么当训练样本中某个交叉特征 xixj 没有出现,那么此交叉特征的权重参数就是 0 。而参数共享的情况下,权重几乎不可能是 0,并且对于一些噪声数据也可以由大部分正常样本来纠正参数的学习。
要注意的是,输入的 x0 是在 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 的整体结构:
模型的输入部分与 DCN 类似,本节重点讲解 CIN 部分,如下图所示:
CIN 的输入来自 embedding 层,假设有 m 个 field,每个 field 的 embedding 维度为 D ,则输入可以表示为矩阵 X0∈Rm∗D。
CIN 内部有 k 层,每一层都会输出一个矩阵 Xk∈RHk∗D ,k 表示第 k 层的输出, Hk 表示第 k 层有 Hk 个维度为 D 的向量。要得到 Xk ,需要接收两个矩阵作为输入,一个是 Xk−1 ,另一个是 X0 ,具体的计算公式如下:
Xh,∗k=i=1∑Hk−1j=1∑mWijk,h(Xi,∗k−1∘Xj,∗0)∈R1∗D, where 1≤h≤Hk
其中 Wk,h∈RHk−1∗m,表示要得到第 k 层第 h 个向量所需要的权重矩阵, Hk−1 表示第 k−1 层的输出矩阵 Xk−1 由 Hk−1 个维度为 D 的向量组成。∘ 表示Hadamard乘积,即逐元素乘,例如:
<a1,b1,c1>∘<a2,b2,c2>=<a1b1,a2b2,a3b3>
式子中 Xi,∗k−1∘Xj,∗0 是表示取出 Xk−1 的第 i 个向量与输入层 X0 中第 j 个向量进行 Hadamard 乘积运算。整个公式的计算过程可以用下图表示:
上面的公式的计算结果只得到了第 h 个向量 Xh,∗k ,实际上我们会使用 Hk 个不同的权重矩阵 Wk,h 来获得不同的向量,最后这些向量拼接成输出 Xk∈RHk∗D 。
需要注意的是,在第 l 层实际上只包含了第 l+1 阶的交叉特征,为了得到不同阶的输出,需要在每一层得到计算结果后通过 sum pooling 进行输出。至于一阶特征,则单独由模型的结构图最左侧的 Linear 模块进行加权求和。最右侧的 DNN 模块比较好理解,就不做解释了。
从计算过程就可以看出此模型的复杂度比较高,需要的参数也比较多,因此训练的时候可能出现过拟合现象,为此可以配合 Droupout 或者其他防止过拟合的方法使用。