深入浅出Word2Vec中的分层Softmax:从激活函数到梯度推导

一篇彻底讲透分层Softmax的手算全流程博客,附带完整数学推导


前言:一个参数引发的"血案"

在训练Word2Vec模型时,你可能会在代码中看到这样的参数设置:

python 复制代码
model = Word2Vec(sentences, hs=1, negative=0)  # 使用分层Softmax

或者在深度学习框架中:

python 复制代码
loss='hs'  # 或者 loss='ns'

你或许会产生一连串疑问:

  • 分层Softmax是激活函数吗?还是损失函数?

  • 为什么它被归类在loss参数里?

  • 它的内部到底是怎么工作的?

  • 那个"连乘出来的极小概率"不会导致梯度消失吗?

  • 它名字里带着"Softmax",但Softmax激活函数到底在哪一步用了?

本文将从零开始,手算一个完整的分层Softmax训练流程,把所有这些谜题一次性解开。


一、先搞清楚:分层Softmax到底是什么?

1.1 它不是激活函数,也不是损失函数

名称 是激活函数吗? 是损失函数吗? 内部实际用的激活函数 底层实际用的损失公式
分层Softmax ❌(是一种树结构) ❌(是计算模式) Sigmoid(树上每个节点) 二元交叉熵(路径上累加)
负采样 ❌(是一种采样法) ❌(是计算模式) Sigmoid(二分类) 二元交叉熵

结论 :分层Softmax本质上是一种近似训练算法/策略 ,它通过构建一棵哈夫曼树,将标准Softmax的 O(V) 复杂度降为O(logV)。代码接口之所以叫loss,是因为它决定了"如何计算损失",这是工程上的命名习惯,而非学术上的严格分类。

1.2 标准Softmax在分层Softmax中"消失"了(核心!)

这是绝大多数初学者最迷惑的地方。严格来说,在分层Softmax的整个计算流程中,标准的Softmax激活函数根本就没有被使用 ,它被完全替换掉了

模块 标准 Word2Vec 分层 Softmax(Hierarchical Softmax)
输出层激活函数 Softmax(多分类,算所有词) 已被整体移除,不存在了
替代它的结构 哈夫曼树(二叉树结构)
树上节点的激活函数 无(因为没有树) Sigmoid(每个内部节点做二分类)
最终输出概率 直接由Softmax算出 路径上所有Sigmoid概率连乘

那为什么名字里还带着"Softmax"?

因为它在数学功能上"等效近似"了标准Softmax的结果

  • 标准Softmax:一次性算出所有词的概率,且这些概率总和为 1(全局归一化)。

  • 分层Softmax :虽然没有算所有词,但哈夫曼树的树形结构天生保证了 "从根节点到所有叶子节点的概率总和等于 1"(局部二分类概率相乘,在树结构上天然归一化)。

模仿 了Softmax输出概率分布的特性(总和为1),但内部实现完全抛弃了那个复杂的指数求和运算,改用Sigmoid来节省计算量。学术界为了表明它"起到Softmax的作用但做了层次化加速",所以取名"分层Softmax"。

一句话记住:名字里有"Softmax",但代码里没"Softmax",全是"Sigmoid"!


二、准备阶段:构建哈夫曼树

在训练开始前,模型会根据语料库中所有词的词频构建一棵哈夫曼树:

  • 叶子节点:代表词汇表中的每一个词(共 V 个)。

  • 内部节点:代表一个二分类逻辑回归分类器(共 V−1 个),每个节点有自己的参数向量 θj​。

  • 编码规则:词频越高的词,距离根节点越近(路径越短);词频越低,路径越长。这极大提升了高频词的计算效率。

  • 二进制编码:每个词被赋予唯一的二进制编码(如左子树为0,右子树为1)。

示例 :假设词汇表为 {我, 苹果, 吃, 水果, 好},目标词"水果"的哈夫曼编码为 [1, 0](先走右子树,再走左子树),路径上有2个内部节点。


三、前向传播:如何计算"输出是目标词"的概率?

3.1 核心思想:沿着路径做二分类(注意:用的是Sigmoid,不是Softmax!)

模型通过上下文(或中心词)计算出一个投影向量 ℎh(即词向量)。现在要计算P(目标词∣上下文)。

标准Softmax需要计算V 次,分层Softmax只需要沿着目标词的路径走 L 步(L≈log2​V)。

对于路径上的每个内部节点 j激活函数是Sigmoid,不是Softmax):

  1. 计算得分:

  2. 通过Sigmoid激活:(代表"往右走"的概率)

  3. "往左走"的概率为

最终概率:路径上所有节点"猜对方向"的概率连乘。

3.2 完整数值案例

让我们手算一遍。

预设参数

  • 模型:Skip-Gram(输入"苹果",预测"水果")

  • 词向量维度:d=3

  • 目标词"水果"的编码:[1, 0](先右后左)

  • 学习率:η=0.1

初始参数

  • 输入词向量("苹果"):ℎ=0.2,−0.1,0.4h=0.2,−0.1,0.4

  • 内部节点参数:

    • θ1​=0.5,0.3,−0.2(根节点)

    • θ2​=−0.1,0.4,0.6(左子节点)


第 1 关:根节点 N1​(真实编码 d1​=1,往右)
  • 计算得分:

  • Sigmoid激活(再次强调:这里不是Softmax):

  • 这一关"猜对"(往右)的概率:

第 2 关:节点 N2​(真实编码 d2​=0,往左)
  • 计算得分:

  • Sigmoid激活

  • 这一关"猜对"(往左)的概率:

前向最终结果

总概率

重要澄清 :模型在计算时并没有"做出选择"往左还是往右走,它只是分别计算了每一关"猜对方向"的概率。无论第一关算出的概率是0.1还是0.9,第二关的计算都在真实路径规定的那个节点上进行,不受第一关结果影响。


四、损失函数:从极大似然到最小损失

4.1 极大似然估计的思想

模型算出"水果"的概率是 0.2264。既然"水果"真实出现在语料中,我们就希望这个概率越大越好(最好等于1.0)。

这就是**极大似然估计(MLE)**的核心:让观测到的样本出现的概率最大化。

4.2 为什么要转换成最小损失?

直接最大化 P 面临三个问题:

  1. P 是连乘形式(0.4975 × 0.4551),求导不方便。

  2. 多个小于1的数相乘会得到极小的数值(如 10−610−6),计算机容易下溢。

  3. 工程师习惯用"损失(Loss)"来度量错误,损失越小代表模型越好。

神级转换:最大化P 等价于最小化 −log(P)。

  • 当 P=0.2264 时,−log⁡(0.2264)≈1.485−log(0.2264)≈1.485(损失很大)

  • 当 P=0.9 时,−log⁡(0.9)≈0.105−log(0.9)≈0.105(损失很小)

  • 当 P=1.0 时,−log⁡(1.0)=0−log(1.0)=0(完美)

且 log⁡log 能把连乘变成连加,数值稳定。

4.3 计算损失值

总损失 = 两关损失之和:

  • 第1关损失:

  • 第2关损失:

总损失


五、反向传播:梯度推导(核心!)

5.1 你的担忧:极小概率不会导致梯度消失吗?

这是很多人最困惑的地方。直觉上,总概率 0.22640.2264 这么小,用它去更新权重,梯度应该微乎其微才对。

但事实是:总概率 0.22640.2264 根本不会出现在梯度公式里!

下面我用链式法则完整推导一遍,你就知道那个"极小概率"是怎么被约掉的。


5.2 对第1关参数θ1​ 求导

逐项计算

① 损失对预测概率求导:

② Sigmoid对输入的导数(著名性质 σ′=σ(1−σ)):

③ 得分对参数求导:

三项相乘

见证奇迹:分子分母的 σ1​ 直接约掉!

因为真实编码 d1​=1,所以:

代入数值


5.3 对第2关参数 θ2​ 求导

三项相乘

因为真实编码 d2​=0,所以:

代入数值


5.4 总结:为什么梯度是σ−d?

你的担心 数学真相
总概率 0.2264 太小,梯度会消失 总概率从未进入梯度公式。链式求导时,互相约分,只剩下 σ−d。
为什么公式这么干净? 因为 Sigmoid导数对数导数 恰好互为倒数。这就是BP算法高效的原因------复杂的指数运算在求导时被抵消,只剩下简单的线性误差。

误差信号 σ−d 永远在 -1 到 1 之间,模型不会因为路径长而梯度消失。


六、更新参数:执行一步优化

6.1 更新内部节点参数

更新θ1​

更新 θ2​

6.2 更新输入词向量 ℎh(即"苹果"的词向量)

将路径上所有节点的误差通过各自的θ 传回给 ℎh:

6.3 验证效果

用新参数重新计算第1关:

  • 新得分:

对比 :旧值σ1​=0.4975 → 新值 σ1′​=0.5054,更接近真实编码 1 了!

损失会从 1.485 下降到约 1.38,模型在进步。


七、完整闭环:一句话串起整个流程

步骤 数学操作 数值示例
前向传播 路径概率连乘(节点激活函数是Sigmoid 0.4975×0.4551=0.22640.4975×0.4551=0.2264
极大似然估计 目标:让 P→1.0 希望 0.2264 → 1.0
转最小损失 L=−log(P) −log⁡(0.2264)=1.485−log(0.2264)=1.485
反向求导 链式法则 + Sigmoid约分 δ1​=−0.5025,δ2​=+0.5449
更新参数 梯度下降 ℎθ新=θ−η⋅δ⋅h
循环迭代 重复上述过程 损失持续下降,直至收敛

八、终极对比:标准Softmax vs 分层Softmax

维度 标准 Softmax 分层 Softmax(HS)
输出层是否存在 是,全连接层输出 V 个得分 否,输出层被整棵哈夫曼树替换
使用的激活函数 Softmax(多分类,全局归一化) Sigmoid(每个内部节点做二分类)
计算复杂度 O(V) O(logV)
损失函数形式 多分类交叉熵 路径上二分类交叉熵之和
更新参数范围 更新所有 V 个输出向量 只更新路径上 L 个内部节点的向量
梯度信号强度 依赖全词汇表得分,可能稀疏 每关误差 σ−d,始终在 -1~1 之间,稳定
名字来源 数学函数原名 功能上等效Softmax(输出归一化概率),但实现完全不同

九、常见FAQ

Q1:分层Softmax里到底有没有Softmax激活函数?

A:绝对没有! 它的内部全是Sigmoid。名字里带"Softmax"是因为它起到了"输出归一化概率分布"的作用,而不是因为调用了softmax()这个函数。

Q2:既然没用Softmax,为什么代码参数叫 loss='hs' 而不叫 activation='hs'

A: 因为分层Softmax是一种训练策略/算法 ,它决定了损失如何计算(路径上的二分类交叉熵之和)。在框架设计者看来,用户选它就是为了"用这种方式计算损失并更新参数",所以归类在loss参数下。

Q3:如果路径有10层,连乘出来的概率极小(如 10−510−5),反向传播时梯度真的不会消失吗?

A:绝对不会! 如第五章推导所示,链式求导时,连乘出来的总概率被彻底约掉了。梯度永远等于(σ−d)⋅h,其中σ−d 在 -1 到 1 之间。路径长短不影响梯度幅值,只影响需要累加多少个这样的误差信号。


后记

本文用一个完整的数值案例,手算了分层Softmax从前向传播 → 损失计算 → 反向求导 → 参数更新 的全过程,并专门澄清了**"Softmax激活函数在分层Softmax中完全消失,被Sigmoid树取代"**这一核心误区。

希望你看完后再遇到 loss='hs' 或模型名称里的"Hierarchical Softmax"时,能够会心一笑------因为你已经知道底层发生的每一个数学细节。

相关推荐
郝开1 小时前
Windows 安装 Ostris AI Toolkit
人工智能·windows·toolkit·ai toolkit
张可1 小时前
人类如何利用超长上下文与 AI 协作
人工智能·aigc·ai编程
冷小鱼1 小时前
AI Agent的核心算法:自主学习与反思(Self-Reflection / Critique)
人工智能·学习·算法·自主学习·self-reflection·critique·反思
艾德克斯7 小时前
AI服务器电源POWER SHELF测试破局:HVDC高压架构挑战与ITECH全栈测试方案
人工智能·架构
IvorySQL9 小时前
PG 日报|新增 VACUUM 全维度统计信息补丁迭代
数据库·人工智能·postgresql·ivorysql
QYR_119 小时前
2026年全球氧化锆增韧氧化铝陶瓷市场规模达1.63亿美元,高端制造需求驱动行业持续增长
大数据·人工智能
空中湖10 小时前
AI是如何思考的|AI Agent与AGI:从对话到行动的进阶之路
人工智能·agi
Z-D-K10 小时前
我认为的AGI
人工智能·ai·人机交互·agent·agi
小北的AI科技分享10 小时前
2026视觉检测设备厂家排名:四大主流品牌技术与产品深度测评
人工智能·计算机视觉·视觉检测