.jpg)
| 参数名称 | 参数值 |
|---|
| 上下文窗口大小 | n=3(预测第4个词) |
| 词向量维度 | m=3 |
| 隐藏层大小 | 4 |
| 激活函数 | tanh |
| 输出层 | softmax |
| 学习率 | η=0.1 |
| 训练批次 | 1样本/批次(在线学习) |
| 项目 | 内容 |
|---|
| 词汇表 | V=(v1,v2,...,v11)=(’<s>’,’我’,’喜欢’,’机器’,’学习’,’深度’,’他’,’讨厌’,’传统’,’方法’,’</s>’) |
| 词索引映射 | index(’<s>’)=1、index(’我’)=2、⋯、index(’</s>’)=11 |
| 训练序列 | S1=[’<s>’,’<s>’,’<s>’,’我’,’喜欢’,’机器’,’学习’,’</s>’] |
💡 关键洞见:为什么要引入起始符 <s>?
1.NPLM 的网络结构要求输入层必须始终接收 3 个词向量(即上下文窗口 n=3)。引入 <s> 主要解决了前馈神经网络(FFNN)对固定输入维度的刚性需求。对于句首的单词(如“我”),由于缺乏真实的历史上文,模型无法直接运行。
2.通过填充 <s>,我们不仅在物理上补齐了输入矩阵的维度,使第一步运算得以进行;还在语义上为模型提供了一个明确的“句首信号”。随着训练的进行,<s> 的词向量将学到“句子开始”这一特殊的语义特征,从而帮助模型更准确地预测哪些词(如代词、名词)更倾向于出现在句子的开头。
3.结束符</s>的作用也是:在语义上为模型提供了一个明确的“句尾信号”
💡 关键洞见:为什么要对词汇表进行索引映射 ?
1.为了方便在数学公式中表示和索引。2.为了在数据集上做标注
如 “我”后面应该出现的正确的词是“喜欢”或者“讨厌”,即在做数据标注时,正确集应该是[2,3],[2,8]
在损失计算的过程中,见 \eqrefeq:grad−y−derivation,以预测“我”后面的词举例;对于i = 3或8,公式就变成了 Pt−1。这意味着模型预测的概率 Pt 本应该接近 1,但如果它只有 0.9,那么梯度就是 0.9−1=−0.1。这个负号告诉模型:“你猜低了,赶紧把分数加上去。”
💡 关键洞见:关于数值精度的说明
这里的矩阵参数展示为两位小数,但这是一种人为简化。
从数学上的均匀分布 U(−0.1,0.1) 采样实际上会产生连续的高精度浮点数(如 0.04821...)。在本例中,我们人为将数值限制在两位小数,主要是为了确保简洁易读,方便验证。
在真实的深度学习工程实现中,这些参数通常使用 32 位或 64 位浮点数存储,以保持数值计算的精度。
使用均匀分布 U(−0.1,0.1) 随机初始化:
Cinit=0.05−0.060.03−0.080.07−0.040.03−0.020.09−0.070.08−0.050.02−0.030.07−0.040.05−0.060.06−0.070.02−0.090.04−0.030.01−0.010.08−0.050.09−0.070.04−0.020.01\labeleq:C−init
💡 关键洞见:均匀分布 U(−0.1,0.1) 的背景与“随机初始化”究竟在做什么?
这里的
Cinit∼U(−0.1,0.1)
含义是:词向量矩阵中的每一个元素,都是在区间 [-0.1, 0.1] 上独立、等概率地随机采样得到的。
换句话说,落在这个区间内的任意实数,被选中的概率是一样的,没有“偏爱”某个数值。
之所以要这样做,有三个关键原因:
打破对称性(symmetry breaking)
如果一开始把所有参数都设为 0,那么网络中许多单元在前向和反向传播时会得到完全相同的梯度,导致它们永远学不到互补的功能;
随机初始化则让不同维度、不同词从一开始就略有差别,从而能在训练中走向不同的方向,各自“专精”不同特征。
数值足够小,避免激活函数过早饱和
区间[-0.1, 0.1] 很小,经过一两层线性变换后,送入 (\tanh) 等非线性时,输入通常仍然比较接近 0。
在 |x| 很小的时候,tanh(x)≈x,梯度接近 1,不会出现激活函数“饱和”(梯度非常接近 0)的问题,有利于训练一开始的稳定收敛。
词向量一开始是“无偏好”的
这些随机的小数本身没有语义,只是提供一个“起点”。
之后通过大量样本的梯度更新,模型会逐步把这些向量调整成有语义结构的表示(相似词向量靠近,不同词被区分开)。
在实现层面,“随机初始化”就是对矩阵的每个元素调用一次随机数生成函数。例如用 Python + NumPy,可以写成:
import numpy as np
# C 的形状是 (词向量维度 m, 词表大小 |V|)
C_init = np.random.uniform(low=-0.1, high=0.1, size=(3, 11))
这段代码会为 (C) 中的 3×11 个元素,各生成一个 [-0.1, 0.1] 区间内的随机数,得到的某一次具体结果,就类似你在上方给出的那个 Cinit。
权重矩阵 W(h)∈R4×9:
W(h)=0.01−0.010.02−0.02−0.020.02−0.030.030.03−0.030.04−0.04−0.040.04−0.050.050.05−0.050.06−0.06−0.060.06−0.070.070.07−0.070.08−0.08−0.080.08−0.090.090.09−0.090.01−0.01\labeleq:Wh
💡
W(h) 的尺寸是 4×9。这个尺寸是由 “输入数据的总长度” 和 “隐藏层神经元的数量” 共同决定的。
9 列(宽度)来源:输入层 x 的维度即:$ \text{输入维度} = \text{窗口大小} \times \text{词向量维度} = 3 \times 3 = \mathbf{9}$;
4 行(高度)来源:隐藏层 h 的大小,这意味着我们希望把那 9 个输入特征,经过线性变换后,压缩/映射成 4 个新的特征。
🧠
隐藏层大小(Hidden Layer Size,记为 Nh)是一个 超参数(Hyperparameter)。
如果你把 Nh 设得太小(比如输入 100 维,隐藏层只有 2 维,输出 50 类),后果:欠拟合(Underfitting),即数学上:将高维数据强行投影到极低维空间,导致原本可分的数据挤在一起,变得不可分。
如果Nh 设得极其巨大(比如输入 100 维,隐藏层 10000 维)。后果:过拟合(Overfitting) 和 计算浪费。参数量过多,模型具有了捕捉数据中“随机噪声”的能力,而不是只捕捉“通用规律”。
虽然没有一个万能公式能算出最优的 h,但学术界和工业界有几个常用的“初始设定策略”:
🧠
隐藏层大小(记为 Nh)规定多少合适?
策略一:几何金字塔 (Geometric Pyramid)
通常,隐藏层的大小位于输入层和输出层之间。
Nh≈Nin×Nout
9×11≈99≈10。
注:你的笔记中选了 4,这是一个偏向“压缩特征”的选择,对于演示简单例子是完全合理的。
策略二:输入的倍数 (Expansion Ratio)
在现代深度学习(特别是 Transformer 和 CNN)中,经常反其道而行之,将隐藏层设为输入的倍数。
- 常见设定:2×Nin 或 4×Nin。
- 逻辑:升维打击。将数据投射到更高维的空间,使得数据在那个空间里更容易被线性分割(Cover's Theorem)。
- 例如:Transformer 的前馈网络(FFN)通常把维度放大 4 倍,然后再缩回去。
策略三:2 的幂次 (Powers of 2)
你会发现大牛们的代码里,隐藏层往往是 32, 64, 128, 512, 1024...
逻辑:纯粹的工程优化。GPU 和计算机内存对 2 的幂次大小的数据块读写效率最高(Memory Alignment)。选 500 不如选 512 跑得快。
此案例中
x(9维)W(h)h(4维)W(y)y(11维)
这里选择 4 是一个非常典型的“特征压缩”(Bottleneck)设计。
它的假设是:虽然输入的词向量组合有 9 个维度,但真正决定下一个词是什么的“核心语义”,其实只用 4 个数字就能概括。
偏置向量 :
b(h)∈R4:b(h)T=[0.00.00.00.0]
权重矩阵 W(y)∈R11×4:
W(y)=0.010.020.030.040.050.060.070.080.09−0.01−0.02−0.02−0.03−0.04−0.05−0.06−0.07−0.08−0.09−0.010.020.030.030.040.050.060.070.080.090.010.02−0.03−0.04−0.04−0.05−0.06−0.07−0.08−0.09−0.01−0.02−0.030.040.05\labeleq:Wy
b(y)∈R11$:b(y)T=[0.00.00.00.00.00.00.00.00.00.00.0]
💡 关键洞见:为什么偏置向量 b 通常初始化为 0?
偏置项的初始化策略与权重矩阵截然不同,通常直接设为全 0,这主要基于以下两个原因:
第一,权重的随机性已经打破了对称性。
神经网络初始化的核心任务是防止所有神经元学习到完全相同的特征(即“对称性困境”)。只要权重矩阵 W 是随机初始化的,每个神经元对输入的响应就已经各不相同了。
此时将偏置 b 初始化为 0 不会重新引入对称性问题,因此无需引入额外的随机噪声。
第二,最大化初始梯度收益。
NPLM 使用的是 tanh 激活函数,其导数(梯度)在输入为 0 附近最大(即函数的线性区域)。将偏置初始化为 0,可以保证在训练伊始,神经元的净输入主要由加权后的输入数据决定,大概率落在 tanh 函数的中心区域。这能有效避免初始状态就落入函数的饱和区(即两端平缓区域),从而保证梯度能够顺畅地反向传播,加速模型收敛。
我们将模拟 NPLM 在处理句子时的第一个时间步。
NPLM 使用一个固定大小的“滑动窗口”来预测下一个词。根据规格定义,我们的上下文窗口大小为 3,即根据前 3 个词预测第 4 个词。
当前任务:模型刚开始读取句子,需要预测第一个实际的单词 "我"。
输入上下文 (Context):由于 "我" 是句首,其前文不足,需要用填充符号 <s> (Start of Sentence) 补齐。
样本提取示意图:
| 时间步 (t) | 上下文输入 (wt−3,wt−2,wt−1) | 预测目标 (wt) |
|---|
| t=4 | ['<s>', '<s>', '<s>'] | '我' |
| (下一时刻) | ['<s>', '<s>', '我'] | '喜欢' |
数值化映射(String → Index):
为了送入神经网络,我们需要将单词转换为词汇表中的索引(Index):
输入序列 (Input Indices):
- wt−3=’<s>’→Index: 1
- wt−2=’<s>’→Index: 1
- wt−1=’<s>’→Index: 1
- Input Vector Indices=[1,1,1]
目标标签 (Target Label):
- wt=’我’→Index: 2
- 这是我们在计算损失函数时需要的“标准答案”(Ground Truth)。
x=[c(w1) c(w2) c(w3)]=[0.05−0.060.030.05−0.060.030.05−0.060.03]T\labeleq:x−concat
其中词向量来自 \eqrefeq:C−init
计算 z(h)=W(h)x+b(h)(其中 W(h) 见 \eqrefeq:Wh,x 见 \eqrefeq:x−concat),z(h)∈R4:
z1(h)=0.01⋅0.05+(−0.02)⋅(−0.06)+0.03⋅0.03+(−0.04)⋅0.05+0.05⋅(−0.06)+(−0.06)⋅0.03+0.07⋅0.05+(−0.08)⋅(−0.06)+0.09⋅0.03+0.0=0.0068
。。。。
以此类推
因此:
z(h)=[0.0068 −0.0068 0.0055 −0.0055]\labeleq:zh
应用 tanh 激活函数(使用 tanh(x)≈x 当 x 很小时):
h=tanh(z(h))=tanh(0.0068)tanh(−0.0068)tanh(0.0055)tanh(−0.0055)≈0.0068−0.00680.0055−0.0055\labeleq:h
洞见
计算 y=W(y)h+b(y)(其中 W(y) 见 \eqrefeq:Wy,h 见 \eqrefeq:h),y∈R11:
y1=W1,:(y)⋅h=0.01×0.0068+(−0.02)×(−0.0068)+0.03×0.0055+(−0.04)×(−0.0055)+0.0$=0.000068+0.000136+0.000165+0.000220=0.000589
。。。。
y_{11} = W^{(y)}_{11,:} \cdot h = (-0.02) \times 0.0068 + 0.03 \times (-0.0068) + (-0.04) \times 0.0055 + 0.05 \times (-0.0055) + 0.0$ = -0.000136 - 0.000204 - 0.000220 - 0.000275 = -0.000835\$
因此:
y=[0.0005890.0008350.0010810.0013270.0015730.0018190.0015700.0013210.000955−0.000589−0.000835]T\labeleq:y−output
在神经网络的前向传播末端,模型输出的是一组未归一化的原始分值(Logits)。为了评估模型表现并启动反向传播,我们需要将这些分值转化为概率,并计算其与真实标签之间的距离。
计算 softmax: P(wi)=∑j=111eyjeyi,其中 y 来自 \eqrefeq:y−output。
首先计算 eyi:
- ey1=e0.000589≈1.000589
- ey2=e0.000835≈1.000835
- 。。。。
- ey11=e−0.000835≈0.999165
分母:
j=1∑11eyj=1.000589+1.000835+1.001082+1.001328+1.001574+1.001821+1.001571+1.001322+1.000955+0.999411+0.999165=11.009653
概率分布:
P(w1)=P(’<s>’)=11.0096531.000589=0.09088
P(w2)=P(’我’)=11.0096531.000835=0.09090
。。。。
P(w11)=P(’</s>’)=11.0096530.999165=0.09076
因此,预测分布为:
P=[0.090880.090900.090930.090950.090970.090990.090970.090950.090910.090780.09076]T\labeleq:P−dist
这里的 P 向量计算的是:在当前上下文窗口(<s>, <s>, <s>)条件下,词汇表中每一个词成为“下一个词”的条件概率。
数学定义:
对于单标签分类任务(目标类索引为 t),交叉熵损失衡量了预测概率分布 P 与真实分布(One-hot 编码)的差异:
L=−log(Pt)\labeleq:loss−def
我们可以从以下三个视角来理解为什么要取对数:
信息论视角:衡量“意外感”
在信息论中,一个事件发生的概率越低,它包含的信息量(Information Content)就越大。
- 数学定义:信息量 I(x)=−log(P(x))。
- 逻辑:
- 如果你预测一个概率为 0.99 的事件发生,这很正常,信息量几乎为 0。
- 如果你预测一个概率为 0.01 的事件发生(即你之前的预测完全错了),这非常令人“意外”,信息量极大。
- 交叉熵的本质:它是用模型预测的分布去表达真实分布时,所产生的平均意外感。取对数就是为了把概率转换成这种可以相加的“意外分值”。v
统计学视角:最大似然估计(MLE)
在训练模型时,我们的目标是让模型预测出正确标签的概率最大化。
假设正确类别的概率是 Pt,我们希望 Pt 越大越好。
- 乘法难题:如果我们有多个样本,总概率是 Ptotal=P1×P2×⋯×Pn。
- 对数的妙用:由于概率都在 (0,1) 之间,连乘会导致数值迅速变小(趋近于 0),计算机无法处理。
- 转换:对总概率取对数 log(Ptotal)=log(P1)+log(P2)+⋯+log(Pn)。
- 连乘变连加:计算变得稳定。
- 求导变简单:log(x) 的导数是 1/x,在反向传播时非常优雅。
优化视角:消除梯度消失
为什么不直接用“距离” (1−Pt) 而是用 −log(Pt)?
- 如果不取对数:直接使用均方误差(MSE),梯度会包含激活函数(如 Sigmoid/Softmax)的导数。当预测值接近 0 或 1 时,这些激活函数进入“饱和区”,导数极小,导致模型“学不动了”。
- 取对数后:−log(Pt) 的增长速度在 Pt→0 时极快。
“取对数的操作将概率空间的乘法转换成了能量空间的加法。它不仅符合信息论中对‘意外感’的量化,更在工程上提供了一个永不枯竭的梯度源泉,确保模型在预测错误时能得到足够强的修正信号。”
由于目标词是 ’我’(索引2),根据 \eqrefeq:loss−def 使用交叉熵损失,其中 P(w2) 来自 \eqrefeq:P−dist:
L=−logP(w2)=−log(0.09090)=2.3985\labeleq:loss
物理含义:
交叉熵本质上是在衡量“信息意外感”。如果模型对正确类别的预测概率越低,损失值就越趋向于无穷大,从而给予模型强烈的反馈。
这是神经网络能够高效学习的数学基石。虽然 Softmax 和 交叉熵各自的求导较为复杂,但当它们组合在一起时,损失 L 对原始输出 yi 的偏导数会产生惊人的简化:
∂yi∂L=∂yi∂(−lnPt)=−Pt1⋅∂yi∂Pt=−Pt1⋅∂yi∂(∑jeyjeyt)={−Pt1⋅(∑eyj)2eyt∑eyj−(eyt)2=−Pt1⋅(Pt−Pt2)=Pt−1,−Pt1⋅(∑eyj)20−eyteyi=−Pt1⋅(−PtPi)=Pi,if i=tif i=t=Pi−1(i=t)\labeleq:grad−y−derivation
💡 关键洞见: t 是什么?`?
说明:t 代表 Target(真实标签)的索引
如 “我”(i = 2)后面应该出现的正确的词是“喜欢”(i=3)或者“讨厌”(i=8),即在做数据标注时,正确集应该是[2,3],[2,8]
在损失计算的过程中,,以预测“我”后面的词举例;对于i = 3或8,公式就变成了 Pt−1。这意味着模型预测的概率 Pt 本应该接近 1,但如果它只有 0.9,那么梯度就是 0.9−1=−0.1。这个负号告诉模型:“你猜低了,赶紧把分数加上去。”
- 误差信号源:这个简洁的 P−Label 构成了整个反向传播的起始信号。它直观地告诉网络:预测高了就减小权重,预测低了就增加权重。
- 消除饱和死区:单独的 Sigmoid 在值很大时导数趋于 0(梯度消失),但 Softmax 配合交叉熵后,导数形式中不再含有会导致消失的微小乘积项,只要有偏差,梯度就足够强,保证了深层网络也能快速收敛。
💡 洞见:
权重 W 和偏置 b 是模型的可学习参数 (Learnable Parameters),构成了模型的假设空间;而 Loss 是衡量模型预测分布 P(y∣x) 与真实分布(Label)之间差异的标量度量 (Scalar Metric)。它是参数优化的目标函数,而非模型结构的一部分。
在监督学习框架下,Loss 仅在输出层计算。这是因为训练数据只提供了观测变量 (Observable Variables) y 的真实标签(Ground Truth),而隐藏层属于潜变量 (Latent Variables),缺乏显式的监督信号。因此,必须通过计算输出层的残差,才能构建起优化的起点。
Loss 的核心作用是为反向传播提供全局梯度信号。它定义了优化图谱(Optimization Landscape)的形状。通过链式法则,Loss 将输出空间的误差 (Error) 转化为参数空间的梯度 (Gradient) ∇θL,从而指导随机初始化的参数向损失最小化的方向收敛。
在 4.4 节之前,我们实际上只完成了“构建”工作:我们搭建了神经网络的数学骨架,并用随机数填充了它。此时的模型虽然能运行,但在本质上只是一个“只会输出随机噪声的机器”,它与真实语料库之间存在巨大的偏差(即 Loss)。
从 4.4 节开始,我们将进入“校准(拟合)”阶段。 我们不再关注网络结构本身,而是聚焦于消除偏差。通过反向传播,我们将利用算出的误差去倒推每一个参数的责任,并强制修正它们。 如果说前向传播是模型在“表达”它当前的随机状态,那么反向传播就是语料库在“纠正”模型的错误认知。 这就是神经网络从“随机初始化”收敛到“数据规律”的过程。
计算梯度的唯一目的就是告诉模型如何调整参数,让损失变小。
在训练神经网络时,我们使用梯度下降法来更新参数:
Wnew(y)=Wold(y)−η⋅∂W(y)∂L
bnew(y)=bold(y)−η⋅∂b(y)∂L
其中:
- η 是学习率(步长)
- ∂W(y)∂L 告诉我们:如果改变 W(y),损失会如何变化
- ∂b(y)∂L 告诉我们:如果改变 b(y),损失会如何变化
如果说前向传播是“预测”,那么反向传播就是“复盘”。它利用链式法则(Chain Rule),将输出层的总损失(Loss)按照路径反向拆解,计算出每一个权重 w 和偏置 b 对最终误差的贡献度(即梯度)。
数学表述:
对于每一层,我们需要计算:
∂w∂Loss=∂激活输出∂Loss×∂线性输入∂激活输出×∂w∂线性输入
中间项正是你之前推导过的激活函数导数(如 σ′(x) 或 1−tanh2(x))。这解释了为什么激活函数必须处处可导。
1.首先计算 ∂y∂L(见 \eqrefeq:grad−y−derivation):
∂yi∂L=P(wi)−1!(i=index(target))其中 1(⋅) 是指示函数,当条件为真时为 1,否则为 0。
因此:
∂y1∂L=0.09088−0=0.09088
∂y2∂L=0.09090−1=−0.90910
。。。
∂y11∂L=0.09076−0=0.09076
即:
∂y∂L=[0.09088−0.909100.090930.090950.090970.090990.090970.090950.090910.090780.09076]T\labeleq:grad−y
2.计算 ∂W(y)∂L :
∂W(y)∂L=∂y∂L⋅∂W(y)∂y(链式法则)由于 y=W(y)h+b(y),对于 W(y) 的第 i 行第 j 列元素 Wi,j(y):∂Wi,j(y)∂yi=hj,∂Wi,j(y)∂L=∂yi∂L⋅hj写成矩阵形式:∂W(y)∂L=∂y∂L⋅hT其中h来自\eqrefeq:h,是被激活函数处理的隐藏层向量
因此:
∂W(y)∂L=[0.09088 −0.90910 0.09093 0.09095 0.09097 0.09099 0.09097 0.09095 0.09091 0.09078 0.09076]×[0.0068−0.00680.0055−0.0055]=0.000618−0.0061820.0006180.0006180.0006190.0006190.0006190.0006180.0006180.0006170.000617−0.0006180.006182−0.000618−0.000618−0.000619−0.000619−0.000619−0.000618−0.000618−0.000617−0.0006170.000500−0.0050000.0005000.0005000.0005000.0005000.0005000.0005000.0005000.0005000.000500−0.0005000.005000−0.000500−0.000500−0.000500−0.000500−0.000500−0.000500−0.000500−0.000500−0.000500
计算$ \frac{\partial \mathcal{L}}{\partial b^{(y)}}$ :
由于 y=W(y)h+b(y),对于偏置向量 b(y) 的第 i 个元素 bi(y):
∂bi(y)∂yi=1
因此:
∂bi(y)∂L=∂yi∂L⋅1=∂yi∂L
写成向量形式:
∂b(y)∂L=∂y∂L
数值计算:
∂b(y)∂L=[0.09088−0.909100.090930.090950.090970.090990.090970.090950.090910.090780.09076]T
有了输出层的误差梯度后,我们接下来的任务是将这个误差“传回”给隐藏层。这就好比我们知道了最终结果偏离了多少,现在要问责中间的计算单元(隐藏层神经元):“你们当初输出了什么,才导致最终结果错成这样?”
数学上,这通过计算损失函数 L 对隐藏层输出 h 的偏导数来实现。简单来说,就是把误差 ∂y∂L 通过权重矩阵 W(y) 反向加权求和。
1. 计算过程
我们需要计算 ∂h∂L=(W(y))T⋅∂y∂L。
以隐藏层第一个神经元 h1 为例,它的梯度是所有输出层节点梯度的加权和:
∂h1∂L=j=1∑11Wj,1(y)⋅∂yj∂L
我们可以展开第一项 h1 的具体计算过程,以看清误差是如何被加权求和的:
∂h1∂L=W1,1(y)⋅P10.01×0.09088+W2,1(y)⋅(P2−1)←目标词Contribution0.02×(0.09090−1)+W3,1(y)⋅P30.03×0.09093+…(其余8项)≈0.0009+(−0.01818)+0.0027+…≈0.0177
注意: 这里不能简单地将误差加和后再乘一个权重,而是每一项误差都要乘以其对应的权重(如 0.01, 0.02, 0.03...)。正是因为目标词对应的权重是 0.02,而其他词的权重各不相同(甚至有负数),才共同作用产生了最终的梯度。
代入精确数值计算,我们得到隐藏层四个神经元的误差梯度:
∂h∂L≈[0.0177 −0.0063 −0.0054 0.0169]
2. 通过 tanh 激活函数层
现在的梯度还在 h(激活后的值)上,我们要穿过 tanh 函数,找到 z(h)(激活前的值)的梯度。因为我们之前计算过 z(h) 的值非常小(接近 0),而 tanh 函数在 0 附近的导数 1−tanh2(x) 几乎等于 1。
因此,穿过激活函数后,梯度数值几乎保持不变:
∂z(h)∂L≈∂h∂L=[0.0177 −0.0063 −0.0054 0.0169]
3. 更新隐藏层权重
现在我们知道了隐藏层应该如何调整输出(∂z(h)∂L),就可以结合当时的输入(拼接后的词向量 x),计算出隐藏层权重 W(h) 需要调整的方向。例如对于 W1,1(h),其梯度约为 0.0177×0.05≈0.00088。
最后一步,也是最神奇的一步:误差继续传播,直达输入的词向量。
我们现在的目标是算出 ∂x∂L。这意味着我们要问:“输入的词向量要怎么改,才能让预测更准?” 这正是 Word2Vec 等技术的核心——让词向量在训练中自己学会“移动”到有意义的位置。
1. 计算过程
计算方法是将误差通过隐藏层权重矩阵 W(h) 再反向投射回去:∂x∂L=(W(h))T⋅∂z(h)∂L。
以输入向量的第一个分量 x1 为例(该分量对应第一个 <s> 的第一个维度):
∂x1∂L=k=1∑4Wk,1(h)⋅∂zk(h)∂L
代入 W(h) 的第一列 [0.01,−0.01,0.02,−0.02] 和上面的梯度值:
≈0.01(0.0177)−0.01(−0.0063)+0.02(−0.0054)−0.02(0.0169)≈0.000177+0.000063−0.000108−0.000338≈−0.000206
以此类推,我们可以算出完整的 9 维输入梯度 ∂x∂L。
2. 词向量的累积更新
注意,我们的输入 x 实际上是由三个相同的词 <s> 拼接而成的。因此,<s> 这个词向量在本次训练中的总误差,应该是这三个位置梯度的累加。
∂c(’<s>’)∂L=位置1梯度∂x∂L1:3+位置2梯度∂x∂L4:6+位置3梯度∂x∂L7:9
最终,我们使用这个累加后的梯度来更新 <s> 的词向量:
c(’<s>’)new=c(’<s>’)old−η⋅∂c(’<s>’)∂L
通过无数次这样的微调,原本随机初始化的 <s> 向量就会慢慢变成一个真正能代表“句首”含义的数学向量。
根据计算出的梯度,我们使用学习率 η=0.1 更新模型参数(SGD 步骤)。
1. 输出层权重更新 (W(y))
公式:W(y)←W(y)−η∂W(y)∂L
以连接 h1 到输出目标“我”(y2) 的权重 W2,1(y) 为例:
梯度计算:∂W2,1(y)∂L=∂y2∂L⋅h1≈(−0.9091)⋅0.0068≈−0.00618
更新步:
W2,1(y)new=0.02−0.1×(−0.00618)=0.02+0.000618=0.020618
直观意义:因为 h1 是正数 (0.0068),且我们需要提高“我”的概率(即提高 y2),所以权重应该变大。
2. 词向量更新 (C)
公式:C←C−η∂C∂L
以 <s> 的第一个维度为例(累积了三个位置的梯度):
梯度计算:∂c(’<s>’)1∂L≈−0.000155(数值非常小,因为初始权重还是随机且相互抵消的)
更新步:
c(’<s>’)1new=0.05−0.1×(−0.000155)=0.05+0.0000155=0.0500155
直观意义:虽然变化微小,但经过在海量数据上的无数次迭代,这些微小的推动力汇聚起来,最终会将 <s> 推向向量空间中正确的位置。
3. 更新后的参数全貌
经过这一轮反向传播,我们的模型参数发生了如下微小的物理变化(保留6位小数):
更新后的 词向量矩阵 Cnew(仅第一行 <s> 被修正了):
Cnew=0.050015−0.0800000.030000−0.0700000.020000−0.0400000.060000−0.0900000.010000−0.0500000.040000−0.0600130.070000−0.0200000.080000−0.0300000.050000−0.0700000.040000−0.0100000.090000−0.0200000.029807−0.0400000.090000−0.0500000.070000−0.0600000.020000−0.0300000.080000−0.0700000.010000
更新后的 隐藏层权重 W(h)new:
W(h)new=0.009909−0.0099680.020027−0.020086−0.0198910.019962−0.0300330.0301030.029945−0.0299810.040016−0.040052−0.0400910.040032−0.0499730.0499140.050109−0.0500380.059967−0.059897−0.0600550.060019−0.0699840.0699480.069909−0.0699680.080027−0.080086−0.0798910.079962−0.0900330.0901030.089945−0.0899810.010016−0.010052
更新后的 输出层权重 W(y)new(注意第二行目标词权重的显著提升):
W(y)new=0.0099380.0206180.0299380.0399380.0499380.0599380.0699380.0799380.089938−0.010062−0.020062−0.019938−0.030618−0.039938−0.049938−0.059938−0.069938−0.079938−0.089938−0.0099380.0200620.0300620.0299500.0405000.0499500.0599500.0699500.0799500.0899500.0099500.019950−0.030050−0.040050−0.039950−0.050500−0.059950−0.069950−0.079950−0.089950−0.009950−0.019950−0.0299500.0400500.050050
💡 总结
可以看到,为了让模型更倾向于预测“我”,与“我”相关的输出层连接权重(第二行)得到了最显著的增强(约 +0.0006),而底层的词向量 <s> 仅发生了极为微小的位移(约 +0.000015)。这符合深度学习的规律:越靠近输出层,梯度信号越强,调整越剧烈;越靠近底层,信号越微弱,需要依靠海量数据的长期浸泡才能发生质变。