梯度函数决定学习规律

sin 和 tanh 之间的距离不是 0.76 到 3.06。是"自己打自己"和"方向从不出错"之间的距离。

一个失败的实验,一条意外的规律

Phase 1.0 的 RNN 实验测了 7 组 hidden_size,最佳 BLEU 3.06。到此为止,这是一个标准的调参实验。

然后我们做了一个看似无厘头的尝试:把 RNN 的激活函数从 tanh 换成 sin。

理由很朴素——sin 是周期函数。如果翻译本质是 hash 碰撞,周期函数天然支持"多桶"碰撞——同一个输出值可以从无限多个输入周期产生。tanh 只有一个桶(全局单调),sin 有无限个桶。理论上,多桶应该提供更多自由度。

结果:sin RNN 的最佳 BLEU = 0.76。 远低于 tanh 的 3.06。loss 从 6.32 跌到 6.19 就几乎不动了。

这不是"sin 比 tanh 差"这么简单。这个 0.76 里藏着一个更根本的问题。

梯度函数就是学习规律

神经网络的每一步训练都遵循同一个模式:

loss → 计算梯度 → 用梯度更新参数

而"计算梯度"这一步,激活函数的选择是决定性的一环。换个激活函数,就是换一套梯度规则——也就是换一种学习方式。

激活函数 梯度函数 梯度方向 学习特征
tanh tanh’(x) ∈ (0,1] 永远为正,单调衰减 笨但稳——方向从不出错
ReLU 0 或 1 永远非负 莽但快——不衰减,但会死
sin cos(x) ∈ [-1,1] 正负交替 聪明但自毁——自己否定自己

tanh’ 的范围是 (0,1]。值可以很小(接近饱和区),但符号不变。每一步梯度指向的方向,和全局最优方向一致。衰减只是让步子变小,不会让方向反了。

cos(x) 在每一个 π/2 的倍数处翻转符号。前半句学的方向,后半句亲手推翻。

模拟:为什么方向会反转

假设一段 RNN 的前激活值 z 随句子长度逐步偏移(模拟 W·h_{t-1} 的累积):

时间步 z_t tanh’(z) 方向 cos(z) 方向
1 0.2 +0.96 +0.98
2 0.8 +0.50 +0.70
3 1.8 +0.12 −0.23
4 2.5 +0.02 −0.80
5 3.0 +0.005 −0.99

tanh 所有时间步都往同一个方向推——力度越来越小,但方向一致。箱子被慢慢推到目标位置。

sin 在第 3 步翻转了方向。第 1-2 步往东推,第 3-5 步往西推。净位移接近零——箱子在原点附近来回跳舞。

这就是 BLEU=0.76 的根因。不是学不到东西,是学到的东西被自己亲手毁掉了。

好的梯度函数 = 自洽的向量场

把梯度想象成地形图上的箭头。每个参数位置都有一个箭头指向"下山"的方向——这就是向量场。

  • tanh 的向量场:所有箭头指向同一个象限。可以弱,但方向一致。优化器只要跟着走,迟早到谷底。
  • sin 的向量场:箭头来回翻转。优化器往前走两步,发现箭头指向回头了——刚走的路被否定。

“好的梯度函数"的标准不是"梯度够不够大”,而是"梯度方向是否和全局最优方向一致"。

tanh 的梯度小不是缺陷——它宁愿走得慢,也不走错方向。这是工程上的"慢就是快"。

loss 和 BLEU——两把不同的尺子

如果梯度函数的问题是方向,那还有一个更隐蔽的问题:梯度的方向是谁定的?

答案是 loss——CrossEntropy。但 CrossEntropy 追求的是"逐 token 预测精准",而 BLEU 考察的是"整句 n-gram 碰撞率"。两把尺子量的是不同的事。

H=512 跑 20 epoch 的铁证:

epoch loss BLEU
0 5.69 1.84
2 4.76 3.02
10 4.22 1.79
19 4.46 1.35

loss 持续下降(模型自认为越来越好),BLEU 在 epoch 2 达峰后一路衰退(实际翻译质量越来越差)。模型在 teacher forcing 的镜子里觉得自己很美,但别人看到的是东施效颦。

这意味着:即使我们选了一个"方向自洽"的梯度函数(如 tanh),梯度函数追的目标(loss)和最终评价标准(BLEU)之间存在结构性分歧

BLEU 为什么不能直接当 loss

BLEU 的公式里有一个不可微的步骤——argmax。模型输出 logits 后,必须选一个词(argmax),然后在 n-gram 表里计数。argmax 是离散跳变:无论你把 cat 的概率从 0.4 提到 0.49,选出来的词不变——选的都是其他词。直到概率超过 0.51,argmax 的结果才会突然跳变。

所以 BLEU 对每个参数 W 的偏导链中,∂argmax/∂logits 这一项处处为零。整个梯度链直接断了。

全行业用 CrossEntropy 当 loss 不是因为 CE 比 BLEU “好”——是只有 CE 能传梯度。BLEU 更好,但数学不让你用。

Soft BLEU——让 BLEU 听到梯度的声音

如果把 argmax 换成 softmax,把硬 n-gram 计数换成概率加权:

$$\text{SoftBLEU} = \frac{\sum_{w} \min(p(w|ctx), c_{ref}(w))}{\sum_{w} p(w|ctx)}$$

$p(w|ctx)$ 是模型预测 w 的连续概率。整条链可微——梯度可以直接从 BLEU 近似值流回模型参数。

训练时与 CrossEntropy 混合:

$$L_{total} = \lambda \cdot L_{CE} + (1-\lambda) \cdot (1 - \text{SoftBLEU})$$

λ 从 1.0 逐步降到 0.5,让模型从"学 token"平滑过渡到"学翻译"。

四层 Hash 碰撞框架

把翻译过程看作四次 hash 碰撞:

层次 hash 空间 碰撞判定 参与梯度?
1. Embedding E 维向量 token→向量 ✅ CrossEntropy
2. Hidden (RNN) 或 Attention (Transformer) H 维向量 整句→压缩向量 / 选择性碰撞 ✅ CrossEntropy
3. Decoder output V 维 logits 向量→token ✅ CrossEntropy
4. BLEU n-gram 表 输出←→参考 ❌ 不可微

前三次碰撞都参与梯度。第四次——BLEU 碰撞——模型根本听不到

RNN 的悲剧是第 2 层(时间轴压缩破坏信息结构),Transformer 换了第 2 层(Attention 选择性碰撞),但第 4 层的问题——BLEU 不参与梯度——Transformer 也没解决。

实验结果:SoftBLEU 突破 CE 天花板

H=512, E=128, seed=42, batch=64, 在纯 RNN 上对比 CE-only 和 SoftBLEU 混合损失:

$$L_{total} = \lambda \cdot L_{CE} + (1-\lambda) \cdot (1 - \text{SoftBLEU}), \quad \lambda = 0.5$$
epoch CE-only BLEU CE SoftBLEU BLEU mixed loss SoftBLEU 值
0 2.71 6.14 2.49 3.47 0.204
1 2.44 5.38 3.21 3.07 0.234
2 2.26 5.08 1.81 2.92 0.237
3 2.52 4.88 2.88 2.82 0.238
4 2.17 4.74 2.59 2.75 0.239

SoftBLEU best BLEU = 3.21(epoch 1),CE-only best = 3.06(epoch 0)。首次突破纯 CE 天花板。

SoftBLEU 值自身从 0.204 → 0.239 逐步提升——模型在学会优化 BLEU 的 soft 近似。但提升幅度只有 +0.15,随后也陷入了过拟合。

向量化是关键。最初的 Python 循环版 2 小时跑不出一个 epoch(详见 第六篇)。用 scatter_add_ 替代 4 层 Python 循环后,5 epoch 仅需 5 分钟,速度提升 1000 倍。

这条路通向哪里

sin 实验的失败,不是告诉我们"sin 不好"。它告诉我们:梯度函数的选择不是调参——是选择一种学习规律。 一个不能自洽的梯度函数,再大的模型也救不回来。

tanh 的梯度方向自洽,但目标是 loss 而非 BLEU。模型沿着 loss 的梯度方向走得太远,BLEU 反而掉进了过拟合的沟。这条沟就是 Soft BLEU 要填的。

Transformer 换了更聪明的碰撞策略(QK^T = 多头注意力),但梯度函数依然是 CrossEntropy。如果 Soft BLEU 能在 RNN 上证明可行——哪怕只提升 0.5 个 BLEU——那它在 Transformer 上的效果只会更大。

因为方向对了。从"梯度方向自洽"到"梯度目标对齐",这条规律不看模型架构。


May the Code be with us.


License: GPLv3
本文《SameTime》系列采用 GNU 通用公共许可证第三版 (GNU General Public License v3.0) 协议进行开源发布与分发。允许任何形式的复制、修改和分发,但必须继承相同的开源协议,承认在算力宇宙中所有的迭代与变异。