Sign In

[稍硬核] 常见但冷门的模型训练技术和原理

[稍硬核] 常见但冷门的模型训练技术和原理

介绍

本文将介绍

  • 数据集:加工、维护和处理;

  • 训练参数:原理和用法;

  • 模型融合:原理和算法设计;

  • 负面文本嵌入:训练策略和使用技巧;

1 数据集

1.1 训练器

训练开始前,模型训练脚本会接手处理的数据集。流程为:加载数据 => 分桶图像 => 缓存潜变量。下面逐一介绍。

1.1.1 加载数据

加载数据不仅是加载,还会分析。脚本会读取图像分辨率,制定训练策略。以 Kohya-ss 为例,以下数据信息将被采集或指定:

  • 图像的路径

  • 图像的标注

  • 图像的分辨率

  • 图像的重复次数

  • 图像缓存文件的路径

  • 图像的缓存

1.1.2 分桶图像

自动长宽比分桶,Aspect Ratio Bucket,简称 ARB,是一项 允许不同宽高比分辨率的图像存在于同一数据集中进行训练 的技术。它将宽高比接近的图像分组到一个被称为“桶(bucket)”的结构中,各桶分别训练。

ARB 百利而无一害。它使图像无需经过裁切而得以完整地投入训练,不仅缓解了由于图像裁剪导致的畸形问题(例如断剑、人体畸形、裁切不良等),还节省了裁图的工程量。目前为止无明显缺点。

图 1:SDXL1.0 模型训练中所使用的 ARB 分桶表。他们获得了很棒的结果。

SDXL 架构的模型将训练图像的分辨率作为额外的特征来训练模型,其中使用了位置编码(positional encoding)技术。这导致 SDXL 在生成陌生分辨率的图像时表现较差。因此,在使用 SDXL 架构的模型进行推理时,需要尽量使用模型所支持的分辨率。关于位置编码的介绍请参见后文。

分桶算法

分桶算法的基本思想是,在不过度破坏原图比例的前提下,将原图缩小至某个特定的像素量。这里定义:

图像的像素量 = 图像的宽 x 图像的高

在 ARB 中,每个“桶”是一个 特定的分辨率,例如 512x768,所有训练集中分辨率最接近 512x768 的图像都会被扔进该桶。下文将从易到难地介绍分桶算法的实现。

分桶算法的设计必须遵从以下几个前提:

  1. 控制桶数:桶的数量不能过多,也不能过少;

  2. 比例不变:缩放后的图像与原图比例差距不能过大;

  3. 尺寸一致:所有缩放图像的像素量需要相近;

  4. 不能放大:不能放大图像。

对于前提1,桶过多,每个桶中图像少,其中多余不足一个批次的图像将以更低的批次大小训练,从而干扰训练且拖慢训练速度;桶过少,每个图像可供选择的宽高比变少,有的图像不得不经过不良缩放或裁切以适应桶的尺寸。经验法则是,训练图像越多,桶可以越多,反之

分桶算法一

  • 输入:最大分辨率 (max_w x max_h),一些图像

  • 对于每张图像,将图像 等比缩放 至像素量恰好不超过预设的最大分辨率后,按照将缩放后图像的分辨率,将它归入桶中。

分桶算法一简单地把图像缩放到像素量恰好不超过预设的最大分辨率。它不会损伤原图的比例,但需要大量的桶,来承载各种比例的图像,这违背了前提1。

对此,我们优化分桶算法一,减少桶的数量。我们让输出图像的宽和高均能被某个整数 d 整除。这里,d 常为 32 或 64。

分桶算法二

  • 输入:最大分辨率 (max_w x max_h),一些图像,除数 d

  • 对于每张图像:(i) 首先计算出像素量恰好等于最大分辨率,且宽高比等于该图像分辨率的尺寸,记为宽 w 和高 h。(ii) 之后,逐渐减小 w 和 h 至恰好能被 d 整除,记该衰减后的宽和高为 w' 和 h'。(ii) 最后,将图像缩放至 w' x h',并放入桶 w' x h' 中。

分桶算法二将宽高比相近而非相同的图像划入同一个桶。

在此基础上,我们添加不可放大图像的约束,即分桶算法三。

分桶算法三

  • 输入:最大分辨率 (max_w x max_h),一些图像,除数 d

  • 对于每张图像,在分桶算法二的基础上,额外判断原始图像的像素量是否已经小于最大分辨率,若是,则跳过算法二的步骤 (i),直接将 w 和 h 设为原始图像的尺寸。

在特定情况下,例如训练使用了位置编码的模型,或是想要复刻某个模型的分桶,我们希望图像仅被分入固定的桶中,以此提出分桶算法四。

分桶算法四

  • 输入:一些桶,一些图像,除数 d

  • 定义宽高比为 宽除高,则每张图像和桶均具有一个宽高比。分桶时,对于每张图像,为它匹配 宽高比最接近的桶 并放入。如果图像分辨率不匹配,则将图像缩放为桶的分辨率后再放入。

Kohya 系训练脚本是一种稍微更复杂的分桶算法。博主 @青龙圣者 和 @秋叶aaaki 的训练脚本均属此类。这里将介绍帮助读者手算的简化算法,原版的算法在计算细节上有优化。

Kohya 分桶算法 - 简化版

  • 输入:最大分辨率 (max_w x max_h),一些图像,除数 d

  • 记 max_area = max_w x max_h。

  • 对于每张图像,如果该图像的宽 img_w 乘高 img_h 大于 max_area,记 img_area = img_w x img_h,那么该图像将 能被 d 整除地 被分入桶 ( img_w × (max_area/img_area) x height × (max_area/area)) 之中。

  • 如果该图像的像素量小于 max_area,则该图像将 能被 d 整除地分入桶 img_w × img_h 之中。

注:此处的分桶算法为禁用 upscale 的情况。这种情况下,另两个分桶算法的输入,即最大分辨率 max_resolution 和最小分辨率 min_resolution,其实不会产生实际效果。

有代码基础的读者可尝试用类似的算法模拟自己所用脚本的分桶方式,以提前得知图像经过处理后的状态,检查是否有不良缩放和裁切。

1.1.3. 缓存潜变量

缓存潜变量,cache latents,是以内存换时间的训练提速方式。SD 模型最终训练的是潜变量,潜变量由 VAE 编码图像而得到的,可以简单地认为潜变量为 缩小原始图像至原来的 1/8 得到的压缩图像(具体缩放倍数取决于 VAE)。

在不缓存潜变量时,每一步训练都需要 VAE 解码训练图像来获得潜变量图像,非常耗时。将解码后的潜变量预先存储起来,方便训练期间调用即缓存潜变量。存储这些潜变量需要额外的显存开销,因此这一技术是以内存换取时间。

普通的缓存潜变量仅在运行期间单次有效。因此,将潜变量保存为本地文件后,后续训练中便无需再次缓存。

注:缓存潜变量的优化策略

Kohya-ss 似乎缺少高效的潜变量缓存策略,例如,多卡并行的缓存。以下是一些似乎可以优化的方案:

  • 支持 多卡并行 缓存;

  • 支持 单独地 缓存潜变量,而非在训练脚本开始后才缓存——不需要将整个模型全部加载;

  • 直接将缓存文件直接 视为图像数据 加载,而不是先加载图像,再加载潜变量缓存;

  • 在缓存潜变量时,将缓存结果 异步地 写入硬盘;

  • 将潜变量的加载后移至数据 第一次被需要时,而非训练开始时全部加载——防止在花费长时间去加载大数据集的潜变量后,训练一开始就报错重来。

1.2 图像数据

1.2.1 数据缺失

在训练某种概念,特别是风格训练时,往往会出现训练图像数量不足的情况。在训练集有限的情况下,我们常考虑用以下方法扩充训练集:

  1. 在训练中启用 随机水平翻转。这将在训练中随机水平翻转训练图像。该参数应当总是对训练有帮助的。

  2. 特写裁切将分辨率过大的图像裁切出各种分辨率下的特写。例如,从远到近,可将一张图像加工为整图、人物特写、半身特写和面部特写。

    其中,尽量保证裁切后分辨率不小于训练时的分辨率,且裁切出的内容能独立成图。

    该方法在训练图充足时也是有效的,特别是对于风格学习。推测是因为越贴近原图分辨率的训练图所带有的风格信息越丰富。

    裁切图像的有用工具如网站 Birme, Fotor;软件则如 Microsoft Photos, Photoshop

  3. 相似图补充添加类似风格的其他图像。如用写实填充写实,用油画填充油画。

    您也可以使用高质量 AI 图像去填充。

    但必须与原图具有相当的相似度,否则将画蛇添足。

    一种道听途说的方法是将这些补充图像用作正则化图像参与训练,而不与真正的训练集混淆,即以一个更低的学习率对待它们,而与原风格越不相似的图像,学习率应该越低。

1.2.2 画面损伤

在训练中,SD 模型会敏感地学习到图像上的无用信息,例如污渍、水印、不完整的剑等等,为成品模型带来缺陷。如果您坚持使用这些图像,那么学习如何正确应对这些情况对您至关重要。

除了裁切和手绘修补,我们还可以采用内补绘制来修复图像损伤。现存的内补绘制模型能应对大部分情况。以下是一些常用内补绘制方法和模型:

  • PS 内容识别填充:PS 自带的的内容识别填充功能将能解决大部分的小范围图像损伤,如去除小型的水印或多余的物体等。该功能识别选区附近的内容来填补所选区域的内容。如果填补的区域周围为纯色或为具有规律的图案纹理的效果更佳;

  • PS 魔术橡皮擦:同上,为内补绘制算法。更擅长“去除”某个物体。较低版本的 PS 中似乎无此功能;

  • PS AI:与 PS 内容识别填冲相比,AI 识别填充更胜一筹,效果是所有方法中最好的,且能够修补大范围损伤。缺点是需要付费,且速度较慢;

  • SD 内补绘制模型:众所周知。速度慢,效果一般,未来可期;

  • OpenCV:速度快,效果差,需要一定的编程能力。模型对应 Python 的 cv2 库。使用参考见:OpenCV: Image Inpainting

  • Lama Inpainter:速度适中,效果一般,需要较强的编程能力。推荐用于 批量地重绘某个区域,在给定一批图像时,该区域可设为某个固定区域,或由某个目标检测(如 YOLOv5)或分割模型选择。

除了正确地处理图像损伤,在大量图像中检测它们也非常重要。以下提供一些检测图像内容损伤的思路:

  • 利用脚本检查图像标注:要求一定的编程基础。如果对数据集进行了标注,则可利用脚本检测图像标注中是否存在与内容损伤有关的关键词,例如“watermark”, "signature"等。

  • 利用 AI 技术检测图像内容:要求较强的编程基础。可以使用一些 AI 模型检测图像是否包含内容损伤。如 YOLOv5 等。

1.2.3 图像重复

重复的图像对训练几乎没有影响。可如果您执意想要去重,且具有较强的编程基础,则可以考虑使用本节将介绍的算法思路。

考虑到训练集图像较多,若使用常规的相似度比较算法则会变得复杂且耗时。因此,本节使用 感知哈希算法(Perceptual Hashing Algorithm)。感知哈希算法为每个图像计算一个哈希值,称为 感知哈希(Perceptual Hash),作为图像的“身份证”。相似图像的感知哈希非常接近。不同感知哈希值的差异大小通过汉明距离(Hamming Distance)衡量。缩放图像几乎不会改变感知哈希,而裁切图像会。

一个感知哈希通常可以表示为一个 16 进制的字符串,该字符串可作为哈希表(字典)的键。下文照此思路,提供一个简单的感知哈希去重算法,用于去重一些图像。对于重复图像,该算法删除分辨率较低的一者。

感知哈希去重算法

  • 输入:一些图像

  • 建立一个空的哈希表(字典)

  • 为训练集中的每个图像计算一个感知哈希,以其作为键,图像(或图像文件的路径)作为值,添加到哈希表中。在构造过程中,键冲突的两个图像即为完全重复的图像,保留分辨率高者即可。

注:无需通过计算汉明距离判断图像相似。因为感知哈希的精度受哈希尺寸的影响,所以,我们只需将感知哈希的尺寸由原先的 8 减小至 6,即可模糊化地去重。

1.3 标注

标注描述图像的内容,是训练中非常重要的部分,精细、准确、巧妙的标注将极大地助推训练。

一个优秀的标注通常应具有以下标准:

  • 准确:标注用词准确,没有拼写错误;

  • 精细:不多不少,恰好能描述图像中的所有主要对象;

  • 巧妙:标注的方式适配特定任务(细节见下文)。

1.3.1 标注在训练中的作用

文生图模型训练的每张图像对应一个标注。在训练中,使用标注作为提示词让模型生成一张图像,这张生成的图像与训练的图像相似度越低,即误差越大,则模型会朝着减小误差的方向去学习。训练中,模型会去关联提示词和画面中的物体。

基于上面的描述,让我们看看错误的标注对训练的危害。

当标注过于简单,未能涵盖画面中的主要内容时,模型将尝试使用这段简单的标注作为提示词生成图像。但是,由于提示词简单,这张图像的内容将与原图像相差甚远。但是,模型必须强迫自己用简单提示词生成的图像与训练图像对齐。因此,这样训练出的模型将具有使用简单提示词生成复杂图像的能力,可这也容易导致模型生成出多于提示词描述的内容,且在较长提示词下的标签较差。

反之,当标注过于复杂,训练时,生成图像的内容将少于训练图像,多余的标注难以被准确地分配到画面中的特定物体,从而导致语义和内容的偏差过大。

1.3.2 标注的种类

标注大致可分为两类:自然语言标注 标签标注

常见打标器

自然语言打标器

  • BLIP:传统的打标器,速度快,但内容细节欠缺严重;

  • GPT3.5:待测试……

  • (推荐)GPT4V:质量最高,价格最昂贵的打标器。

  • (推荐)CogVLM:GPT4V的平替,质量高;

标签打标器

  • DeepDanbooru:质量一般,速度很快;

  • (推荐)WaifuTagger:质量高,标签准确,速度快。唯一缺点是支持的标签数量不足。

自然语言标注常能描述物体的位置关系,而标签标注名词密度大,造价便宜,容易修改和优化。

结合二者的优点,可采用自然语言+标签的标注策略。

1.3.3 如何正确选择标注形式?

包括 Stable Diffusion (SD) 在内的文生图扩散模型有一个臭名昭著的特性——模型会过度服从所训练的标注。举个例子,如果您在模型训练前,在所有数据标注前面添加一个 “$” 符号。那么,当您使用训练出的模型做推理时,如果提示词的第一个字符不是 “$” 符号的话,生成效果将大打折扣。

因此,训练和推理所使用标注的形式都应尽量保持一致。这里,标注的形式例如语法习惯,排布规律,标签的顺序,分隔词、句的方式等等。

在讨论训练应使用何种标注前,理解标注选择的前提是至关重要的。

基于上面的原则,就容易理解目前存在的数据标注误区了。目前,常见的数据标注误区是:

i) “使用下划线分隔标注标签中的单词”。该习惯可能常源自于 Danbooru 的标签习惯。例如,使用 ganyu_(genshin_impact) 而不是 ganyu (genshin impact)。这是因为,文本编码器 CLIP 模型和训练的基础模型是在空格形式的标注中训练的,而不是下划线形式。因此,空格形式标注的效果通常要优于下划线形式

ii) “在训练数据的标注中应用括号转义”。转义括号即在标注中出现的括号前添加反斜杠 “\”,以防止括号被解释为加权括号。例如,ganyu \(genshin impact\) 就是一个带转移括号的标注。转移括号应当被用在生成中,而不是在训练中。这是因为,训练脚本通常并不会将训练数据标注中的括号解释为加权,也就不需要转义括号。而如果对括号进行了转义,那么反斜杠和括号就会一同被视为标注的一部分,从而导致训练与预期不匹配。因此,当训练器不将括号解释为提示词加权时,训练数据应使用未经过转义的括号

下面一个例子,帮助读者更好地理解括号转义的影响。当某炼丹师X使用带转义括号的标注,比如说 ganyu \(genshin impact\) 训练模型时,炼丹师X期望标注送到模型眼力是 ganyu (genshin impact) ,但模型看到的实际上是 ganyu \(genshin impact\),即与原标注相同,比预期多了括号。在推理时,炼丹师X使用提示词 ganyu \(genshin impact\) 生成图像,他通过使用转义括号避免加权,他所期望模型看到的提示词和模型实际看到的提示词一致,都是 ganyu (genshin impact)。然而,模型从训练中学到的正确形式应该是 ganyu \(genshin impact\)。因此,炼丹师X的提示词并不能正确地发挥作用。

iii) “标签标注总是优于自然语言标注,或反之”。选用自然语言作为标注还是标签作为标注没有绝对的优劣。在对使用自然语言标注的模型进行推理时,应尽量使用自然语言作为标注,反之亦然

上面的结论只管但有效。除此之外,在训练中,具体标注形式的选择还应该考虑基础模型的影响。这是因为,在训练时,我们衡量训练进度的好坏是通过将数据标注作为提示词输入基础模型后,对比模型输出与原图的差异。换句话说,训练会引导基础模型向数据(图像+标注)的方向靠拢。这么一来,如果数据标注与基础模型习惯的标注形式不同,那么模型会持续认为自己与数据的期望不符。这种情况下的微调不仅会把基础模型的语言习惯“拽”向少量的数据,同时还妨碍了基础模型利用已有知识来学习和关联标注和数据。

因此,训练的标注类型应尽量服从基础模型的提示词习惯。例如,当基础模型偏好自然语言标注时,所选标注也应当是自然语言;当基础模型偏好用下划线分隔标签时,数据标注也应当用下划线分隔标签。然而,当训练数据较多,且训练的是大模型时,则可以使用与基础模型不同的提示词习惯。这是因为,大规模的微调能够大幅调整基础模型的知识,“掰弯”基础模型。

2 WebUI 的部分技术原理

2.1 提示词加权

提示词加权(weighted prompt)即使用扩号来提高某段提示词的权重。例如 male focus of (ironman:1.3)ironman 的权重提高到了原本的 1.3 倍。不过,加权一段提示词也相当于变相地降权其他提示词。

提示词加权的发生在提示词处理时。通常,提示词从文本在输入模型前会经过分词器和文本编码器的处理。具体地,一段文本提示词会首先被分词器翻译为词元(tokens),词元再被文本编码器转换为文本嵌入(text embedding)以输入模型。

提示词加权方式作用于词元转文本嵌入阶段。原先,所有词元具有相同的权重(1.0),而加权算法会将解析出需要加权的词元们乘以对应的权重(例如,ironman 的权重由1.0变为1.3),之后再通过计算,保证加权前后所有词元权重的平均值相等。

2.2 实时预览生成进度

在图像生成过程中,我们无法直接观测到潜变量。只有在经过 VAE 的解码后,我们才能查看图像内容。VAE 的解码大约需要 2~3 个迭代步数的时间,每步迭代都直接使用 VAE 去解码潜变量显然是不合适的。

WebUI 预览生成图像的过程使用了近似 VAE 解码技术。该技术使用一个微型的 VAE,称为 VAE-Approx,快速解码生成过程的潜变量为质量较低的图像以供预览

3 模型训练

3.1 高级参数讲解

请转至文章:[硬核] 扩散模型训练和微调的高级参数讲解 | Civitai

4 文本嵌入

一个文本嵌入(英文名 text embedding,下文简称 emb)模型装载了某个文本概念。它诱导 SD 模型生成该概念的内容。如上所述,一段文本通常会经过分词器变为词元,再经过文本编码器变为文本嵌入,最后输入模型网络,即UNET。一个 emb 文件装在的就是输入模型前的那段文本嵌入。因此,emb 无法生成那些 SD 模型本身不知道的内容。

本节将介绍以下有关文本嵌入(Text Embedding)的内容:

  • 负面文本嵌入

  • .pt 和 .safetensors 的相互转换

  • SD1.5 和 SDXL 文本嵌入的相互转换

为了方便,以下文本嵌入简称为 emb。

4.1 负面文本嵌入

负面文本嵌入,简称 负面 embneg emb,是正常 emb 的逆向使用。但如今,负面 emb 的意义已凌驾于正常 emb 之上。广为人知的负面 emb 如 EasyNegative 和 badhandv4 等。

负面 emb 的思路非常直观,虽然人们难以教会模型关于“好”的概念,因为“好”的定义相对模糊,但“坏”的定义更为具体且容易获取,例如畸变、伪影、低分辨率等。因此,负面 emb 的职责就是告诉模型什么是糟糕的——类似施肥。

本节将介绍负面 emb 的炼制过程和基于经验的推荐参数。

4.1.1 生成数据集:挑选工业原料

既然要教会模型什么是“糟糕透顶”的,就需要准备丑陋至极,不堪入目的训练集。那么,什么才是所谓的“丑陋至极,不堪入目”呢?根据经验法则,满足以下条件的图像/训练集最有利于训练:

  • 为了避免干扰正常概念,图像应基本不具备任何正常的概念。例如,大量的 画面损毁,人体畸变 等。

  • 为了不改变风格,且使 emb 模型有足够的的泛化能力,训练集图像应具有所训练概念之下的各种风格、色彩、对比度、亮度、细节复杂程度、构图、人物动作。

  • 训练图像越多越好。通常来说,应多于 500 张。

训练图像可直接由 SD 模型生成。为了使生成的图像尽可能“难看”,可以选用 SD1.5 类型下较差的模型,并在提示词中填写如“low quality, worst quality, deformity, bad anatomy, distorted fingers”等提示词,同时调低采样步数,换用效果差的采样器等。

为了使训练集多样,您可以在不同的提示词、采样步数、CFG 等参数下生成尽量多的图像。

您可以人工地“破坏图像”,例如,常用的生成分辨率是 512x512。可若要训练“低分辨率”的概念,则可生成低于 512x512 分辨率的图像后再放大为 512x512,即可制作低分图。

发挥您的作图技巧,生成最丑陋,最不堪入目的图像吧!

4.1.2 训练模型:制作化肥

训练负面 emb 模型与训练一般 emb 模型类似,可以为数据集打上标签。向量数设置为 2~16 左右。初始化词元可以置空,也可以填写为所训练概念,但要注意所填写的内容所占用的词元(token)数应与 emb 模型的向量数匹配,您可以通过一些工具来查看某一句话的词元数,例如 WebUI 的 tokenizer 扩展等。

按照经验,负面 emb 应以更高的学习率训练至过拟合,以免泛化过多而误伤其他概念。通常,起始学习率应介于 1e-3~1e-4 之间(即 0.001~0.0001),终止学习率则介于 1e-5~1e-6 之间。训练至少 1000 步,直至 loss 收敛。

4.1.3 模型评估:肥料的好坏

一个好的负面 emb 的应做到如下几点:

  • 有效:修复了所训练的负面概念;

  • 不越俎代庖:不误伤其他正常的概念;

  • 小而美:不占用过多词元数;

  • 泛化:在各种 SD 模型上均有不错的泛化能力(较难实现);

4.2 模型格式的转换

TODO

4.3 模型种类的转换

如今,基于 SDXL 的 emb 模型(简称为 embXL)数量较少,而 SD1.5 具有很多优秀的 emb 模型(简称为 emb1.5)。一个 embXL 包括 CLIP_G 权重和 CLIP_L 权重两部分,其中,CLIP_L 的权重与 emb1.5 具有相同的形状。也就是说,embXL 的其中一部分是 emb1.5 的结构。这使得二者之间相互转化成为可能。

emb1.5 转 embXL

由于 SDXL 具有两个文本编码器,无法完美地将 SD1.5 的 emb 模型转移到 SDXL 上。但是,转移相同部分是可行的。方法是,拷贝 emb1.5 中的权重到 embXL 的 CLIP_L 权重上,并用零来补全 CLIP_G 的权重

embXL 转 emb1.5

embXL 理论上能够无损地转换为 emb1.5。方法是,提取 embXL 中 CLIP_L 上的权重,将其原封不动地拷贝到 emb1.5 上,即可完成转换。

5 模型融合

5.1 模型权重的储存

AI 模型的权重通常储存在一个哈希表(字典) state_dict 中。state_dict 的键为权重名称,值为权重。按照某一比例混合 state_dict 各层的模型权重即为模型融合。

SD 1.5 模型权重储存格式

在 state_dict 中,文本编码器的权重储存在含有 cond_stage_model.transformer.text_model 的键之中,共197个这样的键。

UNET 的权重储存在含有 model.diffusion_model 的键之中,共有686个这样的键。其中,IN 层为 model.diffusion_model.input_blocks,共248个;OUT 层为model.diffusion_model.output_blocks,共384个;MID 层为 ,model.diffusion_model.middle_block共46个。

VAE 储存在含有 first_stage_model 的键之中,共有248个这样的键。

SD XL 模型权重储存格式

对于 SDXL,含有 conditioner.embedders.0.transformer.text_model 的键对应文本编码器 CLIP_L,即 SD1.5 中的文本编码器,共有197个这样的键。含有 conditioner.embedders.1.model.transformer 的键对应 CLIP_G,共有 385 个这样的键。

UNET 的权重的储存格式与 SD1.5 模型相同,IN, OUT, MID 层各有 574, 868, 226 个键。

VAE 储存在含有 first_stage_model 的键之中,共有248个这样的键。

5.1.1 模型的加载和保存

本节将演示在本地使用python简单地加载和保存一个.safetensors模型。鉴于目前大部分模型都被封装为 .ckpt 或 .safetensors 格式,本节将不针对散装pytorch模型。

模型的加载和保存非常容易,只需使用safetensors包即可。您可以通过在终端输入 pip install safetensors 来安装。以 safetensors 格式的模型为例,以下代码实现了模型权重的加载和保存:

# 模型的加载和保存
from safetensors.torch import load_file, save_file
model_path = "Path/to/your/model.safetensors" # 设置模型路径
state_dict = load_file(model_path) # 加载模型文件为 state dict
save_file(state_dict, "Path/to/save/your/model.safetensors") # 保存模

若有需要,您亦可将模型储存为其他易读形式,例如,将模型的键储存为 json 格式以观察模型结构。

5.2 模型融合

模型融合分为三个步骤:(i) 加载模型的 state_dict;(ii) 融合对应键的值,即权重;(iii) 保存模型。本节将详细介绍如何实现模型融合。

5.2.2 分层融合算法

给定需要融合的层,在加载两个模型的 state_dict 后,我们只需按照对应层的键值,依次提取出模型在该层的权重,并进行融合即可。以下模型算法实现了将两个模型 A 和 B 按照一定比例融合。比例越大,融合结果越偏向于模型 B。

模型融合算法一

  • 输入:(i) 两个模型的 state_dict sd_Asd_B;(ii) 融合比例 alpha 和 (iii) 待融合的部分;

  • 定义融合后的新模型为 sd_M,首先将模型 A 的权重复制到 M 上。

  • 对于每个待融合的层,以它为键,在 sd_A 和 sd_B 中找出对应层的权重 w_A 和 w_B,计算 w_M = w_A × (1-alpha) + w_B × alpha。之后,将 w_M 赋值给模型 sd_M 的对应层。

  • 输出 sd_M。

这样,我们就完成了简单的分层融合。下文将介绍一些进阶的模型融合算法,例如 SuperMerger 插件中的 cosine_A/B、TrainDifference 等。这些融合算法有助于平滑融合曲线,使结果模型的权重不过度颠簸。

5.2.3 复杂融合算法

TODO

85

Comments