1. 先搞清楚一件事:为什么值得花时间去啃 Stable Diffusion 的原理
2026 年做 AIGC 的人,几乎绕不开 Stable Diffusion 这套技术栈。你去翻任何一个作品交流区,能看到的最多的问题不是“这个模型怎么下载”,而是“为什么我照抄别人的参数,出图效果差这么多”。这个现象背后其实是一个很朴素的事实:AIGC 里工具会更新,界面会换皮,但 Stable Diffusion 的模型结构、关键组件和训练预测方式这套底层逻辑,从 1.5 时代一直沿用到现在,哪怕后面出现了 DiT、MMDiT、整流流这些新架构,思路也是一脉相承的。
这篇内容我打算按“能上手 + 能解释清楚”的标准来写。适合三类人看:刚接触 AIGC 想搞明白出图流程的新手、用过 WebUI 或 ComfyUI 但一直靠抄参数过日子的中手、以及想动手做微调(LoRA、DreamBooth)但卡在原理上的开发者。我不打算写成一篇公式堆砌的论文,而是按一个做过部署、调过采样器、被显存坑过无数次的从业者视角,把模型结构、关键组件、训练预测方式这三块掰开讲,顺带把我踩过的坑和排查经验一起放进去。
2. 整体设计思路:Stable Diffusion 到底在什么上做文章
2.1 从“会抄参数”到“会判断”的分水岭在哪
大部分人卡住的地方,是不知道自己的操作作用在了哪一层。你调 CFG Scale,作用的是采样循环里的条件引导;你换采样器,换的是求解扩散方程那一步的数值方法;你挂 LoRA,挂的是 U-Net 里注意力层的权重增量;你写提示词,写的是 CLIP 文本编码器的输入。这四件事在代码里是四个完全不同的位置,如果混在一起理解,就会变成“参数玄学”。
我自己判断一个人是“会用”还是“会调”,标准很简单:出图崩了的时候,他能不能在两分钟内说出下一步该改哪一层。这个能力不来自于背参数表,而来自于对整体数据流的清楚认知。所以本节先把完整数据流走一遍,后面再逐层拆。
2.2 一次出图的完整数据流:从文字到像素
把 Stable Diffusion 的一次推理拆开,大概是这么一条链路,我用文字描述清楚,方便你在脑子里成像:
文本提示词先经过Tokenizer变成 token id 序列,送进CLIP Text Encoder,输出一组形状为77 × 768的条件向量(SD1.5 的情况);同时在潜空间里初始化一张纯高斯噪声图,尺寸64 × 64 × 4;接下来进入采样循环,U-Net每一轮接收三样东西——当前带噪潜变量、时间步 t 的嵌入、文本条件向量,输出一个预测噪声;采样器根据这个预测噪声和调度表更新潜变量,重复 N 步;最后把干净的潜变量交给VAE 解码器,还原成512 × 512 × 3的像素图。
这条链路里,只有 U-Net 是在循环里反复执行的,所以它也是推理耗时和显存占用的主要来源。理解这一点很关键:你调步数,就是在调 U-Net 被调用多少次;你调分辨率,就是在调 U-Net 每一轮要处理的 token 数量。
2.3 三大组件分工总览
| 组件 | 角色 | 输入 | 输出 | SD1.5 参数量 |
|---|---|---|---|---|
| CLIP Text Encoder | 把文字翻译成机器能懂的条件 | 文本 token id | 77×768 条件序列 + 池化向量 | 约 123M |
| U-Net | 去噪主力,预测每一步的噪声 | 潜变量 + t + 文本条件 | 同维度噪声预测 | 约 860M |
| VAE | 潜空间与像素空间的双向翻译 | 像素图 / 潜变量 | 潜变量 / 像素图 | 约 83M |
这个表看起来平淡,但它解释了三个非常实用的问题。第一,为什么 SD1.5 在 4GB 显存的老卡上还能跑——因为总参数只有一个多亿级别的一半多,fp16 下权重占用不到 2.2GB。第二,为什么文本编码器是“一次性”的而 U-Net 是“循环”的——所以优化提示词不影响推理速度,但优化步数影响巨大。第三,为什么 VAE 经常是显存杀手——它的解码阶段在高分辨率下会产生巨大的中间激活,1024 以上的图不改配置极易爆显存。
2.4 潜空间这一步,是 SD 最聪明的一次妥协
早期的扩散模型直接在像素空间去噪,512×512 的图就是 786432 个数值,每一步都要在这个尺度上跑一遍网络,算力消耗非常夸张。Stable Diffusion 的做法是先把图压到潜空间再扩散,VAE 下采样 8 倍,通道数从 3 变成 4,于是潜变量尺寸变成64 × 64 × 4,一共 16384 个数值。
算一下压缩比:(512 × 512 × 3) ÷ (64 × 64 × 4) = 786432 ÷ 16384 = 48。整整 48 倍的数值量缩减。
但真正值钱的不是这 48 倍,而是注意力计算的下降幅度。U-Net 里的自注意力复杂度与序列长度平方成正比。在像素空间,序列长度是 262144,平方后是 6.87×10¹⁰;在潜空间,序列长度是 4096,平方后是 1.68×10⁷。两者差了大约 4096 倍。这个数字对做部署的人来说意味着什么?意味着潜空间方案把“需要几台机器”变成了“需要一张卡”。
注意:潜空间不是无损压缩。VAE 编码时会丢掉一部分高频细节,所以 SD 出图在极小纹理、密集文字、手指边缘这类地方容易出问题。这不是采样器的问题,是压缩本身的代价,理解这点能帮你少走很多弯路。
3. 模型结构拆解:三大关键组件各自在干什么
3.1 CLIP Text Encoder:77 个 token 这个硬约束从哪来
先解释一个高频困惑:为什么提示词超过一定长度就不生效了?答案是 CLIP 的位置编码固定为 77 个位置,所以输入序列被硬性截断或填充到 77。这 77 个里,第一个是起始标记,最后一个是结束标记,真正承载语义的有效 token 是 75 个左右。
SD1.5 用的是 CLIP ViT-L/14 的文本塔,隐藏维度 768,输出形状77 × 768,这个张量后面会作为 U-Net 交叉注意力的 Key 和 Value。SDXL 换成了双文本编码器方案,同时用 ViT-L(768 维)和 OpenCLIP ViT-bigG(1280 维),把两路输出在通道维度拼接成 2048 维,并且额外把池化后的全局向量加到时间步嵌入里。这就是为什么 SDXL 对提示词的语义理解明显更好,也更吃显存。
实际操作上有个坑要提醒:不同版本的 SD 必须配对应的文本编码器,SD1.5 和 SD2.x 的文本编码器不通用,SD2.x 用的是 OpenCLIP ViT-H/14,维度 1024。混搭的后果不是报错,而是出图变成毫无意义的色块,因为条件向量的分布完全对不上。诊断这类问题的经验法则是:如果出图颜色正常但内容完全随机,先怀疑文本编码器匹配问题。
3.2 U-Net:去噪主力为什么要做成“U”形
U-Net 这个名字来自它的形状:左侧编码器逐级下采样,右侧解码器逐级上采样,中间用跳连接把同层特征拼起来。为什么这么设计?因为去噪这个任务需要同时看到全局结构和局部细节——深层特征负责“这是一只猫还是狗”,浅层特征负责“猫的毛边怎么长”。下采样扩大感受野抓全局,上采样恢复分辨率还原细节,跳连接保证细节不被丢干净。
具体到 SD1.5 的 U-Net,基础通道数是 320,四级下采样的通道乘数分别是 1、2、4、4,也就是 320、640、1280、1280,最中间那层维持在 1280。每个分辨率层级里堆了两个残差块,注意力层只挂在 32×32、16×16、8×8 这三个低分辨率层级上。为什么不在 64×64 那一级加注意力?因为那一级的序列长度是 4096,再往前是 16384,注意力成本会呈平方级上升,收益却不成比例,工程上就砍掉了。
时间步的注入方式是另一个容易被忽略的细节。时间步 t 先经过正弦位置编码变成向量,再过两层全连接,然后以 scale 和 shift 的形式作用在残差块上。这就是为什么“不同的 t”能让同一个网络表现出完全不同的行为——早期步数它在画大结构,后期步数它在抠细节。
3.3 VAE:像素空间和潜空间的翻译官
VAE 在整条链路里出现两次,编码一次、解码一次。编码器把512 × 512 × 3压成64 × 64 × 4,然后乘一个缩放系数(SD1.5 是 0.18215)把分布拉到一个比较稳定的数值范围;解码器做相反的事,先除这个系数再还原像素。
这里有两个实战经验值得单独说。第一,出图发灰、饱和度低、整体偏暗,很多时候是 VAE 的问题而不是采样器的问题。社区里有不少针对性的 VAE 权重,换上去之后肤色和对比度会有肉眼可见的差别。第二,SD1.5 时代存在 VAE 在 fp16 下数值溢出的问题,表现是一整片纯黑或者纯白的图,解决办法是强制 VAE 用 fp32 计算,很多整合包里的启动参数已经默认处理了这个。
提示:判断黑图是不是 VAE 引起,有个快速验证方法——把同样的潜变量交给两个不同的 VAE 解码,如果一个出一个全黑,基本可以锁定了。
3.4 三大组件的参数量与显存账本
把账算清楚,很多“玄学”就消失了。以 fp16 推理为例,每个参数占 2 字节:
| 组件 | 参数量 | fp16 权重占用 | 备注 |
|---|---|---|---|
| CLIP Text Encoder | 约 123M | 约 0.23 GB | 只跑一次,可缓存 |
| U-Net | 约 860M | 约 1.64 GB | 循环调用,激活占大头 |
| VAE Decoder | 约 83M(共享整体) | 约 0.16 GB | 高分辨率下激活爆发 |
权重加起来不到 2.1GB,但实际跑 512×512 单张图通常要占 3.5 到 4GB,1024×1024 则要 8GB 以上。多出来的部分全是中间激活,尤其是注意力层的激活,它和分辨率是平方关系。这也解释了为什么升级分辨率带来的显存压力远大于升级批次。
4. 训练方式:Stable Diffusion 到底是怎么学会画画的
4.1 前向加噪:一条可以手算的公式
训练的第一步是“造数据”。给一张真实图片的潜变量 x₀,随机抽一个时间步 t,然后按下面这个式子往上面加噪:
x_t = sqrt(ᾱ_t) · x₀ + sqrt(1 - ᾱ_t) · ε, ε ~ N(0, I)其中 ᾱ_t 是累积噪声系数,由噪声调度表决定。SD 用的是 scaled_linear 调度,beta 从 0.00085 到 0.012,总步数 1000。t 越大,ᾱ_t 越接近 0,图像就越接近纯噪声;t 越小,图像保留的结构越多。
这条公式的价值在于它让加噪一步到位,不需要真的循环 t 次。这在训练时是决定性的——如果每次都得迭代加噪,训练成本会高到无法接受。理解这个技巧,你就理解了整条训练流水线能跑起来的前提。
4.2 为什么训练目标是预测噪声,而不是预测原图
直觉上好像让网络直接重建原图更简单,但实践中不是。原因有几个层面。第一个层面是任务难度:从接近纯噪声的输入里重建原图,等价于让网络从几乎为零的信息量里凭空生成一整张图,梯度信号非常弱。而预测噪声是一个“从有到无”的减法任务,输入本身携带着结构的痕迹,学习信号强得多。
第二个层面是数学等价性。给定 x_t 和预测噪声,可以反解出 x₀;反过来也一样。两者在数学上是可以互相推导的,但预测噪声的损失尺度在各个时间步上更一致,训练更稳。用 SD1.5 和 SD2.x 系列为例,主流权重用的都是 epsilon 预测;后续一些版本引入了 v-prediction,把预测目标换成速度和噪声的混合,改进的是极端时间步上的稳定性。
第三个层面是实际观感。如果你尝试过自己训练小规模的扩散模型,会发现用原图重建损失训出来的模型,早期步数特别容易塌成一片灰色。这不是训练不够,而是目标函数选错了。
4.3 主流微调路线:三条路各自的适用场景
预训练好的 SD 只会画通用内容,想让它画你自己的脸、你的产品、你的画风,就得微调。目前主流的三条路线差别很大:
DreamBooth的思路是用少量图片(通常 3 到 5 张)配合一个类别提示词,让模型把“某个特定主体”绑定到一个罕见标识符上。它的效果好,能同时学到主体的外观和变体能力,但训练会改动整个 U-Net,权重文件动辄 2 到 5GB,且容易过拟合,需要配合类先验正则来防止“语言漂移”——也就是模型把整个类别都画成你的主体。
Textual Inversion完全不改模型权重,只在文本编码器的词嵌入空间里学一个新的虚拟 token。文件只有几十 KB,换来的是极致的轻量,代价是表达能力有限,很难还原复杂主体,更适合学画风或者抽象风格。
LoRA是目前最流行的折中方案。它冻结原权重,在注意力层的权重矩阵旁挂一个低秩增量,训练快、文件小(通常 20 到 200MB)、可以叠加组合、随时调权重强度。绝大多数人的第一个微调项目都该从 LoRA 开始。
4.4 LoRA 的低秩账:为什么 1% 的参数能顶用
LoRA 的核心操作是把原权重矩阵 W 的更新量分解成两个小矩阵的乘积:ΔW = B · A,其中 A 是r × k,B 是d × r,r 是秩,通常取 4、8、16、32。
算一下参数量对比。假设原矩阵是1280 × 1280,也就是 1638400 个参数。用 r=8 的 LoRA,参数量是1280 × 8 + 8 × 1280 = 20480,只有原来的约 1.25%。如果整个 U-Net 都这么改,总可训练参数能压到千分之几的量级,显存占用和训练时间都成倍下降。
为什么这么少的参数能学到有用的东西?因为微调这个任务本身的目标变化量就很小——你不是要教会模型新概念,而是把它已经会的东西往某个方向推一把。低秩假设说的是“这种推动可以用少数几个方向描述”,实践中这个假设成立得相当好。
实操心得:LoRA 的 rank 不是越大越好。我试过把 rank 从 8 提到 64,人物细节提升有限,但过拟合和风格固化明显变严重,而且和别的 LoRA 叠加时冲突概率大增。人物类通常 8 到 16 就够,画风类 32 左右,超过 64 除非你有上万张高质量素材。
5. 预测方式:采样、CFG 与参数调优的实战理解
5.1 推理是一个“逐步去噪”的循环
推理和训练最大的区别在于:训练时随机抽一个 t 只算一步,推理时要从 t=999 一路走到 t=0,走满设定的步数。每一轮里,U-Net 预测当前噪声,采样器根据调度表算出下一步的潜变量。
这里有个关键认知:采样步数不等于训练步数。训练用 1000 步的调度表,推理完全可以只用 20 步,因为中间那些步可以跳。跳步的方式不同,就产生了不同的采样器。DDIM 用确定性跳步,Euler 用一阶常微分方程求解,DPM++ 用二阶多步方法,不同方法的精度和稳定性各不相同。
5.2 采样器选型对照表
| 采样器 | 类型 | 推荐步数 | 出图特性 | 适合场景 |
|---|---|---|---|---|
| DDIM | 确定性 | 30 到 50 | 稳定、可复现性好 | 需要严格复现的批量任务 |
| Euler | 一阶确定性 | 25 到 40 | 干净、锐利 | 通用出图 |
| Euler a | 祖先采样 | 20 到 30 | 每步加噪,变化丰富 | 追求创意和随机感 |
| DPM++ 2M Karras | 二阶多步 | 20 到 30 | 细节多、收敛快 | 目前性价比最高的默认选择 |
| DPM++ SDE | 随机微分 | 25 到 40 | 质感细腻但较慢 | 写实人像 |
| UniPC | 预测校正 | 8 到 15 | 极低步数可用 | 快速草稿预览 |
这张表里的“推荐步数”是我在 SD1.5 和 SDXL 上都跑过之后总结的经验值,不是硬标准。有个通用规律值得记住:确定性和祖先采样的最大区别是后者永不收敛,Euler a 在 30 步和 60 步的结果会持续变化,而 Euler 在 40 步之后基本稳定。所以如果你要做“同一张图微调”的迭代,用确定性采样器;如果你在找一个好构图,用祖先采样器刷几十张效率更高。
5.3 CFG Scale 和负面提示词的真实作用机制
CFG 的全称是分类器无关引导,做的事情其实非常直白。每一轮 U-Net 会被调用两次:一次带文本条件,一次不带条件(用空提示词)。然后用下面这个式子做外推:
ε = ε_uncond + scale × (ε_cond - ε_uncond)scale 就是 CFG Scale。它放大的是“有条件和无条件预测之间的差”,也就是让结果更贴合提示词。
理解了这一步,很多现象就有解释了。CFG 调太低(比如 1 到 3),条件几乎不起作用,出图接近随机;调太高(比如 15 以上),差值被过度放大,图像会出现色彩过饱和、边缘硬化、结构崩坏的“烧焦”感。SD1.5 的甜区通常在 6 到 9,SDXL 因为训练方式和文本编码器不同,甜区更低,4 到 7 之间更舒服。
负面提示词的作用机制也就顺理成章了:它不是“让模型避开某个词”,而是把无条件那一支的输入换成负面提示词,让外推方向变成“远离负面描述”。所以负面提示词写得越具体越有效,堆一堆万能词反而稀释了信号。
常见误区:很多人把负面提示词写成几十个词的清单,结果出图反而变糊。原因是条件向量被大量无关语义污染。我的建议是负面提示词控制在 5 到 10 个真正相关的词条内。
5.4 步数、分辨率、随机种子怎么定
分辨率这件事有个硬约束:SD1.5 是在 512×512 上预训练的,SDXL 是在 1024×1024 上。直接拉到远超训练分辨率的尺寸,会出现“双头”“多肢体”“重复图案”这类结构崩坏,因为位置编码没见过那么长的序列。
正确做法是原生分辨率出图,需要大图走放大流程,用专门的超分模型分两步放大。这样既避开结构崩坏,又能在第二步用低去噪强度补充细节。
随机种子决定初始噪声,也决定了同一提示词下的构图走向。批量刷图的实用技巧是固定种子、微调提示词,这样你能看清每个词条的实际影响;反过来,如果你想找构图,就固定提示词、随机种子。这两种模式来回切换,比盲目堆参数高效得多。
6. 实操:从零跑通第一张图
6.1 环境与依赖准备
先明确一个原则:版本对齐比装最新版重要。AIGC 生态的依赖耦合很紧,尤其是 diffusers、transformers、torch 三者的组合,跨大版本升级很容易炸。
以常见的 CUDA 12.1 环境为例,安装命令大致是这样:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install diffusers transformers accelerate safetensors pip install xformers --index-url https://download.pytorch.org/whl/cu121如果下载慢,可以指定国内的 PyPI 镜像源加速。xformers 是可选项,但它能把注意力层的显存占用和耗时都降下来,中低端卡强烈建议装。加速库 accelerate 负责设备分配和混合精度,diffusers 会默认调用它。
6.2 最小推理脚本
下面这段是能直接跑的最小示例,用的是 SD1.5 的管线。注意模型路径需要替换成你本地实际存放的权重目录:
import torch from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler pipe = StableDiffusionPipeline.from_pretrained( "./models/sd15", torch_dtype=torch.float16, safety_checker=None, requires_safety_checker=False, ).to("cuda") # 换成 DPM++ 2M Karras,20 步就能出可用结果 pipe.scheduler = DPMSolverMultistepScheduler.from_config( pipe.scheduler.config, use_karras_sigmas=True ) # 显存优化三件套 pipe.enable_attention_slicing() pipe.enable_vae_slicing() pipe.enable_model_cpu_offload() prompt = "a quiet mountain lake at sunrise, soft light, detailed reflection" negative = "blurry, low quality, extra fingers, watermark" image = pipe( prompt=prompt, negative_prompt=negative, num_inference_steps=25, guidance_scale=7.0, width=512, height=512, generator=torch.Generator("cuda").manual_seed(20260101), ).images[0] image.save("first_try.png")这段代码里有几个细节值得解释。torch_dtype=torch.float16直接把权重占用减半;enable_attention_slicing把注意力计算分块,用时间换显存;enable_model_cpu_offload让不参与当前计算的模块待在内存里,这是 6GB 以下显存能跑起来的关键;固定种子是为了让结果可复现,方便你对照修改的效果。
6.3 ComfyUI 的节点链路拆解
如果你用的是 ComfyUI,界面看起来是一堆方块连线,但映射到原理上其实很清楚,一条标准链路包含这几类节点:
第一类是加载类,Checkpoint Loader 负责把 U-Net、文本编码器、VAE 三件套拆出来;第二类是条件类,CLIP Text Encode 节点把正面和负面提示词分别编码成条件张量;第三类是潜空间类,Empty Latent Image 节点生成初始噪声,尺寸参数决定分辨率;第四类是采样类,KSampler 节点把前面所有东西串起来,它内部的参数就是步数、CFG、采样器、调度器、种子;第五类是解码类,VAE Decode 把潜变量还原成图,再交给 Save Image 落盘。
用节点方式的最大好处是你能肉眼看到条件张量被喂给了谁。想看 CFG 的作用,就去看 KSampler 上那个 cfg 输入;想验证文本编码器的影响,就把两个 CLIP Text Encode 的输出对调一下,出图会立刻变得完全不听话。这种“插拔式验证”比在整合包里猜参数高效得多。
6.4 模型文件格式与来源校验
模型文件常见三种后缀。.ckpt是老格式,本质是 pickle,加载时会执行反序列化,存在安全风险;.safetensors是纯数据格式,不做代码执行,现在应该优先选它;.pt和.bin多见于配套组件。
下载渠道上,官方的开源权重仓库会在模型卡里标明许可范围和适用场景,社区模型站则提供了大量二次训练的版本。不管从哪来,有几个习惯建议养成:核对文件大小和页面标注是否一致;优先选择带哈希值或者有版本说明的文件;不要下载来源不明的小体积“融合模型”,这类文件是最常见的问题来源。
注意:不同基础版本的权重不能混用。SD1.5 的 LoRA 挂到 SDXL 上会直接报维度错误或生成废图,反之亦然。在整理模型目录时,按基础版本分文件夹是最省事的做法。
7. 常见问题与排查技巧实录
7.1 出图崩坏的排查顺序
遇到图崩了,别急着换采样器,按这个顺序查效率最高:
第一,看提示词有没有超出有效长度。超长提示词会被截断,后面的形容全部丢失,表现为“前面描述的东西有,后面描述的东西没有”。
第二,看 CFG 是不是过高。边缘发硬、颜色焦化、结构扭曲,八成是 CFG 超过 12 了。先降到 7 试一次。
第三,看分辨率是否偏离训练尺度。SD1.5 出 512 到 768 之间比较安全,直接上手 1024 大概率出双头。
第四,看是否挂了不兼容的 LoRA。逐个关掉 LoRA 二分法定位,比读日志快得多。
第五,看 VAE 是否匹配。黑图、灰图、颜色异常先换 VAE。
7.2 显存不足的降级组合拳
显存告急时,按下面这个顺序逐级降级,每次只加一项,找到刚好能跑的配置:
| 降级手段 | 显存节省 | 速度影响 | 备注 |
|---|---|---|---|
| fp16 半精度 | 约 40% | 基本无 | 最优先做 |
| 注意力分块 | 约 20% 到 30% | 轻微变慢 | 中端卡必开 |
| VAE 分块/切片 | 高分辨率下显著 | 轻微变慢 | 出大图必开 |
| 模型 CPU 卸载 | 大量 | 明显变慢 | 低显存兜底方案 |
| 降低分辨率 | 平方级下降 | 变快 | 从根上解决 |
经验上,6GB 显存跑 SD1.5 的 512 分辨率,开半精度加注意力分块就够;4GB 需要再加 VAE 切片;8GB 跑 SDXL 的 1024 分辨率,通常还要开 CPU 卸载才能稳。
7.3 提示词不听指挥的四个原因
很多人抱怨“我写了但这个元素就是不出现”,实际问题往往出在这四个地方:
一是词序问题。CLIP 对靠前的 token 更敏感,重要的主体和动作要往前放,风格词、画质词往后放。
二是语义冲突。同时在提示词里写两个互斥的描述,模型会取平均,结果两边都不像。这在风格混合时特别常见。
三是权重语法使用过度。括号权重超过 1.5 很容易让某个 token 支配整体,出图变成单一元素的重复。
四是条件被负面提示词抵消。你正面写了“bright”,负面写了“dark”,看起来没问题,但如果负面里恰好有语义相关的词,效果会被削弱。
7.4 报错速查与踩坑记录
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| CUDA out of memory | 分辨率过高或未开分块 | 降分辨率、开注意力分块 |
| 出图全黑或全白 | VAE 数值溢出 | 强制 VAE 用 fp32 |
| 出图正常但内容随机 | 文本编码器与权重不匹配 | 核对基础版本 |
| LoRA 加载报维度错误 | LoRA 与基础模型版本不符 | 换成对应版本的 LoRA |
| 损失出现 NaN | 学习率过高或精度问题 | 降学习率、检查混合精度 |
| 训练集学不像 | 素材质量差或标注不一致 | 清洗素材、统一标注格式 |
| 出图突然变糊 | 负面提示词污染或 CFG 异常 | 精简负面词、复位 CFG |
最后分享一个我踩过好几次的坑:训练 LoRA 时,素材的分辨率和长宽比尽量统一,并且裁掉背景干扰。我早期用过一批随手拍的照片,构图乱七八糟,结果训练出来的模型对构图极度敏感,稍微换个视角就跑偏。后来把所有素材统一裁成方形,去掉干扰元素,同样的参数下效果提升非常明显。这个经验看起来和原理无关,但它影响的其实是数据分布——你喂进去什么分布,模型就学什么分布,这一点在扩散模型上体现得格外直接。