0%

如果你玩过图像扩散模型,那你一定不会对 “guidance_scale=7.5” 这个参数感到陌生。这个 “guidance” 指的就是 Classifier-Free Guidance (无需分类器指引,CFG)。忽略其背后的数学原理,CFG 的作用和公式其实非常易懂。假设 $F(x, c)$ 是一个输入为图像 $x$ 和约束 $c$ (比如文本) 的神经网络,则使用 CFG 后的结果 $\tilde{F}(x, c)$按下面的公式计算:

其中,$w$ 是指引强度,在 Stable Diffusion 常取 7.5 的强度参数就是这里的 $w$。直观上看,通过增加 $w$ 的大小,我们可以让 CFG 结果更靠近带约束的输出 $F(x, c)$,更远离无约束的输出 $F(x, \emptyset)$。比如 $w=0$ 时,CFG 结果就是无约束输出 $F(x, \emptyset)$;$w=1$ 时,CFG 结果就是带约束输出 $F(x, c)$。在约束 $c$ 为文本的文生图扩散模型中,我们常常可以通过调整这个参数,来提升图像与输入文本的匹配程度。

在 CFG 中,$F$ 可以是任何一个神经网络。比如,它可以是 DDPM 中预测噪声的网络 $\epsilon$,也可以是流匹配中预测速度的网络 $v$。甚至在一些没有用到扩散模型,但是输入中包含 $c$ 的自回归模型中,我们也可以用到 CFG。

但是,上面的 CFG 公式还有没有改进空间呢?近期,以 NTU S-Lab 博士生范洧辰主导的工作探究了一种专门为流匹配 (Flow Matching) 打造的改进版 CFG—— CFG-Zero*。

这个方法可以提升所有流匹配模型的生成质量,比如文生图模型 SD3, FLUX.1,以及视频生成模型 WAN-2.1。以下是 SD3.5 上的实验结果。

由于 CFG 的原理比较简单,在这篇博文中,我会跳过背景介绍,直接讲解 CFG-Zero* 的方法及主要消融实验结果,并给出我对该论文的看法。

项目网站:https://weichenfan.github.io/webpage-cfg-zero-star/

GitHub: https://github.com/WeichenFan/CFG-Zero-star

CFG-Zero* 方法讲解

CFG-Zero* 由两个相对独立的改进组成:

  1. 给 CFG 公式中多乘上一个系数
  2. 在流匹配采样的早期不更新带噪图像

我们来分别学习这两项改动的原理。

改进版 CFG 公式

作者认为,应该在 CFG 中添加一项能够调节无约束和带约束输出相对大小的缩放系数 $s$。按照前文的 CFG 公式,对于输出 $t$ 时刻速度的流匹配模型 $v_t^{\theta}(x, c)$,改进版 CFG 的输出应为:

也就是说,我们在原来 CFG 公式的 $v_t^{\theta}(x, \emptyset)$ 前多乘了一个标量 $s$。

此外,作者还认为,最优情况下,$s \cdot v_t^{\theta}(x, \emptyset)$ 和 $v_t^{\theta}(x, c)$ 应该尽可能接近。即 $s$ 满足

上述两个假设是怎么得出的?在理解公式的原理之前,我们先看一下这个公式的几何意义,再反过头倒推其原理。

我们不妨将 $v_t^{\theta}(x, \emptyset)$ 和 $v_t^{\theta}(x, c)$ 看成平面上的向量。

给 $v_t^{\theta}(x, \emptyset)$ 添加一个系数,就意味着 $s \cdot v_t^{\theta}(x, \emptyset)$ 可以是其所在直线上的任何一个向量。

什么时候向量 $s \cdot v_t^{\theta}(x, \emptyset)$ 与 $v_t^{\theta}(x, c)$ 距离最短呢?当然是 $s \cdot v_t^{\theta}(x, \emptyset)$ 正好位于 $v_t^{\theta}(x, c)$ 在 $v_t^{\theta}(x, \emptyset)$ 上的投影处。

此时 $s$ 的取值 $s^*$ 可以通过求向量内积再除以原向量的模得到。

了解完改进版 CFG 优化目标的几何意义后,我们再来尝试倒推它的两项设计:1)为什么要乘 $s$;2)为什么要令两个向量的距离最短。

CFG 的目的就是让带约束输出能够尽可能远离无约束输出。标准的 CFG 只告诉我们如何让某一个点 (带约束输出)远离另一个点(无约束输出)。现在,假如无约束输出是一条直线,那我们应该以「最快」的方向让带约束输出远离这条直线。这里的「最快」方向其实就是点对直线做垂线所在方向。

根据上述分析,我们解释了第二项设计:令向量距离最短,本质原理是找到一个让带约束输出远离无约束输出所在直线的「最快」方向。

那么,第一项设计又如何解释呢?为什么可以给无约束输出多乘一个系数呢?这就很难给一个严谨的解释了。我们只能猜测,对于输出速度的流匹配模型来说,无约束输出的速度的方向比速度的大小更加重要。因此,我们不仅要远离此时无约束的输出,还要远离无约束输出所在直线(所有速度方向相同的点)。

关于这两项假设,作者在论文中做了一定的解释。

对于为什么要乘 $s$,作者认为这是受到 classifier guidance 的启发。在 classifier guidance 中,无约束项和梯度项(在 CFG 中梯度项即为带约束输出)之间是有一个自定义参数 $s$ 来调节它们的相对比例的。CFG 本来就是 classifier guidance 的一种特殊形式,自然也可以用上这个设计。这样看来,作者也没能从理论上讲清为什么要乘 $s$,只是参照以往工作,尝试性地使用了这项改动。

对于在求解 $s$ 时为什么要令两个向量的距离最短,作者解释如下:假设最优情况下,ground truth 输出应为 $v_t^{*}(x, c)$,那么我们应该最小化改进版 CFG 输出 $\tilde{v}_t^{\theta}(x, c)$ 与其的距离(参数 $s$ 为变量)。

上式其实描述了两个向量之间的距离。根据三角不等式(三角形两边之和不小于第三边),可以估算其上界:

所以问题变成了令 $\tilde{v}_t^{\theta}(x, c)$ 的模尽可能小,也就是令改进版 CFG 里两个向量距离尽可能短。按照我们上文的几何分析,就可以求出最优的 $s$。

我认为上述分析非常牵强。作者引入 GT 输出 $v_t^{*}(x, c)$,只是为了说明模型输出和 GT 的距离的上界包含 $\tilde{v}_t^{\theta}(x, c)$ 的模。可是,优化上界,真的就能优化这个值本身吗?这无法从理论上说明,只能通过实验来验证。应该令 $\tilde{v}_t^{\theta}(x, c)$ 的模尽可能小,同样是一个实验性的结论。

零初始化流匹配早期输出

在简单的高斯混合任务,即从带噪分布 $\mathcal{N}(0, \mathbf{I})$ 生成目标分布 $\mathcal{N}(\mu, \mathbf{I})$ 上,我们可以直接求出流匹配的闭合解。因此,在这种简单任务上,我们可以训练流匹配网络,观察它和流匹配闭合解的误差,进而研究流匹配网络的学习规律。

通过这种研究方法,作者发现了另一个规律:训练刚开始时,在流匹配的开始阶段(即图像为高斯噪声的阶段),网络预测的速度 $\tilde{v}_0^{\theta}$ 非常不准确,甚至不如直接输出 $0$ 向量。只有得到充分训练后,网络的预测值才比输出 $0$ 更好。

在更大的 ImageNet 数据集上,该结论同样有效。

我们可以猜测,现在大型文生图模型或者视频模型都没有充分收敛。因此,我们可以对这些模型做一个非常简单的改进:跳过刚开始几次去噪步骤(即让流匹配输出 $0$ 向量),让纯高斯噪声从某个中间时间步开始去噪。

实验

把 CFG 公式改进,并且加入零初始化后,我们就得到了最终的 CFG-Zero*。作者用大量定性和定量实验证明了 CFG-Zero* 能够从多个指标上提升各类流匹配模型的效果。网上有许多 AI 应用博主分享了 CFG-Zero 的使用效果 (感兴趣的读者可以去 Bilibili 搜索 CFG Zero Star),发现它确实能够增强图像细节。在社区中,CFG-Zero 也广受好评,被加入了各个流匹配模型代码仓库的官方实现中。

在这篇博文中,我们主要关注 CFG-Zero* 的消融实验,并分析它们是怎么支持前文的方法的。

在消融实验一节中,作者首先展示了不同采样配置下,CFG-Zero 相较普通 CFG 的表现。这里的采样配置包括不同的采样步数、不同的 CFG 强度。从图中可以看出,CFG-Zero 在多数情况下都有优势,但数值上不是很明显。

由于 CFG-Zero* 实际上由两个独立的方法组成,我们需要分别验证它们的有效性。作者同样给出了相关表格。可以看出,这两个方法都有用,但数值提升不是很明显。并且,不用零初始化,只加入缩放参数的提升似乎非常微小。

论文里还有一个应该对刚开始多少步做零初始化的拓展消融实验。这个实验和原理验证关系不大,在此略过。

仅从这些实验结果中,我感觉 CFG-Zero 在这几项指标上的提升非常不明显。我猜想 CFG-Zero 的作用可能更需要通过定性结果来反映,在一些普通 CFG 生成得不理想的例子上,CFG-Zero* 或许能够做好,就像论文开头展示的对比结果一样。

以上是论文里展示出的消融实验。但在我看来,为了支撑方法中的假设,论文还需要提供更多的实验结果:

  • 为了证明优化 $|| \tilde{v}_t^{\theta}(x, c) ||_2^2 + || v_t^{}(x, c)||_2^2$ 这个上界,就能优化最后的误差 $|| \tilde{v}_t^{\theta}(x, c) - v_t^{}(x, c)||_2^2$,应该展示一个折线图,说明随着 $|| \tilde{v}_t^{\theta}(x, c) ||_2^2$ 变小,误差也能变小。另外,此时 $\tilde{v}_t^{\theta}(x, c)$ 其实不仅可以是 CFG 结果,还可以是带约束或者无约束的结果。
  • 为了证明 $s=s^*$,即沿着垂线方向远离无约束输出是最好的,应该展示一个折线图,表示不同 $s$ 取值下和和 GT 误差的变化。
  • 文章多次强调这些优化是针对流匹配的,但是没有从原理上讲为什么这些方法用不到 DDPM 上,也没有展示任何相关实验结果。

如果是作为一篇面向应用的论文,文中的实验结果是非常完备的。但从理论贡献上看,如果能够多展示一些支持理论分析的实验,文章就能给读者带来更多启发。

总结

CFG-Zero 是一种针对流匹配设计的即插即用的改进版 CFG。它由两个独立的部分组成:1)给无约束输出添加缩放系数;2)跳过开始几步流匹配去噪步骤。实验表明,CFG-Zero 无论是在文生图模型还是视频模型上都表现得很好。目前,CFG-Zero* 已经加入了 Diffusers 框架,并被各个主流流匹配官方仓库使用。

CFG-Zero 论文的主要贡献是提出了一种实用的流匹配采样增强方法。对于这类方法,用户会希望方法尽可能不需要训练,且效果尽可能好。CFG-Zero 完美契合了这两个条件。可以预见,CFG-Zero* 将成为未来流匹配模型的标配。

CFG-Zero* 论文的另一大贡献是提供了一种分析流匹配模型的方法,即在简单的高斯分布生成任务上,比较深度学习模型和闭合解之间的误差,探索模型的学习规律。基于这种研究方法,论文发现流匹配模型在去噪初期预测结果极其不准这一现象,比巧妙地用零初始化提升了推理的速度和质量。

尽管论文提出了一种 CFG 改进公式,但这套公式背后的原理还没有完全被该论文揭示。后续可能可以探索的点有:

  • 这套方法是否能用到 DDPM 或者其他加噪公式或者预测目标上?如果不能,是哪一个区别导致的?
  • 当前系数 $s$ 的选择真的是最优的吗?除了给无约束项添加系数,有没有其他的 CFG 改进方法?

在分析这篇文章的理论时,我再次感受到了目前深度学习方法面临的窘境:多数有效的方法难以详尽地解释清其原理,很多重要步骤完全是由实验结果决定的。比如 DDPM 中,前面算变分上界算得好好的,到最后一步算损失函数时,突然就把系数省略了。很多方法看上去有一套严密的数学公式,但实际上其中存在若干难以解释的步骤,而往往这些通过实验得到的步骤才是方法成功的关键。所以,在学习这些看起来很复杂的方法时,除了一股脑地接受论文里的推导外,我们也要仔细审视推导中究竟哪些步骤其实是存在逻辑跳跃,是由实验决定的。

前段时间,多模态模型 GPT-4o 推出了新版图像生成功能,又一次点燃了社区的 AI 创作热情。作为一款多模态模型,GPT-4o 不仅有着不输纯图像生成模型的质量,还能借助其内置的强大文本理解功能,打造各种各样有趣的图像编辑应用。

我一直没有写博文介绍 GPT-4o。这是因为 OpenAI 完全没有给出任何技术细节,只能从用户的生成结果中推测模型原理。没有技术报告可以解读,我还不如多去网上看一看网友们的创意呢。

虽然没有任何新技术可以学习,但我一直在关注相关的新闻报道,并从用户的视角思考 GPT-4o:为什么之前的多模态模型没火,这次 GPT-4o 就火了呢?更强的多模态模型又能为用户带来哪些新应用呢?最终,我想把我思考的内容以杂谈的形式分享出来,而不是严谨地去谈某一项技术。

在这篇文章中,我会从多个角度谈一谈 GPT-4o。首先,我会简单展示一下我自己的使用结果,以说明 GPT-4o 新图像生成的功能;之后,我会先概述所有多模态模型的设计空间,再结合其他社区用户的意见,给出我自己对 GPT-4o 图像生成原理的猜测;接着,我会基于最新发表的 GPT-4o 评测报告 (GPT-ImgEval: A Comprehensive Benchmark for Diagnosing GPT4o in Image Generation,后文简称「评测报告」),谈一谈 GPT-4o 的定量分析结果;最后,我会从科研和创业两个角度分享我对 GPT-4o 及多模态模型的未来展望。

新版 GPT-4o 的内置图像生成

此前,比较火热的图像模型,比如 Stable Diffusion, FLUX.1,都是文生图模型。这些模型往往会用一个预训练的文本编码器解读文本内容,再基于解读好的文本内容,只学习图像生成一种任务。这就好比是一个画家突然闭门不出,不再更新自己的沟通能力,一心只磨练画技。而在多模态模型中,模型的输入输出都会包含文本和图像。也就是说,模型要同时学习图像和文本的理解和生成。这就像是一个积极参与社交的画家。

GPT-4o 此前的图像生成功能由 DALL·E 完成。而 DALL·E 是一个独立的图像生成模型。因此,为了实现图像生成,GPT-4o 会解析用户输入中的图像生成命令,输出文本等约束信息,最后把所有约束信息传给 DALL·E。这样看来,旧版 GPT-4o 和图像生成模型的关系不是特别紧密,二者的功能相对独立。而更新过后,GPT-4o 更多地负责了图像生成的功能,更多图像生成的相关知识存储在了 GPT-4o 的模型权重中。

把大语言模型 (LLM) 的知识引入图像生成模型后,有两个显而易见的好处:

  • 提升文本理解力:将预训练文本编码器升级成 LLM 后,图像生成模型对于语言的理解更强了
  • 支持更广泛的任务:由于现在图像既是输出,又是输入,所以多模态模型天生支持各式各样的图像编辑任务。

其实所有多模态模型都有这两个优势,为什么 GPT-4o 就火了呢?在我看来,GPT-4o 成功的关键并不是技术上或者功能上的突破,而仅仅是模型能力上的提升,尤其是图像质量的提升(我简单尝试了目前可以用的在线 demo,此前多数多模态模型都不支持图像输入+图像输出,哪怕支持,生成图像的质量也很差)。之前多模态模型的生成图像质量一直比不过单纯的文生图扩散模型,但 GPT-4o 这次却大放异彩。这种综合能力上的提升从量变引起了质变,进而点燃了社区的热情。

当图像生成的输入从纯文本拓宽成图像+文本后,模型的应用场景一下就变得丰富了起来。按输入输出的格式分类,图像生成应用大体可以归结为以下几类:

  • 纯文本生图
  • 根据一张图像和编辑命令,局部修改/全局重绘图像
  • 根据多张图像和编辑命令,融合多张图像的内容
  • 利用对话机制,对已经生成好的图像做多次编辑

下面,我将展示我自己用 GPT-4o 在各种输入输出格式下跑出来的一些结果。

首先是最热门的风格迁移功能(由于现在 GPT-4o 加上了版权检测,需要想办法让它画出来的东西「不太有名」)。

然后是输入两张图像,对图像中的元素做组合。

最后是多轮图像对话。

可以看出,GPT-4o 确实能实现不少功能,输出图像的质量乍看之下也不错。但仔细一看的话,图像在内容合理度、内容一致性、任务理解力、图像空间理解力上还有很大的提升空间。我还随便想了几个整活的主意,但都实现得很不理想。

GitHub 上已经有人收集并分享了 GPT-4o 的种种应用场景:https://github.com/jamez-bondos/awesome-gpt4o-images 。在此我就不再赘述了。

新版 GPT-4o 的模型架构

多模态图像生成范式回顾

在分析 GPT-4o 的模型架构前,我们先对多模态模型的生成范式做一个回顾。

最早,在用 Transformer 搭建语言模型时,我们要想办法把自然语言转换成神经网络能够理解的数据。由于自然语言可以看成是 token (词元) 组成的序列,而 token 的取值是有限的,我们可以把每类 token 映射成整数。比如,假设 token 从二十六个字母中取,我们就让 a0b1,以此类推。这种把自然语言变成离散 token 的模块叫做 “tokenizer”,把 token 变回自然语言的模块叫做 “detokenizer”。

要把图像加入 Transformer,其实就是要决定怎么把图像翻译成 token (这里 token 的意义从文本的「词元」拓展成了一小块图像,多个小块图像可以像词元一样拼成完整图像),以及怎么把 token 翻译回图像。恰好,图像自编码器的编码器 (encoder) 和解码器 (decoder) 就能完成上述的翻译过程。注意,文本和 token 的转换一般可以用简单的程序规则来描述,而图像的自编码器需要用可学习的神经网络来实现。我们可以把这种经典的架构简称为 “a” 类架构。

这里我们忽略了自编码器的细节。实际上图像自编码器会压缩图像的边长,且有连续和离散自编码器之分。如果读者对这些内容感到疑惑可以先学习图像自编码器的知识。

把 token 变成图像,其实就是在做图像生成。因此,图像解码器理应是一个生成模型。但如果解码器能力太弱,Transformer 在学习图像生成时就要花更多的精力。因此,为了减少 Transformer 学习图像生成的压力,就要让解码器的图像生成能力变强。传统的解码器是一个直接输出图像的神经网络,我们可以将其改造成扩散模型,即让解码过程变成扩散模型去噪过程,解码网络不直接输出解码图像,而是输出扩散模型里的预测值。我们可以把这种解码器升级成扩散模型的架构称为 “b” 类架构。

在上述讨论中,我们其实忽略了一件事:图中的 Transformer 到底输出了啥?其实,这里的 Transformer 的输出取决于我们对文本/图像生成方式的定义。一般来说,我们用自回归的 (AR) 方式生成文本,那么 Transformer 的输出就是已知前 $i-1$ 个 token 时,第 $i$ 个 token 的概率分布(假如词元是二十六个字母,那么 Transformer 的输出就是 26 个概率,即 token 属于每种字母的概率分布)。如果我们也想用同样的方法生成图像 token,就要让自编码器的编码器输出离散 token(即使用 VQVAE)。在上面两张图中,我们默认文本和图像都采用同样的自回归生成方法。

自回归其实不要求模型的输出是概率分布,进而不见得要用离散自编码器。详见 MAR 论文 (Autoregressive Image Generation without Vector Quantization)

这样看来,我们可以对图像的生成方式做一些改进:我们不用自回归一个一个输出图像 token,而是用一个扩散模型一次性建模所有图像 token 的生成(全序列扩散模型)。我们可以把这个混合范式称为 “c” 类架构。

b 和 c 架构都是把扩散模型引入了经典 a 架构,只是在引入时的出发点不一样。事实上,这两项改动是独立的。我们完全可以把 b, c 改动都用上,得出一个使用了两遍扩散模型的 “d” 架构。硬要说 b, c 两个架构有什么区别的话,它们的区别就在于解码器不会受到文本生成任务的干扰,而如果让输出文本 token 分布的 Transformer 也预测噪声,就会影响文本任务的学习。

让我们来尝试把所有多模态模型的设计空间总结一下。在此之前,我必须吐槽几句。我认为现在多模态模型的设计都大同小异,仅仅是某几个互相独立的模块存在着多种选择。但很多人会把相关概念混淆,比如把「自回归」和「扩散模型」看成两个对立的生成方式(「自回归」和一次性生成所有元素的「全序列扩散模型」才是对立的)。很多论文也会把模块上的小变动渲染成大创新,并混淆各类名词的使用。我希望通过下面的总结能够帮助大家理清多模态模型的设计空间。

  1. 文本生成范式既可以用经典下一个词元自回归,也可以预测下 N 个词元 (deepseek),还可以按乱序预测下 N 个词元 (MaskGIT, Block Diffusion,通常会被叫做「离散扩散模型」)
  2. 图像生成范式可以分成下一个词元自回归 (VQGAN)、乱序预测下 N 个词元 (MaskGIT, MAR)、用扩散模型一次性建模整个图像的分布 (Latent Diffusion Model)
  3. 如果在图像生成时用到了词元,既可以是离散词元 (VQGAN),也可以是连续词元 (MAR),其中连续词元的分布是用像素级扩散模型表示的
  4. 图像的自编码器既可以是 CNN, 也可以是 ViT;自编码器的解码器既可以直接输出图像,也可以用扩散模型间接根据 token 输出图像
  5. 图像自编码器可以先按传统自编码器的重建任务单独训练,并在后续训练中冻结权重,也可以用各种各样更灵活的方式,比如使用预训练图像编码器(如CLIP),或让解码器在中间 Transformer 训练好以后再训练

下面是一些现有多模态模型的分类:

  • 以 Chameleon 为代表的经典多模态模型 (a 类架构) 在文本和图像上都使用下一个 token 预测,使用了 VQVAE 以建模离散图像词元,自编码器是经典 CNN 直接输出图像的架构,先单独训练自编码器再训练多模态 Transformer。
  • Emu1, Emu2(b类)在 a 类架构的基础上,把解码器换成了扩散模型,并把词元分布建模成连续分布(但是,这个连续分布不是通过扩散模型而是通过回归任务建模的,详见论文)。此外,解码器会在中间的 Transformer 训练好之后再微调。
  • Show-o 在 a 类架构的基础上,把图像生成任务从下一 token 预测变成随机预测下 N 个 token。
  • Transfusion(c 类)在 a 类架构的基础上,把图像生成任务从下一 token 预测变成全序列扩散模型。因此,网络中不存在离散图像词元了,VQVAE 被自然地换成了 VAE。

新版 GPT-4o 多模态架构猜测

首先,可以确定 GPT-4o 一定包含了扩散模型。多项事实可以印证这一点。

1
2
3
4
5
6
Fixes:
* model compressed representations
* compose autoregressive prior with a powerful decoder"

On the bottom right of the board, she draws a diagram:
"tokens -> [transformer] -> [diffusion] -> pixels"
  • 根据经验,从图片质量上来看,纯自回归模型做不到这么好

为了进一步验证模型中是否含扩散模型,在 GPT-4o 图像生成评测报告中,作者借助了神经网络的帮助——作者先用一个 VAR 和一个扩散模型分别生成大量图片,以训练一个分辨图像是否来自扩散模型的二分类器,再用这个分类器来区别 GPT-4o 生成的图片。结果显示,GPT-4o 的输出更像是扩散模型生成的。

得知 GPT-4o 大概率含有扩散模型之后,下一步我们需要猜测这个扩散模型放在了模型的哪一部分。根据上一节的讨论,我们能够猜测 GPT-4o 里的扩散模型可能出现在以下几处:

  1. 将解码器完全换成扩散模型
  2. 将图像生成任务从自回归换成全序列扩散模型
  3. 仍然使用自回归生成,但是参考 MAR,把离散的类别分布换成连续的像素级扩散模型建模的分布(扩散模型一次只输出一个像素,而不是一次输出整个图像)

由于 1 是 GPT-4o 此前的技术路线,即直接把 DALL·E 当作图像解码器(生成器),我们可以排除可能 1。

那么,是否 GPT-4o 也像 Transfusion 一样,在图像生成时使用了全序列扩散模型呢?根据用户在 X 上分享的观察 (https://x.com/jie_liu1/status/1905761704195346680 ),使用 GPT-4o 生成图像时,网页会依次传输以下几张图片(网页上的动画是靠前端程序实现的)。可以看出,图像确实是从上到下依次生成的。因此,猜想 2 完全不用自回归也不太可能。

那么,经排除法,只剩下猜想 3 了:GPT-4o 没有使用离散视觉 token,而是在建模单个视觉 token 分布时使用了像素级扩散模型。评测报告的作者也是这样认为的。

我们的探索还没有结束。在思考 GPT-4o 原理时,我突然想到了一个实验:输入一张图片,用 prompt 让模型输出一张一模一样的图片会怎么样?如果让 Gemini 2.5 来做,会得到以下结果:输出图片和输入图片完全不一样。这是因为 Gemini 使用了 Imagen 3 来生成图片(作为解码器),图像生成模型和自回归 Transformer 比较独立,难以维持图片的一致性。

而 GPT-4o 则不同,在相同任务下,GPT-4o 的输出一致性高了很多,起码画风是对的,文字也几乎没变。这也是上述猜想 1(将解码器换成扩散模型)不成立的另一个证明。

上面这样实验其实不止我做过,其他人也尝试过类似的事。比如,在评测报告里,作者在「缺点」一节展示了差不多的例子。

但我的实验还在继续:接着上面的对话,我又再次要求 GPT-4o 画一张一模一样的图片。这次,图像的相似度提高了很多。

为了防止多轮对话导致的测试环境不同,我新建对话并要求 GPT-4o 做了两个重绘任务:第一次输入的是 GPT-4o 自己生成的图像,第二次输入的是一张非常简单的图像。在这两种情况下,GPT-4o 的重绘一致性都很不错。

从这个现象中,能得出哪些结论呢?第一,我认为,GPT-4o 并不是不能做重建任务。GPT-4o 的真正问题是输出图像的多样性较低。这一点可以通过 GPT-4o 能够较好地重建自己生成的图像及简单图像反映。开发者可能用了某些特殊手段,比如在高质量数据集上后训练模型,让 GPT-4o 只能输出高质量图片,代价是牺牲多样性。

第二,GPT-4o 一定使用了某种重建任务,让图像解码器能够重建编码器的输出,即让图像的编码器和解码器构成一个自编码器。如上一节的第 5 个设计维度所示,在多模态模型中,既可以用预训练的自编码器来将图像传入 Transformer,也可以单独用预训练的图像编码器、解码器。用预训练模型的好处在于节约训练成本,比如我们可以复用 CLIP 的图像理解能力和 Stable Diffusion 的图像生成能力。但这样做的弊端在于编码器的「输出语言」和解码器的「输入语言」不是同一种语言,Transformer 无法融会贯通地处理图像数据,也无法对生成的图像做多轮编辑。从 OpenAI 官方透露出来的信息「使用更强大的解码器」(见上文中官方网站里的示例 prompt)中,我猜测 GPT-4o 在编码图像时用到了某种程度的 low-level 编码器(保留图像局部特征,使图像有足够的信息被完美重建),并特意以重建任务重新训练了一个强大的解码器。

我再多猜一点,这个解码器可能是 ViT 架构的。因为在自回归生成的过程中,所有 token 并没有生成完,但我们依然能得到完整的中间图像。在 Transformer 中,如果我们使用经典的 encoder-decoder 架构,无论输入 token 数是多是少,输出的 token 数都是一样多的。

最后再分享一点实验结果。我还测试了 GPT-4o 对同一张图片,不同分辨率下的重绘结果。惊人的是,在输入分辨率是 256x256 左右时,文字的重建结果比 1024x1024 左右的输入还要好。这是不是说明模型原生的分辨率就是 256x256 ? GPT-4o 是否对输入图片都做了预处理?在输出 1024x1024 的图片时是否做了 4 倍超分辨率?

总结一下,我认为 GPT-4o 在处理图像时用到了能够保留图像局部特征的编码器,并以重建任务重新训练了一个解码器。图像以连续 token 的形式传入 Transformer,以经典的下一 token 预测学习自回归图像生成,每个 token 的分布是用像素级扩散模型建模的。模型在高质量图像数据上后训练过,以牺牲多样性为代价,保证输出图像总是高质量的。

评测报告概览

讲完我自己的一些猜想后,我们来看一下 GPT-4o 评测报告 (GPT-ImgEval: A Comprehensive Benchmark for Diagnosing GPT4o in Image Generation) 里的一些内容。

先看一下文生图模型都会测试的 GenEval 指标。这个指标反映了模型在多种任务下的指令跟随度。这些任务包括单个物体、两个物体、指定物体数量、指定颜色、指定位置等。GPT-4o 在这个指标上一骑绝尘,战胜所有纯文生图模型和多模态模型。

再看一下反映图像编辑能力的 Reason-Edit 指标。这个指标要求编辑方法能够理解一些复杂的编辑指令,往往要求模型具有一定的文本推理能力。GPT-4o 同样比之前的方法好了很多。

最后,评测报告还分析了 WISE 指标上的结果。WISE 指标不仅要求模型能够理解文字,还要有一定的常识。比如,如果输入是「章鱼面对危险时的举动」,那么模型应该画出一个正在喷墨的章鱼。

在这个指标上,GPT-4o 也遥遥领先,远强于此前最强的 FLUX.1 模型。

以上指标都不涉及图像质量,评价图像质量的最好指标还是社区反馈。在靠用户打分的文生图模型竞技场里,GPT-4o 目前高居榜首。(https://artificialanalysis.ai/text-to-image/arena?tab=leaderboard)

从上述评测结果中可知,GPT-4o 在几乎所有图像评测指标上都领先于之前的模型。

当然,这些评测指标并不能反映 GPT-4o 在所有任务下的表现。在需要精细编辑的任务中,仍然需要使用专门的图像模型。评测报告分享了 GPT-4o 的一些缺点。其中,a, b, d 都是指输出和输入的一致性不够高,c 说明模型在处理非英文文字时不够好,e 说明 GPT-4o 在笔刷编辑任务上做得还不够好。

当然,我认为这些缺点都不是很严重。我们完全可以通过定制化 GPT-4o 来完成某种特定任务。接下来,我来从科研和应用两个方面谈一谈 GPT-4o 的前景。

多模型图像生成未来畅想

科研方面

很多人认为,新版 GPT-4o 出来了,是不是很多图像编辑工作都没有做下去的必要了?在这类「是否悲观看待新技术取代旧技术」的问题上,我的回答永远是积极的。之前的方法不行了,我们就基于 GPT-4o 开发新编辑方法。新的技术虽然会导致部分旧的方案不再可行,但同时也会带来新的可能。我们应该积极地寻求改变,寻找新的方向。从另一个角度来看,GPT-4o 的效果好,也不代表旧的方法就完全没有可取之处。我认为科研工作并不应该以效果好为唯一目的,更重要的是能否带来科研上的启发。

在讨论 GPT-4o 对科研的影响时,其实我觉得最重要的不是谈论哪些任务会被 GPT-4o 直接解决,而是是否有一个像 GPT-4o 一样强大的开源多模态模型供大家研究。去年 Sora 出来时,大家也担心视频生成任务是否就没有做的价值了。但后来以 CogVideoX 为代表的开源视频模型推出后,还是陆陆续续有不少无需训练的视频生成工作发表的。因此,我们可以根据去年的经验,先积攒一些和多模态模型相关的科研想法。等几个月有开源模型出来后,我们再快速上手做实验。

接下来我再谈一些具体的科研想法。思考新科研方向时,最容易的做法是寻找现有方法的缺陷。在 GPT-4o 图像生成中,最明显的缺陷是无法做精细的(比如像素级)编辑,且模型输入输出的一致性不够高。或许这可以通过加数据来解决,但我觉得这个问题可能涉及视觉神经网络的一些本质缺陷:在下采样过程中,图像的像素级特征不可避免地丢失。我会倾向于从更本质入手,分析这个问题出现在模型的哪一块,并加以解决。

GPT-4o 另一个明显的问题是仅有英文生成得比较好。这应该是在大量数据训练下的结果。其他语言效果不好,仅仅是因为数据不够多。我们能不能单独设计一套文字生成方法,再把文字生成融入到图像中呢?当然,这个问题看起来更偏向工程一些。

还有一些子问题也值得探索。比如,就现在的技术趋势来看,我们无法以相同的方式处理文本和图像,无约束图像生成仍是一个有研究价值的子问题。继续研究扩散模型以及自回归和扩散模型的结合方式,仍能帮助多模态模型的发展。另外,我觉得多模态模型的高性能训练一定是一个有价值的方向。如果以后模型大到只有大公司才能微调,学校实验室都无法做微调实验的话,这个领域一定是发展不下去的。

我还想单独谈谈评测的问题。似乎现在在文本任务和图像理解任务上的指标很多,但是在图像生成方面的指标很少。我觉得这篇 GPT-4o 评测报告里用到的指标还不够全面,多数时候我觉得文生图竞技场里的用户体验排名更有说服力。就像现在大家往往用数学题和编程题来检测大语言模型的思考能力一样,我们也需要提出一些更加全面、公正的指标。另外,就无约束图像生成上,我觉得 ImageNet-256 FID 这项指标已经被大家刷得差不多了,很多时候在这个指标上表现好也不能代表同一个模型在大型文生图任务上就表现好。为了加速技术迭代,我们在简单的无约束图像生成上也需要寻找更有挑战性、不增加太多计算量、不容易被暴力过拟合的指标。

应用方面

在思考一项技术的应用时,我完全不会考虑它能不能发论文,而是会考虑有多少用户喜欢这种应用,以及做出了应用能不能赚钱。就目前 GPT-4o 的表现来看,我认为未来多模态大模型有着广阔的应用空间。

在谈这些之前,我想聊另一件事。从开发难度来看,我会把所有基于大模型的应用分为三类:

  1. 像谷歌、OpenAI 一样从头训练基础模型
  2. 完全不修改模型,仅通过调用 API 和编程来开发应用
  3. 为某一项任务定制数据和方法,微调预训练模型,再开发应用

三类应用的本质区别在于花多少资源去训练模型。如果是要创业的话,在我看来,第一个方向太拥挤了,各个公司的模型同质化严重,且需要耗费大量资源,很没有意思。而剩下两个方向可以集百家之长,让大公司开发出来的大模型给自己打工。这两个方向的核心竞争力都在于应用的创意,以及能否早一步抢占市场,吸引用户,以用户社区为壁垒。如果是我自己的话,考虑到我的技术背景,我会选择赛道不那么拥挤的第三个方向,做一些别人不那么容易做出来,同时对用户来说很有帮助的应用。

谈回大模型应用本身。GPT-4o 能带来哪些新应用呢?

我的第一感是开发更加强大、通用的图像编辑应用,以取代 PhotoShop。目前,PhotoShop 在使用上还是有不少门槛,而 GPT-4o 仅需文本指令即可实现编辑。但 GPT-4o 及现在所有 AI 绘画模型,都在可控性上存在着严重的不足,使得它们在功能上还取代不了 PhotoShop。因此,我们要提升多模态模型的可控性。我的一些初步想法是:像 SAM 一样,先允许用户通过鼠标点击或者文本提示,选中输入图像中的某一物体。之后,我们通过文本提示,指出要修改的物体属性,比如位置、大小、亮度等。模型一次性输出某属性从 0~1 逐渐变化的所有输出,我们最后只需要调整滚动条,从中选择一个效果最好的输出即可。

另外,我们也可以思考多模态模型带来的全新功能。从 GPT-4o 当前的多轮编辑结果中,我们发现 GPT-4o 能一定程度地组合不同图像中的内容,比如实现经典的风格迁移任务,把图像 A 的风格迁移到图像 B 的内容上。这一功能对 2D 游戏美术资源生成来说十分有用。一般 2D 游戏都会进行素材的排列组合,以复用资源,并让玩家产生亲切感。比如在《塞尔达传说》一代中,多数怪物都有红色和蓝色两个版本,两个版本的怪物有着类似的行动模式。如果先画出所有怪物的红色版本,给一组怪物的蓝色版本的示例,GPT-4o 可能就能自动把剩下的怪物也变成蓝色版本。推而广之,不止是颜色,我们可以把角色/怪物的所有组成部分进行拆解,比如拆解成头盔、武器、盾牌、衣服等,先让多模态模型分别生成大量部件,再让模型根据某一种排列组合方案,自动生成最终的结果。在 GPT-4o 之前,或许也有方法能做类似的事,但这些方法难以维持多组输出结果的风格一致性;而多模态模型支持多轮对话,通过参考上下文,模型能够理解多轮编辑都是在做同一件事。可以预见,多模态模型一定是未来图像编辑的研究重点。

从上述这个简单应用中,我们能够提炼出一套设计新应用的思考方式:

  • 从现有方法的角度,思考大模型在哪些地方做得不够好。比如,PhotoShop 可以做精细编辑,而大模型只有粗略的文本提示,缺乏可控性。
  • 从大模型的功能出发,思考能够设计哪些新颖的交互方式,以贴合用户的需求。比如多模态大模型能理解图像内容并对内容进行组合,那么我们就基于已有素材,让模型自动生成素材排列组合后的结果。

欢迎大家从这两个方向思考,探索多模态模型的新的可能。

参考资料

我之前的博文:

速览多模态模型 Transfusion 和 Show-o:用 Transformer + 扩散模型同时处理文本和图像

解读何恺明团队新作:不用向量离散化的自回归图像生成

链接/论文:

  • 评测报告:GPT-ImgEval: A Comprehensive Benchmark for Diagnosing GPT4o in Image Generation

  • GPT-4o 新版图像生成官方公告:https://openai.com/index/introducing-4o-image-generation/

  • Emu 系列多模态模型:https://github.com/baaivision/Emu

  • Chameleon 论文:Chameleon: Mixed-Modal Early-Fusion Foundation Models

  • Tranfusion 论文:Transfusion: Predict the Next Token and Diffuse Images with One Multi-Modal Model

  • Show-o 论文:One Single Transformer to Unify Multimodal Understanding and Generation

  • MaskGIT 论文:MaskGIT: Masked Generative Image Transformer

  • MAR 论文:Autoregressive Image Generation without Vector Quantization

潜扩散模型 (Latent Diffusion Models, LDM) 常因生成过程不稳定而备受诟病:哪怕模型的输入只是受到了一点微小的扰动,模型的最终输出也会截然不同。以视频逐帧风格化任务为例,哪怕对每帧使用同样的 Stable Diffusion ControlNet 图生图编辑方法,同样的随机种子,生成的风格化视频会有明显的闪烁现象。

为了找出这一现象的原因,我们设计了一种配置简单的扩散模型编辑实验:平移扩散模型的初始噪声,观察去噪输出。理想情况下,平移输入噪声,输出图片也应该会平滑地平移。然而,实验结果却显示,直接平移输入噪声会大幅改变输出图片;使用了提升内容一致性的 Cross-frame Attention (CFA) 技术后,虽然图片的主体内容不再变化,可是输出图像在平移时还是有不自然的「抖动」现象。

为什么 LDM 的生成过程这么不稳定呢?为什么 CFA 技术又能提升生成的一致性呢?在我们团队近期发表于 CVPR 2025 的论文 Alias-Free Latent Diffusion Models: sImproving Fractional Shift Equivariance of Diffusion Latent Space 中,我们从平移同变性 (shift equivariance) 的角度分析了 LDM 的生成稳定性,并提出了一种能够提升平移同变性的 Alias-Free LDM (AF-LDM) 模型。我们在无约束人脸生成、视频编辑、超分辨率、法向量估计等多个任务上验证了该模型的有效性。

在这篇博文中,我将系统性地介绍一下这篇论文。我会先简单回顾背景知识,让对信号处理不太熟悉的读者也能读懂本文;再介绍论文的方法、实验、贡献;最后从本工作出发,探讨新的科研方向。

项目网站:https://zhouyifan.net/AF-LDM-Page/

背景知识回顾

本节我会先回顾 LDM,再回顾对平移同变性做了深入研究的 StyleGAN3。由于理解 StyleGAN3 需要了解信号处理的基本概念,我会在尽量不用公式的前提下讲清楚图像频率、混叠等概念。为了简化文字,我会省略理论推导,并使用一些易懂却不见得严谨的叙述。对这些原理感兴趣的读者可以系统性地学习一下 StyleGAN3 论文。

潜扩散模型

扩散模型是一种图像生成模型。生成算法的输入是一张纯噪声图,输出是一张清晰图像。算法执行 $T$ 步,每一步都会调用一个去噪网络来去除图像中的部分噪声。

由于扩散模型运算较慢,我们可以借助一个变分自编码器 (VAE) 来压缩要生成的图像,减少要计算的像素数。简单来讲,VAE 由一个编码器 (encoder) 和一个解码器 (decoder) 组成。编码器负责压缩图像,解码器负责将压缩图像重建。网络的学习目标是让重建图像和输入图像尽可能相似。训练结束后,我们可以单独使用编码器或解码器,实现压缩图像和真实图像之间的相互转换。论文里通常会把压缩图像称为潜图像 (latent image 或者 latent)。

潜扩散模型 (Latent Diffusion Models, LDM) 是一种借助 VAE 加速的两阶段扩散模型。普通的像素级扩散模型会生成一张同样大小的清晰图像。而 LDM 会先生成一张潜图像,再用解码器把潜图像还原成真实图像。我们可以把解码操作简单看成一个特殊的上采样。比如在 Stable Diffusion 中,潜图像的边长被压缩了 8 倍,即解码器会对潜图像上采样 8 倍。

训练 LDM 时,我们需要获取训练图像的潜图像。因此,为了构建训练集,我们会用编码器把训练图像转换为潜空间的图像。

图像的频域表示

在计算机中,图像有很多种表示形式。最常见的形式是空域图像:图像由一个像素矩阵表示,每个像素存储了图像在某个位置的颜色值。此外,还可以把图像表示为频域图像:我们认为图像是由许多不同频率的正弦函数叠加而成的,频域图像的每个像素存储每个频率的正弦函数的振幅和相位。直观来看,图像在空域和频域的可视化结果如下所示:

具体来说,我们可以把空域图像和频域图像都看成二维数组。对于空域图像来说,数组的索引是二维位置坐标,值是此位置的颜色值;对于频域图像来说,数组的索引是横向和纵向的一对频率,值是该频率下的正弦函数的振幅和相位。

为什么我们要大费周章地在频域里表示一张图像呢?这是因为图像的各个频率分量从另一个维度拆分了图像中的信息,这种拆分方式有助于我们分析图像。一张空域图像可以通过 FFT 操作变换成频域图像,而频域图像又可以通过 IFFT 操作变回空域图像。那么,我们可以用如下操作可视化不同频率分量的作用:

  1. 把输入空域图像用 FFT 转换到频域
  2. 对频域图像滤波,分别得到低频、中频、高频三张频域图像
  3. 用 IFFT 在空域中可视化三张频域图像

该操作的结果如下所示。可以看出,图像的低频分量描述了图像的全局结构,而中频分量和高频分量进一步完善了图像的细节。

混叠

假设有一根时针在顺时针匀速旋转。现在,我每秒拍一次照片,一共拍下了时针的三张照片。请问,时针的旋转速度是每秒多少度呢?

从照片中可以看出,时针每秒都旋转了 90 度。因此,你可能会说,时针的旋转速度是 90 度每秒。

下面让我揭晓答案。其实,时针的旋转速度非常非常快。每次拍照时,时针都转了一圈多。也就是说,时针每次旋转了 90 + 360 = 450 度,它的速度是 450 度每秒。如果我们拍照的频率更高的话,将会得到下面的结果。

你可能会觉得这很赖皮:「只给三张照片,谁看得出时针已经多转了一圈啊?」这话确实没错。在相邻两次拍照之间,时针可能已经多转了一圈、两圈……。时针的速度究竟是多少?这其实可以有无数个答案。只有我们强行规定两次拍照之间,时针不能转一圈以上,我们才能得到唯一一种答案。在这种规定之下,如果要表示更快的时针,只能通过增加拍照的频率了。

让我们总结一下从这个示例中学到的规律。在现实中,时针是连续旋转的。而由于存储空间有限,我们往往只能对时针的状态拍照(采样),得到离散的指针状态。采样数相同的情况下,能够表达的信息量是等同的,或者说能够记录的时针最大旋转速度(最大频率)是等同的。要表示更快的时针(更高的频率),就必须要增加采样频率。反过来说,由于采样频率有限,我们有时会错判时针(周期信号)的频率。这种错判现象被称为混叠 (aliasing)。比如把速度 450 度每秒的时针看成 90 度每秒就是一种混叠现象。

类似地,我们可以把图像看成空间中的信号。在现实中,我们眼中看到的图像是处处连续的。为了用计算机显示图像,我们只好在空间中对图像采样,记录有限个位置处的颜色值。如果采样的频率过低,也就是在空间中采样的步长过大,就可能会漏掉某些关键信息,从而造成图像信号的混叠。

而在图像处理中,混叠现象一般出现在高分辨率图像的下采样中。我们来用 matplotlib 中的一个示例复现混叠现象。对于一个包含密集纹理的输入图像,如果我们简单地使用最近邻插值,就会在下采样图像中得到不自然的纹理;而换用抗混叠插值后,混叠现象被大大缓解。

抗混叠的原理是什么呢?我们知道,混叠现象是由于某种采样后,图像的采样率(正比于图像尺寸)过低,导致原图像的高频分量无法正确地在采样后图像中显示。既然如此,我们就先用一个低通滤波器,过滤掉原图像中的高频分量,再做采样。也就是说,抗混叠下采样,等于低通滤波+最近邻下采样。

平移同变性与混叠

通常,我们会为图像处理网络引入一些归纳偏置 (inductive bias),以降低网络的学习难度。CNN (卷积神经网络)就是利用归纳偏置的一个经典示例。由于 CNN 主要由卷积操作构成,而卷积操作在某像素处的输出只取决于邻近像素,因此 CNN 满足平移同变性:平移输入图像,CNN 的输出特征也应该对应地平移。而对于基于 CNN + Softmax 的图像分类网络,按理来说,它满足平移不变性:平移输入图像,输出的类别分布不变。

可是,我们训练出来的 CNN 分类网络真的满足平移不变性吗?在经典论文 Making Convolutional Networks Shift-Invariant Again [2] 中,作者发现,平移输入图像时,普通的 CNN 分类网络的输出概率会发现很大的变化。而这背后的罪魁祸首正是混叠现象。而一个抗混叠的神经网络有着更好的平移不变性。

为什么混叠会和平移不变性关联起来呢?为了方便说明,我们先用公式正式地表示一个简化版 CNN。在一个 CNN 分类网络中,输入 x 会经过若干个由卷积和下采样构成的模块,最后得到二维特征图 f。随后,f 会被展平成一维,并经过 MLP 和 Softmax,输出一个概率分布。

1
2
f = down(conv(x))
prob = softmax(mlp(flatten(f)))

在这个过程中,我们希望 fx 是平移同变的。严谨地说,对于输入 $x$,如果函数 $F$ 满足

其中 $T$ 是平移操作,那么操作 $F$ 是平移同变的。在分类网络中,我们希望分类网络的卷积部分 down(conv(x)) 是平移同变的。一旦这个操作满足平移同变性,后面的 softmax(mlp(flatten(f))) 操作都不会考虑输入像素的先后顺序,整个网络就满足了平移不变性。

那么,这么多步骤中,是哪一步出错了呢?论文指出,通常 CNN 都使用最近邻下采样。这种下采样操作会导致图像出现混叠现象。解决方法也很简单,如上一节所述,我们可以将最近邻下采样换成先低通滤波再最近邻下采样,以缓解网络的混叠。果不其然,换用了抗混叠下采样后,CNN 的平移同变性大大提升,最后输出的概率分布的平移不变性也相应地大大提升。

无混叠的 StyleGAN3

经典图像生成网络 StyleGAN2 可以把一张 $4 \times 4$ 的特征图像不断上采样并转换成一张 $1024 \times 1024$ 的高清图像。由于该生成网络也是一个 CNN,我们希望它满足平移同变性。这样,移动输入特征图像,就能对应地移动输出高清图像。但是,在平移 StyleGAN2 的特征图像时,我们能在输出图像中观察到纹理滞留 (texture sticking) 现象:人物的胡须和头发好像停留在了原地,并没有随着输入移动而移动。而 StyleGAN3 的目标正是解决这一问题。

StyleGAN3 同样指出,混叠现象是造成纹理滞留的主要原因。为了彻底解决这一问题,StyleGAN3 系统性地用信号处理知识分析并改进了 StyleGAN2 的模块。如前文所述,一张图像能够表示的频率分量是有限的。图像尺寸越大,能表示的最大频率越大,图像的细节也就越多。而在对图像重采样(改变图像尺寸)前后,如果我们不能正确地调整图像的最大频率,就有可能产生造成图像内容错乱的混叠现象。所以,要系统性地调整每个模块以防止其出现混叠,其实就是要让每个模块正确地处理图像的最大频率。

经分析,StyleGAN3 主要对 CNN 的以下模块做出了改进以去除混叠:

  • 上/下采样层:早期工作设计抗混叠采样时,只是简单地用同一个模糊卷积来近似低通滤波。而 StyleGAN3 精确地为采样率(边长)不同的特征算出了不同的最大频率,并根据此最大频率设计了不同的低通滤波器,用以修正采样前后的信号频率。
  • 非线性函数(激活函数):StyleGAN3 指出,非线性函数会在信号中引入新的高频分量,从而引起混叠。为此,StyleGAN3 的解决方法是,先临时把信号上采样 $m$ 倍,再让信号过非线性函数,最后将信号下采样 $m$ 倍以使其复原。这是因为,采样率越高,能表示的最大频率越高,引起混叠的可能越小。实验中发现,令 $m=2$ 就有不错的效果。这一模块被 StyleGAN3 称为经过滤的非线性函数(filtered nonlinearities)

除此之外,StyleGAN3 还从抗混叠以外的角度提升了 CNN 的平移同变性:

  • 傅里叶特征输入:为了让生成网络的输入平滑移动,即能够移动非整数个像素,StyleGAN3 将输入的 $4 \times 4$ 离散特征图像修改成了一个在空间上可以任意采样的傅里叶特征。
  • 边缘像素裁剪:此前研究表明,CNN 会从图像边缘处的 0 填充卷积中学习到绝对位置信息,这违反了 CNN 平移同变性的假设。因此,StyleGAN3 在同一尺度的特征图像外都填充了一些额外像素,并在每次上采样后丢弃这些边缘像素。

Alias-free Latent Diffusion Models

设计动机

回顾了潜扩散模型理论基础以及神经网络的平移同变性与混叠的关系后,我们来正式学习 AF-LDM 论文。

如本文开头所述,为了分析 LDM 的生成稳定性为什么那么差,我们用一个更简单的平移任务来定位问题的根源。实验结果显示,LDM 网络的平移同变性也很差。更准确地说,LDM 只对整数平移有较好的同变性。

这里先补充介绍一下整数平移和分数平移。假设我们有一个能把图像 2 倍上采样的平移同变的网络。如果我们对输入移动 $n$ 个像素,那么输出就应该平移 $2n$ 个像素。然而,如果只对输入平移整数个像素,那么输出只能平移偶数个像素。为了平滑地让输出平移 1, 2, 3, … 个像素,我们有时需要令输入图像平移分数个像素。

在分数平移时,我们要通过插值获得图像在非整数位置处的值。后文我们会详细讨论该如何选取插值方法。

回到 LDM 的平移同变性上。实验显示,尽管神经网络主干都是理论上应该平移同变的 CNN,LDM 的 VAE 和去噪 U-Net 都只对整数平移有同变性,而在分数平移时同变性较差。如下图所示,我们测试了潜空间下采样 8 倍的 VAE 和去噪 U-Net 的同变性,每一个平移步数表示平移 1/8 个像素。仅当平移步数是 8 的倍数时,网络的同变性指标(以 PSNR 表示)才比较高。

参考了之前工作后,我们认为 CNN 平移同变性下降是由于混叠现象导致的。如果我们去除了 LDM 的 VAE 和 U-Net 中的混叠,那么 LDM 就会有更好的同变性。总结下来,论文的整套思维链如下:

  1. Stable Diffusion 等 LDM 编辑稳定性差。
  2. 在较简单的输入平移任务上,LDM 的稳定性依然很差。
  3. LDM 的分数平移同变性差。
  4. 混叠现象降低了网络同变性。

为了提升 LDM 的稳定性,我们需要倒着解决问题:

  1. 设计抗混叠模块,去除网络中的混叠。
  2. 验证无混叠 LDM (AF-LDM) 的平移同变性确实有所提升。
  3. 验证提升平移同变性的 LDM 在编辑时稳定性更好。

在这一大节里,我们主要会学习 AF-LDM 论文的方法部分,即如何开发一个无混叠的 LDM。在下一大节里,我们再浏览论文里的实验结果,以验证 AF-LDM 确实能提升 LDM 的稳定性。

引入 StyleGAN3 抗混叠模块

我们希望设计一种无混叠的 LDM。同时,为了尽可能利用预训练 LDM (比如 Stable Diffusion)的权重,我们希望对 LDM 模型结构上的改动尽可能小。因此,我们仅将前文所述的 StyleGAN3 的两个抗混叠模块引入了 LDM 的 VAE 和 U-Net 中:

  • 上/下采样层:让上采样层能够正确处理图像频率。和 StyleGAN3 不同的是,StyleGAN3 使用 Kaiser 卷积来近似低通滤波,而我们参考之前的 AF Convnet [3] 工作,使用了基于 FFT 操作的滤波操作以实现理想滤波(恰到好处地过滤图像中的频率)。
  • 非线性函数:我们也使用了同样的经过滤的非线性函数,以抑制高频分量造成的混叠。

当然,仅做这些改动还不足以实现无混叠的 LDM。还需要解决的问题有:

  • 如何定义分数平移。StyleGAN3 将输入特征图像定义成了傅里叶特征,它可以在任意位置采样,天生支持分数平移。而在 LDM 中,我们往往需要分数平移潜图像。而潜图像是离散的,它在分数平移中的插值方式需要慎重定义。
  • 使用同变损失进一步提升同变性。我们在实验中发现,仅靠抗混叠模块还不足以提升模型的平移同变性,我们通过增加损失函数的方式强制让模型在训练中学习平移同变性。
  • 改进自注意力模块。由于自注意力输入是全局操作,其输出对输入的变化非常敏感,平移同变性差。我们分析并缓解了此问题。

连续潜图像表示

对图像做分数平移,其实就是在图像分数位置处重新采样。比如,假设一维信号原来的采样坐标为 0, 1, 2, …,将其向左平移 0.5 个单位后,采样的坐标变为 0.5, 1.5, 2.5, …。为了求解这些新坐标下的像素值,我们需要使用某种插值方法。

在这个工作中,我们假设 LDM 的 VAE 中的潜图像是一种连续图像,即它可以无损被傅里叶变换转换成连续信号。那么,对这种连续图像做分数平移时,就可以使用理想插值:先用 FFT 把图像转到频域,再将分数平移转换成信号的相位变动,最后用 IFFT 把平移后的信号复原回空域。

值得注意的是,将潜图像假设成连续信号,只是规定了我们在分数平移潜图像时用到的算法。模型在训练时并不知道潜图像满足这种表示。在下一节中,我们会学习如何用损失函数让模型学到这种表示。

同变损失

加入了 StyleGAN3 中的抗混叠模块后,一个随机初始化的 VAE 确实有了更好的同变性。然而,我们发现了一个奇怪的现象:随着 VAE 训练的不断进行,VAE 的同变性开始不断下降(稍后我们会在实验部分看到这些结果)。相同的现象在去噪 U-Net 也可以观察到。我们猜测这是因为我们的网络中一些不够完美的设计让模型始终会产生轻微的混叠现象,而这些混叠现象能够帮助网络的学习。因此,随着训练的进行,网络会倾向于放大混叠现象。这些不完美的设计可能包括:

  • 未使用傅里叶特征:StyleGAN3 将输入定义为连续的傅里叶特征,天生支持连续平移。而我们只是假设 VAE 的潜图像可以由连续信号表示,而没有在训练中让模型感知到这一点。
  • 未使用边缘像素裁剪:边缘像素的卷积会泄露绝对位置信息。我们没有像 StyleGAN3 一样使用这个技术。

StyleGAN3 可以简单看成一个不断上采样低分辨率图像的网络,它在结构设计上有很大的自由。而在由 VAE 和 U-Net 组成的 LDM 里,实现上述两种技术的方法并不是很直观。且由于我们想尽可能减少新设计,并通过微调预训练模型来使之具有同变性,我们没有在 AF-LDM 里加入上述技术。

为了防止 LDM 在训练中同变性下降,我们根据同变性的定义,提出了一个额外的同变损失来规范网络模块的学习。对于不同的模块,我们根据其输入输出设置不同的同变损失。比如,对于 VAE 编码器 $\mathcal{E}$,我们定义以下损失:

其中,$T_\Delta(x)$ 表示将 $x$ 平移 $\Delta$ 个单位,$k$ 表示编码器下采样倍数。由于潜图像 $\mathcal{E}(x)$ 的边长缩小了 $k$ 倍,编码器输入平移 $\Delta$ 对应输出平移 $\Delta/k$。除了直接做差以对齐同变性的定义外,我们还设置了掩码 $M_{\Delta/k}$ 以表示需要计算损失的有效区域。之所以平移时存在「有效区域」,是因为我们将平移定义为裁剪平移 (cropped shift),即最右的像素移出图像边界后,最左侧只会填充全零像素。这些全零像素就属于无效区域,我们应该只在另外的有效区域计算同变损失。

VAE 解码器和 U-Net 的同变损失有着类似的形式。欢迎大家阅读论文以了解细节。

由于在计算同变损失时,我们将平移操作中的插值设置成了理想插值,因此模型能够学到我们在上一节定义的连续潜图像表示。

同变注意力

LDM 的去噪 U-Net 一般会使用自注意力操作:

其中,矩阵 $x \in \mathbb{R}^{HW \times d}$ 是 $H \times W$ 个长度为 $d$ 的特征,三个参数矩阵 $W^Q, W^K, W^V \in \mathbb{R}^{d \times d}$ 为可学习参数。

自注意力会严重降低模型的平移同变性。如文本开头的视频所示,原版 Stable Diffusion 在输入噪声平移后,输出会发生极大的改变。而使用 Cross-frame Attention (CFA) 这种提升自注意力稳定性的操作后,模型的输出才稳定起来。

为什么自注意力的平移同步性较差呢?为什么 CFA 能提升同变性呢?在这篇文章中,我们深入地研究了自注意力的平移同变性。准确来说,我们考虑的是裁剪平移下的同变性。

根据同变性的定义,自注意力满足以下条件时才是同变的:

由于此时 $x \in \mathbb{R}^{HW \times d}$,即 $x$ 不是一张图像,而是一个特征序列,图像里的每一个像素是一个行向量,因此这里的平移操作 $T(x)$ 其实是修改 $x$ 的行向量的排序。我们先记住这个性质。

观察上面等式的右边,我们可以将它看成先有输入 $x$,再做了两次矩阵右乘,再做了一次 Softmax,再做了一次矩阵右乘,最后平移。而矩阵右乘和 Softmax 对行向量都是独立的,所以上面的右式可以化简成 $\text{softmax}(T(x)W^Q(xW^K)^\top)xW^V$。

现在,要让自注意力操作满足平移同变性,只需要满足下面两个式子:

然而,由于此时的平移操作为裁剪平移,上面两个式子无法成立,且随着平移的推进误差会越来越大。通过上述分析,我们得出结论:自注意力对裁剪平移不具有平移同变性。这也符合我们直觉上的理解:自注意力是一种全局操作,一旦输入某处发生了一些微小的改变,输出就会出现较大的变动。

想要重新设计一种同变性更好的自注意力操作并不简单。不过,我们可以采取一种权宜之策来提升现有自注意力的相对平移同变性。假设有参考帧 $x_r$ 和平移后的帧 $x_s$,我们将计算 $x_s$ 时的自注意力定义为同变注意力 (Equivariant Attention)

在实现时,我们先正常算所有注意力特征 $x_r$,并将其缓存下来;而在计算某一层的 $x_s$ 的自注意力时,我们再取出对应的 $x_r$。这样,含 $W^K$ 和 $W^V$ 的那两项不随输入 $x_s$ 而变化,整个注意力操作就是一定是平移同变的了。注意,这里的平移同变是 $x_s$ 相对参考帧 $x_r$ 而言的,我们不能保证其他帧之间,如两个平移后的帧之间,仍然具有平移同变性。

这一操作其实就是之前 Stable Diffusion 视频编辑工作 (如 Text2Video-Zero [4]) 里常用的 CFA 操作。而之前工作并没有对 CFA 做深入分析,只是简单解释成「将参考帧的 K, V 注入其他帧能够提升其他帧的风格一致性」。而在我们这篇论文中,我们发现 CFA 有用的原因是它提升了其他帧对参考帧的平移同变性,这其实是一种同变注意力。为了方便其他研究者的理解,我们在后文还是把这种同变注意力称为 CFA。

为了把 CFA 加入模型中,我们在两处使用了 CFA:

  1. 计算同变损失时
  2. 训练结束后,生成平移后的图片时

方法小结

我们的方法可以用下面的示意图概括。除了像 StyleGAN3 一样加入抗混叠模块外,我们的主要改进是在训练时加入同变损失。而在计算此损失时,需要将平移后图像的自注意力运算改成同变注意力。

实验

做完上述所有改进后,我们将这种同变性更好,无混叠的 LDM 称为 AF-LDM。参照这一简称方式,我们会把改进后的 VAE, Stable Diffusion 简称为 AF-VAE, AF-SD。在实验环节,我们会验证改进后模型的有效性,并展示它的一些应用。

同变性消融实验

根据同变性的定义,我们用先平移、再操作和先操作、再平移的图像之间的重建误差来反映同变性的高低。参考之前的工作,我们用 PSNR 来计算重建误差。这种反映同变性的 PSNR 在论文中被简称成 SPSNR。

我们看一下消融实验的结果。Latent PSNR 表示 U-Net 输出的潜图像的同变性,Image PSNR 表示 U-Net + VAE 解码器的同变性。这个表展示了非常多的结论:

  • 比较第一栏和第二栏的结果,使用 SD VAE 看起来 Latent PSNR 还不错,但由于 VAE 不是同变的,最后的 Image PSNR 还是很差。我们必须把 VAE 和 U-Net 都变成无混叠的。
  • AF-LDM random weights 表示做了模块上的改进 (Ideal Sampling, Filtered Nonlinearity) 之后仅随机初始化参数,不训练模型。将它的同变性和训练过的模型做对比,可以发现模型的同变性在训练中下降。
  • 为了防止同变性在训练中下降,我们需要加入同变损失。
  • 在计算同变损失时,我们需要在自注意力中使用 CFA。
  • 比较不做模块上的改进只加同变损失(倒数第二行)和 AF-LDM (倒数第三行)的结果,可以发现同变损失单独是不起作用的,不仅 FID 差了很多,SPSNR 也变差了。因此,同变损失必须和抗混叠模块一起使用。

上表的结果表明,我们在论文中介绍的所有设计都是有效的。最终的 AF-LDM 可以在几乎不降低生成质量的前提下大幅提升模型同变性。除表格外,我们在项目网站上分享了更直观的消融实验结果。

我们还用一个简单的实验说明,此前模型仅在整数平移(潜图像移动 1 个单位,真实图像移动 8 个单位)时具有较好的同变性。而为了实现平滑的图像编辑,我们最大的收获是提升了模型的分数平移同变性。

仅用光流变换的视频编辑

尽管我们在方法设计中仅考虑了裁剪平移的同变性,但在实验中,我们发现模型对更复杂的不规则变换,如光流变换,也具有更好的同变性。这一性质拓宽了 AF-LDM 的应用场景。为此,我们在 AF-VAE 的潜空间里重新训练了加入了抗混叠模块的 Stable Diffusion 1.5 (AF-SD),并在 AF-SD 上做了光流变换同变性的相关实验。

先简单补充一下光流的相关知识:光流描述了视频两帧之间每个像素的平移。因此,光流是一张 $H \times W \times 2$ 的图片,两个通道分别表示每个像素在横向和纵向上的平移。根据光流,我们能够把视频的前一帧变换到后一帧。当然,这种变换是存在误差的。

在实验中,我们主要观察两张图片在扩散模型的 DDIM 反演 (将真实图片变回纯噪声)和 DDIM 重建阶段对光流变换的同变性(以 SPSNR 来评估)。另外,为了知道在光流变换中「较好的」 SPSNR 应该是多少,我们计算了输入帧之间的光流变换误差,用以提供参考。

结果显示,AF-SD 在反演和生成时的光流变换同变性都有所提升。惊人的是,AF-SD 在生成时的重建效果竟然比直接对输入图像做光流变换还要好。也就是说,在用 AF-SD 时,只要对初始噪声做光流变换,输出视频就会自然做对应的光流变换,且比直接在图像上做更加准确。

受到上述实验结果启发,得益于 AF-SD 在反演和生成过程中的同变性,我们设计了一种非常简单的视频编辑方法:对一个视频的每一帧进行 DDIM 反演和再生成(改变生成时 prompt)。我们做的唯一改动是同时在反演和生成的时候都启用后续帧对第一帧的 CFA。

这种简单的视频编辑方法能够为内容变化不大(相对第一帧而言)的视频输出平滑的编辑结果。由于我们的改进主要体现在分数平移同变性上,输入视频的变化越是细微、平缓,我们的编辑方法的优势就越明显。比如,以下是同一个编辑方法下,SD 和 AF-SD 的结果对比。

小声说一句,由于 AF-SD 需要重新训练所有模型,而我们的数据和计算资源都不够,所以 AF-SD 的生成质量较差。当然,它还是可以完成一些简单的编辑任务的。我们主要用这个模型来验证 AF-LDM 的应用潜力。

其他应用

我们还在其他一些任务上简单验证了 AF-LDM 的通用性。在实现所有任务时,我们都采用了基于扩散模型的方法。欲知细节欢迎阅读原论文和访问项目网站。

通过对输入图像做光流变换和插值,我们能够实现视频插帧。

被平移图像的超分辨率。该应用基于 I2SB 工作 [5]。

被平移图像的法向量估计。该应用基于 ControlNet。

贡献总结与未来展望

读完论文的主要内容后,我们可以总结论文的贡献:

  1. 我们追溯了潜扩散模型编辑不稳定的主要原因:缺乏分数平移同变性。

  2. 我们设计了一种无混叠的潜扩散模型 (AF-LDM),它能够有效提升 LDM 的平移同变性。

  3. 从技术贡献上看,我们提出了简明有效的同变损失,以防止加入了抗混叠模块的模型在训练中损失同变性。此外,我们分析了自注意力运算不够稳定的原因,并通过在同变损失里加入同变注意力来提升模型对参考帧的相对同变性。

  4. 我们在多项任务中展示了 AF-LDM 广泛的应用前景。

其中,我认为第一项贡献是最重要的。潜扩散模型的不稳定性是一个老生常谈的问题,但很少有工作对其做深入分析。而我们提供了一个分析此问题的新视角,并且证明此前常见的 CFA 技术其实和同变性密切相关。第四项贡献也很有趣,我们发现 AF-LDM 也能提升不规则平移的同变性,可能可以拓展到更多任务上。剩下两项技术贡献倒相对来说没有那么重要。

按惯例,我也给论文挑挑刺,列举它的几项不足:

  • 社区用户很多时候会关注一项工作的方法能否直接用起来,能否用预训练模型实现一些好玩的应用。但我们在这个项目中训练的文生图模型 AF-SD 生成质量较差,只能做一些简单的应用。

  • 论文没有进一步分析为什么训练时模型的同变性会逐渐下降,只给了解决方法。

  • 我们并没有完美解决自注意力的低同变性问题,目前的同变注意力必须要给一个参考帧。

总体上来,我个人比较喜欢能够深入解释某一问题的工作,我对这个工作的贡献十分满意。

从这个工作出发,我能想到的未来探索方向有:

  • 帮助视频生成和多视角 3D 生成。平移同变性好,意味着模型能够用同样的形式表达不同位置的同一物体。这一性质在图像生成中难以体现,而在视频生成和多视角 3D 生成中比较重要。
  • 更稳定的视频编辑和图像插值方法。我们在论文仅仅展示了简单的视频编辑和图像插值算法。如果将 AF-SD 和之前的方法结合,再稍微加一点新的设计,就能实现一套不错的方法。当然,由于我们提供的预训练 AF-SD 质量较差,开发图像插值应用更可行一点。
  • 获取像素级精确特征。潜图像的一个像素代表了 $8 \times 8$ 个真实像素。而如本工作所示,目前多数 LDM 的 VAE 存在混叠现象。这会导致我们难以准确获取每个真实像素处的特征,只能大概获取其邻域的特征。而 AF-LDM 可以改善这一点。当然,为了验证 AF-LDM 在这方面的优越性,我们需要找到一个合适的任务去测试。我简单测试了像素级匹配任务 (pixel correspondence),但似乎 AF-LDM 在这个任务上没有明显提升。

除了最直接的应用外,这篇论文还能给我们更宏观的一些启示。比如,现在的神经网络在处理图像时并不是完美的。神经网络的上下采样操作一般是固定的,不管网络的其他参数学得有多么好,不够合理的上下采样总会导致一些问题(比如混叠)。我们不能希望靠加数据来解决一切问题,有时候要从更底层的神经网络设计来解决问题。希望这篇论文能够引发大家在科研上的更多思考。

有读者建议:是否应该把 eqvariance 翻译成「等变」?

我查了一下,按照惯例,CV 里确实翻译成「等变」好一点。我的中文直觉是图像的输出会随输入「共同移动」,而不强调移动距离「相等」(由于输入输出的图像大小可能不一样),在这篇文章的语境下「同变」更好听一点。我在之后的文章里会按照惯例翻译成「等变」。

参考文献

[1] StyleGAN3: Alias-Free Generative Adversarial Networks

[2] Making Convolutional Networks Shift-Invariant Again

[3] AF Convnet: Alias-Free Convnets: Fractional Shift Invariance via Polynomial Activations

[4] Text2Video-Zero: Text-to-Image Diffusion Models are Zero-Shot Video Generators

[5] I2SB: Image-to-Image Schrödinger Bridge

最近,备受瞩目的何恺明团队公布了一篇论文——分形生成模型(Fractal Generative Models)。该论文提出了一种叫做分形生成的全新生成范式。以图像分形生成为例,算法会由粗到精地生成每个像素。

但我仔细读过一遍论文后,发现论文的表述不够准确。这篇文章其实提出了一种基于多叉树的效率更高的视觉 Transformer,以降低普通 Transformer 全像素自注意力的高计算复杂度。这种 Transformer 可以用于任何图像生成任务,甚至是图像生成以外的视觉任务。在这篇博文中,我会按我自己的逻辑介绍论文的核心方法,并简单展示论文中的实验结果。之后,我会批判性分析论文的表述,并探讨一些后续可能的科研方向。

知识准备

视觉 Transformer

Transformer 是一种处理序列数据的神经网络。它的核心是自注意力运算。在这个运算中,序列中的元素会两两交换信息。因此,如果序列的长度是 $N$,则自注意力运算的复杂度是 $O(N^2)$。

具体来说,Transformer 的输入和输出的数据是一个形状为 $N \times C$ 的张量。其中,$N$ 为序列的长度,$C$ 为每个数据向量的长度。实际上,自注意力的运算不仅和 $N$ 有关,也和 $C$ 有关。但由于 $C$ 一般是常数,而复杂度分析一般只关注会不断增长的量,所以我们记自注意力运算关于 $N$ 的复杂度是 $O(N^2)$。

图像可以认为是由像素构成的序列。因此,我们可以用 Transformer 处理图像数据,即使用视觉 Transformer (Vision Transformer)。然而,视觉 Transformer 的一大缺陷是计算复杂度过高。假如图像的边长是 $O(N)$,则一次自注意力的计算复杂度高达 $O(N^4)$。

自回归图像生成

自回归(Autoregressive)是一种直观易懂的序列生成范式:在生成第 $i$ 个元素时,生成模型输入前 $i - 1$ 个元素,输出第 $i$ 个元素。以下是文本自回归生成的一个示例:
|步数 | 输入 | 输出 |
|—-|—-|—-|
1 | (空) | 今
2 | 今 | 天
3 | 今天 | 早
4 | 今天早 | 上

如前所述,图像也可以看成一种由像素构成的序列数据。但在自回归生成图像时,我们要给每个像素编号,表示像素生成的先后顺序。只有定义了先后顺序,才能根据前面的像素生成后面的像素。

给图像编号的方式很多。最直接的想法自然是从左到右、从上到下地编号。其他的编号方式也是可行的。以下是 VQGAN 论文 (Taming Transformers for High-Resolution Image Synthesis) 展示的几种像素编号方案。

何恺明团队之前的论文 MAR (Autoregressive Image Generation without Vector Quantization) 表明,可以完全随机地给像素编号。

提升视觉 Transformer 效率

视觉 Transformer 处理图像的效率本身就偏低,再算上多步图像生成带来的计算量,生成一张图像的速度将慢得惨不忍睹。能否加速视觉 Transformer 的计算效率呢?

在早期的 ViT 工作 (An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale) 中,图像在正式输入 Transformer 前会做一步叫做「图块化」(patchify)的预处理操作。原来 $256 \times 256$ 大小的图像会被下采样 16 倍,转换成 $16 \times 16$ 个图块。输入的元素数变少了,Transformer 的计算时间自然也就降下来了。

后续工作延续了这种压缩输入元素数的思想,但采用了不同的图像压缩方式。VQVAE, VQGAN, Latent Diffusion Model (LDM) 等论文使用自编码器对图像做近乎无损的压缩,再仅用生成模型来生成压缩图像。后续的 DiT (Diffusion Transfomrer, 来自论文 Scalable Diffusion Models with Transformers) 把这种借助自编码器的压缩方案集成到了基于 Transformer 的扩散模型中。

树是一种常见的数据结构,它以从整体到局部的顺序描述某种事物。树可以表示抽象的生活概念或者具体的计算机概念。比如,通常书籍的结构都是树形,我们可以用第一章、第 1 小节、第 1.1 小节这种逐级递进的结构组织书的内容。

和植物里的树不同,数据结构中的树是从上往下生长的。最上面的节点叫做根节点。每个节点相邻的下层节点叫做子节点,相邻的上层节点叫做父节点。没有子节点的节点叫做叶节点。树本身还是一种满足递归性质的数据结构:我们可以忽略某节点的父节点及父节点连接的所有其他节点,从而将其看作是一个新的树。也就是说,每个节点其实都代表一个以这个节点为根的树。这样的树被称为子树

我们也可以用树来组织一个一维数组中的信息。比如,我们把四个数存在树的叶节点里,并让其余每个节点都维护整个子树里所有数据的某种统计信息(这里我们把统计信息定义为求和)。

用这种树表示数据有什么好处呢?由于节点都维护了整个子树的所有信息,在查询整个树里某种统计信息时,我们不必访问每个叶节点,而是可以直接从某个中间节点中直接返回信息,以提升计算效率。比如,在上面的求和树中,我们要查询第 1-3 个元素的总和。我们不必逐个访问三个叶节点,而是可以去访问更少的节点。严格来说,普通查询区间和的复杂度是 $O(N)$,用这种求和树的话查询的复杂度会降为 $O(logN)$

多叉树 Transformer

Transformer 的平方复杂度

我们先以一维数据为例,学习这篇论文是怎么加速 Transformer 的。在正式学习方法之前,我们再次复习一遍为什么 Tranformer 的运算是平方复杂度的。

忽略 Transformer 中注意力运算的实现细节,我们仅关心每个运算的输入输出是什么。在 Transformer 中,由于自注意力运算是一种全局运算,每个元素的输出都取决于其他所有元素。从信息的交换次数来看,如果序列有 $N$ 个元素,每个元素都要至少访问 $N$ 个输入元素的信息,总计算的复杂度是 $O(N \times N)= O(N^2)$。

用多叉树维护区间信息

在前文对树的知识回顾中,我们知道树的某个根节点可以维护整个子树内所有数据的统计信息。那么,我们可不可以用树来减少 Transformer 的信息交换次数呢?比如,我们要让 8 号元素查询 1-4 号元素的信息,我们不去逐个查询叶子节点,而是去查询它们构成的子树的根。

那么,怎么让 Transformer 具有这样的树形结构呢?首先,我们先想办法让某个节点表示一个区间内的所有数据。回忆一下,在 Transformer 中,数据的输入输出形状都是 $N \times C$。这里,我们先假设 $C=1$,即每个数据都是一个实数。这样,我们可以通过通道拼接的方式把多个数据放在一个节点里,并用形状修改操作实现树节点的「分裂」操作。

如下图所示。一开始,根结点处只有一个数据,数据的长度为 $8$,表示八个实数的拼接。树的每一条边表示 reshape 操作,表示把数据从通道维度 $C$ 上拆开,放到序列长度维度 $N$ 上。最后,数据变成了原本的形状 $8 \times 1$。

接着,我们把多个 Transformer 加入树中。我们只允许同一个节点的所有子节点交换信息,而不与同一级的其他子节点交换信息。

要进行多个独立的计算,可以利用数据的 batch 维度:我们把数据的形状从 $N \times C$ 拓展成 $B \times N \times C$,其中 $B$ 表示 batch 的数量。每个 batch 之间的运算是完全独立的。在下图中,一个蓝框表示一个 batch 的运算,不同框内运算是独立的。每次运算中,序列长度 $N$ 始终等于 $2$,这个 $2$ 表示的是一个树节点的子节点数。$C$ 和之前一样,表示数据向量的长度乘以区间里数据的个数。由于我们默认数据向量长度为 $1$,所以这里的 $C$ 还是表示区间里数据的个数。

一个树的节点如果最多有 $k$ 个子节点,则该树被称为 $k$ 叉树。我们这里默认使用的是二叉树。

这样的结构乍看起来很奇怪:这个树的每个节点表示什么意思?为什么只允许同一个节点的子节点之间交换信息?这样真的能加速吗?我们来一一解释这些问题。

多叉树 Tranformer 的原理解释

首先,我们先忽略数据间的信息交换方式,仅考查每个节点的意义。由于更底层(更深层)的节点经过了更多次 Transformer 运算,所以深层的节点拥有的信息更加准确;与之相对,更浅层的节点的信息更加模糊,但概括性更强,能够描述多个节点的统计信息。如下图所示,a, b, c 三个节点的概括性越来越弱,但信息越来越准确。

这个树和我们之前见过的求和树一样,每一个节点都维护了整个子树的统计信息。然而,对于求和操作,我们可以精确地维护一个子树里所有数据的和;但对于结果无法直接统计的 Transformer 运算,节点的信息越往上越模糊。我们在之后的分析中,必须要考虑浅层节点的信息损失。

然后,我们来考查某一个节点能够看到哪些信息。不妨来考查 8 号节点。如下图所示,我们可以找出 8 号节点在整趟运算中「看过」的节点。它看过了总结 1-4 号节点的节点 a,看过了总结 5-6 号节点的节点 b,还看到了它自己以及相邻的 7 号节点。这样看来,8 号节点确实看到了序列里的每一项数据的统计信息。

然而,如前所述,浅层节点的概括性虽强,它的准确性也越低。因此,每个节点在访问其他节点时,对于越邻近的节点,获取的信息越准确;相距越远的节点,获取的信息越模糊。这种设计其实假设了数据具有局部性

  • 数据的输出几乎只取决于局部信息,越远的数据影响越小。

由于图像数据满足局部性,因此用这种结构处理图像时,浅层节点的信息损失是能够接受的。

最后,我们来计算这套模型的计算复杂度,以验证这种模型能够加速 Transformer。我们仅考虑一个元素的运算。为了获取一个元素的输出,我们要做 $K$ 次 Transformer 运算,其中 $K$ 为树的高度。每一轮 Transformer 运算中,数据的序列长度、通道数都是常数。因此,一轮运算的复杂度是 $O(K)$。如何计算树的高度 K 呢?假设我们用的是二叉树,则每加一层,树能表示的数据就乘 2。因此 $K=log_2N$。最终,一轮运算的复杂度是 $O(logN)$。

当我们要处理 $N$ 项数据时,只需要对复杂度乘一个 $N$。最终,这个新网络结构处理整个序列的复杂度为 $O(NlogN)$,这比普通 Transformer 的 $O(N^2)$ 快多了。

小结

为了减少 Transformer 中的信息交换,我们重新定义元素的信息交换方式:元素不会直接看到其他元素的准确信息,而是会看到其他元素的统计信息。我们要求越远的信息概括性越强,但准确度越低。这样,每个元素都只需要访问 $O(logN)$ 个信息节点。

为了产生这种不同层级的信息节点,我们使用了一种树状 Transformer 结构。越浅的节点经过的 Transformer 块越少,信息越模糊。为了在不增加复杂度的前提下进行信息交换,我们只允许子节点之间进行 Transformer 信息交换。由于子节点数是常数,每轮 Transformer 计算的计算量是固定的。

我们完全可以让树的子节点更多,以提升效率。比如,我们可以把二叉树拓展成四叉树。

图像自回归生成实例

这套多叉树 Transformer 可以被广泛地用到各种视觉任务上。我们以论文中的图像自回归生成为例,简单了解一下这种模型的拓展方式。

从一维数据到二维数据

把多叉树 Transformer 用到图像上时,我们只需要修改元素的编号方式,它从逻辑上等价于一维数据。比如,我们可以用如下的四叉树划分二维空间。

应用到自回归图像生成

相比于一般直接预测结果的图像任务,自回归生成有一个额外的要求:后生成的像素无法看到先生成的像素,需要为 Transformer 的自注意力生成一个描述先后顺序的掩码。因此,在把多叉树应用到自回归图像生成时,我们只需要决定每一处 Transformer 的掩码是什么。

决定掩码,其实就是决定元素的先后顺序。现在,Transformer 的运算仅在局部进行,我们其实只要随意为同一组做 Transformer 运算的数据标号。比如,对于二维四叉树,我们可以用下面的顺序对各组元素标号。

从整体上看,我们并没有修改自回归生成的定义。上述过程只是一种自回归生成的特例而已,它等价于某种全局标号的普通自回归生成。

重要实现细节

在附录中,作者提供了两项提升图像自回归生成效果的实现细节。

指引像素 在生成高分辨率图像时,让浅层的 Transformer 输出的颜色值与后续深层的输出颜色值对齐。当然,由于浅层的图块更大,这实际上是让深层输出颜色值的平均值和浅层输出对齐。

临近图块生成 直接用这套 Transformer 生成图像,会导致图块与图块的边缘不一致。为此,作者修改了 Transformer 的输出,让它除了输出当前图块的结果外,还输出上下左右四个相邻图块的结果。

作者在代码中用了一些相对复杂的逻辑,只读论文难以理解方法实现细节。对细节感兴趣的读者欢迎阅读开源代码库里的 models/mar.py 文件。

实验结果

我们简单看一下本文的图像生成结果。由于这种 Transformer 复杂度较低,作者实现了一个像素级生成模型,而没有按照流行的方法使用两阶段潜空间 (latent space) 图像生成。由于模型直接输出的是每个像素取某一颜色值的概率(类似于 PixelCNN),该模型能够准确建模图像的概率。这种概率可以用 NLL (Negative Log-Likelihood) 指标反映,越低越好。

先看最重要的 ImageNet-256 图像生成任务。作者仅比较了其他像素级生成模型。本文的最好的生成模型 FractalMAR 的 FID 并不是很好。现在主流生成模型的在此任务上的 FID 都小于 2。

再看一下 NLL 指标的结果,作者比较了那些能够准确输出图像概率的模型。从最大似然估计的角度看,本文的方法确实很不错。

从支持的任务上看,本文和其他类别约束的自回归模型一样,支持图像内插/外插,且可以用 ImageNet 类别作为指引。

论文表述批判性分析

在初次读文章时,我无论是看示意图还是看公式、文字,都不能理解算法的意思。反复读了几遍后,我才大概明白作者提出的其实是一种多叉树结构。我最后通过阅读代码验证了我的理解是正确的。我认为论文的部分叙述不够严谨,且对于贡献的总结不够准确。具体的分析如下。

是否是分治算法

对于一些朴素算法为 $O(N^2)$ 的序列处理任务,分治 (divide-and- conquer) 算法通过把任务拆成子任务并递归求解子任务的方法,将总算法的复杂度降到 $O(N^2)$ 以下。比如我们熟悉的快速排序就是一种分治算法。论文中反复将分形自回归称为分治算法,但它与传统意义上的分治算法存在较大的差别。

分治算法的核心是「治」这一步。在子问题解决完了之后,我们要用低于 $O(N^2)$ 的时间合并两个子问题的解,产生一个新的当前问题的解。而对于本文介绍的图像生成任务,或者说本文介绍的多叉树 Transformer 模型,模型的输出是一次性决定好的,不存在返回父节点再次修改这一步,自然也没有什么低于 $O(N^2)$ 的合并算法。

如果用分治算法实现图像生成,那么我们应该在不依赖全局信息的前提下生成局部像素,然后在父节点里根据邻近像素的生成结果,修改当前节点里所有像素的值。本文先决定整体再决定局部的方法恰好是反过来的。

与其称为分治算法,本文的算法更加靠近树形动态规划:一个区间内所有元素的统计信息可以被综合考虑,而不必逐个访问每个元素。

分形自回归是否是一种高效的新生成范式

作者声称以自回归模型为分形生成器的分形生成模型相较以往自回归建模方式计算效率更高。但是,在我看来,提升效率的本质原因是 Transformer 模型设计,而非生成范式上的改进。同时,作者的理论分析和方法设计完全对不上,实际上使用的还是传统自回归生成范式。

原文的有关说明如下:假设每个自回归模型的序列长度是一个可控常量 $k$,并让随机变量的总长度 $N=k^n$,其中 $n=\text{log}_k(N)$ 表示我们的分形框架的递归层数。第一个分形框架的自回归层随后将(自回归的)联合概率分布划分成 $k$ 个子集,每个子集包含 $k^{n-1}$ 个变量。

这里 $k$ 可以理解成多叉树的最大子节点数,即 $k$ 叉树。$n$ 表示树的高度。包含 $k^{n-1}$ 个变量表示一个大图块里包含 $k^{n-1}$ 个像素。

正式地,我们将概率分布分解成

这个式子表示,总的概率分布可以拆成若干个条件概率。每个条件概率的已知事件是之前所有大图块里所有像素的概率,条件事件是当前大图块所有像素的概率。

每个含 $k^{n-1}$ 个变量的条件分布 $p(…|…)$ 接着被第二级递归的自回归模型建模。以此类推。通过递归地调用这种分治过程,我们的分形框架可以通过用 $n$ 级自回归模型来高效率地应对 $k^n$ 个变量的联合分布求解问题,每一级只需要处理长度为 $k$ 的序列,且该序列长度可控。

以上是论文原文。我开始读论文的时候没读懂这段话的意思。在我写这篇博文的时候,发现作者的意思其实可以用前文的示意图表示。由于每一个递归级的每个子问题都只需要处理长度为 $k$ 的序列,且这个 $k$ 是常量,所以总的复杂度是 $O(NlogN)$。

但是,作者这段话不是针对 Transformer 来讲的,而是针对概率分布来讲的。用严谨的话描述,作者的意思可能是说,上面的那个概率分布按递归展开,最后只会有 $O(NlogN)$ 项。因此,总的计算复杂度降低了。

计算概率分布的算法大致可以被称为分治算法。但计算概率分布和执行多叉树神经网络是两码事。

这一段推理存在逻辑漏洞:概率公式有 $O(NlogN)$ 项,并不能代表最后的计算效率是 $O(NlogN)$。这中间还欠缺一个前提条件:每一项条件概率的计算时间是常数级。实际上,一轮自回归生成的计算时间取决于神经网络的设计,而跟自回归的建模方式无关。比如,同样是计算经典自回归条件概率 $P(x_i|x_1, …, x_{i-1})$,如果用 Transformer,它的复杂度就是 $O(i^2)$;如果用 CNN,它的复杂度就是 $O(i)$。

另外,上述条件概率的计算和论文方法设计完全无关。论文完全没有提如何用模型估计各个叶节点的条件概率,然后用分治算法合并概率,用一套新损失函数优化联合概率。论文的方法完全是按照经典自回归的定义,用神经网络建模 $P(x_i|x_1, …, x_{i-1})$,再用交叉熵损失优化最新像素的类别分布。只不过这个神经网络是一个使用了多叉树优化的 Transformer。

综上,我认为作者对于方法的分析和论文贡献的描述有误。作者并没有用到论文中提出的递归概率分布,只是用到了一个多叉树 Transformer。论文的核心贡献是加速 Transformer,而不是一种全新的生成范式。

科研方向探讨

正如论文所展示的,本文提出的多叉树 Transformer 可以用到 AR 和 MAR 两种生成范式上。我们也可以考虑把它拓展到其他生成范式,甚至其他视觉任务上。比如,将其拓展到 VAR (Visual Autoregressive Modeling: Scalable Image Generation via Next-Scale Prediction) 上就是一个显而易见的改进方向。 VAR 用同一个 Transformer 处理所有尺度的图像,但这显然不是最优的。结合多叉树 Transformer,我们或许能够让不同层的 Transformer 输出不同尺度下的预测;同时,我们也可以用这种结构提升 VAR 的性能,避免使用全序列注意力。

退一步,从更宏观的角度上看,这篇论文以及 VAR 等论文都是通过利用图像特性来减少计算量。我认为最重要的两个特性有:

  • 局部性:像素受到邻近像素的影响更大,受到远处像素影响更小。
  • 局部连续性:一块像素的颜色、信息是类似的,且变化是平滑的。这使得我们可以用下采样/图块化的大像素块来近乎无损地表示一块像素的统计信息,也允许我们将低尺度信息的线性上采样结果作为当前结果的近似。

基于这两种特性,多数工作采用了如下的优化方案:

  • 降低像素间的依赖关系,较远处的像素与此处的像素可以相互独立地并行生成。
  • 用某种下采样表示一整块像素的信息。
  • 将图像生成建模成从低尺度到高尺度的递进式生成。
  • 使用残差设计,将高尺度图像定义为低尺度图像上采样加上残差图像。

问题的关键就在于,我们应该把哪种优化方案放到算法的哪一步中。这篇论文实际上是把图像的多尺度表示用在了 Transformer 模型上。

实不相瞒,我前段时间也尝试设计了一种使用二维四叉树的自注意力,用于加速所有视觉 Transformer,同时也希望实现一个像素级 Transformer。然而,我发现当下采样的比例超过 2 之后,模型的效果就会出现明显下降。当然,我相信对图像做全注意力是没有必要的,肯定存在着更优的加速方案,这个方向仍有研究价值。

总结

Fractal Generative Models 论文提出了一种用多叉树优化的视觉 Transformer 结构。该 Transformer 将图像表达成多个尺度,浅层信息模糊但概括性强,深层信息准确但概括性弱。每个元素最终仅与 $O(logN)$ 个元素做自注意力,距离越远的元素,使用的信息越浅,访问的节点越少。作者用这个 Transformer 实现了自回归生成任务。特别地,作者实现了一个能够准确计算图像概率的像素级自回归模型。该模型在 ImageNet-64 NLL 指标上超越了以往模型,但在 ImageNet-256 的 FID 指标上不尽如人意。

作者将这套方法称为一种新的生成范式,但我认为作者的表述有误。这种效率更高的视觉 Transformer 可以用在任何视觉任务上,比如 VAR 的生成范式上。我们也可以沿着这篇论文的设计思路,继续思考如何利用图像的自身特性,在不显著降低效果的前提下提升 Transformer 的计算效率。

相关解读文章

解读何恺明团队新作:不用向量离散化的自回归图像生成

NIPS 2024 最佳论文 VAR 深度解读:下一尺度预测为何能超越扩散模型?

大语言模型(LLM)社区近期的一大热点研究课题是推理时扩展 (Inference-time scaling),这一热点也逐渐传播到了以扩散模型技术为代表的图像生成社区。相比以往扩散模型图像编辑方法,这类新方法不是那么追求应用的实时性,反而更关注图像生成质量能否随着推理时间增加而提升。那么,在扩散模型中,该怎么实现推理时扩展呢?

虽然扩散模型天生就有采样步数这一灵活的采样参数——采样步数越长,生成效果越好,但有实验表明,一旦采样步数多于一个值后,再扩增采样步数对生成质量的影响微乎其微。而近期不少工作从另一个角度挖掘了扩散模型的推理时扩展能力:既然扩散模型采样的开始和中间过程都受某些随机噪声影响,那么搜索更好的噪声,是不是就能带来更好的生成结果呢?如下图所示,谷歌近期的一篇论文表明,同样是增加函数调用次数(number of function evaluations, NFE),增加噪声搜索量比增加去噪步数的收益更显著。

在这篇博文中,我会分享我这几天学习的几篇有关扩散模型噪声搜索的论文。具体来说,我会分享 “Golden Noise” [1] 这篇直接输出最优噪声的论文,以及使用噪声搜索的 Inference-Time Scaling for Diffusion Models beyond Scaling Denoising Steps [2] 和 Inference-Time Text-to-Video Alignment with Diffusion Latent Beam Search [3] 论文。我会介绍这些论文的主要设计思想和重要实验结果,并分享一些个人科研观点。由于我也是刚开始学习,暂时不能理解论文里的所有公式,叙述时也可能会有一些纰漏。欢迎大家讨论与补充。

知识回顾

扩散模型简述

扩散模型定义了加噪过程和去噪过程:加噪过程中,训练图像 $x_0$ 会加噪 $T$ 次,最后变成纯高斯噪声 $x_T$;去噪过程中,纯噪声 $x_T$ 会被去噪 $T$ 次,最后变成生成图像 $x_0$ 。去噪过程即图像生成过程。为了让模型学习去噪,我们要通过加噪过程为模型提供训练样本。

对于同一个训练好的去噪模型,我们可以用不同的数学模型建模其去噪过程:使用随机微分方程(SDE)建模时,每一步去噪都会受到某随机噪声的扰动;用常微分方程(ODE)建模时,每一步去噪都是决定性的,或者说无随机性的。从随机性的角度看两种建模方式,SDE 在采样初始噪声 $x_T$ 和中间每一步采样时都有随机性,而 ODE 仅在采样初始噪声时有随机性。

如果我们又想用 ODE 采样,又想在采样时加入随机性,该怎么办呢?这时,我们可以利用扩散模型定义好的加噪过程:比如当前采样到了 $x_t$,我们用加噪过程给它随机加噪两步,得到 $x_{t+2}$,再用 ODE 去噪三步,得到 $x_{t-1}$。这样,我们能在正常去噪的同时,通过加噪过程中的随机噪声引入随机性。

得益于 ODE 的确定性,我们可以使用 ODE 反演 (Inversion) 来把真实图像 $x_0$ 重新变回噪声 $x_T$。相比加噪过程,由于这个反演过程没有任何随机性,我们可以保证再次对 $x_T$ 做 ODE 采样得到的重建图像 $\hat{x}_0$ 和原图像 $x_0$ 非常相似。重建图像的误差大小取决于反演算法的精确度。

在去噪时,我们可以启用 CFG (Classifier-free Guidance) 功能,让输出图像更符合某一约束信息。比如,我们可以用 CFG 让输出图像更符合文本提示词。具体来说,对于一个约束于文本信息 $c$ 的网络 $\epsilon_\theta(x_t, t, c)$,我们让它输出 $\epsilon_\theta(x_t, t, c)$, $\epsilon_\theta(x_t, t, \emptyset)$ 两个值,再令这一轮的最终输出为 $(w - 1)(\epsilon_\theta(x_t, t, c) - \epsilon_\theta(x_t, t, \emptyset)) + \epsilon_\theta(x_t, t, c)$,其中 $w$ 为 CFG 强度。直观上看,当 $w > 1$ 时,这一操作相当于是在原输出 $\epsilon_\theta(x_t, t, c)$ 的基础上,让输出更加远离空约束的输出,更加贴近有约束的输出。

在本文中,除非特别指明,否则默认扩散模型使用 ODE 采样。

搜索算法

搜索算法用于从某数据集合中搜索出最符合要求的数据项。一个最简单的场景是数据库搜索:我们输入某数据,将它与数据库中所有数据逐个比对,以判断它在数据库中是否存在。这个算法可以写成:

1
2
3
4
5
input query
for key in database:
if key == query:
return True
return False

我们可以从两个角度拓展这个算法。第一,我们要搜索的数据集合可能是由某个规则指定的搜索空间,而不是一个具体的数据库里的数据。比如,我们要搜索长度为 10 的由小写字母组成的单词,那么尽管我们没有提前存下所有的单词,我们也知道搜索空间里可能的单词有 $26^{10}$ 种。这种数据的搜索空间往往较大,我们通常不能用算法在较短的时间里搜索出最优解,只能用某些启发式算法搜索出一个较优解。

第二,我们不一定要让搜索成功的条件为「完全匹配」,而是可以设置一个评估函数,并从最终搜索中返回价值最大的那个数据项。

1
2
3
4
5
6
7
8
9
input query, evaluator

best_score = -INF
best_data = None
for data in data_space:
score = evaluator(data)
if score > best_score:
best_data = data
return best_data

根据这个算法结构,在阅读这篇博文时,我们可以从以下几个角度整理每篇论文:

  1. 噪声的搜索空间是什么?(是只有开始的噪声可以修改,还是中间每一步去噪也有随机噪声?)
  2. 搜索算法是什么?
  3. 评估函数是什么?

噪声搜索的早期尝试

如前所述,在扩散模型时,我们会先从高斯分布中采样,得到一个纯噪声图像 $x_T \sim N(0, \textbf{I})$,再逐步对其去噪,得到 $x_{T-\Delta t}, x_{T-2\Delta t}, …$,最后得到清晰图像 $x_0$。在各种图像编辑应用中,由于最开始的纯噪声 $x_T$ 信息过少,我们一般会对中间每一步的带噪图像 $x_{T-i\Delta t}$ 都做编辑。经典的 Classifier Guidance 和 Classifier-free Guidance 都是按照这种方式改进生成图像。

随后,有研究发现,仅优化初始噪声 $x_T$ 也能提升生成质量。较早研究这一性质的论文是 CVPR 2024 的 INITNO [4]。该论文发现,对于同样的提示词,有一些「有效」噪声对应的输出更符合提示词。

基于这个观察,INITNO 使用如下算法搜索最优噪声:

  1. 对于当前某个随机噪声 $x_T$,令它每一轮优化后的值为 $\mu + \sigma x_T$。
  2. 每一轮优化中,将 $\mu + \sigma x_T$ 输入进去噪网络中。根据某个来自于去噪网络的可微评价指标,用梯度下降更新 $\mu$, $\sigma$。
  3. 将最后的噪声 $\mu + \sigma x_T$ 加入噪声池。
  4. 多次采样初始噪声 $x_T$ ,执行上述步骤,以扩充噪声池。
  5. 最后,再次根据评价指标,选出噪声池里的最优噪声。

这篇论文选用的评价指标为去噪网络注意力模块响应程度,这种设计受到了 Attend-and-excite [5] 论文的启发。由于该指标的原理与本文主要介绍的内容无关,此处不对其做深入探讨。我们只需知道 INITNO 使用了一个两步噪声搜索算法:1)优化某个随机噪声;2)多次获取优化后噪声,再次选取一个全局最优噪声。评价噪声好坏的标准是其去噪过程中去噪网络注意力块的中间输出,这是因为这些中间输出与文本匹配度密切相关。

稍晚一些的工作 “Not All Noises Are Created Equally” [6] 也汇报了类似的观察:并非所有噪声本质相同,有些噪声的生成质量更高。这个工作实现了两类搜索算法:

1) 噪声选取:从多个随机噪声中直接选取指标最优的那一个。
2) 噪声优化:从当前噪声开始,根据优化指标用梯度下降优化当前噪声。

从搜索算法上看,这个工作相当于把 INITNO 里的两步搜索算法拆成了两种算法。

这个工作用了一种有趣的评价指标。对于噪声 $x_T$,我们求它先生成,再 DDIM 反演 (DDIM Inversion) 得到重建噪声 $\x’_T$。之后,我们令重建稳定性为 $x_T$ 和 $\x’_T$ 的 cos 相似度。论文指出,重建稳定性更好的噪声具有更高的生成质量。尽管作者并没有严谨地分析这个指标,但实验表明这种指标确实有效。我们在后文中会看到,该团队的后续工作对这个指标的原理进行了一定的解释。

Golden Noise: 直接输出临近最优噪声

“Not All Noises Are Created Equally” 的研究团队后续推出了其改进版 Golden Noise for Diffusion Models: A Learning Framework [1]。这篇论文最大的亮点是不直接使用噪声搜索,而是先用噪声优化方法得到「差噪声-好噪声」这样的数据对作为训练样本,再用一个小网络学习差噪声到好噪声的映射。在阅读这篇论文时,我们主要关注两件事:1)如何为差噪声找到对应的好噪声?2)噪声优化网络有何亮点?

在构建训练样本时,Golden Noise 并没有用噪声搜索技术,而是用规则直接得到更好的噪声。和前作”Not All Noises Are Created Equally” 一样,这篇论文把噪声 $x_T$ 先去噪再 DDIM 反演的重建噪声当成一个更好的噪声。稍有不同的是,这篇文章明确指出,去噪时使用的 CFG 强度大于 1,而反演的时候 CFG 强度等于 1。这样得到的重建噪声 $x’_T$ 能够更加贴近约束文本的描述。

作者用公式推导介绍了这项技术的原理。通过合并去噪和 DDIM 反演的公式,我们能够用下面的式子描述原噪声 $x_T$ 和反演噪声 $x’_T$ 的关系。我们可以忽略多数参数,只看后面 $\epsilon$ 做差的那一项。前面的系数 $w_l - w_w$ 表示去噪 CFG 强度 $w_l$ 减去重建 CFG 强度 $w_w$。可以发现,这个式子和 CFG 类似,也是写成了带约束输出 $\epsilon(\cdot, c)$ 减去无约束输出 $\epsilon(\cdot, \emptyset)$ 的形式。那么,它的作用也和 CFG 类似,可以把噪声「推向」更靠近文本描述的方向。

该团队的同期工作 Zigzag Diffusion Sampling [7] 用了更多示意图来直观解释这项技术的原理。从语义贴合度的角度看,使用 CFG > 1 的强度做去噪会提升噪声的语义贴合度,而使用 CFG = 1 的强度做反演几乎不会改变噪声的语义贴合度。因此,只要反复执行去噪-反演过程,就能让噪声的语义贴合度逐渐提升。

先去噪再反演只能提升噪声的语义贴合度,而不能保证图像的质量提升。因此,在构建好噪声时,Golden Noise 还使用了一种拒绝采样策略,以确保找到的「好噪声」确实能生成高质量图片。具体来说,Golden Noise 用一个基于神经网络的图像质量评估器和原噪声 $x_T$ 对应图像 $x_0$ 和新噪声 $x’_T$ 对应图像 $x’_0$ 分布打分。只有 $x’_0$ 的分数比 $x_0$ 高出一点时,才把 $(x_T, x’_T)$ 加入训练集。

我们再简单看一下噪声预测网络的设计。这个网络会根据输入的差噪声 $x_T$ 输出语义贴合度高且质量更高的噪声 $x’_T$。作者发现,每对噪声的 SVD (奇异值分解) 结果非常相似。因此,Golden Noise 设计了一个两路网络:一个网络根据输入噪声,输出预测噪声的大致 SVD,它决定了最终噪声的主要内容;另一个网络根据输入噪声和文本嵌入,输出一个残差噪声,用于补充细节。

一套完整的噪声搜索基础框架

2025 年 1 月,谷歌谢赛宁团队公布了论文 Inference-Time Scaling for Diffusion Models beyond Scaling Denoising Steps。这篇论文依然是从搜索更优的初始噪声或中间噪声的角度来提升扩散模型采样质量。但相比之前的工作,这个工作作出了两大贡献:

  • 从推理时扩展 (Inference-time scaling) 的角度认识噪声搜索,研究采样计算量与图像指标的关系。
  • 提出了一套系统性的噪声搜索框架,指明了框架的两个设计维度:评估图像质量的评估函数和搜索时使用的算法。这篇博文对搜索算法的建模方式主要参考自这一框架。

下图是论文中的第一张示意图。彩色的线是增加噪声搜索量的结果,灰色的线是增加采样步数的结果。可以看出,不管是哪个评价指标,增加噪声搜索量对图像质量的提升都更加明显。

这篇论文的叙述逻辑十分流畅。让我们按照论文的叙述顺序,看看这个工作用到了哪些搜索算法与图像质量评价指标。

早期验证

为了简单验证是否可以通过增加噪声搜索量来提升图像质量,这篇论文开展了一个简单的验证实验:使用在 ImageNet-256 上训练的 SiT [8] 模型,使用较简单的搜索算法和最直接的评估指标,观察模型按类别生成(额外输入一个类别标签)质量随搜索量的变化趋势。

具体来说,在搜索算法上,作者采用了「随机搜索」(random research) 算法:和 “Not All Noises Are Created Equally” 中的噪声选取一样,算法随机采样大量噪声,然后选择效果最好的那个噪声。在评估函数上,作者采用了简单粗暴的「面向答案」策略:如果最终的测试指标是 Inception Score,就选取 Inception V3 网络的对输入类别的分类概率最大的噪声;同理,如果最终测试指标是 FID,就贪心地选取让 FID 尽可能小的噪声。由于实际应用中,我们不能用这种「作弊」的方法提前知道评测指标,因此这种评估函数仅供概念验证使用。

以下是早期验证实验的结果。由图可知,提升计算量能够明显提升图像质量。比如,左侧 FID 结果中,对于 CFG=1 的曲线,不使用噪声搜索(NFE 最少的那个点)的结果很差;然而,增加噪声搜索量后,CFG=1 的 FID 分数甚至能超过 CFG=1.5 的。

之后,论文测试了两个更加合理的评估函数:使用 CLIP 和 DINO 分类器。和前面的 Inception V3 网络一样,评估函数会找出分类概率最大的输出。

用 CLIP 做类别分类器时,需要把类别转换成文本。

使用更合理的评估函数后,依然能观察到图像 Inception Score 随搜索量增长的现象。

其他搜索算法

随机搜索算法虽然表现不错,但这个算法会导致验证器欺骗 (verifier hacking):算法快速在整个高斯噪声空间中找到了那些验证分数更高的噪声。但是,验证器总是存在偏见的,验证器高分不总是代表图像质量高。因此,验证器欺骗会导致输出图像分数虚高,且多样性下降。

为了缓解这个问题,论文尝试了其他搜索算法。论文先尝试了零阶搜索(Zero-Order Search, ZO)算法 :对于某个输入噪声,仅在其小邻域里搜索更好的噪声。搜索时,我们随机从邻域里取一些噪声,然后选评估分数最高的那一个噪声。之所以把这个算法称为「零阶」,是因为先有一阶搜索算法:让评估函数对噪声求梯度,沿着令评估分数更好的梯度方向优化。但作者发现,一阶搜索算法需要反向传播梯度,速度太慢了,相较零阶搜索并没有明显优势。

“Not All Noises Are Created Equally” 的噪声优化算法是一种一阶搜索算法。

另外,论文还尝试了沿路径搜索(Search over Paths)算法。在这个算法中,作者把原来的 ODE 采样改成了 ODE-重加噪采样:每次去噪几步,稍微加噪几步,又去噪几步。这样,就可以在采样过程中也引入随机噪声。沿路径搜索希望找到一条最优的生成路径,这个路径既涉及初始噪声,也涉及中间噪声。

在搜索时,沿路径搜索采用了 beam search 的策略:维护一个大小为 $N$ 的当前最优带噪图像。每一步,对每个带噪图像(也就是每条采样路径)采样 $M$ 个噪声,得到 $NM$ 个可能的下一轮带噪图像。最后,使用评估函数,从 $NM$ 个图像中挑选最优的 $N$ 个。

在前两种搜索算法中,我们都是得到了最终的生成图片 $x_0$,再用评估函数来评估 $x_0$ 的质量。但在沿路径搜索中,我们在采样过程中途就要决定当前带噪图像 $x_t$ 的质量。为了高效评测 $x_t$ 的质量,沿路径搜索会先对 $x_t$ 执行一步去噪,再根据扩散模型公式算出一个临时预估的 $x_0$,最后评估该 $x_0$ 的质量。

如下图所示,这两个新搜索算法也是有效的。各项实验的详细参数请参考原论文。

文生图模型实验

在较为简单的按类别图像生成任务上验证了想法的有效性后,论文展示了文生图任务上噪声搜索的效果。

将任务拓展成文生图后,不需要重新设计搜索算法,只需要更新评估方法。论文选用的评估方法包括 ASP (Aesthetic Score Predictor), CLIPScore, ImageReward。它们的评估标准如下:

  • ASP 模型为输入图像预测一个美学分数。
  • CLIPScore 表示 CLIP 输出的图文匹配程度。
  • ImageReward 能够从图文匹配度、美学质量、无害度等多个角度评估图像。

除了单独使用上述某个评估方法外,还能够将所有评估方法组合成一个集成(ensemble),更综合地评估图像。

在测试时,论文使用了同样的指标。但是,为了确保对比的公平性,我们应该忽略评估方法和测试指标完全相同时的结果。另外,论文还汇报了多模态大语言模型 (MLLM) 的打分结果,作为补充测试指标。

下图展示了在 FLUX.1-dev 上使用上述评估方法做噪声搜索的实验结果。第一行的实验是不使用噪声搜索,只用 30 次 NFE 做去噪的实验结果。后面的实验都是用了 2880 次 NFE 在搜索上。相比之前 ImageNet 上的 FID 实验,此处指标提升得并不明显。

论文还展示定性实验结果。每一行前三张图像是增加去噪 NFE 的结果,从第四张图像开始是增加搜索 NFE 的结果。

与偏好微调结合

现在 LLM 普遍采用了根据奖励函数微调模型的策略,比如直接偏好优化(Direct Preference Optimization,DPO)。这些微调技术也被用到了扩散模型中,比如 Diffusion-DPO [9]。而噪声搜索的做法和这些微调方法很像,都是基于某一个奖励函数,让模型的输出更符合人类的偏好。那么,对于微调过的模型,噪声搜索还有用吗?

论文展示了在 Diffusion-DPO 中的 SDXL-DPO 上应用噪声搜索的实验结果。可以看出,噪声搜索同样兼容偏好微调模型。

最后,我们来简单浏览近期另一篇使用噪声搜索技术的论文: Inference-Time Text-to-Video Alignment with Diffusion Latent Beam Search [3]。相比前几篇论文,这篇论文主要是把噪声技术拓展到了视频模型上。阅读这篇论文时,我们会简单看一下论文的搜索算法和视频评估函数,并讨论论文中的部分理论推导。

Beam Search 噪声搜索

这篇论文用到的搜索策略和上文中的沿路径搜索完全相同。即一轮去噪中,我们维护 $N$ 个最优带噪视频,并从 $NM$ 个下一轮带噪视频中选取最优的 $N$ 个。一个 $N=2, M=2$ 的示意图如下所示:

同样,在这个搜索算法中,我们无法提前获取清晰视频 $x_0$,需要用某种方法即时评估带噪视频 $x_t$。上一篇论文通过一步去噪并提前获取预测 $x_0$ 的方式实现了在线评估。而这篇论文采取了另一种类似的策略:对当前带噪视频执行一个步数极少(2~3)步的 DDIM 采样,以快速预测当前 $x_t$ 对应的 $x_0$,再评估预测的 $x_0$ 的质量。

视频评估指标

这篇论文将 MLLM 的打分结果作为最终优化目标。但由于此评估方法过于耗时,在搜索时,该论文还是用了一些更高效的评价指标,包括:

  • 主体一致性:用帧间 DINO 特征的相似度表示
  • 运动平滑性:借助视频插帧网络提取视频运动特征
  • 运动程度:用光流表示
  • 美学质量:用 LAION 美学预测器评估
  • 图像质量:用一个能够检测图片是否过曝、模糊的 low-level 质量评估网络评估
  • 文本-视频一致性:用一个视频版 CLIP 评估

论文以上述 6 个指标的线性组合为最终评估指标。为了获取最优的线性组合权重,论文暴力尝试了若干组参数,并根据 MLLM 的打分结果选取最优线性组合。

理论推导

尽管这篇论文最后还是使用了简单粗暴的噪声搜索技术来优化扩散模型生成质量,但论文为这种做法提供了一些理论解释。作者借助了随机最优控制 (stochastic optimal control)中的相关技术,把让采样结果更符合某个奖励函数这一问题,转化成先求奖励函数对带噪图像梯度,再把该梯度加入去噪公式。这一技术和 Classifier Guidance 的思想非常相似:如下图所示,在 Classifier Guidance 中,我们可以对网络输出加上一个对 $x_t$ 求梯度的项,使得 $x_t$ 往某个奖励函数 (此处的奖励函数是一个分类网络,用于让图像更像某类物体)的方向靠近。

但是,求梯度太耗时了。而求梯度的一种高效近似就是零阶搜索:随机搜索附近的一些 $x_t$,找到值最优的那个 $x_t$。所以,我们可以把噪声搜索看成是 Classifier Guidance 的一种近似操作。

总结与评价

噪声搜索是扩散模型采样过程中一种通过选取最优初始、中间噪声来提升图像质量的技术。在这篇博文中,我们主要学习了早期利用扩散模型自身性质搜索噪声的 INITNO 和 Not All Noises Are Created Equally,直接用神经网络预测更优噪声的 Golden Noise,系统性建模噪声搜索的 Inference-Time Scaling for Diffusion Models beyond Scaling Denoising Steps,以及将噪声搜索应用到视频生成的 Inference-Time Text-to-Video Alignment with Diffusion Latent Beam Search。我们可以从是否搜索中间噪声、搜索算法、评估函数三个维度描述一个噪声搜索方法。本文的方法可以总结如下:

方法 搜索中间噪声? 搜索算法 评估函数
INITNO 局部一阶搜索+全局随机搜索 扩散模型注意力输出
No All Noise 全局随机搜索/局部一阶搜索 去噪-反演稳定性
Golden Noise 神经网络预测 去噪-反演稳定性+图像质量
beyond Scaling Denoising Steps 仅在沿路径搜索中 全局随机搜索/局部零阶搜索/沿路径零阶 beam search 分类器最大概率/美学打分器/ImageReward
Inference-Time Text-to-Video Alignment 沿路径零阶 beam search 主体一致性/运动平滑性/运动程度/美学质量/图像质量

从任务目标来看,噪声搜索希望模型输出能够满足某个奖励函数,尤其是满足人类偏好的奖励函数。它和相关技术的对比如下:

  • Classifier Guidance: 用奖励函数一阶导数指导生成。
  • 噪声搜索:使用零阶搜索时,可以看成 Classifier Guidance 的高效近似版。
  • 偏好优化 finetune:用奖励函数微调模型参数。

我认为这项技术并不是一个很好的科研方向,原因有:

  • 噪声搜索的做法太简单暴力了。使用过 AI 绘画的用户,应该都尝试过随机生成大量图片,然后从中挑选自己满意的图片。所谓噪声搜索只不过把这个过程自动化了而已。所有这些方法中只有 Golden Noise 的直接预测噪声看起来比较新奇。
  • 不同噪声对应的图像质量不同是一个有趣的观察。但是,这些论文并没有深入分析这个问题的原因,只是用实验去证明哪些做法可以缓解这个问题。从科研上,我们更希望有文章能够深入解释某一个现有问题。
  • 虽然有文章宣称噪声搜索是一种推理时扩展,但仔细浏览实验表格,噪声搜索带来的提升太有限了:网络运算次数从 30 增大到了 2880,是原来运行时间的 96 倍,换来的只是一点微小的指标提升。还不能排除文章中展示的定性图片是精挑细选的结果。

我猜测不同噪声的本质不同在于,当前扩散模型的初始噪声是放在一个二维像素网格里的。在随机采样完成后,像素间的某些关联性就被确定下来了。无论在后续采样过程中怎么干预,一开始的图像结构都难以改变。但我们更希望一开始噪声的每个维度都互相解耦合的,这样噪声的生成质量的方差或许会更小一点。要让噪声的每个维度一开始互相独立,我们可能需要设计一种新的图像表示方法,而不用二维像素结构。

我认为扩散模型的推理时扩展确实是一个有价值的研究方向。但就目前来看,噪声搜索显然不是实现这个目标的最优解。

部分参考文献

对于某些经典论文 (如 CLIP, Classifier Guidance),可以直接通过搜索关键词找到论文。其他一些较新的论文有:

[1] Golden Noise for Diffusion Models: A Learning Framework

[2] Inference-Time Scaling for Diffusion Models beyond Scaling Denoising Steps

[3] Inference-Time Text-to-Video Alignment with Diffusion Latent Beam Search

[4] INITNO: Boosting Text-to-Image Diffusion Models via Initial Noise Optimization

[5] Attend-and-excite: Attention-based semantic guidance for text-to-image diffusion models

[6] Not All Noises Are Created Equally: Diffusion Noise Selection and Optimization Zipeng

[7] Zigzag Diffusion Sampling: Diffusion Models Can Self-Improve via Self-Reflection

[8] Sit: Exploring flow and diffusion-based generative models with scalable interpolant transformers.

[9] Diffusion model alignment using direct preference optimization.

自从 ChatGPT o1 在 2024 年 9 月发布后,人们逐渐把研究重点放在了推理时扩展 (Inference-time scaling) 上。对于扩散模型而言,除了在推理时增加步数外,谷歌今年 1 月的研究 Inference-Time Scaling for Diffusion Models beyond Scaling Denoising Steps 表明,搜索更好的扩散模型噪声也能提升图像质量。但实际上,选取更好的噪声这一想法早在 2024 年 6 月的论文 Not All Noises Are Created Equally:Diffusion Noise Selection and Optimization 中就已提出。可惜的是,这篇论文最后在 ICLR 上被主动撤稿了,详情可参考 OpenReview:https://openreview.net/forum?id=R5xozf2ZoP

在这篇博文中,我会简单介绍「并非所有噪声本质相同」这篇论文。之后,我们来尝试批判性分析这篇论文及其审稿意见,并反思该如何做出高质量的工作。

知识准备

想读懂这篇论文,只需了解 DDPM 和 DDIM 这两篇扩散模型经典论文。

DDPM 定义了加噪过程和去噪过程:加噪过程中,训练图像 $x_0$ 会加噪 $T$ 次,最后变成纯噪声 $x_T$;去噪过程中,纯噪声 $x_T$ 会被去噪 $T$ 次,最后变成生成图像 $x_0$ 。

DDIM 修改了 DDPM 中的部分假设,让扩散模型在去噪时不再有随机性,而唯一的随机性来自于随机噪声 $x_T$ 的采样。保证了去噪过程不再有随机性后,我们就可以通过公式反演 (inversion),找出某个真实图像 $x_0$ 对应的 $x_T$。当然,这个反演过程存在误差。

核心思想:更好的噪声带来更高的图像质量

作者认为,过去的工作仅通过优化网络结构或参数来提升扩散模型生成质量,却忽视了噪声的重要性。作者从两个方向探索了噪声空间(即所有可能的噪声构成的集合):能否用某种定量指标选取更好的噪声?能否优化某一现有噪声?这两个方向的应用如下所示。噪声选取能够提升模型在同一个文本提示下的随机生成质量,而噪声优化能够提升现有某噪声对应图像的质量。

用反演稳定度反映噪声质量

根据以上设计思想,这个工作的重点就落在了噪声质量的评估指标上。只要有了一个这样的指标,我们就能用简单的算法实现噪声选取和噪声优化:

  • 噪声选取:随机采样大量噪声,然后根据指标选出最好的那一个。
  • 噪声优化:把指标变成损失函数,用损失函数优化给定噪声。

作者认为,函数不动点具有某些良好性质。因此,如果一个噪声在做完一轮生成-反演(去噪-加噪)后变化越小,那么这个噪声的质量越好。

所谓不动点,就是令函数 $f$ 满足 $x=f(x)$ 的输入 $x$。这里作者把函数 $f$ 定义成先生成再反演这一整个过程,把 $x$ 定义成某个高斯噪声。

具体来说,作者用 cos 相似度来反映噪声的「变化幅度小」,即对于噪声 $\epsilon$ 和其生成-反演结果 $\epsilon’$,反演过程的稳定度,或者说噪声的质量,等于 $cos(\epsilon, \epsilon’)$。

有了这样一个指标后,我们可以用下面伪代码所示算法实现噪声选取和噪声采样:

1
2
3
4
5
6
7
8
9
10
11
input number of samples K, denoising model M
eps_list = sample_noise(K)
stability_list = []
for i in range(K):
eps = eps_list[i]
img = generate(M, eps)
eps_inversion = inversion(M, img)
stability = cos(eps, eps_inversion)
stability_list.append(stability)
best_idx = argmax(stability_list)
output eps_list[best_idx]
1
2
3
4
5
6
7
input denoising model M, optimization steps n, noise eps
for i in range(n):
img = generate(M, eps)
eps_inversion = inversion(M, img)
loss = 1 - cos(eps, eps_inversion)
eps = gradient_descent(loss, eps)
output eps

实验结果

实验配置

本工作用 SDXL 和 SDXL-turbo 模型做实验。去噪步数分别为 10, 4。

本工作在测试时只需要设置提示词。提示词来自于 Pick-a-pic, DrawBench, GenEval 这三个数据集。

  • Pick-a-Pic 收集了真实文生图应用中用户的输入提示词及相同提示词下不同输出的偏好程度。每项数据包含提示词、一对图像、一个表示用户偏好哪个图像的标签。
  • Drawbench 的每项数据包含提示词、一对图像、一个反映图像质量的偏好标签、一个反映提示词对齐度的偏好标签。这些提示词能从颜色、物体数量等多个角度测试模型生成属性。
  • GenEval 提供了能从单个物体、两个物体、物体数量、颜色、位置等多个角度测试模型的提示词。

为了对比生成图像质量,本工作用了 HPS v2, AES,PickScore, ImageReward 指标。除 AES 只考虑美学分数外,其他几个指标都是通过训练一个匹配人类偏好的模型来评估图像质量。除了给出这些指标的值外,论文还展示了方法的胜率——在同样的提示词下,比较使用/不使用新方法的定量结果,统计使用新方法胜利的频率。

定性定量结果

以下是噪声选取的实验结果。

以下是噪声优化的实验结果。

从定量结果中可以看出,本文的方法一定程度上确实是有效的。但是,这些数值上的提升真的总能反映在视觉效果上吗?这些数值上的提升足够大吗?由于缺乏同类方法的比较,这些实验还是欠缺说服力。

博文开头已经展示了本文的定性结果,这里就不再重复展示了。值得一提的是,本文的方法可以拓展到其他模型上,比如生成 3D 模型的 SV3D。以下是 3D 模型上的可视化结果。

审稿意见分析

在看 ICLR 2025 官方审稿人意见之前,我先给出我的一份简单审稿意见。我把本文的主要内容都在这篇博文里写出来了,读者也可以在阅读下面内容之前尝试给该论文打分。如果读者想着重提升审稿能力,可以先去把这篇论文细读一遍,再去 OpenReview 上审视其他审稿人的意见。

这篇文章发现了并非所有扩散噪声生成质量相同这一问题,并设计了反演稳定度这一衡量噪声质量的指标。定性和定量实验表明该方法确实能提升图像质量。

本文的优点在于新问题的发现:确实之前多数研究者只考虑如何优化模型,而忽视了噪声也是影响图像的重要因素。

本文在叙述和论证上均存在缺陷:

  • 在为何要使用反演稳定性这件事上,作者说是受到了不动点理论的启发。但作者完全没有深入分析反演稳定性的原理,本质上是通过实验来直接验证假设的合理性。哪怕不好从数学原理上解释,作者也应该通过列出相关工作,并补充部分探究原理的实验,来巩固该指标的合理性。
  • 本文的技术贡献并不多:优化噪声是一个非常常见的策略。
  • 在实验中,作者没有和其他方法对比,完全看不出这个方法是否具有优越性。找出一个基准方法其实并不难:将 CFG (classifier-free guidance) 分别设置成 1 和 7.5 就是一个非常容易实现的基准方法。

如果是现在已经知道了优化噪声是一个有潜力的方向,我可能会给这篇论文一个位于接收临界线上的分数。但如果是去年的话,很可能给的是低于临界线的分数。

我们来简单看看官方审稿人的评价。

审稿人 A 给了明确的差评。文章的主要缺陷有:

  • 虽然作者宣称这是首次研究噪声空间的论文,但实际上之前已经至少有三篇工作做过类似研究。因此,文章的创新性是有限的。
  • DDIM 反演稳定性差一部分是因为反演算法不够好导致的,而和扩散模型无关。最好能够展示更好反演算法下的结果。
  • 评估不充分:这篇文章没有展示任何和其他方法的比较。另外,最好能展示诸如 FID,IS 等其他指标。文中的指标提升看起来微不足道,仅有约 1%,感觉不到明显的进步。
  • 每次评估都需要执行一次加噪-去噪过程,方法性能较低。

审稿人 A 是这个方向的专家,他用已有的噪声空间优化工作来质疑本文的创新性。我简单看了一下该审稿人引用的工作,确实已经有不少往期工作做了类似的事,但实现方法还是比本文的方法更复杂一点。所以,不能说这篇论文一点创新也没有,作者必须严谨地比较这篇论文和之前的论文,并强调出创新点在哪。

审稿人 B 给了一份评价略低于临界线的短评。他认为这篇论文提出的「反演稳定性」及其实验结果还是非常合理的。他认为论文的主要缺陷是方法质量受到反演算法的影响,并希望作者给出不同反演策略下的结果。

审稿人 C 也给出了略低于临界线的评价。他认为这种使用反演稳定性来评估噪声质量的想法有新颖性,且实验的设置比较合理。在 3D 上的应用或许很有潜力。但他也列出了几条主要缺陷:

  • 作者认为反演质量与生成质量强相关。但这个假设只是通过一个具体示例来验证,而缺少理论分析或者系统性的研究。一个可能的分析方法是:测试10万个样本,计算图像质量和反演稳定性的相关性。
  • 和上面两个审稿人一样,审稿人 C 也提到了 DDIM 反演本身就不够好的问题。
  • 噪声选取和噪声优化都需要大量计算资源。

审稿人 D 也给出了略低于临界线的评价。他同样认为反演稳定性是一种新颖的指标,作者对于方法设计动机阐述得十分清晰。他提到的主要缺陷有:

  • 研究反演中的不同点并不是一个新颖的想法,之前至少有三篇工作从迭代优化来寻找反演不动点的文章。这篇文章没有与这些工作做对比。
  • 从 K=100 个样本里选取噪声的方法太简单直接了。之前有类似工作用一种更深入的方式选取噪声。
  • 缺乏和其他噪声优化论文的比较。
  • 计算效率太低。

总结与反思

本工作明确指出了扩散模型不同初始噪声质量不同这一设计动机。为了找出更好的噪声,本工作用一种听起来比较有趣的「反演稳定性」来评估噪声质量。基于这个指标,本工作设计了噪声选取和噪声优化两个算法,用于两类生成任务。

多数审稿人都认可该工作的方法有一定的新颖性,但其论文有着诸多缺陷:

  • 没有深入分析反演稳定性的原理,且没有考虑是否应该用更好的反演算法。
  • 噪声优化是一种常见的策略。没有与相关工作做对比。
  • 指标提升的说服力不足。
  • 每次评估反演稳定性都需要执行去噪和加噪过程,运算效率过低。

当然,在目前的眼光看来,如果生成质量足够高,增加推理时间也是可以接受的。但是,论文的前几个缺陷是实实在在的,被评价为低于接收临界线合情合理。作者最后也没有 rebuttal,直接撤稿了。我感觉这篇论文还是比较可惜的,毕竟它的研究方向比较有潜力。

从这篇论文以及最近做推理时扩展的论文可以看出,哪怕对于同一项技术,改变看待问题的角度也算创新。比如同样是更换扩散模型噪声,如果是从推理时扩展的角度来看,那么运算时间长就不是缺陷了;但与之相对,我们需要证明随着运算时间的增加,生成质量会按某种规律逐渐变好,表明花更多的时间是有意义的。

从这篇论文投稿失败的教训中,我们也可以总结出顶会论文应该满足哪些最基本的要求。当论文的方法和之前方法非常相似时,一定要与之前的方法对比,并明确写出本文的创新点在哪。另外,虽然很多时候我们是先有了实验结果再去补充说明方法中的假设,但我们依然可以在论文里尝试解释我们这样做的原因。这种解释可以是通过原理上的推导,也可以是通过解释性实验。

Ave Mujica 第五集一直在沉重的气氛中度过。无言的悲伤透过屏幕,来到我的心里。就像被巨石堵住的水流,我想要做些什么来释放这些情绪,却茫然无措。终于,在这集动画最后,灯的一句「一起组乐队吧!」瞬间击穿了我的心理防线,泪水止不住地流了下来……

在生活中,我一直保持了理性的态度,冷静地看待一切。只有偶尔有闲暇时,才会把感性的一面留给自己。一边哭着,我一边试图分析剧情,分析自己的想法,想要弄清楚现在的我究竟在想什么。可是,泪水就是止不住。本以为是困了,可是两天后在外面吃饭时,我又想起了这段剧情,眼睛又湿润了起来。

前几周,哪怕观点大不相同,我也积极地浏览着网上关于 Ave Mujica 热火朝天的讨论。而看完这集后,稍微看了一下贴吧,我就知道我的感想果然难以在网上找到共鸣。这一次,我害怕了,我不敢再去看别人的评论了。我想去诉说我的想法,传递给人海中的那些同路人,但不被理解的痛苦与恐惧占了上风。所以,这一周,我不敢看,也不敢想 Ave Mujica 的事。

一周快过去了,新的一集要播出了。不把此刻的想法说出来,我好像就会失去什么。最后我打算鼓起最后的一点勇气,在博客上讲一讲我的感想。不管是这篇文章,还是之后有关 Ave Mujica 的文章,很多东西不是在写剧中的角色,而是在写我自己;我也明白,这些文章不怎么会照顾他人的阅读体验,而只是写给我自己看的。不管自己的内心被暴露成了什么样子,我都想坦荡地面对。


在接连不断的事件后,第四集快节奏地结束了。剧终,在队员们的宣言下,Ave Mujica 解散了。

正如那干脆利落的解散宣言,第五集的开头,我们直接看到的是乐队解散后的场景。当红乐队在巡演中途突然解散,观看最后一场演出的观众排队退票。随后,网上布满了乐队解散的流言蜚语。面对事务所的一次次来电,祥子无力地趴在初华家的桌子上。初华想关心祥子,但真正在意的问题却是「我们还能在一起吗?」某日,仅留下一段简短告别便签的祥子,彻底宣告乐队的一切关系都结束了。

就像什么都没有来过似的,队员们的生活重新步入了「正轨」。在海铃看来,不过又多见识了一支解散的乐队,自己雇佣兵的身份并没有改变。初华还留在 Sumimi,和她的好搭档真奈继续着这个组合的活动。喵梦还是当着网红,活跃地出现在人们面前。祥子回到了破碎的家庭,重操客服旧业,赡养老爹,如轮回般过着和之前一样的日子。

可是,真的什么都没改变吗?

初华焦急地寻找着她心心念念的祥子,海铃则断然拒绝了一切有关过去的讨论。以知名演员为梦想的喵梦,也因再怎么努力也无法拥有天才般的演技,而回绝了演戏的良机。睦更是从人们的视线里消失了。

「那个人好像是 Ave Mujica 的 Oblivionis 哦。」

即使是听到了这样的杂谈,祥子依然带着职业般的微笑结束了客服的工作。平常地坐着电车,平常地回到出租屋。

老爹又被带到了警局。领他回家的时候,他又狼狈地坐在了地上。「如果是以前的话,这里是不是应该生一生气比较好呢?」在这一刻,祥子或许这样想过。但最后,她选择了最平常的做法,默默地看着。

对祥子来说,一切平常才是最大的不寻常——她已经不再有激情反抗,而是无奈地接受了一切。

无论是努力学习,努力工作,努力养家,都只是机械般重复地之前的动作,只是自我麻痹与忘却的手段。她不再努力改变自己的命运了。

动画的前半段正是这样如流水般展示主角们的生活片段。虽然没有深刻的剧情,但这淡淡的日常中流露出一股抹不去的哀伤。乐队解散就像还未下雨的乌云一样,笼罩在角色们的心里。


事情到这里还没有结束。很快,祥子的外公在她放学时找了过来。乐队解散的违约金已经全部付清,只需要告别不争气的父亲,回去当大小姐就行了。这一次离开破旧的出租屋时,祥子只是平静地带走了行李。重返大宅的那一刻,那个要强的她也已经死了。

——不,或许比真正的死亡还要痛苦。

作为一个在战争中失利的士兵,最渴求的应该是一次痛快的死亡。可是,如果被带到了敌国,好吃好喝伺候着,甚至连反抗的意识也完全失去了。这不是更大的一场悲哀吗?

只是为了和亲生父亲生活在一起,为了这小小的愿望,她头也不回地告别了大小姐的生活。命运给了和她年龄不匹配的生活压力,让她在学习之余不得不打工养家。只是为了和父亲在一起,只是想要反抗这不对的命运,她摧毁了心爱的乐队,冷酷地斩断了一切。可是,不管身体能够承受住多少劳累,心灵的腐朽只会与日俱增。在心理被压垮之后,她只好把一切押在了一支新乐队上。这或许是无意识间把乐队和幸福联系在了一起,又或许是通过获取名利向世界的一次复仇。为了保护那破碎不堪的心,祥子把自己抽离出来,全部寄托在乐队上——只要乐队做到了世界最好,我是不是就能获得幸福呢?

但很可惜,乐队又一次解散了。连接灵魂和世界的最后的一根弦,最后也断掉了。

「不就是大小姐过家家吗?」

当她放弃父亲,重回大宅的那一刻,她连最初反抗一切的念头也消失了。一切看似都回来了,但她的一切也全部消失了。

「我讨厌我自己」

她也意识到,丰川祥子,在精神上死去了。


在悲伤的镜头之间,穿插着高松灯幸福的回忆。

因为独特的爱好,她总是离人群有些疏远。但她从来没有放弃解读他人的想法,想要尽可能融入进去。

那一天,在随风飘散的花瓣前,她碰到了生命中那个重要的人。

祥子带她加入了乐队,认识了乐队的大家。就像春天的阳光一样,样子为她驱散了身边的寒气。《春日影》的每一个字,每一声调,都是大家共同创作的结晶。那是一段多么美好的日子啊!

后来,CRYCHIC 解散了。几经沉浮,灯用自己的心声,在 MyGO 乐队里与陌生和熟悉的朋友建立了牢固的关系。这一回,她终于融入了自己所属之处。

可是,回头望去,春日的暖阳已经不在了。留下的,只有月光下一个孤寂的影子,就和当年的自己一样。明明同样在弹着曲子,灯却无法从祥子那里再感受到之前的温暖。她敏锐地察觉到了祥子的变化,想要为祥子做点什么。虽然不知道这么做有没有用,她一直用便签试图和祥子沟通。

「祝你幸福」是祥子给她留下的最后的话。向他人送上最诚挚的祝福时,祝福的话语并不是为对方量身定做的考虑,而只是希望对方获得自己最求之不得的东西。可能是想到了这里,灯又一次在便签上写道:「小祥,你幸福吗?」


幸福是什么呢?

最早,祥子的幸福只是简简单单地和家人生活在一起。母亲去世后,幸福变成了用自己双手就可以争取到的东西。而父亲的事业失败,则让幸福变成了牺牲一切也难以把握的东西。

越是缺失,越想追求。越是追求,越是求而不得。幸福就是香气扑鼻的毒药,惹人沉醉,使人沦陷,最后带走一切。又一次经历乐队解散的祥子,终于明白,追求幸福才是一切的元凶。只有忘记一切,忘记幸福,才能「正确地」在世界上活下去。

「幸福是什么呢?」

灯向立希问道。经历了种种后,MyGO 队员间的关系已经牢不可破。望着眼前这个自己在意的人,想着眼前这美好而平凡的日子能够一天一天持续下去,立希害羞地说:「我现在就很幸福……」。

是啊,我们都很幸福。灯虽然不见得理解了立希对她特别的关心,但她能够感觉到,在 MyGO 里,大家都是幸福的。

所以,察觉到了什么的灯,向祥子发出了「是否幸福」的拷问。其他的话语都可以回避,唯有这个可望不可及的词语,一击突破了祥子的心防。她粗暴地回应了这个便签。她只是害怕,害怕这个总是给自己带来痛苦的东西。

祥子又一次逃跑了。只有灯的话,大概永远都追不上吧。而这一次,有行动力极强的爱音在她身边。机缘巧合之下,灯和爱音坐上了祥子回家的车,灯有了和祥子直面对话的机会。

灯又进入了那座豪宅。可是,一切都物是人非。再次来到那个曾经熟悉的琴房后,「你幸福吗」的答案已经显而易见了。现在,在这里的,只是一名楚楚可怜、自我封闭的少女。现在的她,就像当年的自己一样。而现在的自己,又能够说些什么,来表达自己的关心呢?

「我喜欢你」只是压在对方身上的一副重担。

「希望你能一直幸福」只是一句廉价的同情。

「小祥!一起组乐队吧!」

这是高松灯此时能说出的最恰当的话语。

听到这句话后,作为观众的我不禁潸然泪下。


看过 MyGO 的我们,知道「组乐队」对于灯来说有着特别的含义。要组乐队,就是一辈子的事。进乐队,不只是为了演奏,更是希望和关系紧密的朋友在一起,一直,一直,直到走完一辈子。

灯一直在尝试理解他人的心灵。哪怕没有交流,仅仅是通过乐曲,通过行为,通过一架钢琴,灯就体会到了祥子的那份孤独。所以,她下意识地说出要一起组乐队的话。组乐队,不仅是「希望你能幸福」的期望,还是「我会让你一辈子幸福」的承诺,更是「让我们携手共同幸福」的祈求。灯想表达的一切的一切,都浓缩在这句话里了。

如果祥子和我一样,能站在上帝视角理解灯的心意,也会情不自禁地落泪吧?

断绝一切的原因,是不相信会被他人理解。哪怕说出去了,换来的浮于表面的关心,只会让不被理解的痛苦又增一分。所以,对于自己难过的情绪,祥子永远都不会说出去。可是,哪怕只是有一刻,祥子会不会幻想着,有人能跨越一切,真正来到她的身边,为她抚平伤痕呢?

但幻想只是幻想,祥子不会在生活中相信这种事的存在。没有看过对方心路历程的她,或许无法理解灯的心意。这就是另一种悲哀了。


总之,Ave Mujica 第五集对氛围的渲染十分恰当,将细腻的情感一点一点展现出来。我一直把心安在角色们的身上,希望能体会她们每刻的感受。在片尾灯说出要组一起乐队的那一刻,在我的理性理解一切之前,我自然而然地明白到了灯的心意,感受到了祥子的寂寞。我心中暂存的两个灵魂连结到了一起,强烈的情感洪流涌了上来。这一幕着实让我感动。

我知道,和我有同样感受的观众太少了。我也害怕自己的想法无法被人理解。但我在网上,也看到了少数和我一样愿意仔细分析角色心理的人。所以,在新的一集播出之前,我也想分享我此时的感受,分享给同样用心感受动画的观众。

Transformer 中平方复杂度的注意力运算一直是其性能瓶颈,该问题在序列长度极大的视觉生成任务中尤为明显。为了缓解此问题,并生成分辨率高至 4K (4096x4096) 的图像,英伟达近期推出了文生图模型 Sana。Sana 不仅使用了线性注意力的 Transformer,还通过增加 VAE 压缩比等方式,极大提升了超高分辨率图像生成的性能。

Sana 论文示意图如下,生成的图像看起来还不错。值得注意的是,在 1K 图像生成上,Sana-0.6B 的生成速度是 FLUX-Dev 的 25 倍。由于采用了线性注意力机制,这一速度优势会随着图像尺寸增加而越发明显。

在这篇博文里,我们来着重学习 Sana 相较标准 DiT 文生图模型 (SD3, FLUX.1) 做了哪些提升性能的改进。

本文中,计算机术语「性能」表示运行速度。

线性注意力真的是「免费午餐」吗?

在看完论文摘要后,我的第一反应不是去细读论文,而是立刻去网上搜索社区用户对于 Sana 的评价。我一直坚持一个观点:「天下没有免费的午餐」。线性注意力比标准注意力快了那么多,那么它的质量一定会有所下降,不然所有人都会立刻把标准注意力换成线性注意力。

有网友在 Reddit 上分享了 Sana 的生成结果并对此开展了讨论:https://www.reddit.com/r/StableDiffusion/comments/1hzxeb7/it_is_now_possible_to_generate_16_megapixel/ 。有关图像质量的讨论有:

  • 楼主:「Sana 的 4K 模型生成出的图片没有 2K 的真实」。
  • 社区用户:「生成结果看起来更靠近抽象艺术,但这些效果已经能够通过 SD 1.5 + 超分辨率达到了。能否用 Sana 生成照片级图片?」楼主:「它不太擅长生成照片级图片。」
  • 社区用户:「4K 图片质量太差了,没有比 1K 图片更多的细节。许多上采样算法的结果会比这个更好。论文应该宣传其生成速度而不是丢人的 4K 图像质量。」
  • 楼主:「手画得不是很好。」

另一个帖子 (https://www.reddit.com/r/StableDiffusion/comments/1h5xujr/sana_nvidia_image_generation_model_is_finally_out/ )里的评论有:

  • 「考虑模型大小和速度的话,它的效果非常惊人。第一感看起来艺术感比 FLUX 好,但文本生成能力不太行。」
  • 「有 SD 1.5 的质量,但很快。再训练它可以达到 SDXL 的质量。」同楼里另一个用户则认为,Sana 质量在 SDXL 基础版和 FLUX 之间。

简单看下来,根据社区用户的反馈,Sana 的质量没有显著好于 SDXL。它的 4K 图像生成效果跟先生成较低分辨率再使用超分辨率算法的结果差不多。我们应该着重关注 Sana 的加速方式而不是其生成质量。

以往工作

潜扩散模型(Latent Diffusion Models)

扩散模型能够在多次迭代中生成高质量图像。而有研究发现,扩散模型在生成时早早就生成完了图像结构,后续时间都浪费在完善图像细节上。为此,潜扩散模型 (Latent Diffusion Models, LDM)使用了一个两阶段方法来提升扩散模型的性能:

  • 先用一个自编码器 VAE 把图像压缩成尺寸更小、信息更丰富的潜图像 (latent images)。
  • 用标准扩散模型生成潜图像。

目前主流的扩散模型几乎都属于 LDM。其中,最经典的 LDM 是 LDM 论文作者实现的 Stable Diffusion (SD)。根据 LDM 论文的实验结果,令 VAE 的压缩比 f 为 4 或 8 比较好。SD 采用了 f=8 的 VAE。后续多数 LDM 都使用了这样的压缩比。

Diffusion Transformers

早期扩散模型是用 U-Net 实现的,而后来大家逐渐把 U-Net 替换成了 DiT (Diffusion Transformers)。基于 DiT 的著名文生图模型有 SD3, FLUX。这些模型的核心仍是标准 Transformer 中的多头注意力运算。

线性注意力

设矩阵 $Q \in \mathbb{R}^{n \times d}, K \in \mathbb{R}^{n \times d}, V \in \mathbb{R}^{n \times d}$,其中 $n$ 为序列长度,$d$ 为特征长度,则注意力操作可以简写成:

对于矩阵 $A \in \mathbb{R}^{a \times b}, B \in \mathbb{R}^{b \times c}$,不做加速的前提下,朴素的矩阵乘法 $A B$ 的复杂度是 $O(a \cdot b \cdot c)$。

根据这个结论,注意力操作的复杂度为 $O(n^2d)$。这是因为 $Q \in \mathbb{R}^{n \times d}, K^T \in \mathbb{R}^{d \times n}$,第一步 $QK^T$ 的复杂度是 $O(n^2d)$。类似地,后一步矩阵乘法也是同样的复杂度。总复杂度不变,仍是 $O(n^2d)$。

由于模型特征长度 $d$ 是常数,我们只考虑序列长度 $n$ 的变化,所以可以认为标准注意力操作的复杂度是 $O(n^2)$。

但假如注意力运算中没有 softmax 操作的话,注意力运算就是两次矩阵乘法。

由于矩阵乘法满足结合律,通过改变矩阵乘法的顺序,我们能够神奇地改变注意力运算的计算复杂度。

由于 $K^T \in \mathbb{R}^{d \times n}, V \in \mathbb{R}^{n \times d}$,$K^TV$ 操作的复杂度是 $O(nd^2)$。由于 $Q \in \mathbb{R}^{n \times d}, K^TV \in \mathbb{R}^{d \times d}$,第二步矩阵乘法的复杂度还是 $O(nd^2)$。因此,总复杂度变为 $O(nd^2)$。不考虑 $d$ 的增长的话,这种注意力运算就是线性复杂度的。

我们再回头看去掉 softmax 对注意力运算有什么影响。softmax 函数同时做了两件事:1)保证 QK 的输出非负,以表示相似度;2)保证对于一个 query,它对所有 key 的相似度权重之和为 1,使得输出向量的范数(向量的「大小」)几乎不变。所以,线性注意力都会设置一个非负相似度函数 $sim(q, k)$,并用下面的注意力公式保证权重归一化。

根据 NLP 社区的反馈,线性注意力的效果比不过标准注意力。

相比之下,CV 社区对线性注意力的探索没有那么多。Sana 主要参考的早期工作为 Efficientvit: Lightweight multi-scale attention for high-resolution dense prediction。

Sana 模型架构改进

在模型架构上,Sana 主要做了三大改进:增加 VAE 压缩比、使用线性注意力、使用轻量级文本编码器。

深度压缩 VAE

在基于 LDM 的 DiT 中,和图块数(参与 Transformer 计算的元素个数,类似于 NLP 的 token)相关的超参数有:

  • VAE 压缩比例 f
  • DiT 图块化 (patchify) 比例 p

此外,VAE 输出的潜图像通道数 c 决定了重建质量与学习难度:c 越大,自编码器的重建效果越好,但扩散模型的学习难度也变大。

经过 VAE 后,图像会从 $H \times W \times 3$ 压缩成 $\frac{H}{f} \times \frac{W}{f} \times C$。又经过图块化操作后,图像大小会变成 $\frac{H}{fp} \times \frac{W}{fp} \times d$,其中 $d$ 是 Transformer 的特征长度。决定 Transformer 运算量的是图像尺寸 $\frac{H}{fp} \times \frac{W}{fp}$。标准 Transformer 一般至多处理 $64 \times 64$ 大小的图像。

此前的文生图 DiT 一般采用 f8 c4 p2f8 c16 p2 的配置。而 Sana 直接把 f 设置成了 32,实现了一个 f32 c32 的 VAE。

其实,LDM 论文尝试用过一个 f32 c64 的 VAE,但生成效果并不好。为什么这次 Sana 的 VAE 就好了不少呢?论文对此没有做深入分析,只是简单列举了一些 Sana 的 VAE 做出的改进:

  • 从模型设计与训练策略上:此前 f32 VAE 效果不好的一大原因是模型没有充分收敛。为此,Sana VAE 将模型中的标准自注意力换成了线性注意力。另外,为了提升在高分辨率图像上的生成效果,Sana VAE 先在低分辨率上训练,之后在 1K 图像上微调。
  • 从通道数上:作者比较了 f=32c=16, 32, 64 的实验结果,发现将 c=64 改成 c=32 会显著提升扩散模型收敛速度。

尽管 VAE 是卷积网络,里面还是包含了自注意力运算。

除了调整 f, c 外,作者还认为 p=1,即不使用图块化操作,是最好的。论文展示了总压缩比 fp 不变的前提下, p 提升、 f下降后扩散模型的学习进度。结果发现,不用图块化而让 VAE 全权负责压缩是最好的。

线性 Transformer 块

为了降低注意力操作的复杂度以提升性能,Sana 使用了线性注意力操作。如前所述,线性注意力会去掉 softmax 并通过矩阵乘法的结合律来降低计算复杂度,但需要通过额外设计一个相似度指标。

Sana 对线性注意力的定义如下:

其中,$O_i$ 是某一条 query $Q_i$ 的输出。这其实是让相似度函数为:

ReLU 函数用于让相似度非负。

在上面的线性注意力公式中,不管是分子还是分母,都可以通过结合律来优化计算复杂度。此处细节可参见原论文。

根据之前的经验,线性注意力会降低注意力运算的效果。为了弥补这一点,Sana 参考 EfficientViT,把前馈网络 (FFN) 从 MLP (即 1x1 卷积网络)换成了带 3x3 逐深度卷积的网络。这个小卷积网络不仅提升了整个 Transformer 块的拟合能力,还省去了位置编码——由于卷积网络天然能够建模相对位置关系并打破 Transformer 的对称性,不需要再加入位置编码了。

轻量级文本编码器

之前的 SD3 和 FLUX 都用 T5 作为文本编码器,但 Sana 为了提升模型性能,使用了速度为 T5-XXL 6 倍的 Gemma-2-2B。

定量实验

最后,我们简单看一下论文的主要实验结果。论文用到的实验数据集为 MJHQ-30K,包含三万张 Midjourney 生成的图像。

从定量指标上看,Sana 确实好过不少此前的文生图模型。但是,这些指标无法如实反映人类的审美偏好。如前文所述,社区用户认为 Sana 并没有明显好于 SDXL,但指标无法反映这一点。这些指标参考价值不大的另一个证据是 FLUX-dev 和 FLUX-schnell 的比较结果——作为一个被进一步蒸馏出来的模型,schnell 显然是比 dev 的生成质量更差的,但它的 FID, GenEval, DPG 竟然比 dev 还好。因此,在比较文生图模型质量时,我个人建议不要参考文生图的定量指标,而是去参考社区用户的反馈。

另外,虽然 Sana-1.6B 比 FLUX-dev 快了很多,但它比 FLUX-schnell 只快了一倍。或许 Sana 也可以通过蒸馏获得进一步的推理加速。

总结

Sana 是一个以降低运算开销为主要目标的高分辨率文生图模型。它主要通过增加 VAE 压缩比例、使用线性注意力来提升 DiT 的效率。为了弥补线性注意力的能力损失,Sana 将 FFN 改成了 3x3 卷积网络,在提升模型能力的同时免去了位置编码。除了这两大主要设计外,Sana 还通过使用轻量级文本编码器等诸多细节改进提升了模型的训练与推理效率。整体上看,这个工作主要在工程上作出了贡献,线性注意力的设计几乎照搬了之前的工作,没有使用比较新颖的模块设计。

从生成效果上看,尽管 Sana 论文给出的定量指标还不错,但这些指标是否能如实反映文生图质量还存疑。据社区用户反映,Sana 的质量没有明显好于 SDXL。另外,虽然论文一开头就宣称 Sana 能够生成 4096x4096 的图片,但这种图片的细节很差,和 1024x1024 的差不多。这是因为不管是 VAE 还是 DiT 都只在 1024x1024 上训练过。在加大生成尺寸后,图像的清晰程度没有变,只是看起来像素变多了。这篇论文真正应该强调的是生成 4K 图像的速度会更快,而不应该去强调 4K 图像的质量有多高。

从生成速度上来看,Sana 确实比最强开源文生图模型 Flux-Dev 要快很多。但尴尬的是,在 1024x1024 图像生成上,Sana 的速度仅仅是精心蒸馏的 Flux-schnell 的两倍。当然这个对比可能不是很公平,因为 Sana 还没有经过蒸馏。但就目前来看社区用户在生成 1024x1024 的图像时难以感受到 Sana 性能优势。

这篇文章很好地指明了 DiT 的一个发展方向:我们能不能将线性注意力更好地引入 DiT 呢?按我目前的理解,线性注意力就是通过牺牲自注意力全局依赖关系来提升模型计算速度。它的本质和 MAR (Autoregressive Image Generation without Vector Quantization)、VAR (Visual Autoregressive Modeling: Scalable Image Generation via Next-Scale Prediction) 很像,都是通过减少计算像素间的依赖关系来提升速度。这个假设在视觉任务中是很合理的:在确定了图像的主要结构后,理解细节只需要局部像素信息。然而,这些加速方法都不可避免地降低模型的能力。在完全不用和完全使用全局信息之间,我们或许要找到一个平衡点,来使 DiT 具有最佳性能和效果。

最近一篇论文因其吸引眼球的标题而刷屏科技自媒体:”The GAN is dead; long live the GAN!
A Modern Baseline GAN (GAN 已死?GAN 万岁!一个现代 GAN 基模)”。我不喜欢这种浮夸的标题,因为真正有价值的论文不必靠标题吸引人。带着怨气通读完论文后,我发现这篇论文果然没有做出特别大的创新。

这篇论文提出了一种叫做 R3GAN (读作 “Re-GAN”)的 GAN 基准模型。R3GAN 综合使用了 RpGAN 损失函数和特殊的梯度惩罚 (Gradient Penalty, GP) 损失函数,并基于前沿卷积网络 ConvNeXt 重新设计了一套 GAN 网络。实验表明 R3GAN 在 FFHQ 和低分辨率 ImageNet 图像生成上有着比肩扩散模型的 FID 分数。该工作主要是在工程实验上做了贡献,没有提出太多科研创新。在这篇博文里,我会简单介绍 R3GAN 的主要实现细节,并为各项细节提供参考文献而不做深入讲解,感兴趣的读者可以查阅文末总结的参考文献。

GAN 回顾

在这一小节里,我们会回顾读懂 R3GAN 必备的和生成对抗网络 (GAN) 相关的知识。

GAN 基础知识

和其他多数生成模型一样,GAN 的训练目标是建模一个好采样的分布(高斯分布)到一个不好训练的分布(训练数据集)的映射方式。具体来说,GAN 会用一个生成器 (Generator) 把来自高斯分布的噪声 $z$ 设法变成图像 $x$。其他生成模型大多有自己的一套理论基础,并根据某理论来设置生成器的学习目标。而 GAN 用另一个神经网络——判别器 (Discriminator) 来学习生成器的训练目标。

两个模型的学习是通过一种博弈游戏实现的:判别器试图分辨图片是否是「假」的,或者说是否是生成出来的,而生成器通过提升生成图像质量来让判别器无法分辨图片真假。二者共用一套优化目标,但一方希望最小化目标而另一方希望最大化目标。

在上面的损失函数中,$f$ 有很多种选取方式。R3GAN 选用了 softplus 函数,如上图所示。

两大经典结构 DCGAN 和 StyleGAN

GAN 的开山之作是用全连接网络实现的。在 GAN 的后续发展过程中,有两个经典的网络结构:2016 年 的 DCGAN 和 2019 年的 StyleGAN。

DCGAN 是一个生成器基于卷积神经网络 (CNN) 的 GAN。它的特点是对低通道特征逐渐上采样并逐渐减少通道数,直至生成目标大小的三通道图像。

StylGAN 是一个训练较稳定且适合做图像编辑的 GAN。和传统 GAN 生成器相比,StyleGAN 从一条「旁路」输入被映射网络 (Mapping Network) 预处理过的噪声 $z$,此处信息注入的方式是风格转换 (Style Transfer) 中的 AdaIN 操作。由于输入噪声的方式变了,原来的低分辨率特征图输入被换成了一个常量。

两大难题:难收敛和模式崩溃

相比其他生成模型,GAN 常被诟病「难训练」。这种难训练体现在难收敛、模式崩溃 (Mode Collapse)。难收敛即意味着模型对数据集拟合效果不佳,我们可以用 FID 来评估模型输出与训练集的相似程度。模式崩溃指的是对于一个多类别数据集,模型只能生成其中少数类别,示意图如下所示(图片来源)。为了检测是否出现模式崩溃,我们既可以让网络随机生成大量图片并用另一个分类网络统计其中出现的类别数,也可以用生成召回率 (recall) 来大致评估模型的采样丰富度。

R3GAN 实现方法

R3GAN 在引言里把 StyleGAN 中的各种提升 GAN 稳定性的小技巧批判了一通,并主张应该使用尽可能简洁的 GAN 生成器。虽然论文是这样写的,但实际上 R3GAN 是在更早的 DCGAN 的基础上,更新了损失函数,并用上了最新的 CNN 结构,和 StyleGAN 结构几乎无关。我们来从这两个方面来学习 R3GAN:损失函数和模型结构。

损失函数

在描述博弈游戏的 GAN 损失上,R3GAN 把标准 GAN 损失换成了 RpGAN (relativistic pairing GAN) 论文中的 GAN 损失。相比之下,RpGAN 损失用一对真假样本的判别器输出之差送入激活函数 $f$,而不是分别把真假样本的判别器输出送入 $f$。

根据之前的研究结果,作者从直觉和理论上简单解释了 RpGAN 的好处:

  • 以前的 GAN 损失只要求判别器区分真假样本,而不要求真假样本之间的距离要尽可能大。通过把一对真假样本的差输入损失函数,RpGAN 损失可以让真假样本之间的差距尽可能大。
  • 根据之前论文的理论分析,在一些简单的配置下,标准 GAN loss 有数量指数增长的较差局部极小值,而 RpGAN loss 的每个局部极小值都是全局最小值。

R3GAN 还重新对最佳的梯度惩罚(Gradient Penalty, GP)损失函数做了消融实验。$n$-GP 表示让模型对输入的梯度尽可能靠近实数 $n$,从而使得训练更加稳定。常用的 GP 有 0-GP 和 1-GP:

  • 0-GP:最优情况下,模型对任意输入都给出完全一样的结果。
  • 1-GP:最优情况下,模型的输出对输入平滑变动:输入张量的范数变 1,输出张量的范数也变 1。

作者认为 0-GP 对 GAN 判别器是较好的。因为当生成器的输出完全和训练集一样时,判别器是无法区分任何输入的,对任何输入都会给出一样的输出。

对判别器的 GP 有 $R_1$ 和 $R_2$ 两种形式,分别表示对真/假数据采取 GP。作者发现同时用 $R_1$ 和 $R_2$ 更好。

总结一下,R3GAN 使用的损失函数为 RpGAN + $R_1$ + $R_2$。作者用简单的实验证明这是最优的。如下图所示,在一个有 1000 种类别的简单数据集上,最佳损失配置能够生成所有类别的数据,且有更小的分布距离 $D_{KL}$ (和 FID 指标类似,越小越好)。不用 RpGAN 损失会同时降低输出多样性和收敛度,不用 $R_2$ 会使训练完全无法收敛。

现代化的卷积网络

找出了一个简洁而有效的损失函数后,R3GAN 论文接着探究了更优的卷积网络结构。文中提及了五套配置:

  • A:原版 StyleGAN2
  • B:把 StyleGAN2 里的绝大多数设计删掉,让模型和 DCGAN 几乎相同。
  • C:换用上一节里发现的新损失函数
  • D:把类似 VGG 的神经网络加上 ResNet 中的残差连接
  • E:用 ConvNeXt 里的模块更新 ResNet

我不明白作者为什么要强行从 StyleGAN2 开始做实验,然后把 StyleGAN2 中的设计批评一顿,删掉全部设计,再换成一个更早期的 DCGAN。直接用 DCGAN 不是更加简洁吗?我猜测是因为作者用了 StyleGAN2 的代码做开发,花很多精力把 StyleGAN2 删成了 DCGAN。既然代码都改了,不写进论文就太浪费了。

我们跳过配置 A,直接看配置 B 和早期的 DCGAN 有什么区别。经作者总结,配置 B 中比较重要的几项区别有:

  • a) 用 $R_1$ 损失。
  • b) 用较小的学习率且关闭 Adam 优化器中的动量
  • c) 所有网络中均不使用 Normalization 层
  • d) 用 bilinear 上采样代替反卷积

其中,不改 a), b), c) 会使训练失败。d) 是当今神经网络上采样的标准配置,可以防止出现棋盘格 artifact。

配置 C 中的新损失函数我们已经在上一节中讨论过了。

包括 StyleGAN 在内,此前 GAN 的神经网络多采用了类 VGG 结构,没有残差块。而配置 D 将标准 ResNet 中的 1-3-1 残差块引入了网络。

配置 E 则是进一步更新了卷积层的设计。该配置首先引入了 group convolution 操作(将通道分组,同组通道相互连接。group=1 即 depthwise convolution)。由于该操作更加高效,网络能够在总运行时间不变的前提下加入更多参数。此外,配置 E 还用到了 ConvNeXt 中的反瓶颈(inverted bottleneck)块,该模块的设计灵感来自于 Transformer 中的全连接层。

我们再看一次各配置的简单消融实验结果。看起来新损失函数并没有多大的提升,最后比较有效的是网络架构的改进。最好的配置 E 模型能够略优于 StyleGAN2。

定量实验结果

最后,我们来看论文中的定量结果。如前所述,我们主要关心 GAN 的两个指标:多样性与收敛度/图像质量。前者能够通过数类别或者召回率反映,后者能够通过与训练集的 FID(以及本文使用的 $D_{KL})$反映。

多样性

在小型多类别数据集上,R3GAN 能够生成出所有类别,且有最好的训练集相似度。而 StyleGAN2 无法生成少数类别。

另一个能够反映图像多样性的指标是召回率,它大致表示了训练集有多少内容能够在生成集里找到。论文没有提供表格,只简单说了一下 CIFAR-10 上 StyleGAN-XL 是 0.47,R3GAN 0.57。但总体来看 R3GAN 的召回率总是会低于扩散模型。

收敛度

本工作宣传时的一大亮点是它在部分数据集的 FID 分数超过了扩散模型。我们看一下本工作在单一、多样数据集上的 FID 结果。

先看经典的 FFHQ 人脸数据集。在这种多样性较低的数据集上,GAN 的表现一直很不错。R3GAN 的 FID 好于 StyleGAN2 和多数扩散模型,且只需要一次推理 (NFE=1)。但它的 FID 并没有优于此前最好的 GAN 模型。当然,此前的 GAN 用了一种提升 FID 而不提升图像质量的技巧,R3GAN 没有用。

再看类别比较丰富的 CIFAR-10 和 ImageNet 数据集。R3GAN 的表现也比所有扩散模型和大部分 GAN 强。但 R3GAN 没有在更大分辨率的 ImageNet 上测试。现在前沿生成模型一般都会测试 ImageNet-256,而 R3GAN 并没有提供相关实验结果。

总结与评论

R3GAN 是一种现代版的 DCGAN。它从损失函数和模型结构两个方面做了改进。损失函数方面,DCGAN 用了 RpGAN + $R_1$ + $R_2$ 损失函数;模型结构方面,R3GAN 用 ConvNeXt 中的最新卷积结构代替了原来的 VGG 结构。实验表明 R3GAN 在 FFHQ-256 和 ImageNet-64 的 FID 分数上超过了全部扩散模型和多数 GAN,略差于此前最强的 GAN。在生成多样性上,R3GAN 依然没有扩散模型好。

从科研贡献上来看,这篇文章并没有提出新理论或新想法,完全复用了之前工作提出的方法。这篇文章主要提供了一些工程上的启发,有助于我们去开发更好的基于 CNN 的 GAN。从结果来看,R3GAN 并没有在目前主流指标 ImageNet-256 上测试,没有迹象表明它能好过扩散模型。我们可以从其他数据集上的实验结果推断,R3GAN 的最佳表现和之前的 GAN 差不多,没有对 GAN 作出本质的改进。综上,我认为这篇文章是质量满足顶会要求的平庸之作,被 NIPS 2024 选为 Poster 合情合理。

参考文献

DCGAN: Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks

StyleGAN: A Style-Based Generator Architecture for Generative Adversarial Networks

StyleGAN2: Analyzing and Improving the Image Quality of StyleGAN

GP (WGAN-GP): Improved Training of Wasserstein GANs

RpGAN: The relativistic discriminator: a key element missing from standard GAN

RpGAN Landscape Explanation: Towards a Better Global Loss Landscape of GANs

ConvNeXt: A convnet for the 2020s

ImageNet FID Trick: The role of imagenet classes in fréchet inception distance

最近,我在网上看到了一个专门介绍深度学习源代码的网站(https://nn.labml.ai/index.html ),各类流行网络架构一应俱全。这个网站最吸引我的,是它的双列式结构。如下所示,每个页面右边显示代码,左边显示每段代码对应的注释。

一直以来,我在博客中介绍代码时,都是先写描述文字,再贴一段代码。这种方式对作者和读者来说都十分低效。受到上面那个网站的启发,我决定以后也采用这种方式介绍源代码。很可惜,上面那个网站是由 labml.ai 这个组织维护的,似乎并没有提供开源的、可定制的注释网站搭建方式。为此,我找到了一个替代品:Pycco。它可以方便地为 Python 代码生成双列带注释的静态页面。

Pycco 安装与使用

Pycco 是页面生成工具 Docco 的 Python 实现版。它们都是「快而脏」的文档生成器:源代码只有几百行,没有多余封装,直观而暴力地生成 HTML 页面的所有内容。它们为初学者只提供了一个文档页面,这个文档页面讲的是它们的源代码,且文档页面就是由它们本身创建出来的。Pycco 的官方文档页面如下所示。一开始介绍完 Pycco 的背景信息和安装方式后,文档就会直接开始介绍源代码。

根据文档的指示,我们可以通过下面的命令在 pip 里安装 Pycco:

1
pip install pycco

在使用 Pycco 时,我们完全不用去理解其源代码的意思,只需要准备一个带注释的 Python 源文件就行。Pycco 提供了一键式命令,帮我们把源文件里的注释和代码分离,并生成左侧为注释,右侧为代码的静态文档页面。这里的注释既包括了 # 开头的单行注释,也包括了 """ """" 包裹的多行注释。注意,三个单引号构成的多行注释 ''' ''' 不会被该工具识别。

我们来看一个示例。假设我们有下面一个名为 hello.py 的 Python 源文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
"""
Hello
World
"""
import torch
import pycco

# Hello
print("hello world")

"""
End of file
"""

我们用下面的命令为其生成文档页面。

1
pycco hello.py

页面会生成在目录下的 docs 子目录里,子目录包含一个 hello.html 文件和一个 pycco.css 文件。我们可以用浏览器打开 .html 网页,看到下图所示内容。

注释默认使用 Markdown 语法。如果平时就习惯用 Markdown 写博客的话可以无缝切换。但是,默认情况下网页无法渲染公式。

页面跳转

除了普通的 Markdown 语法外,Pycco 还支持一个特殊的功能:文档跳转。我们可以把文件名写在 [[ ]] 内,实现源文件内部或源文件间的跳转。特别地,我们还可以在页面某处打上标签,并跳转到某页面的标签处。以下是一个示例。为了给「注释」加上注释,我别出心裁地把注释写进了代码部分。

让页面渲染 LaTeX 公式

刚才我们发现,目前的 Pycco 页面并不支持公式渲染。而在解释深度学习代码时,很多时候不得不用到公式。因此,我决定给 Pycco 加上渲染公式的功能。

Pycco 这种直观暴力的实现方法让网页开发者能够快速地修改页面生成逻辑。然而,我已经把 HTML 的知识快忘光了,配不上「网页开发者」这个名号。因此,我让 ChatGPT o1 来帮我开发这一功能。

经指导,我认识了 MathJax 这个在网页上渲染公式的工具。只需要在 HTML 的 head 里导入一些包,网页就可以自动识别单行公式 和多行公式 $$$ $$$。我不记得 head 是什么了,但大概能猜到这个是一个相当于声明全局变量的语句块。

我在 pycco_resources\__init__.py 文件里找到了设置 head 的地方。这个文件提供了生成网页的模板,包括了写死的 CSS 文件和部分 HTML 代码。打开这个文件的最快方式是在某处写 import pycco_resources,之后用 IDE 的代码跳转找到这个包在本地的位置。

我们在该文件下图所示的位置插入和 MathJax 相关的代码。

1
2
3
4
5
6
7
8
<script>
MathJax = {
tex: {
inlineMath: [ ["$","$"] ]
}
};
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>

修改模板后,重新生成网页,可以发现现在公式能够正确渲染了。

高亮新增/删除代码

在以后讲解代码的时候,我想高亮新增的或者删去的代码,就像 GitHub 里显示代码的更改一样。由于 Pycco 非常容易做二次开发,我又去请教 ChatGPT o1 该如何实现这样的功能。

经指导,我了解到代码高亮可以通过设置背景颜色 style 来实现。为此,我需要做两件事:

  1. 新增有关背景颜色的 CSS style
  2. 想办法指定要高亮的代码块

第一件事很简单,只需要打开模板文件 pycco_resources\__init__.py,添加背景颜色 style 即可。我添加了两种背景颜色,分别表示删去的和新增的代码块。我是在 VSCode 里找到想要的颜色值的。随便打开一个 CSS 文件,输入一个大概的颜色值,VSCode 就会弹出一个选择颜色的小窗口。

1
2
3
4
5
6
.highlighted-line-1 {
background-color: #fa53208c;
}
.highlighted-line-2 {
background-color: #68fc5d;
}

第二件事就比较难了。我需要先看懂目前高亮源代码的逻辑,再在其基础之上添加背景高亮的功能。Pycco 的主函数在文件 pycco/main.py 里,我们可以用导入 Python 包的方式快速找到这份源文件。原来的高亮关键字的逻辑如下,我在其中加入了一些代码用于输出中间结果。

函数的主要输入是列表 sections,函数的输出存储在列表项 section"docs_html", "code_html" 字段。

sections 内容如下,可见其中是过了解析器的注释块和代码块。每一段注释都会对应一段代码。

1
[{'docs_text': '=== BeGin ===\r\n\n', 'code_text': 'import torch\r\nimport pycco\r\n\r'}, {'docs_text': '# Hello\r\n## World\r\nThis is a **Python** [code](https://www.python.org/).\r\nTry formula $\\epsilon$ .\r\n$$\r\n3 = 1 + 2\r\n$$\r\n\n\n', 'code_text': 'print("hello world")\r\n\r'}, {'docs_text': 'End of file\r\n\n', 'code_text': '\n'}]

函数的输出是完整 HTML 文件的一部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
<h3><span id="begin" href="begin"> BeGin </span></h3>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">torch</span>
<span class="kn">import</span> <span class="nn">pycco</span></pre></div>
<h1>Hello</h1>
<h2>World</h2>
<p>This is a <strong>Python</strong> <a href="https://www.python.org/">code</a>.
Try formula $\epsilon$ .
$$
3 = 1 + 2
$$</p>
<div class="highlight"><pre><span class="nb">print</span><span class="p">(</span><span class="s2">&quot;hello world&quot;</span><span class="p">)</span></pre></div>
<p>End of file</p>
<div class="highlight"><pre></pre></div>

那么,我的目标就是修改这个文件。我需要先根据输入的原始代码块,判断这段代码是否要高亮,再修改 HTML 代码块的内容。

首先,从用户的角度考虑,应该怎么指定要背景高亮的代码呢?既然现在代码被拆成了一块一块的,且每块代码对应一段注释,我决定用一些特殊注释来高亮一整块代码,就像前面的设置标签和跳转标签的特殊注释一样。我加入了以下判断:如果注释前几个字符是 '===ADD===''===DEL===',就用对应的颜色高亮这段代码。

判断了是否要高亮后,我还需要做对应的修改。我不仅要在 HTML 代码块里高亮代码,还需要把注释块里的特殊命令删掉。通过观察相关代码,我忽然回忆起了 HTML 的部分实现原理:背景高亮就是把一段 HTML 字符串封进一个带有背景高亮样式的标签 <div></div> 里。那剩下的删除注释也很简单,只需要对字符串做一点简单操作就行了。代码修改过程及使用示例如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
# Add following code

delete_background_start = '<div class="highlighted-line-1">'
add_background_start = '<div class="highlighted-line-2">'
background_end = '</div>'

if section["docs_text"].startswith('===ADD==='):
section["docs_text"] = section["docs_text"][9:]
section["code_html"] = add_background_start + section["code_html"] + background_end
elif section["docs_text"].startswith('===DEL==='):
section["docs_text"] = section["docs_text"][9:]
section["code_html"] = delete_background_start + section["code_html"] + background_end

修正多行注释缩进

在写这篇博文的时候,我又发现 Pycco 的一个 bug:在多行注释带缩进时,HTML 页面上的注释也会缩进,导致页面布局完全乱套。

我找到了 pycco/main.py 里处理多行注释的代码。我把旧代码全部删掉,换了一种暴力的字符串处理方式:删除多行注释左侧所有空格及制表符。

1
docs_text += line.lstrip('\t ') + '\n'

改完之后 bug 就修好了。

总结

在这篇文章中,我介绍了 Pycco 这个为 Python 代码生成双列注释-代码静态页面的 Python 工具,并分享了我对其的三个修改:支持 MathJax 公式、支持背景高亮、修复多行注释缩进 bug。相比介绍开源项目的 readthedocs 等文档构建工具,Pycco 能够为每行 Python 代码写注释,尤其适合详细讲解深度学习代码。对于熟练的网页开发者,Pycco 的源代码很短,改起来很方便;但另一方面,Pycco 似乎比较小众,多年都没人维护,可能有各种 bug。主要推荐想在个人博客里讲解代码的朋友使用这个工具。我以后都会用它来讲解代码。

ChatGPT 小插曲

ChatGPT 在指导我添加 MathJax 时,先叫我下载 python-markdown-math 这个 pip 包,让 Python 脚本里的 Markdown 解析器能够解析公式。但我在实践中发现不解析 $$$$ 符号,在正确导入 MathJax 包后,网页依然能够正确渲染。

ChatGPT 一开始提供的 MathJax 导入代码是 MathJax V3 的。经过 python-markdown-math 解析后,双行注释被转换成了 <script type="math/tex; mode=display"></script> 包裹的内容。这些公式没法成功渲染。我把添加双行公式的详细过程提供给 ChatGPT 后,它分析出这种 <script> 的写法是 MathJax V2 的,让我把 head 里的包改成 MathJax V2。修改过后,双行公式果然渲染成功了。然而,如上一段所述,我发现哪怕不用 python-markdown-math 也行。于是我又用回了 MathJax V3。可见 ChatGPT 有功有过,给我制造了一些麻烦,也算是能够帮我解决。

在求助 ChatGPT 添加背景高亮功能时,它非常出色地分析了问题:先理解 Pycco 生成 HTML 的过程,再决定用户如何指定要高亮的内容,最后根据不同的方案做实现。但它给的三个信息输入方案都不尽如人意:(1) 在命令行里指定要高亮的行;(2) 在每行要高亮的代码前都加特殊注释;(3) 用另一个配置文件来指定要高亮的行。这些方法的用户体验都不好,且难于实现。当然,由于我没有把所有源代码给它,它或许没有发挥完整的实力。最后我还是靠自己理解了 Pycco 的页面生成逻辑,选了一种好实现且好用的方案。

我从来没有用 ChatGPT 做过特别复杂的编程任务,这次稍微体会了一下。感觉它的能力有时候会出乎意料地强(比如发现 HTML 的风格是 MathJax V2 的),但不见得次次都靠谱。它的能力主要还是基于大量数据的「搜索」,加上一点浅显的思考。只是因为看过的数据过多,所以很多时候能从历史经验中直接给出很好的答案。当然,作为语言模型,它对于语言的理解能力极强,哪怕描述不完全也能准确理解用户的意图。做为使用者,我们要在用之前就想好能从 ChatGPT 那里获取到哪些帮助,并提供充足的信息,而不能全盘接受它的输出,就和我们日常和别人交流一样。