1. 先想清楚:4张图微调扩散模型到底在调什么
第一次看到“4张图片就能微调扩散模型”这个说法,我第一反应是怀疑。做图像生成这行的人都清楚,常规全量微调动辄要几百上千张图、几十小时算力,4张图听起来像是营销话术。但真动手跑过几轮之后,我改变了看法:4张图确实能做出可用的效果,前提是你得明白它到底在改模型的哪一部分,以及哪些事情它天生做不到。把这个边界划清楚,后面的参数、步数、数据组织才有意义,否则很容易练出一个“只认那4张图、其他什么都画不出来”的废模型。
这里说的“微调”,指的是一种轻量适配思路:底座模型的主体权重全部冻结,只在注意力层里插入一小对低秩矩阵,把权重更新量约束成两个小矩阵相乘的形式,参数量通常只占原模型的千分之几到百分之一。训练时只更新这几百万个参数,外加一个用于绑定概念的文本嵌入向量。因为可训练容量被压得极小,模型没有足够空间去“死记”训练图,反而被迫去学那4张图里最本质的共性特征,这本身就是一个很强的正则化手段。这也是为什么少量样本反而更容易练出泛化性——容量小,是限制,也是保护。
我把它理解成给底座模型贴了一张“便签”,而不是给它换一套“大脑”。便签上写的字很少,只能记一个概念名、一种风格基调或者一个主体的外观特征;换大脑则是全量微调,4张图的信息量根本撑不住,几十步之内就会出现明显的灾难性遗忘,原本会的构图、光影、材质全部塌掉。所以4张图的正确用法是概念注入,不是知识注入:你可以让模型记住“某个特定外观的主体”或“某种画风”,但没法让它学会一个它从没见过的新构图逻辑或者新光照体系。
从行业现状看,这条路之所以现在能走通,核心是三件事同时成熟了:低秩适配让显存门槛从多卡降到单卡、梯度检查点和混合精度让1024分辨率训练变得现实、以及图像编码器与文本编码器解耦之后,文本侧的概念绑定可以单独训练。三者叠加,4张图微调才从“理论可行”变成“笔记本上能跑完”。但我要提前说一句:能跑完不等于能出好图,后面几节我会把踩过的坑一个个摊开。
2. 4张图的信息量边界:能学到什么,学不到什么
2.1 一张图提供的信息维度拆解
要判断4张图够不够,得先知道一张图里到底有多少可学的东西。我通常把它拆成五个维度:主体形状与结构、表面材质与纹理、配色分布、光照与阴影关系、背景与构图习惯。一张图能清晰表达的,通常只有前三个维度加上部分光照;背景和构图是这张图的“偶然属性”,不是主体的“必然属性”。如果你的4张图全是在同一个房间里拍的,模型很可能把那个房间也当成主体的一部分学进去,这就是典型的背景污染。
所以选图的第一原则是让共性收敛、让差异发散。共性指的是你想让模型记住的那个东西——比如某个角色的脸型、发色、服装剪裁;差异指的是你不想让模型记住的东西——背景、光线方向、拍摄角度、画面构图。4张图里,共性要反复出现且高度一致,差异要尽量拉开。这个思路和传统机器学习里的数据增广是一回事,只是样本量小到极致时,每一张图的权重都被放大了,选错一张,整体效果就打对折。
2.2 4张图的黄金配额
我试过很多种分配,最后固定下来一套比较靠谱的配额,见下表。这套配额的逻辑是:先用两张图把主体的“正面识别特征”钉死,再用一张图补充侧向结构信息,最后一张图专门用来“打散”背景关联。少于4张时优先砍掉第4张,因为前三张决定像不像;多于4张时优先增加不同光照的样本,而不是增加同一角度的重复图。
| 图片序号 | 承担职责 | 选择要点 | 常见错误 |
|---|---|---|---|
| 图1 | 主识别图 | 正面或微侧,主体占画面60%以上,光照均匀 | 主体太小、背景杂乱 |
| 图2 | 主识别图备份 | 与图1同主体、不同表情或姿态,验证共性 | 与图1几乎重复,浪费配额 |
| 图3 | 结构补充图 | 侧面或半侧面,交代厚度与轮廓 | 角度过大导致特征失真 |
| 图4 | 去关联图 | 不同背景、不同光照,弱化场景绑定 | 背景仍是同一房间 |
这套配额不是教条。如果你要做的是画风微调而不是主体微调,配额就完全反过来:4张图应该覆盖同一画风下的不同题材(人物、静物、场景、特写),让模型提取“笔触与配色规律”,而不是记住某一个具体对象。画风微调比主体微调更吃样本多样性,4张是下限,能到8到10张效果会明显好一截。
2.3 明确告知自己:这几件事4张图做不到
我得把丑话说在前面,免得你练完发现不对劲又找不到原因。4张图微调做不到的事情至少有四件:第一,学不会一个全新的、训练图里没出现过的姿态或动作,模型只会把训练姿态换皮;第二,学不会精确的文字渲染,除非你的4张图里就有清晰文字;第三,学不会多主体组合,4张图只够绑定一个概念,硬塞两个概念会互相污染;第四,学不会高精度细节还原,比如衣服上的细小刺绣纹样,低秩容量根本装不下。
认清这四条,你在评估结果时就不会把“模型能力不足”误判成“参数没调好”,从而避免在一堆无效方向上反复试错。我自己最早就是不信邪,非要用4张图去练一个复杂机械结构,结果练了800步,出图永远是那4张图的翻版,换角度就崩。后来把目标改成“练这个机械的配色和材质质感”,同一批图、同一套参数,效果立刻就对了。目标定得越窄,4张图越够用。
3. 数据预处理:这步做不好,后面全是白工
3.1 分辨率、裁剪与压缩的三条硬规矩
预处理听起来是杂活,但它对最终效果的影响,我估计占到三成以上。第一条规矩:训练分辨率必须和底座模型的原生分辨率对齐。底座是1024就喂1024,是512就喂512,不要图省事混着来。分辨率不匹配时,模型内部的特征图尺度会错位,表现出来就是出图发虚、边缘毛刺,你调学习率也救不回来。第二条规矩:裁剪要留边距,主体四周至少留出画面10%的空白,不要把主体顶到边缘,否则模型学到的“主体总是贴边”会成为固定构图习惯。
第三条规矩关乎压缩质量。手机直出的图、社交平台存下来的图,往往带有明显的块状压缩伪影,这些伪影会被模型当成纹理特征学进去,出图时就表现为莫名其妙的噪点和色块。我的做法是统一转成无损或高质量格式,尺寸不足的用高质量插值放大,宁可轻微柔化也不要锐化过度——锐化产生的白边同样会被学进去。这一步花十分钟,能省掉后面两小时的排查。
注意:不要对4张图做自动增强(随机翻转、随机裁剪、色彩抖动)。样本量这么小的时候,增强会把有限的信息搅乱,模型分不清哪个是主体的真实特征。增强是给大数据集用的,4张图请老老实实手工处理。
3.2 触发词设计与描述文本的写法
概念绑定靠的是文本侧的一个可学习嵌入,它由一组罕见字符组成的触发词来激活。触发词的设计要点是在自然语言里几乎不可能出现,比如三到四个字母加数字的随机组合。用常见词当触发词是新手最容易犯的错,因为底座模型对这个词本来就有语义理解,你的新嵌入会和旧语义打架,出图时概念时有时无。
描述文本的写法,我总结成一句话:短、具体、只写看得见的东西。每张图的描述控制在二三十个词,结构是“触发词 + 主体类别 + 两到三个显著属性 + 背景简述”。举个例子,如果主体是一个戴圆框眼镜的短发角色,描述就写成“触发词, 短发角色, 圆框眼镜, 深色外套, 灰白背景”。不要写“一个看起来很聪明的角色站在窗边思考人生”这种带主观解读的长句,长句里的每一个词都会参与概念绑定,无关词会把噪声带进去。
背景要不要写进描述,取决于你的目标。想让模型在任意背景下都能画出主体,就必须把背景写清楚,让模型明白“这部分是可变的”;反过来,如果你希望出图自带某种氛围,那就把背景描述统一成同一个词。我一般建议前者,因为灵活性更重要,需要氛围时在推理提示词里加就行。
3.3 图片与描述的一一对应检查
训练前我有个固定动作:把4张图和4段描述并排放着看一遍,逐条确认描述里写的每个属性在图上都真实可见,图上每个显著特征在描述里都有对应。漏写显著特征,模型会把它当随机变量;写了图上没有的东西,模型会强行编造。这个检查只要两分钟,但能挡掉大量“练完不像”的问题。
4. 参数配置:rank、学习率、步数怎么定
4.1 低秩维度的选择逻辑
低秩矩阵的秩 r 决定了可训练容量。r 越大,能装下的细节越多,过拟合风险也越高。对4张图这个量级,我的经验区间是 r 取4到8。r=4 适合画风、材质、色调这类“整体感觉”的目标;r=8 适合主体外观这类需要保留结构细节的目标;r=16 以上在4张图场景下基本是浪费,练到后期必然出现“换个姿势就不像”的僵硬感。
另一个参数 alpha 是缩放系数,常规做法是取 r 的一倍或两倍。alpha 相对 r 越大,低秩更新的实际影响越强,等效于放大学习率。我的习惯是 alpha = r,这样调整 r 的时候不用同步改 alpha,变量少一个,排查问题时少一个怀疑对象。这个细节很多教程不讲,但它在你反复试参数时能省不少时间。
4.2 学习率与调度器的搭配
学习率是最容易翻车的参数。主网络侧我用 1e-4 量级,文本嵌入侧用 1e-3 到 5e-3 量级,两边差一个数量级是有原因的:文本嵌入是从零初始化的小向量,需要更快地“找到位置”;低秩矩阵是叠加在已有权重上的修正,步子太大会直接破坏原有特征。调度器用余弦退火或者带预热的常数调度,预热步数占总步数的5%到10%,避免开头几步就把权重带偏。
优化器用 AdamW 系,权重衰减设 0.01,二阶矩衰减用默认值就行。这里有个小技巧:如果你发现训练前期损失下降特别快然后突然抖动,多半是文本嵌入侧学习率过高,把它砍半再跑,不要急着动主网络侧。
4.3 步数计算与批量配置的实算
步数按“每张图训练多少步”来估,比按总步数估更直观。4张图场景,每张图100到150步比较合适,总步数400到600。低于300步大多欠拟合,主体特征出不来;高于800步过拟合明显,出图开始复刻训练图背景。
批量受显存限制,通常设1,靠梯度累积凑等效批量。梯度累积设4,等效批量就是4,刚好等于图片总数,一个优化步看完所有图,梯度方向比较均衡。下面这段是显存估算的伪代码,你可以按自己的硬件套一下:
# 显存粗算(单卡,混合精度训练) resolution = 1024 # 训练分辨率 latent = resolution // 8 # 潜空间边长,128 channels = 4 # 潜空间通道 base_model_gb = 6.0 # 底座权重(半精度) trainable_gb = 0.05 # 低秩参数+优化器状态 activation_per_sample = 3.5 # 单样本激活值,开梯度检查点后可降到约1.2 batch = 1 use_checkpoint = True act = activation_per_sample * (0.35 if use_checkpoint else 1.0) total = base_model_gb + trainable_gb + act * batch + 1.5 # 1.5为编码器与缓冲 print(f"预计显存占用约 {total:.1f} GB") # 不开检查点约 11GB 起步;开检查点约 8GB 起步,1024分辨率建议留 2GB 余量实测下来,1024分辨率、批量1、开梯度检查点,12GB显存的卡能跑,但余量不多,建议关掉其他占显存的程序。512分辨率就宽松很多,8GB也能凑合。
5. 完整实操流程:从目录结构到出第一张图
5.1 目录与配置文件
我习惯把每次实验的配置、数据、输出放在同一个目录下,方便回溯。结构大致是这样:
project/ ├── data/ │ ├── 01.png │ ├── 02.png │ ├── 03.png │ └── 04.png ├── captions/ │ ├── 01.txt │ └── ...(与图片同名) ├── config.yaml └── output/配置项我写成一份可读的 yaml,关键字段都在下面,你可以直接照着改数值:
model: base_path: "./base_model" # 底座模型目录 resolution: 1024 precision: "bf16" # 不支持bf16就用fp16 adapter: rank: 8 alpha: 8 dropout: 0.0 target_modules: ["attn_q", "attn_k", "attn_v", "attn_out"] text_encoder: train_embedding: true trigger_token: "zx7q" init_from: "category_word" # 用类别词初始化,收敛更快 train: batch_size: 1 grad_accum: 4 lr_main: 1.0e-4 lr_text: 2.0e-3 scheduler: "cosine" warmup_ratio: 0.08 max_steps: 500 save_every: 100 sample_every: 100 seed: 1234 gradient_checkpointing: true有一个字段值得单独说:文本嵌入初始化。如果从零随机初始化,模型需要花不少步数才能把触发词和视觉概念对上;用主体类别词(比如“人像”“插画”)的现有嵌入做初始化,收敛会快很多,通常能省下几十步。这个方法在少样本场景里特别有用。
5.2 启动训练与过程观察
启动命令没什么花哨的,关键是看日志。下面这段是通用写法,脚本名换成你自己的:
python train_adapter.py \ --config config.yaml \ --data_dir ./data \ --caption_dir ./captions \ --output_dir ./output/run01 \ --log_every 10训练开始后我盯三样东西:损失曲线的形状、每100步的样张、显存占用是否持续攀升。正常的损失曲线是开头快速下降,200步左右进入缓慢下降,400步后基本走平。如果曲线在下降过程中出现尖峰然后回不来,多半是某张图的描述和图片严重不符,或者那张图本身质量有问题,回去查数据。
样张评估有个小方法:每100步用同一个固定提示词(触发词 + 一个中性场景)出4张图,横向对比。第100步往往只是雏形,第200到300步通常是甜点区,第400步之后开始出现背景复刻和色彩偏移。我在很多次实验里都观察到这个规律,宁可早停,不要练满。
5.3 中途保存与版本管理
每100步存一个检查点,是必须的。少样本训练的效果曲线不是单调的,最好的那一版常常在中间某个位置。保存时顺手记一个简短备注,比如“step300 结构最像但偏亮”,等你练了五六个版本之后,这些备注就是你的救命稻草。命名上我一般用“日期_目标_步数”的格式,不要用“最终版”“最终版2”这种命名,一周后你绝对分不清哪个是哪个。
6. 效果验证:过拟合和欠拟合怎么认、怎么救
6.1 三种典型症状的对照判断
少样本微调的结果,基本落在三个区间里:欠拟合、刚好、过拟合。判断方法不是看训练损失,而是看推理时的表现。我整理了一张对照表,照着症状对号入座,比盲调参数高效得多。
| 症状 | 具体表现 | 根因 | 处置 |
|---|---|---|---|
| 欠拟合 | 主体只有五六分像,提示词里不加触发词也能隐约出现类似形象 | 步数不足、学习率偏低、rank过小 | 加100到150步;文本侧学习率上调50% |
| 过拟合 | 出图背景固定为训练图背景,姿态僵化,颜色整体偏移 | 步数过多、rank过大、描述太泛 | 回退到300步的检查点;rank降到4 |
| 概念泄漏 | 不加触发词也大量出现该主体,污染其他提示词 | 文本嵌入训练过度、描述太短 | 降低文本侧学习率;描述补足属性词 |
| 特征游离 | 主体某些部件会跑到画面别的位置 | 训练图主体被裁切或遮挡 | 换图,确保主体完整 |
概念泄漏是我踩得最深的一个坑。当时练完发现,随便写个“人像”都会带上训练角色的发色,整批出图全被污染。排查半天才想明白,是文本侧训练过头,触发词的嵌入和类别词的嵌入混在一起了。后来把文本侧学习率降了一半、描述里补上足够的属性词,问题就消失了。
6.2 推理参数的联动调节
训练只是前半程,出图效果还取决于推理参数。引导强度(CFG)在少样本适配模型上不能用常规值,我一般从5起步试到7,超过7容易出现色彩过饱和和边缘硬边,因为低秩修正被过度放大。采样步数25到30足够,再往上收益很小。采样器选带二阶信息的类型,收敛更快,同样步数下细节更完整。
还有一个很多人忽略的点:触发词在提示词里的位置和权重。放在句首、给1.1到1.3的权重,通常比放在句尾效果好,因为前面位置的注意力权重更高。如果发现主体时有时无,先调权重,不要急着怀疑模型没练好。
6.3 快速回归验证清单
每次改完参数重新训练,我都会跑一遍固定清单:不加触发词出一张图,确认没有概念泄漏;用触发词加中性背景出一张图,确认主体识别;用触发词加另一个场景出一张图,确认背景没有绑定;换一个姿势描述出一张图,确认泛化边界。四张图,两分钟,能覆盖八成以上的问题。这个习惯养成之后,我基本不会出现“练完才发现方向错了”的情况。
7. 常见问题与排查实录
7.1 出图发灰、对比度低的处理顺序
这是最高频的问题。排查顺序我固定成三步:先看训练图本身是不是灰蒙蒙的(原图问题,换图);再看推理引导强度是不是太低(低于4会发灰,提到5.5试试);最后看是不是训练步数不够导致概念没压实。三步走完还不行,就检查混合精度训练时有没有出现数值溢出,日志里如果有大量跳过步,说明梯度炸了,把学习率砍半重跑。
7.2 主体像但“死板”的成因
很多人反馈说主体是像了,但怎么摆姿势都别扭。这不是训练不足,恰恰是训练过度加上数据单一的结果。4张图如果全是同一个姿态,模型就只学会这一个姿态,你让它换姿势,它只能用原姿态强行拼贴。解法不是加步数,而是换掉第4张图,用一张明显不同姿态的图替换,重新跑300步,通常一次就改善。如果实在没有别的姿态的图,就把步数压到300以内,宁可欠一点也不要僵。
7.3 显存溢出的几种非常规原因
显存不够时,大家第一反应是降分辨率,但有时候降了还是爆。我遇到过三个非常规原因:一是图片尺寸不一致,预处理时没统一,运行时按最大尺寸分配,白吃显存;二是日志里开了逐层梯度统计,这个开销不小;三是数据加载进程数设太高,每个进程都占一份内存和显存缓冲。把这三处收拾干净,往往不降分辨率也能跑起来。
7.4 训练日志正常但样张全糊
这种情况一般是潜空间编码环节出了问题,训练损失看着正常,是因为损失是在潜空间算的,而解码环节的精度不够。检查两处:底座模型的精度设置是否和推理时一致,训练用半精度推理用全精度会导致解码偏差;以及样张生成时的采样步数是不是设得太低(低于15就会糊)。这两个地方我都栽过,尤其是第一个,排查了很久才想到。
7.5 一份可打印的速查表
| 现象 | 第一怀疑对象 | 快速验证 | 处置 |
|---|---|---|---|
| 主体不像 | 描述与图不符 | 逐图核对描述 | 改描述,重跑 |
| 背景被绑定 | 4张图背景同源 | 换背景出图 | 换第4张图 |
| 颜色整体偏 | 学习率过高 | 看200步样张 | 回退检查点 |
| 换姿势就崩 | 姿态单一 | 看4张图姿态分布 | 换图或减步数 |
| 触发词失效 | 嵌入训练不足 | 加权到1.5试 | 加步数或提文本学习率 |
| 画面有噪点 | 原图压缩伪影 | 放大原图看 | 换高质量图 |
8. 几个让我少走弯路的实操心得
第一个心得关于实验记录。少样本微调的参数空间不大,但组合起来也有几十种,靠脑子记必然混乱。我的做法是一张纸一支笔,每次实验写四行:改了哪个参数、总步数、最佳检查点、一句话评价。三个月下来这张纸比任何教程都有用,因为它记录的是你这批数据、这个底座、这台机器上的真实规律。
第二个心得关于底座选择。不是所有底座都适合少样本适配。偏写实、训练数据干净的底座,概念绑定更精准;偏插画、风格化强的底座,绑定会带上一层风格滤镜。同一个主体,换个底座练出来完全是两个味道。如果你对风格有明确要求,先花时间挑底座,比调参数收益大得多。
第三个心得是别迷信步数。我早期有个执念,觉得练得越久越像,结果每次都是300步的版本最好用,600步的版本只能看不能用。后来想明白了,4张图的信息量就那么多,模型在第300步左右已经把这部分信息榨干了,再往后就是纯粹的过拟合。现在我默认上限设500步,每100步存一次,最后从中间挑。
第四个心得关于触发词的复用。同一个触发词不要跨项目复用。我图省事用同一个触发词练了两个不同主体,结果两套嵌入在推理时互相干扰,出图变成了两个主体的混合体,五官错位。从那以后我给每个项目单独生成触发词,练完就归档,绝不复用。
第五个心得是接受不完美。4张图微调出来的模型,本质上是一个“概念速写”,不是精雕。它在训练概念附近的表现很好,稍微偏离一点就会露出破绽。我现在做项目时会提前规划:这个模型只用于什么场景、什么构图范围、什么分辨率区间,把使用边界写清楚,反而比追求“什么都能画”更实用。真正需要高保真的场合,还是老老实实上几十张图的中等规模微调,4张图那条路走不到那个精度。
这套方法我前后用了十几次,从角色外观到画风质感都试过,成功的和翻车的都有。翻车的那几次,事后复盘几乎全落在两个地方:要么是目标定得太宽,要么是数据里混进了不该有的共性。把这两点守住,4张图这条路是真的能走通的,而且成本低到你可以一晚上试三个方向。