简单得令人尴尬的FSQ:“四舍五入”超越了VQ-VAE

af0942352779b14f2680f61687d23d4b.gif

©PaperWeekly 原创 · 作者 | 苏剑林

单位 | 月之暗面

研究方向 | NLP、神经网络

正如 “XXX is all you need” 一样,有不少论文都以“简单得令人尴尬”命名(An Embarrassingly Simple XXX),但在笔者看来,这些论文大多数都是噱头多于实力。不过,笔者最近阅读到的一篇论文,真的让人不由得发出“简单得令人尴尬”的感叹~

论文的标题是《Finite Scalar Quantization: VQ-VAE Made Simple》[1],顾名思义,这是一篇旨在用 FSQ(Finite Scalar Quantization)简化 VQ-VAE 的工作。随着生成模型、多模态 LLM 的逐渐流行,VQ-VAE 及其后续工作也作为“图像的 Tokenizer” 而“水涨船高”。然而,VQ-VAE 的训练本身也存在一些问题,而 FSQ 这篇论文则声称通过更简单的“四舍五入”就可以达到同样的目的,并且有着效果更好、收敛更快、训练更稳的优点。

FSQ 真有这么神奇?接下来我们一起学习一下。

a2e4e5deec8cb589f5a11a873141d601.png

VQ

首先,我们来了解一下 “VQ”。VQ 全称是 “Vector Quantize”,可以翻译为“向量量子化”或者“向量量化”,是指将无限、连续的编码向量映射为有限、离散的整数数字的一种技术。如果我们将 VQ 应用在自编码器的中间层,那么可以在压缩输入大小的同时,让编码结果成为一个离散的整数序列。

假设自编码器的重构损失能够让我们满意,那么这个整数序列就是原始图像的等价物,所有关于原始图像的操作都可以转化为整数序列上的操作。比如我们想训练图像生成模型,就只需要训练整数序列生成模型,而这跟本文生成等价,所以我们可以用它来训练一个 GPT,模型和流程都跟文本一模一样,训练完成后,我们就可以从 GPT 模型中采样整数序列,然后送到解码器中得到图像,从而完完成了图像生成模型的构建。

说白了,“VQ +自编码器”将任意输入都转化为跟文本一致的整数序列,统一了不同模态数据的输入形式,同时也统一了它们的处理和生成模型。

而这样的一个带有 VQ 功能的自编码器,就被称为 “VQ-VAE”。

dcee4b15b0ac91ae566500be3c619eaa.png

AE

早在四年前的文章《VQ-VAE的简明介绍:量子化自编码器》[2] 中我们就介绍过了 VQ-VAE,尽管被冠以 “VAE(Variational AutoEncoder)”之名,但它实际上跟 VAE 没啥关系,如上一节所说,它只是一个带有 VQ 功能的 AE(AutoEncoder)。

既然是 AE,那么有 encoder 和 decoder,一个普通的 AE 是这样的:

947582d79497b78052abc3c52df0b3bf.png

VQ-VAE 则稍微复杂一些:

c96ce2883fec1b53718b54de05d6360f.png

让我们来逐步解释一下。首先,第一步是相同的,输入 到 encoder 中,输出编码向量 。然而,我们并不是直接将 输入到 decoder 中,而是先维护一个编码向量表 (Codebook),从中选取与 最相近的一个 送入到 decoder 中进行重构 。由于编码表是有限的,所以我们也可以将实际的编码结果理解为一个整数(即与 最相近的 的 ),这就是 VQ-VAE 中 “VQ” 的含义。

当然,实际应用中,为了保证重构的清晰度,encoder 的输出可能是多个向量,每个向量经历同样的量化步骤变成一个整数,所以结果就是一张原本在连续实数空间的图片,被编码 VQ-VAE 编码为了一个整数的序列,这跟文本 Tokenizer 的作用是类似的,所以就有了“图像的 Tokenizer” 的说法。

e3d144aca6a9a8abb866bba93ca77f08.png

梯度

然而,由于整个前向计算的流程中出现了 ,所以梯度无法回传到 encoder,这意味着我们无法优化 encoder。此时常见的手段是 Gumbel Softmax,但 Gumbel Softmax [3] 的效果通常也是次优的,所以作者巧妙地借助了 Straight-Through 为 VQ-VAE 设计了更好的梯度。可以说,这是 VQ-VAE 最精彩的内容,它告诉我们什么 “Attention is all you need” 都是虚的,“Gradient” 才是真正的 “all we need”!

具体来说,VQ-VAE 利用了深度学习框架基本上都自带的 stop_gradient(即公式中的 )函数来自定义梯度,所有经过 的输入,都会保持同样的输出,但梯度被强迫为零。所以对于式(2)中的 ,我们有

fedb3ea1b80ee1b2feb64be10a47ac72.png

这样一来,送入 decoder 的还是量化过后的 ,但优化器求梯度时用的是 ,而 是 encoder 出来的,所以 encoder 也能够被优化了。这个操作就叫做 “Straight-Through Estimator(STE)”,是为神经网络的不可导模块设计梯度的常用技巧之一。

由于基于梯度的优化器依然是当前的主流,所以直接设计梯度往往比设计 loss 更贴近本质,当然通常也更难、更让人由衷地赞叹。

aed1270c519bde629c4c569ce09779ba.png

Loss

不顾,事情还没完,此时有两个问题:1、现在 encoder 是有梯度了,但是编码表 却没了梯度;2、 虽然可以随意定义梯度,但不是胡乱定义一个梯度都可以成功优化模型的。从(3)可以看成,要使它在数学上严格成立,那么唯一的解是 ,这告诉我们如果 STE 是合理的,那么 与 至少是相近的。于是为了梯度的合理性,同时也为了优化编码表,我们还可以补充一项辅助 loss:

dcd070ec470845531eb215aae1bf93d0.png

这样既可以迫使 与 接近,又可以让 也拥有了梯度,一举两得!但细想之下,还是有点美中不足:理论上 encoder 和 decoder 的重构 loss 已经足够优化 了,所以额外引入的一项应该主要用来优化 ,而不应该反过来明显影响 。为此,我们再次利用 技巧,不难证明式(4)的梯度等价于

7168ca76146d3f5cf720ac3dbfd525d4.png

第一项把 的梯度停掉了,剩下 的梯度,第二项则反过来,目前两项是 1:1 的权重求和,意味着两项相同程度地相互影响,而刚才我们说了,这辅助 loss 应该主要用来优化 而不是 ,所以我们引入 ,将辅助 loss 改为

d07aa5ee728be1aea5365698491e6845.png

然后再加到重构 loss 中,就得到了 VQ-VAE 总的 loss 了

除此之外, 的优化还有另外的方案:首先将式(6)的 置零,这样一来 就又没有梯度了;然后我们观察到,VQ-VAE 的 VQ 操作其实跟 K-Means 聚类是有点相似的, 相当于是 个聚类中心。根据我们对 K-Means 的了解,聚类中心等于该类的所有向量的平均,所以 的一种优化方案就是 的滑动平均

25b09298c36b43a06f12f145f4e98ac0.png

这等价于指定使用 SGD 优化 这一项 loss(其他项可以用 Adam 等)。该方案被 VQ-VAE-2 [4] 所使用。

5c76793be4bf7af68e1411f424dd045f.png

FSQ

可能有些读者疑惑,本文的主题不是 FSQ 吗?前面介绍 VQ-VAE 的篇幅是不是有点多了?事实上,由于 FSQ 完全对得起“简单得令人尴尬”这个评价,相比 VQ-VAE,介绍 FSQ 只需要“寥寥几行”,所以 VQ-VAE 不写长点,这篇博客就没几个字了哈~ 当然,将 VQ-VAE 写详细一点,也能让大家更深刻体会到 FSQ 的简单。

准确来说,FSQ 只是用来替代 VQ-VAE 中的 “VQ” 的,它的离散化思路非常非常简单,就是“四舍五入”。首先,假设我们有一个标量 ,我们定义:

e931af0ddb0b6dac5aa13679be0b9aa8.png

这里的 是一个超参数, 就是 sigmoid 函数(原论文用了 ,笔者认为用 sigmoid 更科学), 就是四舍五入为一个整数,所以不难看出 ,即 FSQ 运算将输出限制在了 个整数之中,从而实现了离散化。当然,多数情况下一个标量还不够,对于 ,每一维可以执行 FSQ 运行,于是

a3d52ab05e6636d39316716cbf29f444.png

即 维向量 被离散为 个整数之一。但要注意, 操作同样是没有梯度的(或者说梯度为零),不过经过 VQ-VAE 的铺垫,有些读者可能已经猜到接下来要做什么了:同样是利用 STE 技巧

d15dbd6083cc1f21fc55a3c13b06b1d1.png

即反向传播用 之前的 求梯度。由于 前后本身是数值近似的,所以 FSQ 不需要额外 loss 来迫使近似的出现,也没有额外的编码表需要更新,FSQ 的简洁可见一斑!

0c2361f270d1910c9b2fc9e68beab66a.png

▲ VQ 与 FSQ 对比(来自原论文)

c88fa56af7b69e5200a5156ad03c91f6.png

实验

如果将 VQ 理解为直接将编码向量聚类为 个不同的类别,那么 FSQ 就是将编码向量归纳出 个属性,每个属性划分为了 个等级,从而直接表达了 个不同的整数。当然,从最一般的考虑,每个属性的等级数也可以是不相同的 ,从而不同的组合数为 。

按照原论文的建议 (之前的 LFQ [5] 则相当于 ),所以如果要对齐 VQ-VAE 的编码数量 的话,对于 FSQ 来说应该有 ,即 FSQ 对编码向量的维度 是有限制的(一般就只是个位数),并且通常是远小于 VQ-VAE 的编码维度(一般是三位数),这个直接后果是当编码总数 比较小(从而 也比较小)时,FSQ 的效果通常不如 VQ:

83e85fbb23e2377e5dfed2c145221103.png

▲ 不同编码表大小下 VQ 与 FSQ 的效果差异

从图上可以看到,当编码表大小在 1000 左右时,FSQ 与 VQ 的效果接近;当编码表大小明显超过 1000 时,FSQ 占优;反之当编码表大小明显小于 1000 时,则 VQ 占优,这跟笔者自己的实验结果相近。笔者的参考代码为:

https://github.com/bojone/FSQ

其他实验就是比较常规的证明 FSQ 比 VQ 更优异的各种任务实验了,读者自行阅读原论文就好。

3193b12a40bf12ef2a26a3c0566fbb77.png

思考

从形式上来看,假设 ,那么 VQ 就好比是“一个 类的分类器”,而 FSQ 则是“ 个 级的打分器”,不管是从参数量、几何直观或者表达能力来看,其实 FSQ 都不如 VQ,但为什么 FSQ 有机会取得比 VQ 更好的结果呢?笔者认为有两方面的原因。

第一个原因,是 encoder 和 decoder 太强。虽然 FSQ 本身弱一些,但是 encoder 和 decoder 都足够强了,所以基于神经网络的万能拟合能力假设,FSQ 相对于 VQ 的劣势,完全可以在 encoder 和 decoder 中弥补过来。而在 的设定下,两者的离散化程度都是一样的,也就是说 encoder 与 decoder 之间的“信息瓶颈”是一样的,因此 FSQ 本身的问题就显得微不足道了。

第二个原因,是 VQ 的“队友”(梯度)太弱。VQ 的经典问题是编码表坍缩:当编码表增大时,编码表并没有被充分利用起来,反而由于恶性竞争导致编码表聚集到一块了,经典表现就是一个 5000 的编码表,最终效果还不如 500 的编码表。

归根结底,这是梯度不够合理所致,尽管 VQ 已经巧妙地设计了梯度,但对于 这种硬指派的运算,基于梯度的优化都存在“赢者通吃”问题,这是坍缩的根本原因,而 FSQ 的 运算并不涉及到指派,它是直接取的近似值。当然,往大了讲,其实跟 VQ 类似的 K-Means 经常也有聚类中心坍缩的问题,可见 难优化已经是一个老大难的问题了。因此与其说 FSQ 太强,倒不如说是 VQ 的“队友”太弱。

从以上两点分析可以看出,FSQ 要想超过 VQ,除了编码表要足够大之外,还有 encoder 与 decoder 要足够复杂,但这并非总能满足,比如有些场景下,我们希望模型的每一层输出都被量化,这时候平摊下来的 encoder 和 decoder 未必足够复杂,此时 FSQ 本身的不足就成为效果的瓶颈了。

此外,VQ 之后的向量维度没有变化,可以是任意多维,而 FSQ 之前的向量必须投影到 维,这是很严重的降维,当我们需要用到投影之前的高维度近似向量时,就很难靠 FSQ 之后的低维向量简单恢复过来。

所以,如果单纯是作为“图像的 Tokenzier”,那么 FSQ 或许已经可以取代 VQ,但这并不意味着任意场景下 VQ 都可以被 FSQ 取代。

ff11ba0f490daca3e5dfa23dea75456e.png

小结

本文介绍了 VQ-VAE 的 “VQ” 的一个及其简单的替代品—— FSQ(Finite Scalar Quantization),它直接通过四舍五入来对连续向量进行离散化,并且不需要额外的 loss 进行辅助。实验结果表明,当编码表足够大时,FSQ 比 VQ 更有优势。

outside_default.png

参考文献

outside_default.png

[1] https://arxiv.org/abs/2309.15505

[2] https://kexue.fm/archives/6760

[3] https://kexue.fm/archives/6705

[4] https://arxiv.org/abs/1906.00446

[4] https://arxiv.org/abs/2310.05737

更多阅读

a84518c4a4fadf24689efc815fd782c1.png

5e2e0360a25f7a9431ea176b68e85de8.png

c0eccc02552d501de334b67d37d9742a.png

316eca4564ef5899d5310e305a63db11.gif

#投 稿 通 道#

 让你的文字被更多人看到 

如何才能让更多的优质内容以更短路径到达读者群体,缩短读者寻找优质内容的成本呢?答案就是:你不认识的人。

总有一些你不认识的人,知道你想知道的东西。PaperWeekly 或许可以成为一座桥梁,促使不同背景、不同方向的学者和学术灵感相互碰撞,迸发出更多的可能性。 

PaperWeekly 鼓励高校实验室或个人,在我们的平台上分享各类优质内容,可以是最新论文解读,也可以是学术热点剖析科研心得竞赛经验讲解等。我们的目的只有一个,让知识真正流动起来。

📝 稿件基本要求:

• 文章确系个人原创作品,未曾在公开渠道发表,如为其他平台已发表或待发表的文章,请明确标注 

• 稿件建议以 markdown 格式撰写,文中配图以附件形式发送,要求图片清晰,无版权问题

• PaperWeekly 尊重原作者署名权,并将为每篇被采纳的原创首发稿件,提供业内具有竞争力稿酬,具体依据文章阅读量和文章质量阶梯制结算

📬 投稿通道:

• 投稿邮箱:hr@paperweekly.site 

• 来稿请备注即时联系方式(微信),以便我们在稿件选用的第一时间联系作者

• 您也可以直接添加小编微信(pwbot02)快速投稿,备注:姓名-投稿

2d6d7c1797a162ceef87d3e233c1dd58.png

△长按添加PaperWeekly小编

🔍

现在,在「知乎」也能找到我们了

进入知乎首页搜索「PaperWeekly」

点击「关注」订阅我们的专栏吧

·

·

f0c0b21e6239ba92671b1ae78435e748.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/132676.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Oracle(16)Managing Privileges

目录 一、基础知识 1、Managing Privileges管理权限 2、System Privileges 系统特权 3、System Privileges : Example系统权限:示例 4、Who Can Grant or Revoke? 谁可以授予或撤销权限? 5、The PUBLIC 6、SYSDBA and SYSOPER 7、Revoke with A…

3D模型人物换装系统

3D模型人物换装系统 介绍遇到的问题问题修复具体实现换装1.准备所有模型部位和模型骨骼部位准备材质准备模型根骨骼准备创建文件夹将上述模型拖成预制体创建一个动画状态机给他们附上待机动画 2.脚本驱动Mesh合并代码 UCombineSkinnedMgr.cs创建Mesh以及实例化对象的代码 UChar…

一文带你了解栈的基本概念以及栈的实现

✏️✏️✏️今天给大家分享一下栈的基本概念、线性栈的自定义实现,以及栈的应用题目。 清风的CSDN博客 😛😛😛希望我的文章能对你有所帮助,有不足的地方还请各位看官多多指教,大家一起学习交流&#xff01…

阿里云配置ECS实例的IPv6地址,开通公网IPv6

1.阿里云ECS服务器开通IPv6地址,开通公网IPv6 1.1.官网教程 配置ECS实例的IPv6地址 1.2.相关截图 1 2 3 4 5 6

ElasticSearch中常见的分词器介绍

文章目录 ElasticSearch中常见的分词器介绍前言分词器的作用如何指定分词器分词器的组成分词器的类型标准分词器空格分词器简单分词器关键词分词器停用词分词器IK分词器NGram分词器正则匹配分词器语言分词器自定义分词器 ElasticSearch中常见的分词器介绍 前言 ElasticSearch是…

抖音小程序开发:探索技术创新的代码之旅

随着抖音小程序的兴起,企业纷纷将目光投向这个充满活力的平台。抖音小程序开发不仅为品牌提供了更广泛的曝光机会,更是技术创新的舞台。本文将带领读者深入探索抖音小程序开发的技术要点,探讨如何通过代码实现个性化、高效的小程序。 1. 小…

【2】Gradle-快速入门使用【Gradle项目结构概念】

目录 【2】Gradle-快速入门使用【Gradle项目结构概念】安装本地安装先决条件 官网安装教程 Gradle 快速指南初始化项目查看Gradle的项目结构了解Gradle Wrapper调用Gradle包装器了解Gradle的项目结构了解settings文件了解构建脚本 IDEA中使用Gradle创建一个新项目创建一个Sprin…

【STM32】

STM32 1 CMSIS1.1 概述1.2 CMSIS 应用程序文件描述 2 库2.1 简介2.2 标准外设库(standrd Peripheral Libraries)2.3 HAL 库2.3.1 目录结构2.3.2 HAL库API函数和变量的命名规则2.3.3 HAL库对寄存器位操作的相关宏定义2.3.4 HAL库回调函数2.3.5 HAL使用注意…

6.存储器概述,主存储器

目录 一. 存储系统基本概念 (1)存储系统的层次结构 (2)分类 (3)存储器的性能指标 二. 主存储器的基本组成 三. SRAM和DRAM 四. 只读存储器ROM 五. 提升主存速度的方法 (1)双…

【tg】 5 :线程切换

manager 可以切到 其他类的其他线程去执行。线程切换 先通过 networkmgr 线程 执行 ,但是传递了Manager 自己的线程 进去。在networkmgr 的network线程中,获取到stats数据,然后扔给 manager的线程thread ,去posttask 还行这个task里调用了mediamanager 的perform ,在media…

U盘不可以访问的维护

u盘打不开,可按下图,设置:winR→gpedit.msc;配置“管理模板”→“系统”→“可移动存储访问”→“所有可移动存储类”。 然后,选择“未配置”,如下图

环形处理习题,举例:约瑟夫环,魔方阵

目录 约瑟夫环 魔方阵 约瑟夫环 题目描述:有n 个人围成一圈,顺序排号。从第1个人开始报数从1到3报数凡是报到3 的人退出圈子,问最后留下的是原来的第几号? 环形处理:依次遍历数据集的每个元素(每个人依次报号),直到遍历到最后…

xlua游戏热更新(lua访问C#)

CS.UnityEngine静态方法访问unity虚拟机 创建游戏物体 CS.UnityEngine.GameObject(new by lua);静态属性 CS.UnityEngine.GameObject(new by lua); -- 创建 local camera CS.UnityEngine.GameObject.Find(Main Camera); --查找 camera.name Renamed by Lua;访问组件 loca…

思维模型 斯金纳箱原理

本系列文章 主要是 分享 思维模型,涉及各个领域,重在提升认知。通过合理奖惩,塑造行为,此名为“学习”。 1 斯金纳箱原理的应用 1.1 斯金纳箱在游戏设计中的应用-《糖果传奇》 《糖果传奇》是一款由 King 开发的三消游戏&#x…

C语言--定义一个包含年月日的结构体Day,实现一个函数,根据传入的结构体指针计算,该日期是当年的第几天?

一.题目要求 输入2000年6月5日,输出:这是2000年的第157天。 二.思路分析 首先定义一个包含年月日的结构体 年份:要判断是否是闰年,闰年的二月有29天,平年的二月有28天。 月份:一个月份分大月和小月&#…

leetCode 493 翻转对

给定一个数组 nums &#xff0c;如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。你需要返回给定数组中的重要翻转对的数量。 未完待续~

IDEA的优化配置教程

前言 IDEA 全称 IntelliJ IDEA&#xff0c;是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具&#xff0c;尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以…

Win11专业版安装Docker Desktop,并支持映射主机的gpu

一、Windows环境下安装 Docker 必须满足: 1. 64位Windows 11 Pro(专业版和企业版都可以) 2. Microsoft Hyper-V,Hyper-V是微软的虚拟机,在win11上是自带的,我们只需要启动就可以了 二、下载Docker Desktop安装包 方式一:进入官网下载 https://docs.docker.com/desktop…

基于VSCode + PlatformIO创建运行第一个esp32程序

文章目录 使用VSCode创建项目安装驱动下载驱动安装驱动连接开发板电脑识别开发板 编写程序烧录程序第一步、编译程序第二步、烧录程序第三步、开发板观察效果 原理讲解项目源码 在之前的课程&#xff0c;我们已经介绍了ESP32单片机&#xff0c;并且也已经安装好了开发环境&…

matplotlib 创建图和子图

Matplotlib 可能是 Python 2D-绘图领域使用最广泛的套件。它能让使用者很轻松地将数据图形化&#xff0c;并且提供多样化的输出格式。这里将会探索 matplotlib 的常见用法。 plt方式是先生成了一个画布&#xff0c;然后在这个画布上隐式的生成一个画图区域来进行画图&#xff1…