SDXL图像生成存在的漏洞和修复方法

2024 / 2 / 17

Stable Diffusion XL(简称SDXL)是一款流行的图像生成工具,它能够根据用户的输入提示(prompt)生成高质量的图像。然而,用户在使用过程中发现了一个问题:在特定的输入条件下,生成的图像会出现不正常的元素,这些元素被称为“artifacts”,即图像错误。这些错误让图像看起来有瑕疵或者不自然,影响了图像的整体质量。

问题现象

这个问题的具体现象是,在使用SDXL的Web-UI版本,尤其是Animagine XL V3等产品时,用户在输入特定的指令后,生成的图像中会出现意料之外的错误。这些错误并不是传统意义上的噪声,而是看起来不正常或者不自然的图像元素,比如扭曲的图形、不协调的颜色块或者不合逻辑的图像组合。这些问题在其他版本如Comfy等中并未出现,表明问题可能与Web-UI版本的特定功能有关。具体而言,是设置中的一个名为"Enable emphasis"的设置项。

下面是打开这个设置项和关闭这个设置项得到的图片差异

问题原因

经过分析,开发者发现这个问题的根源与一个名为“Enable emphasis a1111”的强调计算功能有关。这个功能旨在根据用户输入的指令对图像的某些部分进行强调处理。然而,在特定的输入条件下,这个强调计算会出错,导致生成的图像中出现了上述的错误。具体的触发条件可能包括输入提示的顺序、强度或者特定的组合方式,但这些条件难以确定,使得问题的复现和诊断变得复杂。

解决方案

为了解决这个问题,开发者「hakomikan」进行了深入的代码分析,并最终找到了可能导致图像错误的特定代码段:(使用OpenAI翻译自note文章 ,对于部分专业用语翻译不一定准确)

从Animagine XL V3开始,我听说当输入特定提示时会出现一些奇怪的图像。虽然一听到"奇怪的图像"这个词,我就会想到古代武器,但在这里所说的"奇怪的图像"指的是不是噪音,而是一些奇怪的图像。

这是Web-UI系列中发生的问题,而在Comfy等中并不会发生。由于在增加强度时出现类似的瑕疵,推测是Web-UI的强度计算存在错误。

我觉得很有道理,但同时也觉得很奇怪。因为如果Web-UI的强度计算存在错误,早就应该被指出来了,但不知为何从Animagine开始流行后才开始有人报告。说到强度计算的错误,比如说当设定为(mikan:1.3)时,mikan的张量会增强1.3倍,但即使将这个计算公式叠加起来,强度也不应该变成10这样奇怪的数值。

即使是像(kotatu,(mikan:1.3), cat:1.5)这样复杂的提示,只有在相乘后强度达到2以上才会发生。

从各种报告来看,似乎有很多现象不能仅仅通过强度来解释,即使改变提示的顺序也会产生影响。因此,强度本身并不是决定因素。

那么,我们决定展开一次围绕代码的冒险,探究其中的原因。处理提示的Web-UI代码到处都是,相当复杂难解呢。

首先,输入的提示会根据强度进行分割并进行处理。

a girl in (kotatu:1.3) eating mikan

如果是这样的提示的话

a girl in : 1.0 kotatu : 1.3 eating mikan : 1.0

就像这样。虽然强度计算是在这里进行的,但似乎没有任何错误的余地。之后,prompt会被转换为token的形式,并传递给transformers,变成张量。然后,通过将每个张量与其相应的强度相乘,生成用于生成的张量。我觉得可疑的是周围处理的强度相乘部分,让我们来看看那里。

z = z * (original_mean / new_mean)

在编程中,除法总是会产生意料之外的输出。从这个表达式来看,如果new_mean的值比original_mean小,那么张量的强度可能会意外地变大。根据原始代码的说明,这是为了防止产生人工痕迹,但这段代码是在XL模型出现之前的,所以可能与XL模型不兼容。

这里的original_mean和new_mean分别是在进行强度计算之前和之后的张量的平均值。通常情况下,强度的值大于等于1,所以(original_mean / new_mean)的值不应该偏离1太远。

可能是在animagine中存在一些token,导致张量偏向负值,从这些token计算出的张量混合在一起时,当强度增加时,(original_mean / new_mean)的计算结果会变大,从而导致结果崩溃。另外,也有可能是强度计算导致original_mean和new_mean的符号发生了反转。

所以,为了验证这个假设是否正确,我将尝试重新制作问题。为了避免盲目地寻找可疑的令牌,我将计算由所有令牌生成的张量的平均值。然后,我发现存在正张量和负张量(在SD1.X中也是如此)。

"eni"这个词在负张量中最强,而1在正张量中最强,这是我理解的。那么"eni"是什么?

于是,通过组合这些部分,我们成功地再现了问题。只输入1和eni时并没有发生问题,但是当输入(1 2 am:1.3) eni时,系统发生了故障。

在这里,我们来看一下计算得到的 original_mean 和 new_mean 的值,original_mean 为 -0.0003,new_mean 为 -7.8293e-05。这样一来,倍率就变成了3.83,这就导致了破烂的发生。而且,当强度从1.3增加时,似乎还会发生正负反转。这个提示的组合恰好处于正负的分界线上。只要稍微改变一下提示,就不会发生破烂。这与之前的报告也是一致的。因此,只需要禁用以下的代码就可以了。

z = z * (original_mean / new_mean)

通过修改这部分代码,成功解决了图像生成中的错误问题。为了让用户能够方便地应用这一修复,「hakomikan」开发了一个名为"sd-webui-prevent-artifact"的脚本 。用户可以通过常规安装这个脚本,并在设置中进行配置,从而禁用引起问题的功能,避免在图像生成过程中出现错误。

用户反馈

这个解决方案得到了其他用户的积极反馈。许多用户在应用了"sd-webui-prevent-artifact"脚本后,确认了其有效性,并鼓励其他遇到相同问题的用户尝试使用。这表明,通过社区的合作和开发者的努力,SDXL的用户体验得到了显著的改善。

结论

总结来说,SDXL在生成图像时出现的错误问题是由Web-UI版本中的一个强调计算功能引起的。通过开发者的努力和社区的合作,这个问题已经得到了有效的解决。用户现在可以通过安装"sd-webui-prevent-artifact"脚本 来避免这种问题,享受更加顺畅和高质量的图像生成体验。