介绍
在本文中,我们将比较SDXL 1.0与其前身稳定扩散2.0的结果。我们还将探讨新的SDXL专家模型管道中Refiner模型的作用,并使用扩张和非扩张分割掩码比较输出结果。最后,我们将使用Comet来组织所有数据和指标。
什么是SDXL 1.0 ?
SDXL 1.0是来自Stability AI的新基础模型,作为用于文本到图像(Stable Diffusion)合成的潜在扩散模型(LDM)的大幅改进版本,它引起了轰动。作为稳定扩散的最新发展,它将其前身从水中吹出来,并产生与Midjourney等黑盒SOTA图像生成器竞争的图像。
这些改进是一系列有意的设计选择的结果,包括一个3倍大的UNet骨干网络、更强大的预训练文本编码器,以及引入了一个单独的基于扩散的修复模型。修复模型使用一种事后图像扩散技术来改善样本的视觉保真度,该技术首次在SDEdit中提出。在本教程中,我们将使用具有或不具有这种修复模型的SDXL,以更好地了解它在管道中的作用。我们还将将这些结果与稳定扩散2.0的输出进行比较,以对SDXL引入的改进有一个更全面的了解。
但这些改进确实是有代价的;SDXL 1.0包含一个令人印象深刻的3.5B参数基础模型和6.6B参数细化模型,使其成为当今最大的开放图像生成器之一。这种增加主要是由于更多的注意块和更大的交叉注意上下文,因为SDXL使用第二个文本编码器(OpenCLIP vitbigg与CLIP vitl)。
SDXL 1.0和未来
不过,仅去年一年,Stable Diffusion就成为了3D分类、可控图像编辑、图像个性化、合成数据增强、图形用户界面原型、从fMRI脑扫描中重建图像和音乐生成等领域的基础模型。SDXL 1.0承诺继续稳定扩散的传统,扩大生成人工智能的可能性。
SDXL 1.0 与世界对比
SDXL 1.0与其他文本到图像生成AI工具相比如何?根据SDXL所说,表现非常出色。事实上,它现在被认为是世界上最好的开源图像生成模型。尽管Midjourney仍然似乎是最受欢迎的选择,但SDXL作为一个免费的开源替代方案,绝对有能力与之一争高下。
SDXL 1.0是开源且开放访问的,这意味着只要你有足够的计算资源,就可以免费使用。但它并不需要太多资源;本文中的所有图像都是使用Google Colab的A100 GPU生成的。根据Stability AI的说法,SDXL 1.0甚至可以在只具有8GB VRAM的消费级GPU上有效运行,使生成式文本到图像模型比以往更易于使用。
那么,是什么使SDXL的图像生成效果更好呢?根据Stability AI的说法,SDXL具备以下特点:
1. 更好的对比度、光线和阴影
2. 更生动准确的颜色
3. 本机支持1024 x 1024的分辨率
4. 能够生成易读的文字
5. 更好的人体解剖学(手部、脚部、四肢和面部)
我们将在下面更详细地探讨其中一些要点。
用于模型可解释性的SDXL 1.0
生成式人工智能在模型解释性、透明度和可复现性的争议中继续处于前沿。随着人工智能的发展,模型的决策变得几乎不可能解释,即使是创建它们的工程师和研究人员也很难理解。这对于许多最先进的生成式人工智能模型尤为关键,这些模型的不透明性限制了我们完全评估其性能、潜在偏差和固有限制的能力。因此,Stability AI将SDXL打造成一个开放模型,以实现模型的可解释性和透明度,这是一项值得赞赏的举措。
缺乏模型解释性可能导致许多意想不到的后果,如偏见和刻板印象的延续、对组织决策的不信任,甚至法律责任。此外,它还阻碍了可复现性,阻碍了协作,并限制了进一步的进展。稳定扩散模型开源和开放访问的决定符合行业对开放人工智能的日益增长的趋势,鼓励从业者在现有工作基础上进行延伸和贡献新的见解。
SDXL 1.0的实际应用
在下载Artifact之后,我们将使用HuggingFace的SDXL修复流水线进行图像修复和增加。
作为提醒,图像修复是填补图像指定区域中缺失数据的过程。作为图像的扩展,我们将通过修复图像的背景掩码来实现。修复流水线接受正面和负面提示,并设置了随机种子,这样你就可以在本地环境中得到相同的结果。
跟踪我们的实验
我们将从实例化一个Comet Experiment开始,这样我们就可以跟踪输入、输出、代码和其他系统指标。你需要从你的帐户设置中获取API密钥。如果你还没有账户,你可以在这里免费创建一个。
import comet_ml
comet_ml.init(api_key= "<YOUR-API-KEY>",
project_name="<YOUR-PROJECT-NAME>")
# Create the Comet Experiment for logging
exp = comet_ml.Experiment()
彗星的工件
工件是按类似文件夹的结构排列的任何版本化对象。通过这种方式,Comet允许你跟踪与机器学习生命周期相关的任何数据。我们的工件结构如下:
在你的本地环境中下载Artifact就像运行下面的代码一样简单。如果Artifact不在你的个人工作区中,请确保Artifact的所有者与你共享或将其设为公开(就像我们的示例一样)。下面,我们将Artifact下载到我们的工作目录中,保留其原始文件结构,没有进行任何额外的解析。
logged_artifact = exp.get_artifact(
"SAM_SDXL_outputs",
"examples")
local_artifact = logged_artifact.download("./")
加载SDXL与Hugging Face
我们将从HuggingFace加载SDXL基本模型、精化模型和绘制管道。我们可以用下面的代码这样做:
sd_pipe = StableDiffusionXLInpaintPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16,
variant="fp16",
use_safetensors=True
).to(device)
refiner = StableDiffusionXLInpaintPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-refiner-1.0",
text_encoder_2=sd_pipe.text_encoder_2,
vae=sd_pipe.vae,
torch_dtype=torch.float16,
use_safetensors=True,
variant="fp16",
).to(device)
# Log model graph to Comet
exp.set_model_graph([sd_pipe, refiner])
超参数
与任何传统的机器学习模型一样,SDXL有许多可调整的超参数会影响输出的质量。我们将在这里介绍一些重要的超参数,并使用Comet来跟踪哪些值的组合产生了哪些输出。
然而,与许多传统的机器学习模型不同,这个实验中的“质量”主要是主观的。虽然有一些“客观”评估生成模型创建的图像质量的指标(例如NRMSE、PSNR、SROCC和KROCC等),但对于修复而言,每个指标都存在一些问题。因此,在本教程中,我们将使用自己作为人工评估者。正因为如此,我们将大量依赖我们的实验跟踪工具,以追踪哪些值导致了哪些图像输出。
让我们来看一些重要的超参数设置。
引导比例
引导比例(也称为无分类器引导比例或CFG比例)控制生成的图像与原始提示的相似程度。较低的引导比例值可以使模型更具“创造力”,但如果设置得太低,图像可能会变得难以识别。较高的引导比例值会强制生成器更紧密地匹配提示,但有时以图像质量或多样性为代价。
指导量表值通常在7-15之间,尽管Hugging Face建议的值在7.5到8之间。对于本教程中生成的大多数图像,我们将坚持使用默认值7.5。
我们的大部分提示都相当客观(如“一只紫色章鱼”,“一个红色的糖果熊”)。但是,当我们开始引入主观词汇(如情感)时,引导比例值的作用变得更加明确起来。一个“高兴的卷发男子”应该有多高兴?我们可以看到,随着引导比例的增加,模型在这个提示上更加自由地表达。
推理步骤数量
一般来说,图像的质量随着推理步骤的增加而提高,但正如我们在下面的图像中所看到的,到了某个点,改进变得微不足道。更多的推理步骤也意味着模型生成图像的时间更长,这可能对某些使用场景产生问题。稳定扩散模型在推理步骤相对较少的情况下效果非常好,所以在本教程中,我们每个图像将使用大约70个推理步骤。
高噪声分数
在使用SDXL作为专家组合流水线时,我们还需要指定高噪声比例。高噪声比例是基础模型和修复模型每个阶段运行的推理步骤的百分比。基础模型始终作为高噪声扩散阶段的专家,而修复模型则作为低噪声扩散阶段的专家。
我们使用基础模型的denoising_end参数或修复模型的denoising_start参数来设置这些推理间隔。两者均不是必需的,因为它们的和总是为1。每个参数接受一个介于0和1之间的浮点数,表示该专家模型运行的总推理步骤的比例。
例如,如果我们指定了总共100个推理步骤,并将denoising_end设为0.7,那么我们的输入将在基础模型中迭代70个步骤,在修复模型中迭代30个步骤。另一方面,如果我们将denoising_end值设置为0.9(总共推理步骤相同),那么我们的输入将在基础模型中迭代90个步骤,在修复模型中迭代10个步骤。
这种两阶段的架构使得SDXL非常稳健,而又不损失计算资源或推理时间。
内核大小和迭代次数
如果我们使用与我们要替换的对象完全对齐的掩码,我们可能会注意到原始图像和修复后的图像之间存在一些尴尬的像素过渡。通过略微扩张掩码,并使修复流水线能够访问靠近修复对象的背景像素,修复器可以在两者之间实现更流畅的整合。
为了扩张掩码,我们需要设置内核大小和迭代次数。这些值可能会根据你使用的图像而变化。我们还将定义一个函数来简化扩张过程:
def dilate_mask(init_mask, hp_dict):
kernel = np.ones(hp_dict["kernel_size"], np.uint8)
img_dilation = cv2.dilate(init_mask, kernel, iterations=hp_dict["kernel_iterations"])
dilated_mask_pil = Image.fromarray(img_dilation)
return dilated_mask_pil
最后,我使用random.randint(-1000,1000)重新生成图像,直到找到我喜欢并希望使用的图像为止。然后,我使用相同的种子来微调超参数。
嵌套超参数
除了上述的超参数之外,我们还将记录一些其他的超参数,包括我们的提示和负面提示。因为我们将为我们的修复和增加流水线设置相同的超参数,所以我们将使用嵌套字典来定义我们的超参数。例如:
hyper_params = {
"inpainting" : {"category": category,
"seed" : 116,
"kernel_size": (5,5),
"kernel_iterations" : 15,
"num_inference_steps" : 70,
"denoising_start" : 0.70,
"guidance_scale" : 7.5,
"prompt" : "old man with curly hair, realistic, happy",
"negative_prompt" : "low resolution, ugly"
},
"outpainting" : {"category": category,
"seed" : 933,
"kernel_size" : (5,5),
"kernel_iterations" : 20,
"num_inference_steps" : 70,
"denoising_start" : 0.95,
"guidance_scale" : 7.5,
"prompt" : "a casino in Las Vegas",
"negative_prompt" : "low resolution, ugly, people"}
}
exp.log_parameters(hyper_params)
这些将相应地作为Comet中的嵌套超参数记录下来,使它们更容易访问和组织,并有助于避免重复和混淆。
我们的SDXL 1.0修复-增加流水线
为了与本教程的第一部分保持一致,对于我们的五个原始输入图像,我们生成一个修复图像和一个增加图像。对于每个示例,我们将使用以下方法生成样本:
1. SDXL(仅基础模型)
2. SDXL(基础模型+修复模型)
3. SDXL(基础模型+修复模型+扩张掩码)
然后,我们将比较这些不同方法的结果,以更好地了解修复模型和扩张分割掩码的作用。一旦我们选择了最佳输出,我们将与稳定扩散2.0的最佳输出进行比较。
编写了一些函数来抽象一些噪声后,生成这些样本(并将其记录到Comet)就像以下几行代码一样简单。
#SDXL base only
generated_image = generate_image(
image = image_source_pil,
mask = image_mask_pil,
hp_dict = hyper_params["inpainting"]
)
exp.log_image(generated_image, name="inpainting_sdxl_base")
#SDXL base + refiner
refined_image = generate_refined_image(image = generated_image,
mask = image_mask_pil,
hp_dict = hyper_params["inpainting"])
exp.log_image(refined_image, name="inpainting_sdxl+refiner")
#SDXL base + refiner and dilated mask
dilated_mask_pil = dilate_mask(init_mask = seg_dog1, hp_dict = hyper_params["inpainting"])
dilated_image = generate_refined_image(image = generated_image,
mask= dilated_mask_pil,
hp_dict = hyper_params["inpainting"])
exp.log_image(dilated_image, name="inpainting_sdxl+refiner+dilation")
提炼者
即使快速浏览一下,也很容易看到细化器在提高图像质量方面的作用。但是,当你检查更精细的细节(如线条、纹理和面部)时,SDXL细化器的功能最为明显。为此,仔细看看会有所帮助:
扩张的面具
放大也能帮助我们看到放大面具带来的不同。原始背景像素和生成图像之间的过渡在蒙版被放大的地方平滑得多。
SDXL 1.0结果
在所有这些艰苦的工作之后,SDXL 1.0与Stable Diffusion 2.0相比如何?让我们来看看结果!首先,我们的手绘图像:
显然,SDXL 1.0是对稳定扩散2.0的巨大改进!现在让我们来看看我们的外画图像(我鼓励你放大结果,以真正看到更细微的差异):
比较我们在Comet中的SDXL 1.0结果
正如你可能想象的那样,跟踪用于创建哪些输出图像的输入图像、提示、掩码和随机种子可能会很快变得令人困惑!这就是为什么我们在彗星上记录了我们所有的图像。
现在让我们转到Comet UI,看看我们的每个输入图像和绘制和绘制后的输出图像:
我们也可以选择单独的实验进行比较。如果我们试图重现我们多次实验的图像,或者如果我们试图调试特定的实验运行,这可能特别有用。
用Comet跟踪我们的图像提示
我们还需要确保跟踪如何创建每个输出,以便稍后可以重现任何结果。也许我们已经多次运行同一提示符的不同版本。或者我们尝试了不同的随机种子,想要选择我们最喜欢的结果。通过将提示记录到Comet的Data Panel,我们可以轻松地检索所有相关信息,以重新创建任何图像输出。
结论
1. 了解SDXL 1.0基本模型和细化模型,并比较各自的输出;
2. 探索了guidance_scale、num_inference_steps和denoising_start超参数;
3. 跨超参数值比较我们的图像输出;
4. 将我们嵌套的超参数记录到Comet;
5. 在Comet中构建一个广泛的仪表板来跟踪、记录和组织我们的结果。