AI绘画入门:扩散模型,Stable Diffusion,LoRA,ControlNet相关技术原理整理

转载自本人知乎https://zhuanlan.zhihu.com/p/611310582,由于b站不支持链接和公式因此删掉了部分内容。本文禁止转载。
相关资源整理
社区
civitai:比较主流的AI绘画模型分享网站。可以看到大佬们跑出的图,并复现别人的tag或者下载别人finetune的模型。使用webui的时候这个网站用起来比较方便。
Huggingface:一个数据集、模型分享的网站,很多业界大牛也在使用和提交新模型,我们可以从stable diffusion分区下载一些作者训练的模型。个人感觉使用diffusers的时候这个网站比较方便。个人常用的模型是"runwayml/stable-diffusion-v1-5"和"Linaqruf/anything-v3.0"(类似NovelAI的leak模型)
代码库及工具
Stable Diffusion(Latent Diffusion Model):目前比较火的AI绘画大部分都是基于Stable Diffusion的,比如之前比较火的NovelAI。这个代码库可以用来学习Stable Diffusion的算法细节,因为其他工具都对代码进行了封装,不容易看懂算法流程。
stable-diffusion-webui: 这是目前大家最常用的工具,是一个Gradio库开发的浏览器UI界面,其后端依旧是Stable Diffusion以及一系列相关的工具包。其优点是操作简单,调参方便,方便分享模型,并且方便组合各种模块,非常适合广大没有相关基础的人使用。
diffusers:一个进行扩散模型训练、推理、Fine-Tuning的工具包。通过几行代码就可调用各种模块,比如Stable Diffusion,当然它还支持很多其他的扩散模型。当我们要批量处理或者魔改代码时,webUI改起来不太方便,这个时候就可以用一用这个库。并且diffusers可以自动帮你下载Hungingface社区的模型,缺点是civitai社区中分享的模型有的需要手动转换,比如lora目前还没有官方的支持,不过可以方便地找到大佬写好的代码来跑。
扩散模型相关技术
Denoising Diffusion Probabilistic Models(DDPM)
参考资料:论文,大佬的blog , 跟李沐学AI - DALL·E 2(含扩散模型介绍)
个人理解的扩散模型可以看成一种逐步去噪的网络,且网络的输出还condition on给定的噪声等级参数。扩散模型需要从噪声图像中逐步恢复出原图像,从而达到从随机噪声中逐步生成图片的效果。

上图中从右侧的原始图像到左侧的噪声图像就是扩散过程,每一步都会添加高斯噪声,最终图像就接近完全随机了。这个扩散过程可以当成制作训练数据的过程,我们要做的是训练一个网络,从每一步噪声图像估计出扩散过程时在这一步添加的噪声(或直接估计去除噪声的图像)。
由于去除噪声的过程中我们使用的是同一个网络,而每一步的噪声的总量并不相同,因此可以给网络一个额外的输入T,告诉网络现在是在哪一步了。大佬的blog中指出网络可以根据噪声等级的不同,自动选择关注全局特征还是局部特征。因为图像中每一个像素添加的高斯噪声是独立的,当噪声较多时(对应上图左侧)网络只能将更大范围里所有像素的特征合并起来考虑,这样各个像素特征中的高斯噪声能够在融合的过程中相互抵消,此时网络只能恢复出图像的大致轮廓。当噪声比较少时(对应上图右侧),网络可以更关注更小范围内的细节,从而恢复出图像细节。
综上所述,扩散模型在生成图像时,网络首先从一个随机噪声图像中“幻视”出某个物体(比如以为这个噪声图像是一个人脸),然后网络会从噪声图像中恢复出人脸的轮廓。之后网络会逐步在更小的范围内“幻视”出一些细节(比如眼睛鼻子等),最终恢复出一张比较真实的图片。整个过程由额外输入的时间参数T来控制网络应该关注整体轮廓还是局部细节。
Guidance
参考资料:Diffusion Models Beat GANs 论文,Classifier-free guidance 论文,大佬的blog
上文所述的扩散模型只能随机生成图片,然而我们在使用时一般希望能够指定生成的内容,比如给网络提供额外的输入y作为条件,比如物体类别或者文字描述。Classifier guidance 就是额外训练一个分类器,从而将无条件的扩散模型变成有条件的扩散模型。

因此,如果我们在无条件模型的基础上,训练一个分类器,这个分类器能够对不同等级的噪声图像进行分类,那么分类器训练完成后,分类器反传到图像的梯度也可以优化这个图像,使图像向着指定类别的方向去优化。
Classifier guidance 的优点是我们不需要finetune无条件的扩散模型,只需要额外增加一个分类器即可实现出有条件的扩散模型。因为扩散模型通常是在很大的数据集上训练的,重新训练一个有条件扩散模型非常耗资源,而classifier guidance只需要额外训一个分类器即可将已有的无条件扩散模型变成有条件模型。Classifier guidance 的缺点是这个分类器也比较难训练,因为需要让分类器学习各种带噪声的图像输入。
Classifier-free guidance对有条件的扩散模型进行直接训练,而不需要训练分类器。其特殊之处在于训练有条件扩散模型的过程中,在一些情况下将输入的条件屏蔽掉,相当于训练一个扩散模型同时支持无条件和有条件两种情况。在生成图像时,我们可以按一定权重比例混合有条件和无条件模型两种情况的输出,达到控制输出内容的效果,而不需要再单独训练分类器。
Classifier-free guidance的优点是不需要再训练额外的分类器。缺点是模型训练需要更多的资源;且生成图像时需要跑两次网络分别得到有条件和无条件的输出。
Latent Diffusion Model (Stable Diffusion)
Latent Diffusion Model 的原理很简单,其实就是将上面图像去噪的过程从图像像素空间的操作变成了隐空间中对图像特征操作。通过将图像投影到隐空间可以更多关注一些语义方面的特征而非像素本身,并在隐空间进行操作可以大大降低计算资源的要求,使落地更简单,让普通人在一般的GPU上也可以用了。投影到隐空间的过程在论文中对应Perceptual Image Compression章节。这一部分可以通过训练自编码器的Encoder和Decoder来实现。

有了Encoder和Decoder,就可以在隐空间中添加噪声,并训练一个扩散模型在隐空间中去噪。作者使用了一个类似UNet的架构,其输入为隐空间的带噪声特征图 、时间t以及条件y ,其中条件y可以是来自语言、图像、语义图等等。作者还在UNet中加入cross-attention层,其中Query是来自图像特征图,KV来自输入条件(的编码)。
至于比较火的Stable Diffusion主要是Latent Diffusion Model 的一种实现形式。比如v1版本的模型使用VAE来投影图像至隐空间,并从隐空间采样并恢复图像(用文本生成图像时只需要VAE的Decoder);条件输入使用了CLIP ViT-L/14作为文本编码器;UNet大约是860M参数量(以float32 的精度存储大概需要 3.44GB 空间)。
Finetune相关技术
扩散模型Finetune相关的技术都是为了实现这样的效果:通过对预训练好的大模型使用少量样本进行finetune,使模型可以根据用户自定义的提示词生成个性化的图像,同时还基本不会影响模型在其他方面原本生成能力。例如,我们希望用动漫角色的名字作为自定义的提示词,同时只提供最多几十张这个角色的图片,使微调后的模型能够在给定动漫角色名字的情况下生成该角色的图片。但同时我们还希望预训练模型保留原本强大能力,不希望模型永远只会输出这个角色的图片,即当我们去掉该角色的提示词时,需要模型能生成和原模型一样的图片。
Textual Inversion (TODO)
参考资料:https://textual-inversion.github.io/

Textual Inversion的原理非常简单,该方法只向Text Encoder中增加了自定义的token,而模型的其他模块如Stable Diffusion的UNet和VAE均不会改变。
具体来说,一般我们使用的Text Encoder如CLIP的过程是先将输入的文本打散成一个个token,每个token会对应一个固定embedding(如512维向量),类似于一个字典。Textual Inversion做的就是在字典中新增一个token,这个token可以对应自定义的概念,比如动漫的某个角色名字。通过给网络这个角色的图片,以及带有这个角色名字的文本描述,来优化这个token对应的embedding(即512维向量)。
那么训练完成后,只要我们的描述文本中有这个角色名,那么相应的embedding就会被取出并一起送到后续的网络中,这样就可以生成对应角色的图片。同理如果从描述文本中去掉这个角色名,相应的token没有被激活,网络还会像finetune前一样正常输出。
Dreambooth
参考资料:https://dreambooth.github.io/

Dreambooth提出了一种finetune流程,通过Prior-Preservation Loss,使我们finetune后的模型能够在生成自定义图片的同时保留预训练模型保留原本的性能。
方法的大致流程如下(其他的细节可参考原论文):
采集数据:包括少量的(如几张或几十张)某种狗的照片(比如你自己的狗)以及你自定义的提示词(比如狗的名字+"dog")也可以包含其他相关的描述,如照片中狗的动作,所在的场景等。
用自定义的样本对应的提示词输入给网络,生成图像,将对应的自定义图像作为真值计算loss。
将提示词中自定义的部分(比如狗的名字)去掉,只保留“dog”及其他描述,输入给原本预训练模型及finetune模型分别生成图像。
原本预训练模型输出的图像作为真值,和finetune模型生成的图像计算Prior-Preservation Loss,来约束finetune的过程,使finetune模型能保留原模型的能力。
LoRA
参考资料:论文(原本是用于大语言模型的) 大佬的代码


Controlnet
参考资料:github 论文

原理介绍
Controlnet主要的功能是将大模型(比如预训练Stable Diffusion)在自己的小数据集上finetune,使新的模型能够适应自己的数据集(比如自定义的风格/任务)且对原模型的影响较小(还保留原模型绝大部分能力)。
主要原理如上图所示。将我们需要修改的网络层复制一份,并在前后分别增加参数全为0的卷积层,作为右侧的 trainable分支。trainable分支的输出和原来的网络输出加在一起作为最终的输出。由于复制出来的网络前后的增加的卷积层参数均为0,输出也一定是0,因此整个网络的输出还和原来的网络完全一样。在自己的数据集训练时将原来的网络固定,只训练trainable的那个部分即可。
相关资源
Controlnet的作者为Stable Diffusion训练了各种模型,通过不同的方式控制生成的内容,例如通过canny边缘、人体位姿、甚至线稿等等,可以用webui方便地使用。至于diffuser也有大佬写了代码。
我们可以使用这个工具制作自己想要的pose,自己画人体姿态或者从图片生成姿态,例如

当然我们还可以将ControlNet与LoRA结合起来用,例如
