深度学习剖析

1. 神经网络基础

基本构件: 神经元 (Neuron)

神经网络的基本计算单元,它接收输入,通过权重和偏置进行计算,然后通过激活函数产生输出。

  • 权重 (Weights): 调节输入信号的重要性。
  • 偏置 (Bias): 一个额外的可调参数,用于微调输出。
  • 激活函数 (Activation Function): 引入非线性,使网络能学习复杂模式。

网络结构

神经元被组织成不同的“层”:

  • 输入层 (Input Layer): 接收原始数据。
  • 隐藏层 (Hidden Layer): 处理数据,提取特征。
  • 深度学习: 指包含多个(深的)隐藏层的网络。
  • 输出层 (Output Layer): 产生最终结果或预测。

激活函数 (Activation Function)

激活函数的关键作用是引入非线性 (俗称“掰弯”),允许网络学习复杂的决策边界。下面是两种最常见的激活函数及其特性。

ReLU 函数 (修正线性单元)

ReLU 是一种“铰链”式的掰弯方式,是目前最流行的激活函数,因为它计算高效且能有效缓解梯度消失。

  • 规则: max(0, x)
  • 问题: 死亡ReLU (Dying ReLU) - 负输入导致神经元停止学习。
  • 解决方案: Leaky ReLU - 为负输入保留一个微小的斜率。

Tanh 函数 (双曲正切)

Tanh 是一种“S形”平滑曲线,它将输出“挤压”到一个固定的范围内。

  • 输出范围: -1 到 1 (挤压效应)。
  • 问题: 梯度消失 (在两端平坦区域,梯度接近0,导致深层网络学习缓慢)。

训练过程 (Training)

网络通过“训练”来学习。这个过程的目标是调整权重和偏置,以最小化预测错误。

  • 损失函数 (Loss Function): 衡量模型预测的“错误”程度。
  • 目标: 最小化损失函数的值。
  • 反向传播 (Backpropagation): 一种算法,用于计算误差对每个权重的“责任”(即梯度)。
  • 核心: 梯度下降 (Gradient Descent): 一种优化算法,沿着梯度的反方向(最陡峭的下坡路)小步更新权重,以逐步减小损失。

2. 梯度下降与训练挑战

梯度下降的三种类型

根据用于计算梯度的样本数量,梯度下降有三种主要“风格”。

1. 批量梯度下降 (BGD)

使用**所有**训练样本计算梯度并更新一次。方向最准,但计算量巨大,不适用于大数据集。

2. 随机梯度下降 (SGD)

每处理**一个**样本就更新一次。速度快,但更新不稳定(抖动大)。

3. 小批量梯度下降 (Mini-batch)

使用一小批(如 32, 64 个)样本计算梯度并更新。**这是目前的主流方法**,在速度和稳定性之间取得了最佳平衡。

训练挑战

梯度消失 (Vanishing Gradient)

在深层网络中,梯度在反向传播时逐层相乘,变得越来越小,导致底层网络学习缓慢或停止。

  • 原因: Tanh/Sigmoid 激活函数饱和,权重过小。
  • 解决方案: 使用 ReLU 激活函数, 更好的权重初始化。

梯度爆炸 (Gradient Explosion)

梯度逐层相乘变得过大,导致权重更新过猛,训练崩溃 (结果变为 NaN)。

  • 破坏: 权重更新过猛导致 NaN。
  • 解决方案: 梯度裁剪 (Gradient Clipping) - 为梯度设置一个上限。

权重初始化

作为梯度消失/爆炸的解决方案,合理的初始化能让网络在训练之初就处于一个易于学习的状态。

Xavier (Glorot) 初始化

目标是保持信号方差稳定。适用: Tanh / Sigmoid。

Var(w) ≈ 1 / nin

He 初始化

补偿 ReLU 激活函数丢掉的那一半信号。适用: ReLU。

Var(w) ≈ 2 / nin

正交初始化 (Orthogonal Initialization)

原理是保持向量长度不变(只旋转,不缩放),非常适用于 RNN。

QTQ = I

3. CNN (卷积神经网络)

CNN 的核心定位是擅长处理图像等空间数据。它通过模仿人类视觉系统,逐层提取特征。

核心思想

局部感受野 (Receptive Field)

每个神经元只连接到输入的一小块区域(如 3x3 像素),用于提取局部特征。

权重共享 (Weight Sharing)

一个滤波器(一组权重)在整个图像上滑动,用同一组权重检测同一个特征(如一条垂直线)。

特征层次化学习

CNN 逐层构建更复杂的特征:

第一层 (L1)

边缘、颜色

第二层 (L2)

形状、纹理 (如: 眼睛、耳朵)

第三层 (L3)

目标部件 (如: 脸)

卷积层 (Conv Layer)

网络的核心,通过滤波器提取特征。

  • 滤波器 (Filter / Kernel): 提取局部特征的权重矩阵。实例: 边缘检测、锐化。它们通过反向传播自动学习。
  • 特征图 (Feature Map): 滤波器在图像上滑动计算后的输出,代表一种特定特征在原图的分布。
  • 激活函数: 通常使用 ReLU。

池化层 (Pooling Layer)

对特征图进行压缩(下采样),以减少计算量并保留关键信息。

  • 类型: 最常用的是最大池化 (Max Pooling)。
  • 优点: 聚焦最显著的特征,减少参数数量。

全连接层 (Fully Connected)

在网络的最后,将所有提取到的高级特征组合起来,进行最终的分类(如判断“是猫”还是“不是猫”)。

CNN 网络拓扑结构

输入层 (4×4)

1
2
0
3
0
1
1
5
2
0
1
0
4
1
0
2

卷积层

滤波器 (2×2)
0.5
0.1
0.2
0.3
特征图 (3×3)
1.0
1.5
2.0
0.5
0.9
1.2
2.1
0.3
1.1

ReLU激活

max(0, x)
1.0
1.5
2.0
0.5
0.9
1.2
2.1
0.3
1.1

最大池化 (2×2)

1.5
2.0
2.1
1.2

展平

1.5
2.0
2.1
1.2

全连接层

权重 W_fc
0.5
0.1
-0.2
0.3
偏置: 0.1
Logit: 0.99

Sigmoid输出

σ(0.99)
0.73
是猫概率

CNN 重构实例 (4x4 '猫' 分类)

这是一个详细的、重构后的单步训练过程。目标:预测 '是猫' (y_true=1.0), 使用逻辑回归 (Sigmoid) 和二元交叉熵损失。

输入 (X) (4x4):

[[1, 2, 0, 3], [0, 1, 1, 5], [2, 0, 1, 0], [4, 1, 0, 2]]

卷积权重 (W_conv) (2x2):

[[0.5, 0.1], [0.2, 0.3]]

FC 权重 (W_fc) (4x1):

[[0.5], [0.1], [-0.2], [0.3]]

FC 偏置 (b_fc): 0.1

目标 (y_true): 1.0 (是猫)

第 1 部分:正向传播 (Forward Propagation)

  1. 卷积层 (Conv):

    使用 2x2 滤波器 (W_conv) 在 4x4 输入 (X) 上滑动,步长S=1。输出为 3x3 矩阵 Z_conv。

    例如, 左上角 z₁₁ = (1*0.5)+(2*0.1)+(0*0.2)+(1*0.3) = 1.0

    右上角 z₁₃ = (0*0.5)+(3*0.1)+(1*0.2)+(5*0.3) = 2.0

    输出 (Z_conv):

    [[1.0, 1.5, 2.0], [0.5, 0.9, 1.2], [2.1, 0.3, 1.1]]
  2. 激活层 (ReLU):

    应用 max(0, x)。由于所有值都 > 0,输出不变。

    输出 (A_conv):

    [[1.0, 1.5, 2.0], [0.5, 0.9, 1.2], [2.1, 0.3, 1.1]]
  3. 池化层 (Max Pooling):

    使用 2x2 池化,步长S=1。输出为 2x2 矩阵 Z_pool。

    z₁₁ = max(1.0, 1.5, 0.5, 0.9) = 1.5 (来自 A_conv[0,1])

    z₁₂ = max(1.5, 2.0, 0.9, 1.2) = 2.0 (来自 A_conv[0,2])

    z₂₁ = max(0.5, 0.9, 2.1, 0.3) = 2.1 (来自 A_conv[2,0])

    z₂₂ = max(0.9, 1.2, 0.3, 1.1) = 1.2 (来自 A_conv[1,2])

    输出 (Z_pool):

    [[1.5, 2.0], [2.1, 1.2]]
  4. 展平层 (Flatten):

    将 2x2 矩阵变为 1x4 向量。

    Z_flat = [1.5, 2.0, 2.1, 1.2]
  5. 逻辑回归层 (FC + Sigmoid):

    A. 计算 Logit (z) = (Z_flat · W_fc) + b_fc

    z = (1.5*0.5) + (2.0*0.1) + (2.1*-0.2) + (1.2*0.3) + 0.1 = 0.99

    B. 计算 Sigmoid (y_pred)

    y_pred = sigmoid(0.99) = 1 / (1 + e-0.99) ≈ 0.73
  6. 计算损失 (BCE Loss):

    L = -[y·log(y_pred) + (1-y)·log(1-y_pred)]

    L = -[1.0 * log(0.73) + 0] ≈ 0.31

第 2 部分:反向传播 (Backward Propagation)

  1. 起始梯度 (BCE+Sigmoid 捷径):

    传递给线性层 (Logit z) 的梯度 G_z 是:

    G_z = y_pred - y_true = 0.73 - 1.0 = -0.27
  2. 全连接层 (FC) 梯度:

    G_W_fc = G_z · Z_flatT

    G_W_fc = -0.27 * [1.5, 2.0, 2.1, 1.2]T = [[-0.405], [-0.54], [-0.567], [-0.324]]

    G_b_fc = G_z = -0.27

    G_Z_flat (传给下一层) = G_z · W_fcT

    G_Z_flat = -0.27 * [0.5, 0.1, -0.2, 0.3] = [-0.135, -0.027, 0.054, -0.081]
  3. 展平层 (Flatten) 梯度:

    将 1x4 梯度 G_Z_flat 恢复为 2x2 形状。

    G_Z_pool (2x2):

    [[-0.135, -0.027], [0.054, -0.081]]
  4. 最大池化层 (Max Pooling) 梯度:

    [关键原理 1: 梯度路由] 梯度只通过前向传播时的“赢家”位置。 G_A_conv (3x3) 初始化为全 0。

    -0.135 (来自 G_Z_pool[0,0]) → 路由到 A_conv[0,1] (赢家是 1.5)

    -0.027 (来自 G_Z_pool[0,1]) → 路由到 A_conv[0,2] (赢家是 2.0)

    0.054 (来自 G_Z_pool[1,0]) → 路由到 A_conv[2,0] (赢家是 2.1)

    -0.081 (来自 G_Z_pool[1,1]) → 路由到 A_conv[1,2] (赢家是 1.2)

    G_A_conv (3x3):

    [[0, -0.135, -0.027], [0, 0, -0.081], [0.054, 0, 0]]
  5. 激活层 (ReLU) 梯度:

    G_Z_conv = G_A_conv ⊙ ReLU'(Z_conv)。由于 Z_conv 全为正, ReLU' 全为 1。

    G_Z_conv (3x3):

    [[0, -0.135, -0.027], [0, 0, -0.081], [0.054, 0, 0]]
  6. 卷积层 (Conv) 梯度:

    [关键原理 2: 梯度汇总] G_W_conv 的梯度 = G_Z_conv (输出梯度) 与 X (输入) 卷积的结果。G_w11 的梯度是所有使用 w11 的地方的梯度总和。

    G_w11 = (G_Z_conv[0,0]·X[0,0]) + (G_Z_conv[0,1]·X[0,1]) + (G_Z_conv[0,2]·X[0,2]) + ...

    G_w11 = (0·1) + (-0.135·2) + (-0.027·0) + (0·0) + (0·1) + (-0.081·1) + (0.054·2) + (0·0) + (0·1)

    G_w11 = 0 - 0.27 - 0 + 0 + 0 - 0.081 + 0.108 + 0 + 0 = -0.243

    对 W_conv 的所有 4 个权重重复此“汇总”过程:

    G_w12 = -0.351

    G_w21 = 0.054

    G_w22 = 0.027

    G_W_conv (2x2):

    [[-0.243, -0.351], [0.054, 0.027]]
  7. 权重更新 (LR=0.1):

    new_weight = old_weight - (LR * gradient)

    new_W_fc = [0.5, 0.1, -0.2, 0.3]T - (0.1 * [-0.405, -0.54, -0.567, -0.324]T)

    new_W_fc = [0.5405, 0.154, -0.1433, 0.3324]T

    new_W_conv = [[0.5, 0.1], [0.2, 0.3]] - (0.1 * [[-0.243, -0.351], [0.054, 0.027]])

    new_W_conv = [[0.5243, 0.1351], [0.1946, 0.2973]]

关键计算原理 (补充)

1. 权重共享与梯度汇总

疑惑: 如果权重是共享的,但反向传播时在不同位置计算出的梯度(如 `-10` 和 `+4`)不同,如何更新?

答案: 汇总 (Summation)

网络会把一个共享权重 (如 `w1`) 在**所有**位置上计算出的局部梯度**全部加起来**,得到一个“总梯度”。最后,只用这个“总梯度”来对那个唯一的、共享的 `w1` 进行一次更新。

总梯度 = (-10) + (+4) + (-7) + ...

2. 最大池化 (Max Pooling) 的反向传播

疑惑: 池化层不是激活层,它如何传递梯度?

答案: 梯度路由器 (Gradient Router)

池化层只把梯度传递给那些在前向传播中被选为最大值的神经元。

  • 前向传播: 假设一个 2x2 区域为 `[[1, 8], [3, 2]]`。最大值是 `8`。
  • 反向传播: 假设后一层传来的梯度是 `2.0`。
  • 路由结果: 这个 `2.0` 的梯度会**只**传递给 `8` 所在的位置。所有其他“输掉”的位置 (1, 3, 2) 对应的梯度都为 `0`。
  • 该区域梯度图: `[[ 0, 2.0 ], [ 0, 0 ]]`

4. RNN (循环神经网络)

RNN 的核心定位是擅长处理序列数据(如语言、时间)。它通过内部的“循环”结构来拥有“记忆”。

核心结构: 循环与权重共享

RNN 的隐藏层在计算后,会把结果的一部分传回给自己,供下一步计算使用。这使得信息可以在时间步之间传递。

  • 核心概念: 隐藏状态 (Hidden State): 作为“记忆”在时间步之间传递的向量。
  • 可按时间步展开: 更容易理解信息如何逐词流动。

关键原理: 时间步上的权重共享

一个常见的混淆点是“每个时间步是不是一个新的隐藏层”。答案是:不,它是**同一个隐藏层**在不同的时间步被**重复使用**了。

  • 比喻: 可以把隐藏层想象成一个 `for` 循环中的代码块。
  • 权重 (W_xh, W_hh): 是循环体内的固定代码 (共享的权重)。
  • 时间步 (t=1, t=2, ...): 是 `for` 循环的迭代次数。
  • 结论: 无论序列多长(10个词或100个词),编码器(或解码器)都只使用**一套**共享的隐藏层权重来处理所有时间步。

核心挑战: 长程依赖问题

简单的 RNN 很难将序列开头和结尾的信息联系起来(即“长期记忆”差)。

  • 原因: 梯度在反向传播经过很多时间步时会连乘,导致梯度消失或爆炸。
  • 解决方案: 更复杂的单元 (LSTM, GRU), 正交初始化。

应用: Seq2Seq (Encoder-Decoder)

常用于机器翻译,由两个RNN组成:

Encoder (编码器)

读取输入序列 (如: 英文),将其压缩成一个“上下文向量”,即对原句的"理解"。

Decoder (解码器)

接收上下文向量,然后逐词生成输出序列 (如: 中文)。

🔑 RNN计算核心原理

📐 梯度计算基础

🔗 链式法则应用

对任意变量 wx 的梯度计算:

使用链式法则,从后往前一层层的推导

∂Loss/∂w = ∂Loss/∂output × ∂output/∂w

🎯 函数对应原理

每一步的梯度计算公式取决于正向计算使用的函数:

  • • tanh → (1 - tanh²(x))
  • • sigmoid → σ(x)(1 - σ(x))
  • • ReLU → 1 if x>0 else 0
  • • 矩阵乘法 → 转置交换
🔍 为什么使用转置?深度剖析

💡 直观理解:维度匹配

转置 (h)^T 是为了匹配矩阵维度,实现外积运算

h(1×4) × G(1×5) → 无法计算
h^T(4×1) × G(1×5) → 外积(4×5) ✓

🧮 数学原理:链式法则

目标:求 ∂L/∂W_ij (第i行第j列权重的梯度)

链式法则展开:

∂L/∂W_ij = Σ(∂L/∂s_k × ∂s_k/∂W_ij)

🔑 关键发现:逐元素分析

当 k ≠ j 时:

∂s_k/∂W_ij = 0

权重不影响该输出

当 k = j 时:

∂s_j/∂W_ij = h_i

梯度等于输入值

最终结果:

∂L/∂W_ij = h_i × g_j

✅ 外积验证

外积 h^T × g 产生的矩阵:

h₁g₁
h₁g₂
h₁g₃
h₁g₄
h₁g₅
h₂g₁
h₂g₂
...
...
h₂g₅

位置(i,j)的值 = h_i × g_j 🎯

💎 核心洞察:转置不是技巧,是数学必然!

外积运算正是链式法则逐元素应用后的矩阵重组

🔄 权重共享处理

➕ 梯度累积原则

当正向计算存在权重共享变量共享时:

把所有影响该权重的梯度全部加起来

Total_∂Loss/∂w = Σ(各时间步的 ∂Loss/∂w)

变量类似权重,变量被多个权重共享,点积过程正是累加过程:输出 (1×4): G(h₂ᵈᵉᶜ) ≈ [0.03, 0.04, 0.03, 0.04]

📏 矩阵维度规则

默认约定:

  • • 所有向量默认为行向量
  • • 权重矩阵行数 = 每个神经元的权重个数
  • • 权重矩阵列数 = 神经元个数
例: W_xh(3×4) 表示输入3维,隐藏层4个神经元

💡 实践要点

1️⃣ 识别共享
找出所有使用同一权重的地方
2️⃣ 逐步计算
按链式法则从后往前推
3️⃣ 累积更新
将所有梯度贡献求和

RNN BPTT (穿越时间的反向传播) 完整网络拓扑结构

共享权重矩阵

W_xh (3×4)
[[0.1,0.2,0.3,0.4],
[0.5,0.6,0.7,0.8],
[0.9,1.0,1.1,1.2]]
W_hh (4×4)
[[0.1,0.2,0.3,0.4],
[0.5,0.6,0.7,0.8],
[0.9,1.0,1.1,1.2],
[1.3,1.4,1.5,1.6]]
W_out (4×5)
[[0.1,0.8,0.9,0.1,0.1],
[0.2,1.0,1.0,0.1,0.1],
[0.3,1.1,1.1,0.2,0.1],
[0.4,1.2,1.2,0.2,0.1]]
b (1×4)
[0.1, 0.1, 0.1, 0.1]

正向传播 (Forward Pass)

编码器 (Encoder)
t=1
输入 x₁
"I" = [1.0, 0.5, 0.2]
h₀ᵉⁿᶜ
[0, 0, 0, 0]
RNN计算
x₁·W_xh = [0.53, 0.86, 1.19, 1.52]
h₀·W_hh = [0, 0, 0, 0]
Sum₁ = [0.63, 0.96, 1.29, 1.62]
h₁ᵉⁿᶜ = tanh(Sum₁)
h₁ᵉⁿᶜ
[0.56, 0.74, 0.86, 0.92]
t=2
输入 x₂
"am" = [0.8, 0.1, 0.7]
h₁ᵉⁿᶜ
[0.56, 0.74, 0.86, 0.92]
RNN计算
x₂·W_xh = [0.76, 1.03, 1.3, 1.57]
h₁·W_hh = [2.396, 2.704, 3.012, 3.32]
Sum₂ = [3.256, 3.834, 4.412, 4.99]
h₂ᵉⁿᶜ = tanh(Sum₂)
C = h₂ᵉⁿᶜ
[0.997, 0.999, 0.9996, 0.9999]
上下文向量
解码器 (Decoder) - 强制教学
t=1
输入 y₁
"<start>" = [1.0, 0.0, 0.0]
h₀ᵈᵉᶜ = C
[0.997, 0.999, 0.9996, 0.9999]
RNN计算
y₁·W_xh = [0.1, 0.2, 0.3, 0.4]
h₀·W_hh = [3.98, 4.58, 5.18, 5.77]
Sum₁ᵈᵉᶜ = [4.18, 4.88, 5.58, 6.27]
h₁ᵈᵉᶜ = tanh(Sum₁ᵈᵉᶜ)
h₁ᵈᵉᶜ
[0.9995, 0.9998, 0.9999, 0.9999]
输出计算
Logits₁ = h₁ᵈᵉᶜ·W_out = [0.99, 3.09, 4.19, 0.59, 0.39]
Probs₁ = softmax(Logits₁) = [0.03, 0.08, 0.88, 0.01, 0.01]
Target₁ = "我" = [0, 1, 0, 0, 0]
Loss₁ = -log(0.08) ≈ 2.52
t=2
输入 y₂
"我" = [0.0, 1.0, 0.0]
h₁ᵈᵉᶜ
[0.9995, 0.9998, 0.9999, 0.9999]
RNN计算
y₂·W_xh = [0.5, 0.6, 0.7, 0.8]
h₁·W_hh = [3.99, 4.59, 5.19, 5.78]
Sum₂ᵈᵉᶜ = [4.59, 5.29, 5.99, 6.68]
h₂ᵈᵉᶜ = tanh(Sum₂ᵈᵉᶜ)
h₂ᵈᵉᶜ
[0.9998, 0.9999, 0.9999, 0.9999]
输出计算
Logits₂ = h₂ᵈᵉᶜ·W_out = [0.99, 3.09, 4.19, 0.59, 0.39]
Probs₂ = softmax(Logits₂) = [0.03, 0.08, 0.88, 0.01, 0.01]
Target₂ = "是" = [0, 0, 1, 0, 0]
Loss₂ = -log(0.88) ≈ 0.12
总损失
Total Loss = Loss₁ + Loss₂ = 2.52 + 0.12 = 2.64

反向传播 (BPTT - 从t=2到t=1)

解码器 t=2 梯度
G(Logits₂) = [0.03, 0.08, -0.12, 0.01, 0.01]
G(h₂ᵈᵉᶜ) ≈ [0.03, 0.04, 0.03, 0.04]
G(Sum₂ᵈᵉᶜ) ≈ [0.000012, 0.000008, 0.000006, 0.000008]
⚠️ tanh饱和导致梯度变小
解码器 t=1 梯度汇合
G(Logits₁) = [0.03, -0.92, 0.88, 0.01, 0.01]
G(h₁ᵈᵉᶜ)_from_L1 ≈ [0.06, 0.08, 0.07, 0.09]
G(h₁ᵈᵉᶜ)_from_t2 ≈ [0.00002, 0.00003, 0.00004, 0.00005]
G(h₁ᵈᵉᶜ)_Total ≈ [0.06002, 0.08003, 0.07004, 0.09005]
梯度流向编码器
G(C) ≈ [0.00001, 0.00002, 0.00002, 0.00003]
G(Sum₂ᵉⁿᶜ) ≈ [6e-8, 4e-9, 1.6e-9, 6e-10]
G(Sum₁ᵉⁿᶜ) ≈ [6.8e-9, 9e-9, 7.8e-9, 6e-9]
🚨 梯度消失问题严重!
共享权重梯度汇总 (关键步骤)
Total G(W_xh)
= G(W_xh)_t1_enc + G(W_xh)_t2_enc + G(W_xh)_t1_dec + G(W_xh)_t2_dec
四个时间步的梯度相加
Total G(W_hh)
= G(W_hh)_t1_enc + G(W_hh)_t2_enc + G(W_hh)_t1_dec + G(W_hh)_t2_dec
四个时间步的梯度相加
Total G(W_out)
= G(W_out)_t1 + G(W_out)_t2
两个解码器时间步的梯度相加
Total G(b)
= G(b)_t1_enc + G(b)_t2_enc + G(b)_t1_dec + G(b)_t2_dec
四个时间步的梯度相加
梯度消失问题分析
问题现象:
  • 解码器中tanh函数饱和 (输出接近±1.0)
  • 梯度在反向传播中急剧衰减
  • 编码器t=1的权重几乎收不到更新信号
  • 网络"忘记"了序列开头的信息
梯度量级对比:
解码器t=2: G(Sum₂ᵈᵉᶜ) ~ 10⁻⁵
解码器t=1: G(Sum₁ᵈᵉᶜ) ~ 10⁻⁵
编码器t=2: G(Sum₂ᵉⁿᶜ) ~ 10⁻⁸
编码器t=1: G(Sum₁ᵉⁿᶜ) ~ 10⁻⁹

权重更新 (学习率 α)

new W_xh = old W_xh - (α × Total G(W_xh))
new W_hh = old W_hh - (α × Total G(W_hh))
new W_out = old W_out - (α × Total G(W_out))
new b = old b - (α × Total G(b))

RNN BPTT 详细计算过程

目标: 严格遵循"穿越时间的反向传播"(BPTT)原则,详细展示共享权重的梯度在所有时间步的累加过程。

实验设置

  • 输入序列: "I am" → "我是"
  • 词典: ["<start>", "我", "是", "一个", "学生"] (5个词)
  • 方法: 强制教学 (Teacher Forcing)
  • 隐藏层维度: 4,输入维度: 3

第 1 部分:正向传播 (Forward Propagation)

A. 编码器 (Encoder)
Encoder t=1 (输入: "I")
  1. 输入 h₀ᵉⁿᶜ (1×4):
    [0, 0, 0, 0]
  2. 输入 x₁ (1×3):
    [1.0, 0.5, 0.2]
  3. x₁ · W_xh (1×4):
    [1.0, 0.5, 0.2] · W_xh = [0.53, 0.86, 1.19, 1.52]
  4. h₀ᵉⁿᶜ · W_hh (1×4):
    [0, 0, 0, 0] · W_hh = [0, 0, 0, 0]
  5. b (1×4):
    [0.1, 0.1, 0.1, 0.1]
  6. Sum₁ᵉⁿᶜ (1×4):
    [0.53, 0.86, 1.19, 1.52] + [0, 0, 0, 0] + [0.1, 0.1, 0.1, 0.1] = [0.63, 0.96, 1.29, 1.62]
  7. h₁ᵉⁿᶜ (1×4):
    tanh(Sum₁ᵉⁿᶜ) = [0.56, 0.74, 0.86, 0.92]
Encoder t=2 (输入: "am")
  1. 输入 h₁ᵉⁿᶜ (1×4):
    [0.56, 0.74, 0.86, 0.92]
  2. 输入 x₂ (1×3):
    [0.8, 0.1, 0.7]
  3. x₂ · W_xh (1×4):
    [0.8, 0.1, 0.7] · W_xh = [0.76, 1.03, 1.3, 1.57]
  4. h₁ᵉⁿᶜ · W_hh (1×4):
    [0.56, 0.74, 0.86, 0.92] · W_hh = [2.396, 2.704, 3.012, 3.32]
  5. b (1×4):
    [0.1, 0.1, 0.1, 0.1]
  6. Sum₂ᵉⁿᶜ (1×4):
    [0.76, 1.03, 1.3, 1.57] + [2.396, 2.704, 3.012, 3.32] + [0.1, 0.1, 0.1, 0.1] = [3.256, 3.834, 4.412, 4.99]
  7. h₂ᵉⁿᶜ (1×4):
    tanh(Sum₂ᵉⁿᶜ) = [0.997, 0.999, 0.9996, 0.9999]

编码结束。上下文向量 🧠 C = h₂ᵉⁿᶜ = [0.997, 0.999, 0.9996, 0.9999]

B. 解码器 (Decoder) - 强制教学
Decoder t=1 (输入: "<start>", 目标: "我")
  1. 输入 h₀ᵈᵉᶜ (1×4):
    C = [0.997, 0.999, 0.9996, 0.9999]
  2. 输入 y₁ (1×3):
    [1.0, 0.0, 0.0]
  3. y₁ · W_xh (1×4):
    [1.0, 0.0, 0.0] · W_xh = [0.1, 0.2, 0.3, 0.4]
  4. h₀ᵈᵉᶜ · W_hh (1×4):
    [0.997, ...] · W_hh = [3.98, 4.58, 5.18, 5.77]
  5. b (1×4):
    [0.1, 0.1, 0.1, 0.1]
  6. Sum₁ᵈᵉᶜ (1×4):
    [0.1, 0.2, 0.3, 0.4] + [3.98, 4.58, 5.18, 5.77] + [0.1, 0.1, 0.1, 0.1] = [4.18, 4.88, 5.58, 6.27]
  7. h₁ᵈᵉᶜ (1×4):
    tanh(Sum₁ᵈᵉᶜ) = [0.9995, 0.9998, 0.9999, 0.9999]
  8. Logits₁ (1×5):
    h₁ᵈᵉᶜ · W_out = [0.9995, ...] · W_out = [0.99, 3.09, 4.19, 0.59, 0.39]
  9. Probs₁ (1×5):
    softmax(Logits₁) = [0.03, 0.08, 0.88, 0.01, 0.01]
  10. Loss₁ (标量):
    CrossEntropy(Probs₁, t₁) ≈ -log(0.08) ≈ 2.52
Decoder t=2 (输入: "我", 目标: "是")
  1. 输入 h₁ᵈᵉᶜ (1×4):
    [0.9995, 0.9998, 0.9999, 0.9999]
  2. 输入 y₂ (1×3):
    [0.0, 1.0, 0.0]
  3. y₂ · W_xh (1×4):
    [0.0, 1.0, 0.0] · W_xh = [0.5, 0.6, 0.7, 0.8]
  4. h₁ᵈᵉᶜ · W_hh (1×4):
    [0.9995, ...] · W_hh = [3.99, 4.59, 5.19, 5.78]
  5. b (1×4):
    [0.1, 0.1, 0.1, 0.1]
  6. Sum₂ᵈᵉᶜ (1×4):
    [0.5, 0.6, 0.7, 0.8] + [3.99, 4.59, 5.19, 5.78] + [0.1, 0.1, 0.1, 0.1] = [4.59, 5.29, 5.99, 6.68]
  7. h₂ᵈᵉᶜ (1×4):
    tanh(Sum₂ᵈᵉᶜ) = [0.9998, 0.9999, 0.9999, 0.9999]
  8. Logits₂ (1×5):
    h₂ᵈᵉᶜ · W_out = [0.9998, ...] · W_out = [0.99, 3.09, 4.19, 0.59, 0.39]
  9. Probs₂ (1×5):
    softmax(Logits₂) = [0.03, 0.08, 0.88, 0.01, 0.01]
  10. Loss₂ (标量):
    CrossEntropy(Probs₂, t₂) ≈ -log(0.88) ≈ 0.12

正向传播总结: Total Loss = L₁ + L₂ ≈ 2.52 + 0.12 = 2.64

第 2 部分:反向传播 (BPTT)

我们的目标是计算 Total Loss 对所有共享权重的梯度。我们将从 t=2 开始,一步步向后传播。我们将使用 G(...) 来表示"...的梯度"。

A. 解码器 (Decoder) t=2

这一步只受 Loss₂ 的影响。

  1. G(Logits₂) (1×5 向量):

    公式: ∂L₂/∂Logits₂ = Probs₂ - t₂

    输入 1 (1×5): [0.03, 0.08, 0.88, 0.01, 0.01]
    输入 2 (1×5): [0, 0, 1, 0, 0]
    输出 (1×5): G(Logits₂) = [0.03, 0.08, -0.12, 0.01, 0.01]
  2. G(W_out)_t2 (4×5 矩阵):

    公式: ∂L₂/∂W_out = (h₂ᵈᵉᶜ)ᵀ · G(Logits₂)

    输入 1 (4×1): [0.9998, 0.9999, 0.9999, 0.9999]ᵀ
    输入 2 (1×5): [0.03, 0.08, -0.12, 0.01, 0.01]
    输出 (4×5 矩阵): G(W_out)_t2 ≈ [[0.03, 0.08, -0.12, 0.01, 0.01], [0.03, 0.08, -0.12, 0.01, 0.01], [0.03, 0.08, -0.12, 0.01, 0.01], [0.03, 0.08, -0.12, 0.01, 0.01]]
  3. G(h₂ᵈᵉᶜ) (1×4 向量):

    公式: ∂L₂/∂h₂ᵈᵉᶜ = G(Logits₂) · (W_out)ᵀ

    输出 (1×4): G(h₂ᵈᵉᶜ) ≈ [0.03, 0.04, 0.03, 0.04]
  4. G(Sum₂ᵈᵉᶜ) (1×4 向量):

    通过 tanh 激活函数反向传播

    公式: G(h₂ᵈᵉᶜ) · (1 - (h₂ᵈᵉᶜ)²)

    输入 2 (1×4): (1 - h₂ᵈᵉᶜ · h₂ᵈᵉᶜ) = (1 - [0.9998, ...]²) ≈ [0.0004, 0.0002, 0.0002, 0.0002]
    输出 (1×4): G(Sum₂ᵈᵉᶜ) ≈ [0.000012, 0.000008, 0.000006, 0.000008]
    ⚠️ 梯度因 tanh 饱和而变得极小
  5. G(W_xh)_t2_dec (3×4 矩阵):

    公式: (y₂)ᵀ · G(Sum₂ᵈᵉᶜ)

    输入 1 (3×1): [0.0, 1.0, 0.0]ᵀ
    输出 (3×4 矩阵): G(W_xh)_t2_dec = [[0, 0, 0, 0], [0.000012, 0.000008, 0.000006, 0.000008], [0, 0, 0, 0]]
  6. G(W_hh)_t2_dec (4×4 矩阵):

    公式: (h₁ᵈᵉᶜ)ᵀ · G(Sum₂ᵈᵉᶜ)

    输出 (4×4 矩阵): G(W_hh)_t2_dec ≈ [[0.000012, ...], [0.000012, ...], [0.000012, ...], [0.000012, ...]]
  7. G(b)_t2_dec (1×4 向量):
    输出 (1×4): G(b)_t2_dec = G(Sum₂ᵈᵉᶜ) = [0.000012, 0.000008, 0.000006, 0.000008]
  8. G(h₁ᵈᵉᶜ)_from_t2 (1×4 向量):

    🔥 关键:流向 t=1 的梯度

    公式: G(Sum₂ᵈᵉᶜ) · (W_hh)ᵀ

    输出 (1×4): G(h₁ᵈᵉᶜ)_from_t2 ≈ [0.00002, 0.00003, 0.00004, 0.00005]
B. 解码器 (Decoder) t=1

这一步同时接收 Loss₁ 的梯度和 t=2 传来的梯度。

  1. G(Logits₁) (1×5 向量):

    来自 L₁

    公式: Probs₁ - t₁

    输出 (1×5): G(Logits₁) = [0.03, -0.92, 0.88, 0.01, 0.01]
  2. G(W_out)_t1 (4×5 矩阵):

    公式: (h₁ᵈᵉᶜ)ᵀ · G(Logits₁)

    输出 (4×5 矩阵): G(W_out)_t1 ≈ [[0.03, -0.92, 0.88, 0.01, 0.01], [0.03, -0.92, 0.88, 0.01, 0.01], ...]
  3. G(h₁ᵈᵉᶜ)_from_L1 (1×4 向量):

    来自 L₁ 的梯度

    公式: G(Logits₁) · (W_out)ᵀ

    输出 (1×4): G(h₁ᵈᵉᶜ)_from_L1 ≈ [0.06, 0.08, 0.07, 0.09]
  4. G(h₁ᵈᵉᶜ)_Total (1×4 向量):

    🔥 关键:梯度汇合

    公式: G(h₁ᵈᵉᶜ)_from_L1 + G(h₁ᵈᵉᶜ)_from_t2

    输入 1 (1×4): [0.06, 0.08, 0.07, 0.09]
    输入 2 (1×4): [0.00002, 0.00003, 0.00004, 0.00005]
    输出 (1×4): G(h₁ᵈᵉᶜ)_Total ≈ [0.06002, 0.08003, 0.07004, 0.09005]
  5. G(Sum₁ᵈᵉᶜ) (1×4 向量):

    公式: G(h₁ᵈᵉᶜ)_Total · (1 - (h₁ᵈᵉᶜ)²)

    输出 (1×4): G(Sum₁ᵈᵉᶜ) ≈ [0.00006, 0.00003, 0.00001, 0.00002]
    梯度仍然很小
  6. G(W_xh)_t1_dec (3×4 矩阵):

    公式: (y₁)ᵀ · G(Sum₁ᵈᵉᶜ)

    输出 (3×4 矩阵): G(W_xh)_t1_dec = [[0.00006, 0.00003, 0.00001, 0.00002], [0, 0, 0, 0], [0, 0, 0, 0]]
  7. G(W_hh)_t1_dec (4×4 矩阵):

    公式: (h₀ᵈᵉᶜ)ᵀ · G(Sum₁ᵈᵉᶜ)

    输出 (4×4 矩阵): G(W_hh)_t1_dec ≈ [[0.00006, ...], [0.00006, ...], [0.00006, ...], [0.00006, ...]]
  8. G(b)_t1_dec (1×4 向量):
    输出 (1×4): G(b)_t1_dec = G(Sum₁ᵈᵉᶜ) = [0.00006, 0.00003, 0.00001, 0.00002]
  9. G(C) (1×4 向量):

    🔥 关键:流向 Encoder 的梯度

    公式: G(h₀ᵈᵉᶜ) = G(Sum₁ᵈᵉᶜ) · (W_hh)ᵀ

    输出 (1×4): G(C) ≈ [0.00001, 0.00002, 0.00002, 0.00003]
C. 编码器 (Encoder) t=2
  1. 起始梯度:
    G(h₂ᵉⁿᶜ) = G(C) = [0.00001, 0.00002, 0.00002, 0.00003]
  2. G(Sum₂ᵉⁿᶜ) (1×4 向量):

    公式: G(h₂ᵉⁿᶜ) · (1 - (h₂ᵉⁿᶜ)²)

    输入 2 (1×4): (1 - [0.997, ...]²) ≈ [0.006, 0.002, 0.0008, 0.0002]
    输出 (1×4): G(Sum₂ᵉⁿᶜ) ≈ [6e-8, 4e-9, 1.6e-9, 6e-10]
    梯度几乎消失了
  3. G(W_xh)_t2_enc (3×4 矩阵):
    输出 (3×4 矩阵): [[4.8e-8, ...], [6e-9, ...], [4.2e-8, ...]]
  4. G(W_hh)_t2_enc (4×4 矩阵):
    输出 (4×4 矩阵): [[3.36e-8, ...], [4.44e-8, ...], [5.16e-8, ...], [5.52e-8, ...]]
  5. G(b)_t2_enc (1×4 向量):
    输出 (1×4): G(b)_t2_enc = G(Sum₂ᵉⁿᶜ) = [6e-8, 4e-9, 1.6e-9, 6e-10]
  6. G(h₁ᵉⁿᶜ)_from_t2 (1×4 向量):
    输出 (1×4): G(h₁ᵉⁿᶜ)_from_t2 ≈ [1e-8, 2e-8, 3e-8, 4e-8]
D. 编码器 (Encoder) t=1
  1. 起始梯度:
    G(h₁ᵉⁿᶜ) = G(h₁ᵉⁿᶜ)_from_t2 = [1e-8, 2e-8, 3e-8, 4e-8]
  2. G(Sum₁ᵉⁿᶜ) (1×4 向量):

    公式: G(h₁ᵉⁿᶜ) · (1 - (h₁ᵉⁿᶜ)²)

    输入 2 (1×4): (1 - [0.56, 0.74, 0.86, 0.92]²) = [0.6864, 0.4524, 0.2604, 0.1536]
    输出 (1×4): G(Sum₁ᵉⁿᶜ) ≈ [6.8e-9, 9e-9, 7.8e-9, 6e-9]
  3. G(W_xh)_t1_enc (3×4 矩阵):
    输出 (3×4 矩阵): [[6.8e-9, ...], [3.4e-9, ...], [1.36e-9, ...]]
  4. G(W_hh)_t1_enc (4×4 矩阵):
    输出 (4×4 矩阵): [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
  5. G(b)_t1_enc (1×4 向量):
    输出 (1×4): G(b)_t1_enc = G(Sum₁ᵉⁿᶜ) = [6.8e-9, 9e-9, 7.8e-9, 6e-9]

第 3 部分:共享权重的梯度汇总 (关键!)

现在,我们将所有共享权重的部分梯度加在一起,得到最终的总梯度。

Total G(W_out) ➕ (4×5 矩阵)
= G(W_out)_t1 + G(W_out)_t2

我们将两个 (4×5) 矩阵相加

Total G(W_xh) ➕ (3×4 矩阵)
= G(W_xh)_t1_enc + G(W_xh)_t2_enc + G(W_xh)_t1_dec + G(W_xh)_t2_dec

我们将四个 (3×4) 矩阵相加

Total G(W_hh) ➕ (4×4 矩阵)
= G(W_hh)_t1_enc + G(W_hh)_t2_enc + G(W_hh)_t1_dec + G(W_hh)_t2_dec

我们将四个 (4×4) 矩阵相加

Total G(b) ➕ (1×4 向量)
= G(b)_t1_enc + G(b)_t2_enc + G(b)_t1_dec + G(b)_t2_dec

我们将四个 (1×4) 向量相加

第 4 部分:权重更新

最后,我们使用这些汇总后的总梯度来更新我们的权重(假设学习率为 α):

new W_hh = old W_hh - (α × Total G(W_hh))
new W_xh = old W_xh - (α × Total G(W_xh))
new W_out = old W_out - (α × Total G(W_out))
new b = old b - (α × Total G(b))

🚨 观察:梯度消失问题

正如您所看到的,由于 tanh 函数的饱和(正向传播的输出值接近 1.0),梯度在反向传播时变得极小(例如 G(Sum₂ᵈᵉᶜ) ≈ [0.000012, ...])。

当这个极小的梯度 G(C) 传回编码器时,它变得几乎为零(例如 G(Sum₂ᵉⁿᶜ) ≈ [6e-8, ...])。

梯度消失的实际例子:
  • 编码器 t=1 步的权重(G(W_xh)_t1_enc)几乎没有收到任何更新信号
  • 网络因此"忘记"了第一个词 "I" 对最终损失的贡献
  • 这就是梯度消失(Vanishing Gradient)的实际例子!

5. 词向量 (Word Vectors / 词嵌入)

核心思想是将词语映射为高维空间中的向量,使得词语的“语义”可以通过数学计算来衡量。

应用: 类比计算 (加减法)

向量运算可以捕捉词语间的类比关系:

国王 - 男人 + 女人 ≈ 女王

巴黎 - 法国 + 德国 ≈ 柏林

应用: 相似度计算

使用**余弦相似度 (Cosine Similarity)** 来衡量两个词的相似性。

  • 原理: 衡量向量间的夹角(方向),而非距离。方向越接近,越相似。
  • 公式: (A·B) / (||A|| * ||B||)
  • 点积 (A·B): 衡量方向对齐度。
  • 模 (||A||): 向量的长度。

训练模型: Word2Vec

基于“观其伴而知其言”的思想,通过一个“伪任务”来学习词向量。

CBOW (Continuous Bag-of-Words)

用上下文的词(如 "quick", "fox"),来预测中间的词 ("brown")。

Skip-gram

用中间的词 (如 "brown"),来预测上下文的词 ("quick", "fox")。

训练优化: 负采样 (Negative Sampling)

将复杂的多分类问题,转换为简单的二分类问题 (是/否),极大提高了训练效率。

  • 正样本 (Positive Sample): 真实的上下文词对 (如 "brown", "fox")。
  • 负样本 (Negative Sample): 随机抽取的无关词对 (如 "brown", "apple")。
  • 采样策略: (词频)3/4 (提高中低频词的采样率)。

Skip-gram (Softmax) 网络拓扑结构

输入层

输入词: "brown"
One-Hot 向量
the quick brown fox jumps
0 0 1 0 0
输入权重矩阵 W_in
(词汇表大小 5 × 隐藏层维度 3)
the quick brown fox jumps
0.5
0.6
0.4
0.2
0.3
0.1
0.7
0.8
0.1
0.9
0.2
0.8
0.1
0.4
0.7
向量查找计算
One-Hot × W_in = 输出向量
[0,0,1,0,0] × W_in = [0.7, 0.8, 0.1]
由于只有第3个位置为1,结果直接取W_in的第3行
W_in[2,:] = [0.7, 0.8, 0.1]

隐藏层 (h)

[0.7, 0.8, 0.1]
直接复制输入词向量
维度: 3

输出权重 W_out

W_out (3×5)
0.5
0.1
0.9
0.4
0.8
0.6
0.2
1.0
0.3
0.7
0.7
0.3
1.1
0.2
0.6

输出层

原始分数
the: 0.90
quick: 0.30
brown: 1.51
fox: 0.54
jumps: 1.16
Softmax 概率
the: 0.13
quick: 0.05
brown: 0.26
fox: 0.09
jumps: 0.47
目标词: "fox"
[0, 0, 0, 1, 0]
损失: 交叉熵
梯度反向传播
G_scores = [0.05, 0.13, 0.26, -0.91, 0.47]
G_h = [0.316, 0.404, 0.492]

Skip-gram (Softmax) 详细计算

目标: 用 "brown" 预测 "fox"。这是原始的、计算量大的方法。 (LR=0.01)

输入 "brown" (v_brown / h):

[0.7, 0.8, 0.1]

目标 "fox" (t):

[0, 0, 0, 1, 0]

W_in (5x3, "brown" 行):

...
[0.7, 0.8, 0.1] <-- 索引 2
...

W_out (3x5):

[[0.1, 0.5, 0.9, 0.4, 0.8],
 [0.2, 0.6, 1.0, 0.3, 0.7],
 [0.3, 0.7, 1.1, 0.2, 0.6]]

  1. 前向传播 (计算分数): Scores = h · W_out = [0.3, 0.9, 1.51, 0.54, 1.16]
  2. 前向传播 (计算概率 p): p = softmax(Scores) ≈ [0.05, 0.13, 0.26, 0.09, 0.47]
  3. 反向传播 (1. 计算误差信号):

    这是 (Softmax+交叉熵) 的组合导数:

    G_scores = p - t
    [0.05, 0.13, 0.26, -0.91, 0.47]
  4. 反向传播 (2. 计算 Grad(W_out)):

    Grad(W_out) = hT · G_scores (3x1 外积 1x5 = 3x5)

    [[0.035, 0.091, 0.182, -0.637, 0.329],
     [0.040, 0.104, 0.208, -0.728, 0.376],
     [0.005, 0.013, 0.026, -0.091, 0.047]]
  5. 反向传播 (3. 计算 Grad(h)):

    Grad(h) = G_scores · W_outT (1x5 矩阵乘 5x3 = 1x3)

    [0.316, 0.404, 0.492]
  6. 更新 W_in:

    梯度 Grad(h) 只应用于 "brown" 对应的行 (LR=0.01):

    new_v_brown = [0.7, 0.8, 0.1] - (0.01 * [0.316, 0.404, 0.492]) new_v_brown = [0.697, 0.796, 0.095]

Skip-gram (负采样) 网络拓扑结构

输入层

输入词: "brown"
One-Hot 向量
the quick brown fox jumps
0 0 1 0 0
输入权重矩阵 W_in
(词汇表大小 5 × 隐藏层维度 3)
the quick brown fox jumps
0.5
0.6
0.4
0.2
0.3
0.1
0.7
0.8
0.1
0.9
0.2
0.8
0.1
0.4
0.7
向量查找计算
One-Hot × W_in = v_brown
[0,0,1,0,0] × W_in = [0.7, 0.8, 0.1]
负采样中,输入向量直接作为中心词向量
v_brown = W_in[2,:] = [0.7, 0.8, 0.1]

负采样输出层

正样本
"fox"
[0.4, 0.3, 0.2]
u_fox
点积计算
v_brown · u_fox = 0.54
Sigmoid
σ(0.54) = 0.63
与目标比较
目标: 1.0
误差: +0.37
负样本
"jumps"
[0.8, 0.7, 0.6]
u_jumps
点积计算
v_brown · u_jumps = 1.18
Sigmoid
σ(1.18) = 0.76
与目标比较
目标: 0.0
误差: -0.76
梯度更新 (LR=0.1)
正样本更新
Grad(u_fox) = +0.37 × v_brown
Grad(v_brown_pos) = +0.37 × u_fox
效果: 拉近 brown 和 fox
负样本更新
Grad(u_jumps) = -0.76 × v_brown
Grad(v_brown_neg) = -0.76 × u_jumps
效果: 推开 brown 和 jumps
中心词汇总
Total_Grad(v_brown) = 正 + 负
= [-0.46, -0.421, -0.382]
最终更新中心词向量

Skip-gram (负采样) 详细计算

目标: "拉近" ("brown", "fox"),"推开" ("brown", "jumps")。 (LR=0.1)

1. 正样本 ("brown", "fox") [目标=1]

  1. 前向传播 (Score):

    v_brown · u_fox

    [0.7, 0.8, 0.1] · [0.4, 0.3, 0.2] = 0.54
  2. 前向传播 (Prob): Prediction = sigmoid(0.54) ≈ 0.63
  3. 反向传播 (Error): Error = Target - Prediction = 1 - 0.63 = 0.37
  4. 反向传播 (Gradients): Grad(u_fox) = 0.37 * v_brown = [0.259, 0.296, 0.037] Grad(v_brown_pos) = 0.37 * u_fox = [0.148, 0.111, 0.074]

2. 负样本 ("brown", "jumps") [目标=0]

  1. 前向传播 (Score):

    v_brown · u_jumps

    [0.7, 0.8, 0.1] · [0.8, 0.7, 0.6] = 1.18
  2. 前向传播 (Prob): Prediction = sigmoid(1.18) ≈ 0.76
  3. 反向传播 (Error): Error = Target - Prediction = 0 - 0.76 = -0.76
  4. 反向传播 (Gradients): Grad(u_jumps) = -0.76 * v_brown = [-0.532, -0.608, -0.076] Grad(v_brown_neg) = -0.76 * u_jumps = [-0.608, -0.532, -0.456]

3. 权重更新 (LR=0.1)

  1. 更新 u_fox (被拉近): new_u_fox = [0.4, 0.3, 0.2] + (0.1 * [0.259, 0.296, 0.037]) new_u_fox = [0.4259, 0.3296, 0.2037]
  2. 更新 u_jumps (被推开): new_u_jumps = [0.8, 0.7, 0.6] + (0.1 * [-0.532, -0.608, -0.076]) new_u_jumps = [0.7468, 0.6392, 0.5924]
  3. 更新 v_brown (汇总梯度):

    Total_Grad(v_brown) = Grad(v_brown_pos) + Grad(v_brown_neg)

    Total_Grad(v_brown) = [0.148, 0.111, 0.074] + [-0.608, -0.532, -0.456] Total_Grad(v_brown) = [-0.46, -0.421, -0.382]

    new_v_brown = v_brown + (0.1 * Total_Grad(v_brown))

    new_v_brown = [0.7, 0.8, 0.1] + [-0.046, -0.0421, -0.0382] new_v_brown = [0.654, 0.7579, 0.0618]

CBOW (Softmax) 网络拓扑结构

输入层 (上下文词)

上下文词: "quick"
One-Hot 向量
the quick brown fox jumps
0 1 0 0 0
上下文词: "fox"
One-Hot 向量
the quick brown fox jumps
0 0 0 1 0
输入权重矩阵 W_in
(词汇表大小 5 × 隐藏层维度 3)
the quick brown fox jumps
0.5
0.6
0.4
0.4
0.5
0.6
0.7
0.8
0.1
0.2
0.3
0.4
0.1
0.4
0.7
向量查找过程
One-Hot(quick) × W_in = v_quick
[0,1,0,0,0] × W_in = [0.4, 0.5, 0.6]
One-Hot(fox) × W_in = v_fox
[0,0,0,1,0] × W_in = [0.2, 0.3, 0.4]
CBOW中,每个上下文词的one-hot向量分别查找对应的词向量

平均层

h = (v_quick + v_fox) / 2
([0.4, 0.5, 0.6] + [0.2, 0.3, 0.4]) / 2
[0.3, 0.4, 0.5]

输出权重 W_out

W_out (3×5)
0.5
0.1
0.9
0.4
0.8
0.6
0.2
1.0
0.3
0.7
0.7
0.3
1.1
0.2
0.6

输出层

原始分数
the: 0.5
quick: 0.2
brown: 0.8
fox: 0.3
jumps: 0.6
Softmax 概率
the: 0.15
quick: 0.11
brown: 0.22
fox: 0.12
jumps: 0.40
目标词: "brown"
[0, 0, 1, 0, 0]
损失: 交叉熵
梯度反向传播
G_scores = [0.11, 0.15, -0.78, 0.12, 0.40]
G_h = [-0.248, -0.352, -0.456]
梯度平分给所有上下文词:
G_v_quick = G_h / 2
G_v_fox = G_h / 2

CBOW (Softmax) 详细计算

目标: 用 ["quick", "fox"] 预测 "brown"。 (LR=0.01)

v_quick:

[0.4, 0.5, 0.6]

v_fox:

[0.2, 0.3, 0.4]

目标 "brown" (t):

[0, 0, 1, 0, 0]

W_out (3x5): (同上)

  1. 前向传播 (计算 h): h = (v_quick + v_fox) / 2 = ([0.4+0.2], [0.5+0.3], [0.6+0.4]) / 2 h = [0.3, 0.4, 0.5]
  2. 前向传播 (分数/概率 p): Scores = h · W_out ≈ [0.2, 0.5, 0.8, 0.3, 0.6] p = softmax(Scores) ≈ [0.11, 0.15, 0.22, 0.12, 0.40]
  3. 反向传播 (1. 计算误差信号): G_scores = p - t = [0.11, 0.15, -0.78, 0.12, 0.40]
  4. 反向传播 (2. 计算 Grad(W_out)):

    Grad(W_out) = hT · G_scores (3x1 外积 1x5 = 3x5)

    [[0.033, 0.045, -0.234, 0.036, 0.120],
     [0.044, 0.060, -0.312, 0.048, 0.160],
     [0.055, 0.075, -0.390, 0.060, 0.200]]
  5. 反向传播 (3. 计算 Grad(h)):

    Grad(h) = G_scores · W_outT

    Grad(h) = [-0.248, -0.352, -0.456]
  6. 反向传播 (4. 分配梯度):

    梯度被平分给所有上下文词 (除以2):

    Grad(v_quick) = Grad(h) / 2 = [-0.124, -0.176, -0.228] Grad(v_fox) = Grad(h) / 2 = [-0.124, -0.176, -0.228]
  7. 更新 W_in (LR=0.01): new_v_quick = [0.4, 0.5, 0.6] - (0.01 * [-0.124, ...]) new_v_quick = [0.40124, 0.50176, 0.60228] new_v_fox = [0.2, 0.3, 0.4] - (0.01 * [-0.124, ...]) new_v_fox = [0.20124, 0.30176, 0.40228]

CBOW (负采样) 网络拓扑结构

输入层 (上下文词)

上下文词: "quick"
One-Hot 向量
the quick brown fox jumps
0 1 0 0 0
上下文词: "fox"
One-Hot 向量
the quick brown fox jumps
0 0 0 1 0
输入权重矩阵 W_in
(词汇表大小 5 × 隐藏层维度 3)
the quick brown fox jumps
0.5
0.6
0.4
0.4
0.5
0.6
0.7
0.8
0.1
0.2
0.3
0.4
0.1
0.4
0.7
向量查找过程
One-Hot(quick) × W_in = v_quick
[0,1,0,0,0] × W_in = [0.4, 0.5, 0.6]
One-Hot(fox) × W_in = v_fox
[0,0,0,1,0] × W_in = [0.2, 0.3, 0.4]
CBOW负采样:上下文词向量平均后用于预测中心词

平均层

h = (v_quick + v_fox) / 2
[0.3, 0.4, 0.5]

负采样输出层

正样本
"brown"
[0.7, 0.8, 0.1]
u_brown
点积计算
h · u_brown = 0.58
Sigmoid
σ(0.58) = 0.64
与目标比较
目标: 1.0
误差: +0.36
负样本
"jumps"
[0.8, 0.7, 0.6]
u_jumps
点积计算
h · u_jumps = 0.82
Sigmoid
σ(0.82) = 0.69
与目标比较
目标: 0.0
误差: -0.69
梯度更新 (LR=0.1)
正样本更新
Grad(u_brown) = +0.36 × h
Grad(h_pos) = +0.36 × u_brown
效果: 拉近上下文和 brown
负样本更新
Grad(u_jumps) = -0.69 × h
Grad(h_neg) = -0.69 × u_jumps
效果: 推开上下文和 jumps
上下文汇总
Total_Grad(h) = Grad(h_pos) + Grad(h_neg)
= [-0.3, -0.195, -0.378]
平分给所有上下文词
上下文词更新
Grad(v_quick) = Total_Grad(h) / 2
Grad(v_fox) = Total_Grad(h) / 2
每个上下文词获得相同梯度

CBOW (负采样) 详细计算

目标: 用 ["quick", "fox"] "拉近" "brown", "推开" "jumps"。 (LR=0.1)

1. 计算平均上下文 `h`

  1. 计算 `h`: h = (v_quick + v_fox) / 2 = ([0.4, 0.5, 0.6] + [0.2, 0.3, 0.4]) / 2 h = [0.3, 0.4, 0.5]

2. 正样本 ("brown") [目标=1]

  1. 前向传播 (Score): Score_pos = h · u_brown = [0.3, 0.4, 0.5] · [0.7, 0.8, 0.1] = 0.58
  2. 前向传播 (Prob): Prediction_pos = sigmoid(0.58) ≈ 0.64
  3. 反向传播 (Error): Error_pos = 1 - 0.64 = 0.36
  4. 反向传播 (Gradients): Grad(u_brown) = 0.36 * h = [0.108, 0.144, 0.18] Grad(h_pos) = 0.36 * u_brown = [0.252, 0.288, 0.036]

3. 负样本 ("jumps") [目标=0]

  1. 前向传播 (Score): Score_neg = h · u_jumps = [0.3, 0.4, 0.5] · [0.8, 0.7, 0.6] = 0.82
  2. 前向传播 (Prob): Prediction_neg = sigmoid(0.82) ≈ 0.69
  3. 反向传播 (Error): Error_neg = 0 - 0.69 = -0.69
  4. 反向传播 (Gradients): Grad(u_jumps) = -0.69 * h = [-0.207, -0.276, -0.345] Grad(h_neg) = -0.69 * u_jumps = [-0.552, -0.483, -0.414]

4. 汇总梯度并更新 (LR=0.1)

  1. 更新 `W_out` (u_brown, u_jumps): new_u_brown = [0.7, 0.8, 0.1] + (0.1 * [0.108, 0.144, 0.18]) new_u_brown = [0.7108, 0.8144, 0.118] new_u_jumps = [0.8, 0.7, 0.6] + (0.1 * [-0.207, -0.276, -0.345]) new_u_jumps = [0.7793, 0.6724, 0.5655]
  2. 汇总 `h` 的总梯度: Total_Grad(h) = Grad(h_pos) + Grad(h_neg) = [0.252, 0.288, 0.036] + [-0.552, -0.483, -0.414] = [-0.3, -0.195, -0.378]
  3. 分配梯度到 `W_in` (v_quick, v_fox): Grad(v_quick) = Total_Grad(h) / 2 = [-0.15, -0.0975, -0.189] Grad(v_fox) = Total_Grad(h) / 2 = [-0.15, -0.0975, -0.189]
  4. 更新 `W_in` (v_quick, v_fox): new_v_quick = [0.4, 0.5, 0.6] + (0.1 * [-0.15, -0.0975, -0.189]) new_v_quick = [0.385, 0.49025, 0.5811] new_v_fox = [0.2, 0.3, 0.4] + (0.1 * [-0.15, -0.0975, -0.189]) new_v_fox = [0.185, 0.29025, 0.3811]