前言
最近训练了在搞公司项目的时候,训练了一个大头玩具的LoRA,其中发现了不少有意思的现象。记录的同时,顺便分享给大家。说是指南,但谈不上,只是尽可能将做过的内容讲解出来。
目标
生成一个固定造型的大头玩具,头的部分:身体的部分 = 1:1。同时具有玩具原来的风格。
数据集
数据集如下
标注
图像生成标注的原则:“描述你想要控制的属性,希望固定生成的内容,只需要加一个触发词,不要额外描述。”
标注格式为:触发词 + 描述的属性。目前主流的模型为Flux,但是速度比较慢,所以我也尝试了SDXL和SD1.5。这三个模型对自然语言的描述要求不同。
- SD1.5:最早的模型,以tagger为主,基本不理解caption。描述格式为[“man”, “dog”, “beach”, “sunset”, “walking”]
- SDXL:可以理解tagger和部分caption。过长的标注内容无法理解。描述格式为"A man walking his dog along the beach during sunset."
- Flux:可以理解tagger和caption。但官方推荐使用caption,由于有两个text encoder,对于很长的句子也能理解。
由于玩具都是在正中间,背景都是纯白色的,姿势和身体比例基本一致,玩具的风格是塑料材质 + 特定的设计风格(我也说不上是什么风格)。能控制的其实是脸的样子、肤色、发型、衣服样式、鞋子样式。
关于触发词的选定,越冷门越好,怎么测试是否冷门呢?可以用同一个触发词,不同的随机种子,如果每次跑出来的图,没什么相似性,说明这个词在这个Base model中是比较少用的。我这里选用PNBPT作为触发词。
以下示例为:
PNBPT toy. black hair styled in a short, curly manner, is facing the viewer with a neutral expression. black skinned with black hair and a little beard, wearing a white jersey and white shorts, shoes is yellow.
the chest area of the jersey displays the green text “BUCKS”, and the number “34” centered below it. the edge of the jersey sleeves are green.
其中还有一些技巧,后面再补充。
训练
我是使用了kohya-ss/sd-scripts的仓进行训练。当然有些读者可能用的是秋叶的炼丹炉,或者其他的工具如AI-toolkit。这些都不重要,原理都是一样的。利用Dreambooth的模式注入特定的概念,可以训练LoRAs,也可以全量微调。这里顺带解释一下,LoRA本身是一个低秩分解技术Low-Rank Adaptation of Large Language Models。它不是指模型,也不是训练手段。只不过现在习惯了,所以常说训练一个LoRA。
目前主流的特定概念注入思想就是Google出的Dreambooth技术,只需要利用少量图像就可以训练出特定的概念。当然,它里面还有正则化防止过拟合,这里就不展开说了。
sd-scripts有使用tensorboards记录loss和训练参数。训练曲线如下:
我主要对比了三种参数组合,Learning Rate,Batch Size,Epoch。为什么没有对比其他参数呢?一个是,其他领域的经验(目标检测、图像分类等),基本不用管其他参数。而且Loss基本可以反应模型的好坏,Loss越低,图像生成质量越好(仅仅针对这个项目而言)。
另外我还对比了不同的打标方式,一个是详细标注,如上一章节说的一样。另一个是,所有图片都用一个触发词标注(强迫模型只学习固定的风格)。比较有意思的一个发现是:图片不变的情况下,无论用标注内容怎么变,训练曲线趋势都是一样的。
这是由于我在训练过程中,不断调整标注内容,希望模型表现得更好,但其实Loss趋势都差不多。所以我总结出一个方法,如果拿到一个新的数据集,希望快速测试出最佳参数,那就用一个触发词作为标注训练就好了。
实验与结果
颜色控制
由于我们在标注中,定义了肤色,而数据集中也对应有不同肤色的玩具。那么模型是会根据规定,将文字和属性绑定。但要注意的是,我们只训练了Unet的部分,CLIP部分是没训练的,也就是说Text encoder和Image encoder是没有训练的,所以Image embedding和Text embedding是固定的。但是虽然Unet只负责生图,但Unet也有一定特征绑定能力。
数据集里面其实没有黄皮肤的图片,只有黑和白。但是由于Base Model的泛化能力,LoRA也能生成黄皮肤的球员。
Learning Rate,Batch Size,Epoch参数对比
明显最右边的结果会更好。
单个触发词
单个触发词版本的LoRA,它Loss曲线虽然不错,也能生成质量很好的结果,但是它的文字控制力是比较差的。用于快速验证参数是可以的。
不过以防万一,我也输入了完整的提示词去测试。
结果会比只用一个触发词控制的效果好很多。但其实从结果也能看到,很多属性的控制没起作用,比如多余的篮球,衣服的颜色并不是完全白色的,肤色也是错的。
对比完整的caption版本:
其一致性会比单个触发词的版本要好不少。但其实在发型的部分,也不是很一致,当然和我们提示词的完整性有比较大的关系。
在后续的换脸任务中,这个不稳定的现象也会体现出来
比如我鸡哥莫名其妙多了胡子,副Goat鞋子很大(可能是他的大胡子给了一定的参考)。
过拟合问题
有过其他模型训练经验的小伙伴肯定都知道过拟合,比如行人检测如果过拟合,会把一些不是行人的物体,误检测为行人,那么图像生成领域的过拟合是什么样的呢?
上面这个例子就很好的显示了过拟合的问题。当我提示词中一输入"curly hair",球衣的颜色,鞋子颜色,胡须类型都变了。模型像是记住了某个玩具的样子。
我分析,为什么会有这种问题呢?理想的特征绑定,是希望它像下图这样的。
但实际上大多数情况下是这样的:
某些词没有得到正确的绑定,而某些词又绑定了太多特征。而且我发现,过拟合的问题总是发生在脸部相关的特征,而衣服,鞋子,肤色的结果总是符合文字描述的。所以我观察了一下,球衣,球鞋,肤色这些属性,在数据集中能找到重复的样例。但是脸部的特征,很难找到一样的(每个人的无关都不同),所以本质上还是过拟合的问题。相似的样本太少,模型只会记住固定的样子。
多触发词
缓解过拟合的问题,如果参考之前的经验,无非是Early Stop,DropOut和增加数据集。但其实除了Early Stop,其他部分我都做不到。LoRA的结构是定好的,数据集是由设计师提供的,没有多的。所以我这边想了另一个方法,将脸部的特征绑定到另一个触发词上去。
什么意思呢?即我除了PNBPT这个触发词是生成玩具的,我希望有另外一个属性可以控制人脸的样子。例子如下:
PNBPT toy. ANTETOKOUNMPO face. black skinned with black hair and a little beard, wearing a white jersey and white shorts, shoes is yellow.
PNBPT toy. EMBIID face. black skinned with black hair and full beard, wearing a blue jersey and blue shorts, shoes is white.
就像一个人,你用各种文字描述,“浓眉大眼”,“高鼻梁”等词语,都是难以让人在脑海中有一个比较具体的形象。但是如果你之前看过这个人,也知道他的名字,那很快就能想象出来。所以这里直接用球员的名字作为额外的触发词。
基于这个原则,LoRA训练完后,生成结果的一致性更好了。不过衣服的样式也还是被绑定了。当然如果你不指定是谁的脸,模型就会随机生成了。
不过到这里,我就没有继续了。因为后续还需要换脸和换衣服,所以只需要把脸部、衣服和风格的属性解耦就行了。
这个多触发词在Dreambooth中也是有提及的,不过在我实测下来,对于不同玩具,还是要数量多。如果每个球员只有一个对应的玩具,效果也是很差的。我这一批次中,一个球员会有主队、客队的球衣。所以样本还是有重复的。
代码
我从kohya-ss的仓库中frok了一份出来https://github.com/Runist/sd-scripts/tree/sd3,基本只在sd3分支下开发。config文件中设置好了SD1.5/SDXL/Flux的训练参数模板,还有数据集参数模板,对应也有训练命令的启动脚本。如果有帮助,可以给我点个star,谢谢各位老板。