万字详述推荐系统经典模型DCN,DCN_v2,PNN

公众号:ChallengeHub(持续分析机器学习,数据挖掘,推荐系统,NLP方面的知识)

前言:

之前文章介绍了特征交叉的一些方式和几种推荐系统中关于特征交叉的一些论文。上篇文章中主要介绍了wide&deep,deepFM,和NFM模型。其中wide&deep,和deepFM模型的特征交叉就一层,属于浅度的特征交叉,NFM可以实现更深的特征交叉。这篇文章将介绍两种经典的推荐模型,它们可以实现更深度的特征交叉。分别为DCN,DCN_v2和PNN。

1:DCN

论文名称:Deep & Cross Network for Ad Click Predictions
论文链接:https://arxiv.org/pdf/1708.05123.pdf
论文结构
在这里插入图片描述
模型结构解读:
(1): x 0 = [ x e m b e d , 1 T , . . . x e m b e d , k T , x d e n s e T ] x_0=[x_{embed,1}^{T},...x_{embed,k}^{T},x_{dense}^{T}] x0=[xembed,1T,...xembed,kT,xdenseT]
x 0 x_0 x0代表的就是将输入特征处理之后进行concat起来的结果。每篇论文其实对于特征的处理形式都不一样,这篇论文是将类别的embedding,然后数值型的直接输入,将所有特征处理后拼接成一个大的embedding就是 x 0 x_0 x0
(2)右边结构 h 1 = R e L U ( W h , 0 x 0 + b h , 0 ) h_1=ReLU(W_{h,0}x_0+b{h,0}) h1=ReLU(Wh,0x0+bh,0)
这就是一个普通的DNN结构,激活函数采用的是ReLu并没有什么特殊之处。
(3)左侧结构 x l + 1 = x 0 x l T w l + b l + x l x_{l+1}=x_0x_l^{T}w_l+b_l+x_l xl+1=x0xlTwl+bl+xl
这个就是DCN的核心之处了, x 0 x_0 x0是一个维度为 d d d的列向量, w l w_l wl d l d_l dl同样是一个 d d d维度的向量,是模型的参数。公式可视化之后是这样的:
在这里插入图片描述
(4)上方结构: p = σ ( [ x L 1 T , h L 2 T w l o g i t s ] ) p=\sigma([x_{L_1}^{T},h_{L_2}^{T}w_{logits}]) p=σ([xL1T,hL2Twlogits])
将左侧和右侧得到的两个embedding直接concat起来成为一个大的embedding,经过一个浅层的神经网络输出一个一维数字,再经过一个sigmoid函数就是最终的结果了。

2:DCN_V2

论文名称:DCN V2: Improved Deep & Cross Network and Practical Lessons for Web-scale Learning to Rank Systems
论文地址:https://arxiv.org/pdf/2008.13535v2.pdf
看论文的名字,就知道该模型与DCN很相似了,该模型确实基于DCN的一个改进模型。与DCN最大的区别是特征交叉的地方有一点点地差异
在这里插入图片描述
仔细一对比的话就是将DCN中一维向量 w l w_l wl改变成了二维矩阵 W W W。除此之外,这篇论文中还提出了DCN的两种结构:
在这里插入图片描述
一篇论文肯定不会这么简单嘛,工作量这么少的论文也发不了顶会呀。于是作者们又对DCN进行了改进,原因是DCN_V2中计算的复杂度太高了。

模型改进:

改进的地方同样来自于特征交叉的地方,论文中说的让人有点不能理解,我们直接看修改的部分吧:

(1):由于将特征embedding之后再拼接起来成了一个 d d d维的向量,这个 d d d太大了,而矩阵 W W W的维度是 d × d d\times d d×d。所有导致这个计算的复杂度就很高了,于是我们可以 W = U V T W=UV^{T} W=UVT,类似于矩阵分解的方法,将维度比较大的 d × d d\times d d×d矩阵分解成两个维度小一些的 d × r d\times r d×r矩阵。其中 r r r远小于 d d d这种方法叫做矩阵的低阶分解,和SVD有点类似。这也我们的交叉公式就发生了变化:
原来 : x l + 1 = x 0 ⊙ ( W l x l + b l ) + x l x_{l+1}=x_0\odot(W_lx_l+b_l)+x_l xl+1=x0(Wlxl+bl)+xl
现在: x l + 1 = x 0 ⊙ ( U l ( V l T x l ) + b l ) + x l x_{l+1}=x_0\odot(U_l(V_{l}^{T}x_l)+b_l)+x_l xl+1=x0(Ul(VlTxl)+bl)+xl
参数的数量和运算的复杂度都有效的变低了。

(2):收到今年比较受欢迎的 Mixture-of-Experts (MoE)的影响,作者将这种结构也放入了交叉中之中。于是我们的交叉的公式又变为了以下的形式:
x l + 1 = ∑ i = 1 K G i ( x l ) E i ( x l ) + x l x_{l+1}=\sum_{i=1}^{K}G_i(x_l)E_i(x_l)+x_l xl+1=i=1KGi(xl)Ei(xl)+xl
E i ( x l ) = x 0 ⊙ ( U l i ( V l i T x l ) + b l ) E_i(x_l)=x_0\odot(U_{l}^i(V_{l}^{iT}x_l)+b_l) Ei(xl)=x0(Uli(VliTxl)+bl)
其中 G i G_i Gi就是一个门函数,可以是sigmoid或者是softmax。

(3):增加非线性。
E i ( x l ) = x 0 ⊙ ( U l i ⋅ g ( C l i ⋅ g ( V l i T x l ) ) + b l ) E_i(x_l)=x_0\odot(U_{l}^i \cdot g(C_{l}^{i}\cdot g(V_{l}^{iT}x_l))+b_l) Ei(xl)=x0(Ulig(Clig(VliTxl))+bl)
注:论文中用的是点乘,但是从浅梦大佬的代码中用的是正常的矩阵乘法。我也认为是矩阵乘法,否则最后 U l i ⋅ g ( C l i ⋅ g ( V l i T x l ) ) U_{l}^i \cdot g(C_{l}^{i}\cdot g(V_{l}^{iT}x_l)) Ulig(Clig(VliTxl))的维度对不上,如果是矩阵乘法的话,那么 C l i C_{l}^{i} Cli的维度则为 r × r r\times r r×r

改进的后模型据作者所说在降低了30%的复杂度的情况下,保留了模型的精度,模型可以称之为:the mixture of low-rank DCN(DCN-Mix)。

大佬的代码地址:https://github.com/shenweichen/DeepCTR-Torch/blob/bc881dcd417fec64f840b0cacce124bc86b3687c/deepctr_torch/layers/interaction.py#L464-L537

灵魂问答

这篇文章比较有趣的是关于实现部分是采用问答的形式展开的,总共有5个问题,同样也给出了答案。担忧翻译问题于是我附上了英文。(文中对mDCN应该就是DCN-Mix的意思,命名有点混乱,希望我没弄错)
(1):什么时候特征交互学习方法会比基于relu的DNN更有效?
(When would feature interaction learning methods become more efficient than ReLU-based DNN)

从论文后续的答案中,作者从三个方面回答了该问题(不过更像是回答怎样可以提升DCN_v2的效果)
a:提升交叉的复杂度
文中使用了三种二维的交叉方法( x i x_i xi代表的是某个特征),按照交叉的复杂程度有以下三种。
在这里插入图片描述
然后就是三种交叉的结果:
在这里插入图片描述
b:DCN中各个部分的作用
在这里插入图片描述
c:提升交叉的阶数
在这里插入图片描述
总结:总而言之,即使使用更深更大的网络,ReLU 在捕获显式特征交叉(乘法关系)方面效率低下。 当交叉模式变得更复杂时,精度会大大降低。 DCN 准确地捕获了简单的交叉模式,但无法捕捉更复杂的交叉模式。 另一方面,DCN-V2 对复杂的交叉模式保持准确和高效

(2):如果去除掉DNN结构的话,各种交叉方法的表现是怎么样的?
(How does the feature-interaction component of each baseline perform without integrating with DNN)

在这里插入图片描述
总结:高阶方法表现出优于二阶方法的性能。 这表明高阶交叉在该数据集中是有意义的。在高阶方法中,交叉网络取得了最好的性能,与 DNN 相比略胜一筹。

(3):DCN-Mix的于基础的DCN_v2相比表现如何,我们应该这么在模型的精确度和模型的消耗之间进行平衡(以DCN为例)。
(How does the proposed mDCN approaches compare to the baselines? Could we achieve healthier trade-off between model accuracy and cost through mDCN and the mixture of low-rank DCN)

在这里插入图片描述
总结:在所有方法中,DCN-V2效果最好,同时相比性能不差DCN-Mix 进一步降低了模型消耗,在模型性能和效果之间取得了更好的平衡。(作者主要对比了DCN_v2和DCN-Mix与其它模型之间的效果)

(4):DCN-Mix参与与模型效果之间的关系
(Q4 How does the settings in mDCN affect model quality)

在这里插入图片描述
上图左边展现的是DNN模型和Cross NetWork在不同网络层数下的效果。右侧是展现在不同的矩阵维度情况下的效果。(这个matrix rank就是在改进模型中 W l , r × d W_l,r\times d Wl,r×d r r r的大小,也就是从高维分解为低维中,低维的数量)。

(5):DCN-Mix是否捕捉到了重要的特征交叉?模型的解释性如何?
(Q5 Is mDCN capturing important feature crosses? Does the model provide good understandabilit)

在这里插入图片描述
作者直接从实验结果回答了该问题。左侧是DCN_v2中的 W W W权重矩阵,右侧显示的特征交叉,颜色越深代表者交叉越重要,从两张图的重合程度可以看出还是学到了一些重要的显示交叉特征,比如gender-UserId和Movied-UserId。

作为近年来推荐系统中的SOTA模型,这篇论文还是有很多启发的。DCN的改进,高阶矩阵分解等操作还是很有学习的意义。并且实验阶段给我们展示了很多扩展性的工作。

PNN

论文地址:https://arxiv.org/pdf/1611.00144v1.pdf
模型结构
在这里插入图片描述
首先看模型结构,从下面往上面看,第一层input就到embedding就不需要再解释了吧。
(1) z \boldsymbol{z} z z z z就是简单地将所有feature地embedding直接复制过去。
其中 z = ( z 1 , z 2 , . . . z N ) = = = ( f 1 , f 2 , . . . f N ) \boldsymbol{z}=(\boldsymbol{z_1},\boldsymbol{z_2},...\boldsymbol{z_N})===(\boldsymbol{f_1},\boldsymbol{f_2},...\boldsymbol{f_N}) z=(z1,z2,...zN)===(f1,f2,...fN) . f i .\boldsymbol{f_i} .fi就是特征 i i i的embedding。

(2) p \boldsymbol{p} p p i , j , i = 1... N , j = 1... N p_{i,j},i=1...N,j=1...N pi,j,i=1...Nj=1...N,其中 p i , j = g ( f i , f j ) p_{i,j}=g(\boldsymbol{f_i},\boldsymbol{f_j}) pi,j=g(fi,fj),可以看出 p i , j p_{i,j} pi,j就是特征 i , j i,j i,j之间交叉的结果。至于怎么交叉,我们稍后再说。

(3) z , p z,p z,p层到达 L 1 L_1 L1我感觉是我看这篇论文没有想到的。
在我看来,应该就是将 z \boldsymbol{z} z展开,再将 p \boldsymbol{p} p展开,然后concat成一个一维的向量,然后再经过一层网络得到L1,但其实并不是这样的。我们介绍L1的构成:
l 1 = r e l u ( l z + l p + b 1 ) \boldsymbol{l}_1=relu(\boldsymbol{l}_z+\boldsymbol{l}_p+\boldsymbol{b}_1) l1=relu(lz+lp+b1)

可以看出 l z , l p , b 1 \boldsymbol{l}_z,\boldsymbol{l}_p,\boldsymbol{b}_1 lz,lp,b1三个一维的向量加起来再经过relu得到的L1。与我看模型结构时的猜想并不一致。

首先我们来介绍 l z \boldsymbol{l}_z lz的构成:
l z = ( l z 1 , l z 2 , . . . l z D 1 ) \boldsymbol{l}_z=(l_z^1,l_z^2,...l_z^{D_1}) lz=(lz1,lz2,...lzD1)
l z n = W z n ⊙ z = ∑ i = 1 N ∑ j = 1 M ( W z n ) i , j z i , j l_z^n=\boldsymbol{W}_z^n\odot \boldsymbol{z}=\sum_{i=1}^N\sum_{j=1}^M(\boldsymbol{W}_z^n)_{i,j}z_{i,j} lzn=Wznz=i=1Nj=1M(Wzn)i,jzi,j可以看出, W z n \boldsymbol{W}_z^n Wzn时一个维度与 z \boldsymbol{z} z完全相同的矩阵,最后 l z \boldsymbol{l_z} lz的维度有多少( D 1 D_1 D1),则 W z n \boldsymbol{W}_z^n Wzn的个数有多少。

同理我们可以得到:
l p n = W p n ⊙ p = ∑ i = 1 N ∑ j = 1 N ( W p n ) i , j p i , j l_p^n=\boldsymbol{W}_p^n\odot \boldsymbol{p}=\sum_{i=1}^N\sum_{j=1}^N(\boldsymbol{W}_p^n)_{i,j}p_{i,j} lpn=Wpnp=i=1Nj=1N(Wpn)i,jpi,j。但是在求 p i , j p_{i,j} pi,j处作者提出了两种方法。

第一种是IPNN

在这里插入图片描述
这种就是最常见的交叉了, p i , j = < f i , f j > = f i T f j p_{i,j}=<\boldsymbol{f_i},\boldsymbol{f_j}>=\boldsymbol{f_i^T}\boldsymbol{f_j} pi,j=<fi,fj>=fiTfj
受到FM模型的启发,将 W p n \boldsymbol{W_p^n} Wpn转化为两个向量。即: W p n = θ n θ n T \boldsymbol{W_p^n}=\boldsymbol{\theta^n}\boldsymbol{\theta^n}^T Wpn=θnθnT
因此原来的等式就可以变为:
l p n = W p n ⊙ p = ∑ i = 1 N ∑ j = 1 N θ i n θ j n T < f i , f j > = < ∑ i = 1 N δ i n , ∑ i = 1 N δ i n > l_p^n=\boldsymbol{W}_p^n\odot p=\sum_{i=1}^N\sum_{j=1}^N{\theta_i^n}{\theta_j^n}^T<\boldsymbol{f_i},\boldsymbol{f_j}>=<\sum_{i=1}^N\delta_i^n,\sum_{i=1}^N\delta_i^n> lpn=Wpnp=i=1Nj=1NθinθjnT<fi,fj>=<i=1Nδin,i=1Nδin>
此时: δ i n = θ i n f i \delta_i^n={\theta_i^n}\boldsymbol{f_i} δin=θinfi。可得 l p n = ( ∣ ∣ ∑ i = 1 δ i 1 ∣ ∣ , . . . ∣ ∣ ∑ i = 1 δ i n ∣ ∣ ) l_p^n=(||\sum_{i=1}\delta_i^1||,...||\sum_{i=1}\delta_i^n||) lpn=(i=1δi1,...i=1δin)。引入这种方法与FM的初衷也是一样,为了降低计算的复杂度,同时也可以减少参数的数量。

本来计算的时间复杂度为: O ( N 2 ( D 1 + M ) O(N^2(D_1+M) O(N2(D1+M)( N N N是特征的数量, M M M为特征embedding的维度, D 1 D_1 D1为最后 l p \boldsymbol{l_p} lp的维度)。由于 p i , j p_{i,j} pi,j只需要计算一次,时间复杂度并不是直接的 O ( N 2 D 1 M ) O(N^2D_1M) O(N2D1M)。通过降维后时间复杂度为 O ( N D 1 M ) O(ND_1M) O(ND1M)

这种通过矩阵分解的方式减少参数数量,降低计算的复杂度在推荐模型中应用还是很多的,上面的DCN-M同样也应用到了。

第二种是OPNN

在这里插入图片描述
这种交叉比较少见, p i , j = f i f j T \boldsymbol{p_{i,j}}=\boldsymbol{f_i}\boldsymbol{f_j}^T pi,j=fifjT。在IPNN中p_{i,j}是一个数字,但是在OPNN中,则是一个矩阵, p i , j \boldsymbol{p_{i,j}} pi,j的维度为 M × M M \times M M×M。此时 p \boldsymbol{p} p再也不是根据 p i , j \boldsymbol{p_{i,j}} pi,j,而是这样的:

p = ∑ i = 1 N ∑ j = 1 N f i f i T \boldsymbol{p}=\sum_{i=1}^N\sum_{j=1}^N\boldsymbol{f_i}\boldsymbol{f_i}^T p=i=1Nj=1NfifiT
总结来说,有N个特征,那么就会交叉N的平方次,每次交叉都会形成一个矩阵,将N的平方和矩阵相加起来就可以得到 p \boldsymbol{p} p了。直观来说就是将上面那个正方形的矩阵层层叠加,最后得到一个相同大小的矩阵。该矩阵的维度同样为 M × M M\times M M×M。此时计算的复杂度达到惊人的 O ( N 2 D 1 M 2 ) O(N^2D_1M^2) O(N2D1M2)

同样上面的公式还是可以进行优化的:
p = f ∑ f ∑ T \boldsymbol{p}=\boldsymbol{f_{\sum}}\boldsymbol{f_{\sum}}^T p=ffT

其中 f ∑ = ∑ i N f i \boldsymbol{f_{\sum}}=\sum_i^N\boldsymbol{f_i} f=iNfi。然后直接采用公式 l p n = W p n ⊙ p l_p^n=\boldsymbol{W}_p^n\odot \boldsymbol{p} lpn=Wpnp就可以得到我们要的结果。

以上就是关于推荐系统中经典模型DCN,DCN_v2,PNN模型的详细介绍,介绍得很详细,对于我自己来说相当于重新仔细地学习了一遍,收获不浅,更加了解了模型中的具体细节。同时也希望可以给有兴趣学习推荐模型的一些朋友有一定的帮助。

此外,喜欢数据挖掘,推荐系统,nlp的朋友可以关注我们公众号,然后回复加群添加我好友。

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一份简单的Python代码,用于使用GRNN和PNN建立鸢尾花识别模型,并对模型性能进行评估。我们也探究了各个属性及各个属性组合与鸢尾花种类之间的相关性。 ```python import numpy as np from sklearn import datasets from neupy import algorithms, layers, environment # 设置随机种子,以确保结果可再现性 np.random.seed(0) # 加载鸢尾花数据集 data = datasets.load_iris() # 将数据集分成训练集和测试集 X_train = data.data[:100] y_train = data.target[:100] X_test = data.data[100:] y_test = data.target[100:] # 模型设置 environment.reproducible() grnn = algorithms.GRNN(std=0.2, verbose=False) pnn = algorithms.PNN(std=0.2, verbose=False) # 训练模型 grnn.train(X_train, y_train) pnn.train(X_train, y_train) # 使用训练好的模型进行预测 y_pred_grnn = grnn.predict(X_test) y_pred_pnn = pnn.predict(X_test) # 计算模型的准确性 accuracy_grnn = np.mean(y_pred_grnn == y_test) accuracy_pnn = np.mean(y_pred_pnn == y_test) print("GRNN模型的准确性: {:.2%}".format(accuracy_grnn)) print("PNN模型的准确性: {:.2%}".format(accuracy_pnn)) # 探究各个属性及各个属性组合与鸢尾花种类之间的相关性 for i in range(X_train.shape[1]): grnn_attr = algorithms.GRNN(std=0.2, verbose=False) pnn_attr = algorithms.PNN(std=0.2, verbose=False) X_train_attr = X_train[:, i].reshape(-1, 1) X_test_attr = X_test[:, i].reshape(-1, 1) grnn_attr.train(X_train_attr, y_train) pnn_attr.train(X_train_attr, y_train) y_pred_grnn_attr = grnn_attr.predict(X_test_attr) y_pred_pnn_attr = pnn_attr.predict(X_test_attr) accuracy_grnn_attr = np.mean(y_pred_grnn_attr == y_test) accuracy_pnn_attr = np.mean(y_pred_pnn_attr == y_test) print("属性{}的GRNN模型准确性: {:.2%}".format(i+1, accuracy_grnn_attr)) print("属性{}的PNN模型准确性: {:.2%}".format(i+1, accuracy_pnn_attr)) for i in range(X_train.shape[1]): for j in range(i+1, X_train.shape[1]): grnn_attr_comb = algorithms.GRNN(std=0.2, verbose=False) pnn_attr_comb = algorithms.PNN(std=0.2, verbose=False) X_train_attr_comb = X_train[:, [i, j]] X_test_attr_comb = X_test[:, [i, j]] grnn_attr_comb.train(X_train_attr_comb, y_train) pnn_attr_comb.train(X_train_attr_comb, y_train) y_pred_grnn_attr_comb = grnn_attr_comb.predict(X_test_attr_comb) y_pred_pnn_attr_comb = pnn_attr_comb.predict(X_test_attr_comb) accuracy_grnn_attr_comb = np.mean(y_pred_grnn_attr_comb == y_test) accuracy_pnn_attr_comb = np.mean(y_pred_pnn_attr_comb == y_test) print("属性{}和{}的GRNN模型准确性: {:.2%}".format(i+1, j+1, accuracy_grnn_attr_comb)) print("属性{}和{}的PNN模型准确性: {:.2%}".format(i+1, j+1, accuracy_pnn_attr_comb)) ``` 这个代码使用了`neupy`库中的`GRNN`和`PNN`算法,对鸢尾花数据集进行分类。首先,我们将数据集分成训练集和测试集。然后,我们使用训练集来训练`GRNN`和`PNN`模型,并使用测试集来进行预测。最后,我们计算模型的准确性。 我们还使用`for`循环探究了各个属性及各个属性组合与鸢尾花种类之间的相关性。我们训练了单个属性的`GRNN`和`PNN`模型,以及属性组合的`GRNN`和`PNN`模型。然后,我们计算每个模型的准确性。 请注意,这只是一份简单的示例代码,您可以根据需要进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值