一句话

sg[·](stop-gradient):算数时它不存在,传梯度时它是路障——数值原样透传,指令到此为止。

学习 = 每个参数收到一条「往哪挪」的指令

训练时发生的事:

  1. 模型算出一个损失(loss):一个数字,衡量「这次干得多差」
  2. 系统计算每个参数的梯度:「你往这边挪一点点,损失会降低一点点」
  3. 每个参数按自己的梯度挪一小步

就这么循环。梯度 = 挪动指令。一个参数如果拿不到指令,它就原地不动、永远学不到东西。

指令沿着「计算路线」倒着送回

参数不是直接连着损失的,中间隔着一串计算:

梯度传递像追责:损失说「重建得不好」,责任沿计算路线一站一站往回追——追到 decoder、追到 、最后追到 encoder 的参数。每一站收到自己的「往哪挪」。

关键推论

追责路线在哪一断了,断点之前的所有参数都收不到指令,全部原地不动。

sg[·] 就是「追责到此为止」的牌子

  • 前向算数时 就等于 ,数值一模一样
  • 反向追责时:追到 sg 就停,括号里的变量不背锅、不收指令、不动

一行代码就是 .detach():不改任何数值,只切断指令传递路线。

用数字看差别

设 encoder 输出 ,码字 ,损失 ,目标:两者靠拢。

不加 sg——两边都收到指令:

  • :「变大一点,去接近 1.0」
  • :「变小一点,去接近 0.8」
  • 结果:两边互相走,在中间 碰头—— 没到 1.0, 也没留在原地,两头各走一半

加 sg——损失写成

  • 前向数值不变,还是
  • 追责时 被 sg 罩住 → 不收指令,钉死在 1.0
  • 只有 收到指令,一路追到 附近

一句话

不加 sg 是两块磁铁互吸;加 sg 是单方面追。

回到 VQ-VAE 里那句话

为 stop-gradient,「这一项里把我当常数、别给我传梯度」

逐字翻译:

  • 这一项里:sg 只影响它所在的这一个损失项; 在别的损失项里照样正常收指令
  • 把我当常数:「我」= sg 括号里的变量,追责时当它是写死的数字
  • 别给我传梯度:不许把挪动指令送到我这,这项损失训不到我

VQ-VAE 有三个学习者(encoder、decoder、码本),设计者要求每个损失项只训指定的一方

损失项sg 括住谁谁被冻结实际训谁
码本项 encoder只把码字拉向 encoder 输出
commitment 项 码本只把 encoder 输出拉向码字

没有 sg,两项都是「两头互拉、中点会合」,encoder 和码本互相迁就,谁也到不了该在的位置——即笔记里的「同一根弹簧两头互拉」。sg 把弹簧一端焊死,让它只朝一个方向收缩。

追问:后两项能不能合并成一项不带 sg 的?

观察很敏锐:sg 不改数值,所以前向上

而且两个 sg 项在一次反向传播里都生效——码本项推 、commitment 项推 ,一次传播两个都在动。那和不带 sg 直接写 有什么区别?

前向数值等价 ≠ 梯度等价

学习由每个变量分到的梯度份额决定,这才是差别所在。

,一步更新量

写成一项不带 sg——两边对称各拿 1 份:

  • :+0.1 → 0.9,:−0.1 → 0.9,各走一半、中点会合
  • encoder 的输出被码本拖着走了一半

拆成两个 sg 项(

  • 从码本项拿 1 份:+0.1 → 0.9,全速追
  • 从 commitment 项只拿 :−0.025 → 0.975,几乎没动

同样一次反向传播、两个变量都动,但速率和角色不同:一个全速追,一个只让四分之一步。

不对称是刻意设计的主从关系:encoder 输出的方向由重建损失主导(怎么编码图像还原得最好),码本只是全速追赶 encoder 输出的聚类中心。若对称互拉:

  1. encoder 输出被码本拉偏一半,主任务被污染
  2. 更新力度锁死 1:1,没有调节旋钮

拆成两个 sg 项后 独立旋钮——码本追多快(系数 1)与 encoder 被拴多紧(系数 )解耦。极端情况 :commitment 消失,剩下「argmin 定划分 + 码字追质心」,恰好退化成在线版 K-means——对称单项永远做不到这个形态。commitment 只是防止 encoder 输出在胞腔边界来回跳的稳定器,不是学习主力。