.. _second_order_feature_crossing: 二阶特征交叉 ============ 针对 Wide & Deep 模型中人工特征工程的局限性,特征交叉自动化成为了一个迫切需要解决的问题。在这一探索过程中,首先要攻克的是:\ **如何自动、高效地捕捉所有成对(二阶)特征的交互,并将其与深度学习模型结合。** 这里的挑战不仅在于“自动”,更在于面对推荐场景下海量、高维、稀疏的数据时如何实现“高效”。直接暴力计算所有特征对的组合是不可行的,我们需要一种更巧妙的机制来参数化这些交互。同时,在解决了二阶交互的自动化表达后,如何将这些捕获到的低阶、显式交互信息,与能够学习高阶、隐式关系的深度神经网络(DNN)进行有效融合,也成为这一阶段模型探索的重点。 FM: 隐向量内积与参数共享 ------------------------ 我们在召回章节 :numref:`fm-matching-model` 已经初步了解了FM :cite:`rendle2010factorization` ,并见证了它如何作为双塔模型的雏形,通过向量匹配实现召回。在精排阶段,FM的价值得到了更核心的体现。它作为解决特征交叉自动化问题的开创性模型,其核心思想——**为每个特征学习一个低维隐向量,并用向量内积来参数化所有二阶交叉项的权重**——不仅有效解决了参数数量过多和数据稀疏性两大难题,也为这一小节后续模型奠定了方法论的基础。 .. figure:: ../../img/fm_model.png FM模型结构 为了捕捉特征间的交互关系,一个直接的想法是在线性模型的基础上增加所有特征的二阶组合项,即多项式模型: .. math:: y = w_0 + \sum_{i=1}^n w_i x_i + \sum_{i=1}^{n-1} \sum_{j=i+1}^n w_{ij} x_i x_j 其中,\ :math:`w_0` 是全局偏置项,\ :math:`w_i` 是特征 :math:`x_i` 的权重,\ :math:`w_{ij}` 是特征 :math:`x_i` 和 :math:`x_j` 交互的权重,\ :math:`n` 是特征数量。这个模型存在两个致命缺陷:第一,参数数量会达到 :math:`O(n^2)` 的级别,在特征数量庞大的推荐场景下难以承受;第二,在数据高度稀疏的环境中,绝大多数的交叉特征 :math:`x_i x_j` 因为在训练集中从未共同出现过,导致其对应的权重 :math:`w_{ij}` 无法得到有效学习。 FM 模型巧妙地解决了这个问题。它将交互权重 :math:`w_{ij}` 分解为两个低维隐向量的内积,即 :math:`w_{ij}=\langle\mathbf{v}_i,\mathbf{v}_j\rangle`\ 。这样,模型的预测公式就演变为: .. math:: y = w_0 + \sum_{i=1}^n w_i x_i + \sum_{i=1}^{n-1} \sum_{j=i+1}^n \langle \mathbf{v}_i, \mathbf{v}_j \rangle x_i x_j 其中\ :math:`\mathbf{v}_i,\mathbf{v}_j` 分别是特征 :math:`i` 和特征 :math:`j` 的 :math:`k` 维隐向量(Embedding)。\ :math:`k` 是一个远小于特征数量 :math:`n` 的超参数,\ :math:`\langle \mathbf{v}_i,\mathbf{v}_j \rangle` 表示两个隐向量的内积,计算方式为 :math:`\sum_{f=1}^k v_{i,f} \cdot v_{j,f}`\ 。 这种\ **参数共享**\ 的设计是 FM 的精髓所在。原本需要学习 :math:`O(n^2)` 个独立的交叉权重 :math:`w_{ij}`\ ,现在只需要为每个特征学习一个 :math:`k` 维的隐向量 :math:`v_i`\ ,总参数量就从 :math:`O(n^2)` 降低到了 :math:`O(nk)`\ 。更重要的是,它极大地缓解了数据稀疏问题。即使特征 :math:`i` 和 :math:`j` 在训练样本中从未同时出现过,模型依然可以通过它们各自与其他特征(如 :math:`k`\ )的共现数据,分别学到有效的隐向量 :math:`v_i` 和 :math:`v_j`\ 。只要隐向量学习得足够好,模型就能够泛化并预测 :math:`x_i` 和 :math:`x_j` 的交叉效应。此外,通过巧妙的数学变换,FM 的二阶交叉项计算复杂度可以从 :math:`O(kn^2)` 优化到线性的 :math:`O(kn)`\ ,使其在工业界得到了广泛应用。 AFM: 注意力加权的交叉特征 ------------------------- FM 对所有特征交叉给予了相同的权重,但实际上不同交叉组合的重要性是不同的。AFM :cite:`xiao2017attentional` 在此基础上引入注意力机制,为不同的特征交叉分配权重,使模型能关注到更重要的交互。例如,在预测一位用户是否会点击一条体育新闻时,“用户年龄=18-24岁”与“新闻类别=体育”的交叉,其重要性显然要高于“用户年龄=18-24岁”与“新闻发布时间=周三”的交叉。 .. figure:: ../../img/afm_architecture.png AFM模型结构 AFM 的模型结构在 FM 的基础上进行了扩展。它首先将所有成对特征的隐向量进行\ **元素积(Hadamard Product, 记为 :math:`\odot`\ )**\ ,而不是像 FM 那样直接求内积。这样做保留了交叉特征的向量信息,为后续的注意力计算提供了输入。这个步骤被称为成对交互层(Pair-wise Interaction Layer)。 .. math:: f_{PI}(\mathcal{E}) = \{(v_i \odot v_j) x_i x_j \}_{(i,j) \in \mathcal{R}_x} 其中,\ :math:`\mathcal{E}` 表示输入样本中所有非零特征的embedding向量集合,\ :math:`\mathcal{R}_x` 表示输入样本中所有非零特征的索引对集合。随后,模型引入一个注意力机制,来学习每个交叉特征 :math:`(v_i \odot v_j)` 的重要性得分 :math:`a_{ij}`\ 。 .. math:: \begin{aligned} a_{ij}' &= \textbf{h}^T \text{ReLU}(\textbf{W} (\mathbf{v}_i \odot \mathbf{v}_j) x_i x_j + \textbf{b}) \\ a_{ij} &= \frac{\exp(a_{ij}')}{\sum_{(i,k) \in \mathcal{R}_x} \exp(a_{ik}')} \end{aligned} 其中,\ :math:`\textbf{W}` 是注意力网络的权重矩阵,\ :math:`\textbf{b}` 是偏置向量,\ :math:`\textbf{h}` 是输出层向量。这个得分 :math:`a_{ij}` 经过 Softmax 归一化后,被用作加权求和的权重,与原始的交叉特征向量相乘,最终汇总成一个向量。这个过程被称为注意力池化层(Attention-based Pooling)。 .. math:: f_{Att} = \sum_{(i,j) \in \mathcal{R}_x} a_{ij} (\mathbf{v}_i \odot \mathbf{v}_j) x_i x_j 最后,AFM 的完整预测公式由一阶线性部分和经过注意力加权的二阶交叉部分组成: .. math:: \hat{y}_{afm}(x) = w_0 + \sum_{i=1}^n w_i x_i + \textbf{p}^T f_{Att} 其中 :math:`\textbf{p}` 是一个投影向量,用于将最终的交叉结果映射为标量。通过引入注意力机制,\ **AFM 不仅提升了模型的表达能力,还通过可视化注意力权重** :math:`a_{ij}` **赋予了模型更好的可解释性**\ ,让我们可以洞察哪些特征交叉对预测结果的贡献最大。 NFM: 交叉特征的深度学习 ----------------------- NFM :cite:`he2017neural` 探索了如何更深入地利用交叉信息。它将 FM 的二阶交叉结果(用哈达玛积表示的向量)作为输入,送入一个深度神经网络(DNN),从而在 FM 的基础上学习更高阶、更复杂的非线性关系。NFM 的核心思想是,\ **FM 所捕获的二阶交叉信息本身就是一种非常有价值的特征,可以作为“原料”输入给强大的 DNN,由 DNN 来自动学习这些交叉特征之间的高阶组合关系**\ 。 NFM 的结构非常清晰。它首先通过一个创新的“特征交叉池化层”(Bi-Interaction Pooling Layer)来对所有特征对的 Embedding 向量进行处理。这一层的操作如下: .. math:: f_{BI}(V_x) = \sum_{i=1}^n \sum_{j=i+1}^n x_i \mathbf{v}_i \odot x_j \mathbf{v}_j 其中 :math:`V_x = \{x_1 v_1, x_2 v_2, ..., x_n v_n\}` 是输入样本中所有非零特征的 Embedding 向量集合,\ :math:`\odot` 仍然是元素积操作。这个操作的结果是一个与 Embedding 维度相同的向量,它有效地编码了所有的二阶特征交叉信息。值得注意的是,与FM中的变换类似,这一层的计算同样可以被优化到线性时间复杂度,非常高效: .. math:: f_{BI}(V_x) = \frac{1}{2} \left[\left(\sum_{i=1}^n x_i \mathbf{v}_i\right)^2 - \sum_{i=1}^n (x_i \mathbf{v}_i)^2\right]. 得到特征交叉池化层的输出向量 :math:`f_{BI}(V_x)` 后,NFM 将其送入一个标准的多层前馈神经网络(MLP): .. math:: z_1 = \sigma_1(\textbf{W}_1 f_{BI}(V_x) + \textbf{b}_1),\ \ldots,\ z_L = \sigma_L(\textbf{W}_L z_{L-1} + \textbf{b}_L) 其中 :math:`\textbf{W}_l, \textbf{b}_l, \sigma_l` 分别是第 :math:`l` 个隐藏层的权重、偏置和非线性激活函数。最后,NFM 将一阶线性部分与 DNN 部分的输出结合起来,得到最终的预测结果: .. math:: \hat{y}_{NFM}(x) = w_0 + \sum_{i=1}^n w_i x_i + \textbf{h}^T z_L 其中 :math:`\textbf{h}` 是预测层的权重向量。通过这种方式,NFM 巧妙地将 FM 的二阶交叉能力与 DNN 的高阶非线性建模能力结合在了一起。FM 可以被看作是 NFM 在没有隐藏层时的特例,这表明 NFM 是对 FM 的一个自然扩展和深度化。 PNN: 多样化的乘积操作 --------------------- PNN :cite:`qu2016product` 认为仅用内积(Inner Product)或元素积(Hadamard Product)不足以捕捉所有特征交互信息。因此,它在经典的内积基础上,\ **增加了外积(Outer Product)作为补充,尝试从更丰富的角度来表示特征间的交互**\ 。PNN 的核心创新在于其“乘积层”(Product Layer),该层专门用于对特征 Embedding 进行显式的交叉操作,其输出再送入后续的全连接网络。 .. figure:: ../../img/pnn.png PNN模型结构 PNN 的乘积层会产生两部分信号,一部分是线性信号 :math:`\mathbf{l}_z`\ ,直接来自于各特征的 Embedding 向量,定义为: .. math:: \mathbf{l}_z^n = \sum_{i=1}^N\sum_{k=1}^M (\mathbf{W}_z^n)_{i,k} \mathbf{f}_i^k 其中 :math:`\mathbf{f}_i` 是特征的 Embedding 向量,\ :math:`\mathbf{W}_z^n` 是第 :math:`n` 个神经元对应的线性信号权重矩阵。\ :math:`N` 为特征字段数量,\ :math:`M` 为 Embedding 维数。 另一部分是二次信号 :math:`\mathbf{l}_p`\ ,来自于特征 Embedding 之间的两两交互。根据交互方式的不同,PNN 的二次信号分为两种主要的变体: **IPNN (Inner Product-based Neural Network)**: 这种变体使用特征 Embedding 之间的\ **内积**\ 来计算二次信号。一个直接的计算方式是: .. math:: \mathbf{l}_p^n = \sum_{i=1}^N \sum_{j=1}^N (\textbf{W}_p^n)_{i,j} \langle \mathbf{f}_i, \mathbf{f}_j \rangle :math:`\textbf{W}_p^n` 是第 :math:`n` 个神经元对应的权重矩阵。这种计算方式的复杂度是 :math:`O(N^2)`\ ,\ :math:`N` 为特征字段数量,开销巨大。为了优化,PNN 引入了矩阵分解技巧,将权重矩阵 :math:`\textbf{W}_p^n` 分解为 :math:`\theta_n \theta_n^T`\ ,即 :math:`(\textbf{W}_p^n)_{i,j} = \theta_{i,n} \theta_{j,n}`\ 。于是,计算过程可以被重写和简化: .. math:: \mathbf{l}_p^n = \sum_{i=1}^N \sum_{j=1}^N \theta_i^n \theta_j^n \langle \mathbf{f}_i, \mathbf{f}_j \rangle = \sum_{i=1}^N \sum_{j=1}^N \langle \theta_i^n \mathbf{f}_i, \theta_j^n \mathbf{f}_j \rangle = \langle \sum_{i=1}^N \theta_i^n \mathbf{f}_i, \sum_{j=1}^N \theta_j^n \mathbf{f}_j \rangle = \left\|\sum_{i=1}^N \theta_i^n \mathbf{f}_i\right\|^2 通过这个变换,\ **计算所有内积对的加权和,转变成了先对 Embedding 进行加权求和,然后计算一次向量的 L2 范数平方**\ ,复杂度成功地从 :math:`O(N^2M)` 降低到了 :math:`O(NM)`\ 。 优化后的完整计算公式为: .. math:: \mathbf{l}_p = \left(\left\|\sum_{i=1}^N \theta_i^1 \mathbf{f}_i\right\|^2, \left\|\sum_{i=1}^N \theta_i^2 \mathbf{f}_i\right\|^2, \ldots, \left\|\sum_{i=1}^N \theta_i^n \mathbf{f}_i\right\|^2\right) **OPNN (Outer Product-based Neural Network)**: 这种变体使用特征 Embedding 之间的\ **外积** :math:`\mathbf{f}_i\mathbf{f}_j^T` 来捕捉更丰富的交互信息。外积会产生一个矩阵,如果对所有外积对进行加权求和 :math:`\sum_{i=1}^N \sum_{j=1}^N \mathbf{f}_i \mathbf{f}_j^T`\ ,计算复杂度会达到 :math:`O(N^2M^2)`\ (\ :math:`M` 为 Embedding 维数),这在实践中是不可行的。OPNN 采用了一种称为“叠加”(superposition)的近似方法来大幅降低复杂度。它不再计算所有成对的外积,而是\ **先将所有特征的 Embedding 向量相加,然后再计算一次外积**\ : .. math:: \sum_{i=1}^N \sum_{j=1}^N \mathbf{f}_i \mathbf{f}_j^T = (\sum_{i=1}^N \mathbf{f}_i)(\sum_{j=1}^N \mathbf{f}_j)^T 这样,计算量得到了节省 :math:`O(M(M+N))`\ 。优化后的完整计算公式为: .. math:: \mathbf{l}_p = \left(\langle\mathbf{W}_p^1, (\sum_{i=1}^N \mathbf{f}_i)(\sum_{j=1}^N \mathbf{f}_j)^T\rangle, \langle\mathbf{W}_p^2, (\sum_{i=1}^N \mathbf{f}_i)(\sum_{j=1}^N \mathbf{f}_j)^T\rangle, \ldots, \langle\mathbf{W}_p^n, (\sum_{i=1}^N \mathbf{f}_i)(\sum_{j=1}^N \mathbf{f}_j)^T\rangle\right) 其中 对称矩阵\ :math:`\mathbf{W}_p^n \in \mathbb{R}^{M \times M}` 是第 :math:`n` 个神经元对应的权重矩阵,矩阵内积\ :math:`\langle \mathbf{A}, \mathbf{B} \rangle = \sum_{i=1}^M \sum_{j=1}^M \mathbf{A}_{i,j} \mathbf{B}_{i,j}`\ 。 在得到线性信号 :math:`l_z` 和经过优化的二次信号 :math:`l_p` 后,PNN 将它们合并,并送入后续的全连接层进行高阶非线性变换: .. math:: \begin{aligned} \mathbf{l}_1 &= \text{ReLU}(\mathbf{l}_z + \mathbf{l}_p + \mathbf{b}_1) \\ \mathbf{l}_2 &= \text{ReLU}(\mathbf{W}_2 \mathbf{l}_1 + \mathbf{b}_2) \\ \hat{y} &= \sigma(\textbf{W}_3 \mathbf{l}_2 + b_3) \end{aligned} PNN 的独特之处在于,它将“乘积”操作(无论是内积还是外积)作为了网络中的一个核心计算单元,认为这种操作比传统 DNN 中简单的“加法”操作更能有效地捕捉类别型特征之间的交互关系。 FiBiNET: 特征重要性与双线性交互 ------------------------------- 虽然 PNN 通过内积和外积丰富了特征交互的表达方式,但它仍然将所有特征视为同等重要。然而,在实际的推荐场景中,不同特征对于预测结果的贡献度显然是不同的。FiBiNET (Feature Importance and Bilinear feature Interaction Network) :cite:`huang2019fibinet` 模型认识到了这个问题,\ **它在进行二阶特征交叉之前,先动态地学习每个特征的重要性权重,然后再通过双线性交互来捕捉更精细的特征关系**\ 。这种设计使得模型能够有选择性地进行特征交互,从而提升二阶特征交叉的质量。 .. figure:: ../../img/fibinet_architecture.png FiBiNET架构图 FiBiNET 的创新主要体现在两个核心模块上:\ **SENET 特征重要性学习机制**\ 和\ **双线性交互层**\ 。 **SENET 特征重要性学习** FiBiNET 引入了来自计算机视觉领域的 **SENET (Squeeze-and-Excitation Network)** :cite:`hu2018squeeze` 机制,用于动态学习每个特征的重要性权重。与传统方法对所有特征一视同仁不同,SENET 能够自适应地为不同特征分配不同的权重,让模型更加关注那些对预测任务更重要的特征。 .. figure:: ../../img/fibinet_senet.png SENET层结构详解 SENET 的工作流程包含三个关键步骤: 1. **Squeeze (挤压)**: 通过全局平均池化将每个特征的 :math:`k` 维嵌入向量 :math:`\mathbf{e}_i` 压缩成一个标量统计量: .. math:: \mathbf{z}_i = F_{\text{sq}}(\mathbf{e}_i) = \frac{1}{k} \sum_{t=1}^k \mathbf{e}_i(t) 2. **Excitation (激励)**: 使用两个全连接层构成的瓶颈结构来学习特征间的相关性,并生成每个特征的重要性权重: .. math:: \mathbf{A} = F_{\text{ex}}(\mathbf{Z}) = \sigma_2(\mathbf{W}_2 \sigma_1(\mathbf{W}_1 \mathbf{Z})) 其中 :math:`\mathbf{W}_1 \in \mathbb{R}^{f \times \frac{f}{r}}` 和 :math:`\mathbf{W}_2 \in \mathbb{R}^{\frac{f}{r} \times f}` 是可学习的权重矩阵,\ :math:`r` 是缩减率超参数。 3. **Re-weight (重新加权)**: 将学习到的权重应用于原始嵌入向量: .. math:: \mathbf{V} = F_{\text{ReWeight}}(\mathbf{A}, \mathbf{E}) = [\mathbf{a}_1 \cdot \mathbf{e}_1, \mathbf{a}_2 \cdot \mathbf{e}_2, \ldots, \mathbf{a}_f \cdot \mathbf{e}_f] **双线性交互层** 在获得原始嵌入 :math:`\mathbf{E}` 和经过 SENET 加权的嵌入 :math:`\mathbf{V}` 后,FiBiNET 设计了双线性交互层来更精细地建模特征间的二阶交互关系。与 FM 中简单的内积操作或 PNN 中的元素积操作相比,双线性交互引入了一个可学习的变换矩阵 :math:`\mathbf{W} \in \mathbb{R}^{k \times k}`\ ,使得模型能够学习更复杂的特征交互模式: .. math:: \mathbf{p}_{ij} = \mathbf{v}_i \cdot \mathbf{W} \circ \mathbf{v}_j 其中 :math:`\circ` 表示哈达玛积。这种双线性变换相比于简单的内积或元素积,能够捕捉到更加丰富和细致的特征交互信息。 FiBiNET 会同时对原始嵌入 :math:`\mathbf{E}` 和加权嵌入 :math:`\mathbf{V}` 进行双线性交互,然后将这两组交互结果、原始特征以及一个深度神经网络的输出结合起来,共同做出最终的预测。通过这种方式,\ **FiBiNET 不仅解决了“哪些特征更重要”的问题,还通过双线性交互提升了二阶特征交叉的表达能力**\ 。 .. _deepfm: DeepFM: 低阶高阶的统一建模 -------------------------- DeepFM :cite:`guo2017deepfm` 是对 Wide & Deep 架构的直接改进和优化。它将 Wide & Deep 中需要大量人工特征工程的 Wide 部分,\ **直接替换为了一个无需任何人工干预的 FM 模型**\ ,从而实现了真正的端到端训练。更关键的是,DeepFM 中的 **FM 组件和 Deep 组件共享同一份特征嵌入(Embedding)**\ ,这带来了两大好处:首先,模型可以同时从原始特征中学习低阶和高阶的特征交互;其次,共享 Embedding 的方式使得模型训练更加高效。 .. figure:: ../../img/deepfm_architecture.png DeepFM模型结构 DeepFM 的结构非常清晰,它由 FM 和 DNN 两个并行的组件构成,两者共享输入。 - **FM 组件**: 负责学习一阶特征和二阶特征交叉。其输出 yFM 的计算方式与标准 FM 完全相同: .. math:: y_{FM} = \langle w, x \rangle + \sum_{i=1}^d \sum_{j=i+1}^d \langle V_{i}, V_{j} \rangle x_{i}x_{j} 这里的 :math:`V_{i}` 就是特征 :math:`i` 的 Embedding 向量。 - **Deep 组件**: 负责学习高阶的非线性特征交叉。它的输入正是 FM 组件中所使用的那一套 Embedding 向量。具体来说,所有输入特征首先被映射到它们的低维 Embedding 向量上,然后这些 Embedding 向量被拼接(concatenate)在一起,形成一个长的向量,作为 DNN 的输入。 .. math:: a^{(0)} = [e_1, e_2, ..., e_m] 其中 :math:`e_i` 是第 :math:`i` 个特征字段的 Embedding 向量。这个拼接后的向量随后被送入一个标准的前馈神经网络,前向传播公式为: .. math:: a^{(l+1)} = \sigma(\textbf{W}^{(l)} a^{(l)} + \textbf{b}^{(l)}) 其中 :math:`l` 是层深度,\ :math:`\sigma` 是激活函数,\ :math:`\textbf{W}^{(l)}`\ 、\ :math:`\textbf{b}^{(l)}`\ 分别是第 :math:`l` 层的权重和偏置。最后输出为: .. math:: y_{Deep} = \textbf{W}^{|H|+1} \cdot a^{|H|} + \textbf{b}^{|H|+1} 其中 :math:`H` 是隐藏层数量. 最终,DeepFM 的总输出是 FM 部分和 Deep 部分输出的简单相加,再通过一个 Sigmoid 函数得到最终的点击率预测值: .. math:: \hat{y} = \sigma(y_{FM} + y_{Deep}) DeepFM模型成功地结合了FM的低阶特征学习能力和深度神经网络的高阶特征学习能力。通过FM组件和深度组件共享相同的特征嵌入,模型可以同时从原始特征中学习低阶和高阶特征交互,无需像Wide & Deep那样依赖专家的特征工程。这种设计使得DeepFM成为一个端到端的自动特征学习模型,在效果和效率上都表现出色。 代码实践 -------- .. raw:: latex \diilbookstyleinputcell .. code:: python import sys import funrec from funrec.evaluation import compare_models models = ['fm', 'afm', 'nfm', 'pnn', 'fibinet', 'deepfm'] results, table = compare_models(models) print(table) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output +---------+--------+--------+---------------+ | 模型 | auc | gauc | valid_users | +=========+========+========+===============+ | fm | 0.5977 | 0.5701 | 928 | +---------+--------+--------+---------------+ | afm | 0.5875 | 0.5655 | 928 | +---------+--------+--------+---------------+ | nfm | 0.5721 | 0.5497 | 928 | +---------+--------+--------+---------------+ | pnn | 0.5918 | 0.5733 | 928 | +---------+--------+--------+---------------+ | fibinet | 0.5982 | 0.5686 | 928 | +---------+--------+--------+---------------+ | deepfm | 0.5917 | 0.5725 | 928 | +---------+--------+--------+---------------+