Deep Crossing

Source

概述

Deep Crossing 模型。这个模型在推荐系统史上具有里程碑意义,因为它第一次将“Embedding + MLP”这一范式确立为工业级推荐精排模型的标配。

一句话概括:Deep Crossing 是微软于2016年提出的用于搜索广告的深度学习模型。

它最大的贡献是证明了深度神经网络可以自动完成特征交叉,从而让工程师从繁重的手工特征工程(如交叉组合性别+年龄)中解放出来。

核心创新点:

  • 端到端自动特征交叉:不用人工构造组合特征,深度网络自己学。
  • 残差网络(ResNet)引入推荐:首次将残差连接引入推荐系统,解决深层网络的梯度消失和模型退化问题。
  • 高维稀疏特征降维:通过Embedding加长尾共享ID策略,解决了广告计划(CampaignID)等百万级枚举值的存储和训练难题。

Deep Crossing 网络结构五层详解

从下到上依次为:Feature层 → Embedding层 → Stacking层 → Multiple Residual Units层 → Scoring层。

层名称 核心作用 输入 输出
Feature层(输入) 接收原始特征(类别型One-hot、数值型标量) 用户、广告、上下文原始数据 高维稀疏向量 / 数值标量
Embedding层 降维:把高维One-hot映射为低维稠密向量;长尾共享:对频次低的ID统一编号,压缩模型体积 稀疏ID(如搜索词、广告ID) 低维稠密向量(如维度256)
Stacking层 拼接:将所有Embedding向量和原始数值特征拼成一个长向量 多个Embedding + 数值特征 一维长向量([v1, v2, ..., vn])
Multiple Residual Units层 特征自动交叉:通过多层全连接网络,让所有特征维度两两交互;残差连接:解决梯度弥散 Stacking输出向量 高阶交叉后的特征向量
Scoring层 输出概率:将最后一层特征映射为点击概率(Sigmoid) 残差网络输出 0~1之间的概率值

完整业务场景与数据推导

场景设定

平台: Bing 搜索广告。

用户行为: 用户在搜索框输入“冬季男士跑鞋”。

目标: 预测用户是否会点击候选广告“Adidas Ultraboost 冬季款”。

真实标签(y):假设用户最终点击了(y=1)。

特征体系(总共选取 4 个特征)

特征字段 特征类型 原始值 One-hot维度 备注 (高基数处理策略)
搜索词 (Query) 类别型 (ID) “冬季男士跑鞋” 词表大小 = 100 直接 Embedding
广告关键词 (Ad Keyword) 类别型 (ID) “跑鞋” 词表大小 = 50 直接 Embedding
广告计划ID (CampaignID) 类别型 (ID) 9527 原始100万 → 截断为1001 取Top1000高频ID,剩余统一为1000号 (长尾共享)
历史点击率 (CTR) 数值型 0.05 (5%) 无 (标量) 不做Embedding,直接输入Stacking层

降维设定:为了手算方便,我们统一设置 Embedding维度 d = 4

假设训练好的 Embedding 表(特征 -> 4维向量)

特征名称 原始ID Embedding向量 (4维)
搜索词:“冬季男士跑鞋” ID_25 [0.1, 0.4, 0.7, 0.2]
广告关键词:“跑鞋” ID_8 [0.9, 0.3, 0.5, 0.8]
广告计划ID:9527 ID_887 (截断后) [0.2, 0.1, 0.6, 0.3]
(数值特征) 历史CTR — 标量: 0.05

补充说明:原论文中,CampaignID 从百万级通过截断(只留TOP 1000)降为1001维,再配合每个ID对应的历史CTR统计特征一起输入,极大地压缩了参数量。
这里是结合了ID Embedding + 统计特征两种信息。

第一层:Feature层(输入层)

接收四个原始特征:

  • 搜索词:One-hot 向量(100维,仅ID_25处为1)
  • 广告关键词:One-hot 向量(50维,仅ID_8处为1)
  • 广告计划ID:One-hot 向量(1001维,仅ID_887处为1)
  • CTR:标量 0.05

输入层不进行计算,只是作为数据入口。

第二层:Embedding层(降维与稠密化)

将三个稀疏ID特征查表映射为稠密向量,数值型特征保持标量跳过Embedding。

  • equery=[0.1,0.4,0.7,0.2]e_{query} = [0.1, 0.4, 0.7, 0.2]equery​=[0.1,0.4,0.7,0.2]
  • ekeyword=[0.9,0.3,0.5,0.8]e_{keyword} = [0.9, 0.3, 0.5, 0.8]ekeyword​=[0.9,0.3,0.5,0.8]
  • ecampaign=[0.2,0.1,0.6,0.3]e_{campaign} = [0.2, 0.1, 0.6, 0.3]ecampaign​=[0.2,0.1,0.6,0.3]
  • vctr=0.05v_{ctr} = 0.05vctr​=0.05 (标量,不经过矩阵变换)

输出维度:4 + 4 + 4 + 1 = 13 维原始表征。

第三层:Stacking层(向量拼接)

核心操作: 将上述所有向量首尾相连,合并成一个长向量 XXX。

X=[equery∥ekeyword∥ecampaign∥vctr] X = [e_{query} \parallel e_{keyword} \parallel e_{campaign} \parallel v_{ctr}] X=[equery​∥ekeyword​∥ecampaign​∥vctr​]

此时 XXX 的维度 D=13D = 13D=13

第四层:Multiple Residual Units层(特征自动交叉 + 残差连接)

这是Deep Crossing的灵魂。我们堆叠 2个残差单元(Residual Unit)  进行演示。

残差单元的结构公式

一个残差单元的数学定义如下(去掉了卷积)
Xout=ReLU(X+F(X)) X_{out} = \text{ReLU}(X +\mathcal{F}(X)) Xout​=ReLU(X+F(X))

其中:

  • F(X)\mathcal{F}(X)F(X) 代表两层全连接 + ReLU 的变换(拟合残差)
  • 外层的 + X 就是残差连接(Skip Connection)

为什么需要计算两个(或多个)残差单元?

Deep Crossing 堆叠多个残差单元,根本原因在于特征交叉的“阶数(Degree)”:

  • 一阶特征(原始特征):就是 XX 本身,比如“搜索词=跑鞋”和“广告ID=9527”是相互独立的。
  • 二阶特征交叉(Residual Unit 1):经过第一层残差单元,网络开始将两两特征进行组合(比如“搜索词”ד广告关键词”的联合作用)。
  • 三阶及以上特征交叉(Residual Unit 2):将 Xout1Xout_{1}Xout1​(已含二阶信息)再次输入新的残差单元,网络就能捕获更复杂的组合(比如“搜索词”ד广告关键词”ד历史CTR”的三元交互)。

单层全连接(或单个残差块)只能模拟到特征的线性加权和浅层二阶交互。

现实中的点击率预估,往往依赖于“品牌+类目+价格”这种三元以上的高阶组合。

残差网络的堆叠特性,使得 Deep Crossing 可以自动构建任意高阶的特征组合,且残差连接(Skip Connection)保证了网络即使堆叠很深,梯度也不会消失(因为恒等映射 +X+X+X 给反向传播提供了一条“梯度高速公路”)。

残差单元1(Residual Unit 1)的具体手算推导

为简化手算,我们假设第一层全连接权重 W1W_{1}W1​ 将 13 维压缩到 8 维,第二层 W2W_{2}W2​ 再将 8 维还原回 13 维。

这里为了展示数学逻辑,我们假设矩阵运算已经完成,重点演示残差连接的效果。

假设经过两层全连接 + ReLU 后,计算出的残差F(X)F(X)F(X) 为(这是一个抽象的中间结果):

F(X)=[0.2,−0.1,0.3,0.0,0.4,−0.2,0.1,0.5,−0.3,0.2,0.1,−0.4,0.2] \mathcal{F}(X) = [0.2, -0.1, 0.3, 0.0, 0.4, -0.2, 0.1, 0.5, -0.3, 0.2, 0.1, -0.4, 0.2] F(X)=[0.2,−0.1,0.3,0.0,0.4,−0.2,0.1,0.5,−0.3,0.2,0.1,−0.4,0.2]

进行残差相加(对应位置相加):
X+F(X)=[0.1+0.20.4−0.10.7+0.30.2+0.00.9+0.40.3−0.20.5+0.10.8+0.50.2−0.30.1+0.20.6+0.10.3−0.40.05+0.2]=[0.30.31.00.21.30.10.61.3−0.10.30.7−0.10.25] X + \mathcal{F}(X) = \begin{bmatrix} 0.1 + 0.2 \\ 0.4 - 0.1 \\ 0.7 + 0.3 \\ 0.2 + 0.0 \\ 0.9 + 0.4 \\ 0.3 - 0.2 \\ 0.5 + 0.1 \\ 0.8 + 0.5 \\ 0.2 - 0.3 \\ 0.1 + 0.2 \\ 0.6 + 0.1 \\ 0.3 - 0.4 \\ 0.05 + 0.2 \end{bmatrix} = \begin{bmatrix} 0.3 \\ 0.3 \\ 1.0 \\ 0.2 \\ 1.3 \\ 0.1 \\ 0.6 \\ 1.3 \\ -0.1 \\ 0.3 \\ 0.7 \\ -0.1 \\ 0.25 \end{bmatrix} X+F(X)= ​0.1+0.20.4−0.10.7+0.30.2+0.00.9+0.40.3−0.20.5+0.10.8+0.50.2−0.30.1+0.20.6+0.10.3−0.40.05+0.2​ ​= ​0.30.31.00.21.30.10.61.3−0.10.30.7−0.10.25​ ​

最后经过外层 ReLU 激活(负数置0):
Xout1=ReLU(X+F(X))=[0.3,0.3,1.0,0.2,1.3,0.1,0.6,1.3,0,0.3,0.7,0,0.25] X_{out1} = \text{ReLU}(X + \mathcal{F}(X)) = [0.3, 0.3, 1.0, 0.2, 1.3, 0.1, 0.6, 1.3, 0, 0.3, 0.7, 0, 0.25] Xout1​=ReLU(X+F(X))=[0.3,0.3,1.0,0.2,1.3,0.1,0.6,1.3,0,0.3,0.7,0,0.25]

关键点:由于残差连接 + X 的存在,即使 W1,W2W_{1},W_{2}W1​,W2​ 全为0,Xout1=ReLU(X)Xout1=ReLU(X)Xout1=ReLU(X),信息依然能无损传递。
这在反向传播时,梯度可以直接绕过 FFF 回传,极大缓解了深层网络的梯度消失问题。

ReLU 计算过程

针对向量中的每一个数值 xix_{i}xi​,执行 max⁡(0,xi)max⁡(0,x_{i})max⁡(0,xi​) 运算。

元素索引 输入值 xix_ixi​ (来自残差相加) ReLU 计算过程 (max⁡(0,xi)\max(0, x_i)max(0,xi​)) 输出值 ReLU(xi)\text{ReLU}(x_i)ReLU(xi​)
1 0.3 max⁡(0,0.3)\max(0, 0.3)max(0,0.3) 0.3 (保留)
2 0.3 max⁡(0,0.3)\max(0, 0.3)max(0,0.3) 0.3 (保留)
3 1.0 max⁡(0,1.0)\max(0, 1.0)max(0,1.0) 1.0 (保留)
4 0.2 max⁡(0,0.2)\max(0, 0.2)max(0,0.2) 0.2 (保留)
5 1.3 max⁡(0,1.3)\max(0, 1.3)max(0,1.3) 1.3 (保留)
6 0.1 max⁡(0,0.1)\max(0, 0.1)max(0,0.1) 0.1 (保留)
7 0.6 max⁡(0,0.6)\max(0, 0.6)max(0,0.6) 0.6 (保留)
8 1.3 max⁡(0,1.3)\max(0, 1.3)max(0,1.3) 1.3 (保留)
9 -0.1 max⁡(0,−0.1)\max(0, -0.1)max(0,−0.1) 0 (负数被截断)
10 0.3 max⁡(0,0.3)\max(0, 0.3)max(0,0.3) 0.3 (保留)
11 0.7 max⁡(0,0.7)\max(0, 0.7)max(0,0.7) 0.7 (保留)
12 -0.1 max⁡(0,−0.1)\max(0, -0.1)max(0,−0.1) 0 (负数被截断)
13 0.25 max⁡(0,0.25)\max(0, 0.25)max(0,0.25) 0.25 (保留)

残差单元2(Residual Unit 2)

经过 Residual Unit 1 得到了:
Xout1=[0.3,0.3,1.0,0.2,1.3,0.1,0.6,1.3,0,0.3,0.7,0,0.25] X_{out1} = [0.3, 0.3, 1.0, 0.2, 1.3, 0.1, 0.6, 1.3, 0, 0.3, 0.7, 0, 0.25] Xout1​=[0.3,0.3,1.0,0.2,1.3,0.1,0.6,1.3,0,0.3,0.7,0,0.25]

现在,将 KaTeX parse error: Expected '}', got 'EOF' at end of input: X_{out1| 作为输入,送入 Residual Unit 2。

假设残差单元 2 内部的 两层全连接 + 内部 ReLU 变换后,计算出的残差为 F2(Xout1)F_{2}(X_{out1})F2​(Xout1​):

F2(Xout1)=[0.1,−0.2,0.4,0.1,0.2,−0.1,0.3,0.4,0.1,−0.1,0.2,0.1,0.15] \mathcal{F}_2(X_{out1}) = [0.1, -0.2, 0.4, 0.1, 0.2, -0.1, 0.3, 0.4, 0.1, -0.1, 0.2, 0.1, 0.15] F2​(Xout1​)=[0.1,−0.2,0.4,0.1,0.2,−0.1,0.3,0.4,0.1,−0.1,0.2,0.1,0.15]

步骤 1:残差相加(对应位置相加)
将输入 Xout1X_{out1}Xout1​ 与残差分支 F2F2F2 逐元素相加:
Xout1+F2(Xout1)=[0.3+0.10.3+(−0.2)1.0+0.40.2+0.11.3+0.20.1+(−0.1)0.6+0.31.3+0.40+0.10.3+(−0.1)0.7+0.20+0.10.25+0.15]=[0.40.11.40.31.50.00.91.70.10.20.90.10.40] X_{out1} + \mathcal{F}_2(X_{out1}) = \begin{bmatrix} 0.3 + 0.1 \\ 0.3 + (-0.2) \\ 1.0 + 0.4 \\ 0.2 + 0.1 \\ 1.3 + 0.2 \\ 0.1 + (-0.1) \\ 0.6 + 0.3 \\ 1.3 + 0.4 \\ 0 + 0.1 \\ 0.3 + (-0.1) \\ 0.7 + 0.2 \\ 0 + 0.1 \\ 0.25 + 0.15 \end{bmatrix} = \begin{bmatrix} 0.4 \\ 0.1 \\ 1.4 \\ 0.3 \\ 1.5 \\ 0.0 \\ 0.9 \\ 1.7 \\ 0.1 \\ 0.2 \\ 0.9 \\ 0.1 \\ 0.40 \end{bmatrix} Xout1​+F2​(Xout1​)= ​0.3+0.10.3+(−0.2)1.0+0.40.2+0.11.3+0.20.1+(−0.1)0.6+0.31.3+0.40+0.10.3+(−0.1)0.7+0.20+0.10.25+0.15​ ​= ​0.40.11.40.31.50.00.91.70.10.20.90.10.40​ ​

步骤 2:外层 ReLU 激活(截断负数)

将相加后的向量逐元素输入 ReLU(x)=max⁡(0,x)ReLU(x)=max⁡(0,x)ReLU(x)=max⁡(0,x):

元素索引 相加后的值 ReLU 计算 输出 Xout2X_{out2}Xout2​
1 0.4 max⁡(0,0.4)\max(0, 0.4)max(0,0.4) 0.4
2 0.1 max⁡(0,0.1)\max(0, 0.1)max(0,0.1) 0.1
3 1.4 max⁡(0,1.4)\max(0, 1.4)max(0,1.4) 1.4
4 0.3 max⁡(0,0.3)\max(0, 0.3)max(0,0.3) 0.3
5 1.5 max⁡(0,1.5)\max(0, 1.5)max(0,1.5) 1.5
6 0.0 max⁡(0,0.0)\max(0, 0.0)max(0,0.0) 0.0
7 0.9 max⁡(0,0.9)\max(0, 0.9)max(0,0.9) 0.9
8 1.7 max⁡(0,1.7)\max(0, 1.7)max(0,1.7) 1.7
9 0.1 max⁡(0,0.1)\max(0, 0.1)max(0,0.1) 0.1
10 0.2 max⁡(0,0.2)\max(0, 0.2)max(0,0.2) 0.2
11 0.9 max⁡(0,0.9)\max(0, 0.9)max(0,0.9) 0.9
12 0.1 max⁡(0,0.1)\max(0, 0.1)max(0,0.1) 0.1
13 0.40 max⁡(0,0.40)\max(0, 0.40)max(0,0.40) 0.40

因此,第二个残差单元的最终输出为:
Xout2=[0.4,0.1,1.4,0.3,1.5,0.0,0.9,1.7,0.1,0.2,0.9,0.1,0.40] X_{out2} = [0.4, 0.1, 1.4, 0.3, 1.5, 0.0, 0.9, 1.7, 0.1, 0.2, 0.9, 0.1, 0.40] Xout2​=[0.4,0.1,1.4,0.3,1.5,0.0,0.9,1.7,0.1,0.2,0.9,0.1,0.40]

注意:这里的 F2F2F2 是我为演示数学流程而构造的抽象向量。在真实的模型训练中,它是通过 ReLU(W2⋅ReLU(W1⋅Xout1+b1)+b2)ReLU(W_{2}⋅ReLU(W_{1}⋅X_{out1}+b_{1})+b_{2})ReLU(W2​⋅ReLU(W1​⋅Xout1​+b1​)+b2​)
实实在在算出来的,而 W1,W2W_{1},W_{2}W1​,W2​ 是通过反向传播从海量数据中学习得到的。

小结

阶段 输入 核心特征交叉阶数 示例交叉组合
Stacking (起始) XXX 一阶 (原始特征) 单独的 搜索词、广告ID
Residual Unit 1 X→Xout1X \rightarrow X_{out1}X→Xout1​ 二阶交叉 搜索词 ×\times× 广告关键词
Residual Unit 2 Xout1→Xout2X_{out1} \rightarrow X_{out2}Xout1​→Xout2​ 三阶交叉 搜索词 ×\times× 广告关键词 ×\times× 历史CTR

在现实推荐系统中,微软论文的实验表明:堆叠 6~10 层残差单元时,模型能捕获足够高阶的特征交互,且由于残差连接的存在,训练依然稳定。

在刚才的案例中,虽然我们只堆叠了 2 层,但完全相同的数学逻辑可以无限延伸:

Xout3=ReLU(Xout2+F3(Xout2)),Xout4=ReLU(Xout3+F4(Xout3)),… X_{out3} = \text{ReLU}(X_{out2} + \mathcal{F}_3(X_{out2})), \quad X_{out4} = \text{ReLU}(X_{out3} + \mathcal{F}_4(X_{out3})), \dots Xout3​=ReLU(Xout2​+F3​(Xout2​)),Xout4​=ReLU(Xout3​+F4​(Xout3​)),…

第五层:Scoring层(输出点击概率)

残差网络的最终输出 Xout2X_{out2}Xout2​仍为 13 维向量。

将其输入一个输出维度为 1 的全连接层(带 Sigmoid 激活函数)。

假设输出层权重 WscoreW_{score}Wscore​(13维转1维)点积加偏置 bbb 后,得到了 logit = 1.8。

计算点击概率 ppp:
p=σ(logit)=11+e−1.8≈11+0.1653≈0.858 p = \sigma(\text{logit}) = \frac{1}{1 + e^{-1.8}} \approx \frac{1}{1 + 0.1653} \approx 0.858 p=σ(logit)=1+e−1.81​≈1+0.16531​≈0.858

模型预测结果:用户点击该广告的概率为 85.8%。

由于真实标签 y=1y=1y=1(用户实际点击了),我们使用 LogLoss(交叉熵)  计算损失:
L=−log⁡(p)=−log⁡(0.858)≈0.153 L = -\log(p) = -\log(0.858) \approx 0.153 L=−log(p)=−log(0.858)≈0.153

这个较小的损失值表明当前模型对该样本的预测较为准确。

在反向传播时,这个 0.153 的标量会驱动所有层的参数(包括底层 Embedding 表)进行梯度下降更新。


反向传播

第一步:前向传播损失计算(起点)

根据案例,前向传播的最终结果:

  • 最终 logit(Scoring 层线性输出):z=1.8z=1.8z=1.8
  • 预测点击概率(Sigmoid 输出):p=σ(z)=0.858p=σ(z)=0.858p=σ(z)=0.858
  • 真实标签:y=1y=1y=1
  • 损失值(交叉熵 Loss):
    L=−[ylog⁡p+(1−y)log⁡(1−p)]=−log⁡(0.858)≈0.153 L = -[y \log p + (1 - y) \log(1 - p)] = -\log(0.858) \approx 0.153 L=−[ylogp+(1−y)log(1−p)]=−log(0.858)≈0.153

第二步:求损失对输出 logit 的导数(整个反向传播的“发动机”)

对于二分类交叉熵损失 + Sigmoid 激活函数,其梯度有一个极其简洁的公式:

∂L∂z=p−y \frac{\partial L}{\partial z} = p - y ∂z∂L​=p−y

代入具体数值:
δout=∂L∂z=0.858−1=−0.142 \delta_{out} = \frac{\partial L}{\partial z} = 0.858 - 1 = -0.142 δout​=∂z∂L​=0.858−1=−0.142

关键含义:这个 -0.142 是全部反向传播的源头。
负号表示“预测值(0.858)高于真实值(1),需要把 logit 往小了调”。

第三步:Scoring 层参数的梯度计算(更新 WscoreW_{score}Wscore​和 bbb)

Scoring 层的表达式为:z=Wscore⋅Xout2+bz = W_{score} \cdot X_{out2} + bz=Wscore​⋅Xout2​+b

根据链式法则:
1. 对偏置 b 的梯度
∂L∂b=δout=−0.142 \frac{\partial L}{\partial b} = \delta_{out} = -0.142 ∂b∂L​=δout​=−0.142

2. 对权重向量 W_{score} 的梯度(逐维度)
公式:∂L∂Wi=δout⋅xi\frac{\partial L}{\partial W_i} = \delta_{out} \cdot x_i∂Wi​∂L​=δout​⋅xi​

其中 xix_{i}xi​ 是 Xout2X_{out2}Xout2​ 中第 i 个元素。

Xout2=[0.4,0.1,1.4,0.3,1.5,0.0,0.9,1.7,0.1,0.2,0.9,0.1,0.40] X_{out2} = [0.4, 0.1, 1.4, 0.3, 1.5, 0.0, 0.9, 1.7, 0.1, 0.2, 0.9, 0.1, 0.40] Xout2​=[0.4,0.1,1.4,0.3,1.5,0.0,0.9,1.7,0.1,0.2,0.9,0.1,0.40]

逐项手算(保留 4 位小数):

维度 iii Xout2X_{out2}Xout2​ 值 梯度 ∂L/∂Wi=−0.142×xi\partial L/\partial W_i = -0.142 \times x_i∂L/∂Wi​=−0.142×xi​ 含义
1 0.4 -0.0568
2 0.1 -0.0142
3 1.4 -0.1988
4 0.3 -0.0426
5 1.5 -0.2130
6 0.0 0 因为该维度输出为0,此处权重不更新
7 0.9 -0.1278
8 1.7 -0.2414
9 0.1 -0.0142
10 0.2 -0.0284
11 0.9 -0.1278
12 0.1 -0.0142
13 0.40 -0.0568

3. 参数如何更新(假设学习率 η=0.1)
以第 3 个权重为例:
W3new=W3old−η⋅∂L∂W3=W3old−0.1×(−0.1988)=W3old+0.01988 W_3^{new} = W_3^{old} - \eta \cdot \frac{\partial L}{\partial W_3} = W_3^{old} - 0.1 \times (-0.1988) = W_3^{old} + 0.01988 W3new​=W3old​−η⋅∂W3​∂L​=W3old​−0.1×(−0.1988)=W3old​+0.01988

因为梯度是负的,这次更新会让 W3W_{3}W3​ 增大,从而轻微降低最终的 logit(使预测概率 0.858 更接近 1)。

第四步:求损失对 Scoring 层输入 Xout2X_{out2}Xout2​ 的梯度(误差传回残差网络)

这个梯度是反向传播进入残差网络的“钥匙”:
∂L∂Xout2=WscoreT⋅δout \frac{\partial L}{\partial X_{out2}} = W_{score}^T \cdot \delta_{out} ∂Xout2​∂L​=WscoreT​⋅δout​

由于我们假设 WscoreW_{score}Wscore​ 是全1向量,所以:
∂L∂Xout2_i=1×(−0.142)=−0.142(对每个维度i) \frac{\partial L}{\partial X_{out2\_i}} = 1 \times (-0.142) = -0.142 \quad (对每个维度 i) ∂Xout2_i​∂L​=1×(−0.142)=−0.142(对每个维度i)

这意味着:残差网络输出的 13 个维度,每个维度都收到一个大小为 -0.142 的梯度信号。

第五步:误差穿过残差单元 2(残差网络的核心反向传播)

现在,误差信号 ∇Xout2=[−0.142,−0.142,...,−0.142]∇X_{out2}=[−0.142,−0.142,...,−0.142]∇Xout2​=[−0.142,−0.142,...,−0.142] 到达了 Residual Unit 2 的输出端。

残差单元 2 的数学结构是:
Xout2=ReLU(Xout1+F2(Xout1)) X_{out2} = \text{ReLU}(X_{out1} + \mathcal{F}_2(X_{out1})) Xout2​=ReLU(Xout1​+F2​(Xout1​))

为了求 ∂L∂Xout1\frac{\partial L}{\partial X_{out1}}∂Xout1​∂L​ ,我们需要分两步走:

1. 通过外层 ReLU 激活函数**

在前向传播中,我们计算了 S=Xout1+F2(Xout1)=[0.4,0.1,1.4,0.3,1.5,0.0,0.9,1.7,0.1,0.2,0.9,0.1,0.40]S = X_{out1} + \mathcal{F}_2(X_{out1}) = [0.4,0.1,1.4,0.3,1.5,0.0,0.9,1.7,0.1,0.2,0.9,0.1,0.40]S=Xout1​+F2​(Xout1​)=[0.4,0.1,1.4,0.3,1.5,0.0,0.9,1.7,0.1,0.2,0.9,0.1,0.40]

ReLU 的导数为:
ReLU′(x)={ 1,x>00,x≤0 \text{ReLU}'(x) = \begin{cases} 1, & x > 0 \\ 0, & x \le 0 \end{cases} ReLU′(x)={ 1,0,​x>0x≤0​

因为 S 中第 6 个元素恰好为 0,其余均 > 0。

所以,误差信号通过 ReLU 后,第 6 个维度的梯度被截断为 0,其余保持不变:
∂L∂S=[−0.142,−0.142,−0.142,−0.142,−0.142,0,−0.142,−0.142,−0.142,−0.142,−0.142,−0.142,−0.142] \frac{\partial L}{\partial S} = [−0.142,−0.142,−0.142,−0.142,−0.142,0,−0.142,−0.142,−0.142,−0.142,−0.142,−0.142,−0.142] ∂S∂L​=[−0.142,−0.142,−0.142,−0.142,−0.142,0,−0.142,−0.142,−0.142,−0.142,−0.142,−0.142,−0.142]

2. 通过加法门(+ 操作)**

在残差单元中,S=Xout1+F2(Xout1)S=X_{out1}+F_{2}(X_{out1})S=Xout1​+F2​(Xout1​)。加法门在反向传播时,会将上游梯度原封不动地复制一份分别传给两个分支:

传给恒等映射(残差连接)的梯度:
∂L∂Xout1+=∂L∂S \frac{\partial L}{\partial X_{out1}} += \frac{\partial L}{\partial S} ∂Xout1​∂L​+=∂S∂L​

传给残差分支 F2F2F2 的梯度:
∂L∂F2=∂L∂S \frac{\partial L}{\partial \mathcal{F}_2} = \frac{\partial L}{\partial S} ∂F2​∂L​=∂S∂L​

关键结论:由于残差连接 + X 的存在,即使 F2F_{2}F2​ 分支内部的权重梯度消失(变得极小),Xout1X_{out1}Xout1​ 依然能接收到一个大小为 -0.142(第6维为0)的实打实的梯度。
这就是残差网络能训练深层模型的数学本质——“梯度高速公路”。

第六步:误差继续回传至 Stacking 层和 Embedding 层

误差信号继续向下流动,到达 Residual Unit 1,再传到 Stacking 层。

1. Stacking 层的反向传播(梯度切分)

Stacking 层在前向时是把 3 个 4 维 Embedding 和 1 个标量拼接而成。

在反向传播时,它按原位置把 13 维梯度切分:

  • 第 1~4 维(来自 e_query):收到梯度 [-0.142, -0.142, -0.142, -0.142]
  • 第 5~8 维(来自 e_keyword):收到梯度 [-0.142, -0.142, -0.142, -0.142]
  • 第 9~12 维(来自 e_campaign):收到梯度 [-0.142, -0.142, -0.142, -0.142]
  • 第 13 维(来自 v_ctr 标量):收到梯度 [-0.142]

2. Embedding 表的更新(以 e_query 为例)

e_query = [0.1, 0.4, 0.7, 0.2],其更新公式(梯度下降)为:

Embnew=Embold−η⋅ablaEmb \text{Emb}_{new} = \text{Emb}_{old} - \eta \cdot abla\text{Emb} Embnew​=Embold​−η⋅ablaEmb

取第一个维度(学习率 η=0.1η=0.1η=0.1):
0.1new=0.1−0.1×(−0.142)=0.1+0.0142=0.1142 0.1^{new} = 0.1 - 0.1 \times (-0.142) = 0.1 + 0.0142 = 0.1142 0.1new=0.1−0.1×(−0.142)=0.1+0.0142=0.1142

同理,其余维度更新为:

  • 0.4→0.4+0.0142=0.4142
  • 0.7→0.7+0.0142=0.7142
  • 0.2→0.2+0.0142=0.2142

这意味着:搜索词“冬季男士跑鞋”的 Embedding 向量在本次更新中被整体拉升(所有维度数值增大),因为它的预测概率还差一点(0.858)
模型需要让它与“点击”这个正标签更紧密地绑定。

小结: 反向传播数值流动全表

传播层级 传递的梯度数值 (示例) 数学作用
损失层 → Logit δout=p−y=−0.142\delta_{out} = p - y = -0.142δout​=p−y=−0.142 启动反向传播,指明“需要降低 logit”
Logit → Scoring 偏置 bbb ∇b=−0.142\nabla b = -0.142∇b=−0.142 全局分数偏移量微调
Logit → Scoring 权重 WWW ∇Wi=δ⋅xi\nabla W_i = \delta \cdot x_i∇Wi​=δ⋅xi​ (如 -0.1988, -0.2414) 控制每个特征维度对最终分数的贡献度增减
Scoring → 残差输出 Xout2X_{out2}Xout2​ ∇Xout2=WT⋅δ=−0.142\nabla X_{out2} = W^T \cdot \delta = -0.142∇Xout2​=WT⋅δ=−0.142 (全1权重) 将误差完整地传回给残差网络
残差单元 2 (ReLU) 第 6 维梯度变为 0,其余保持 -0.142 稀疏化梯度,被 ReLU 截断的神经元不更新
残差单元 2 (加法门) ∇Xout1+=−0.142\nabla X_{out1} += -0.142∇Xout1​+=−0.142 (恒等映射直接复制) 核心优势:底层梯度不衰减,缓解梯度弥散
Stacking 层 (切分) 切分为 4+4+4+1 维,各为 -0.142 将梯度分发回各自的 Embedding 表
Embedding 层 ΔEmb=−0.1×(−0.142)=+0.0142\Delta \text{Emb} = -0.1 \times (-0.142) = +0.0142ΔEmb=−0.1×(−0.142)=+0.0142 对“冬季男士跑鞋”等稀疏 ID 的向量做正向微调

通过这一整套数值流动,Deep Crossing 完成了端到端的自动特征交叉学习——即使你不知道特征之间如何组合

梯度下降算法也会通过上述数学链条,自动帮助你去调整最底层的 Embedding 和中间层权重,让预测越来越准。


Deep Crossing 完整数据推导链路总结表

层级 操作 输入维度 输出维度 手算示例输出
Feature层 原始One-hot / 标量 可变 高维稀疏 Query ID_25, CTR=0.05
Embedding层 查表映射 (降维) 100万 → 4 4维/个 e_query = [0.1, 0.4, 0.7, 0.2]
Stacking层 向量拼接 (Concat) 4+4+4+1 13维 X = [0.1, ..., 0.05]
残差单元1 ReLU(X + F(X)) 13维 13维 X_out1 = [0.3, 0.3, ..., 0.25]
残差单元2 同上 (高阶交叉) 13维 13维 X_out2 = [...] (含三阶交叉信息)
Scoring层 全连接 + Sigmoid 13维 1维 概率 p = 0.858
损失层 交叉熵 (LogLoss) 预测值 p + 真实值 y 标量 Loss = 0.153

总结:Deep Crossing 为什么是经典?

开创性:它是第一个在工业级推荐中验证“深度学习自动交叉特征”可行的模型,奠定了 Embedding + MLP(含残差) 的标准范式。

工程落地细节:针对高基数特征(如 CampaignID),给出了 TopK截断 + 统计特征(历史CTR) 的具体解决方案,至今仍在业界广泛使用。

残差网络的引入:不仅解决了梯度消失,更重要的是证明了推荐系统可以像CV一样搭建深层网络(实验中甚至尝试了数十层),大幅提升了模型的表达上限。

虽然如今已有更复杂的模型(如 DIN、DIEN、Transformer),但Deep Crossing作为“地基”,其设计思想(Embedding降维、Stacking拼接、全连接自动交叉)依然是现代推荐精排模型(如 YouTube DNN、Google Ranking)的核心组成部分。