ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Stable Diffusion 原理拆解:模型结构、训练与预测实战

Stable Diffusion 原理拆解:模型结构、训练与预测实战 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 id77×768 条件序列 池化向量约 123MU-Net去噪主力预测每一步的噪声潜变量 t 文本条件同维度噪声预测约 860MVAE潜空间与像素空间的双向翻译像素图 / 潜变量潜变量 / 像素图约 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 Encoder77 个 token 这个硬约束从哪来先解释一个高频困惑为什么提示词超过一定长度就不生效了答案是 CLIP 的位置编码固定为 77 个位置所以输入序列被硬性截断或填充到 77。这 77 个里第一个是起始标记最后一个是结束标记真正承载语义的有效 token 是 75 个左右。SD1.5 用的是 CLIP ViT-L/14 的文本塔隐藏维度 768输出形状77 × 768这个张量后面会作为 U-Net 交叉注意力的 Key 和 Value。SDXL 换成了双文本编码器方案同时用 ViT-L768 维和 OpenCLIP ViT-bigG1280 维把两路输出在通道维度拼接成 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 到 4GB1024×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 × kB 是d × rr 是秩通常取 4、8、16、32。算一下参数量对比。假设原矩阵是1280 × 1280也就是 1638400 个参数。用 r8 的 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 只算一步推理时要从 t999 一路走到 t0走满设定的步数。每一轮里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 到 9SDXL 因为训练方式和文本编码器不同甜区更低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_dtypetorch.float16, safety_checkerNone, requires_safety_checkerFalse, ).to(cuda) # 换成 DPM 2M Karras20 步就能出可用结果 pipe.scheduler DPMSolverMultistepScheduler.from_config( pipe.scheduler.config, use_karras_sigmasTrue ) # 显存优化三件套 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( promptprompt, negative_promptnegative, num_inference_steps25, guidance_scale7.0, width512, height512, generatortorch.Generator(cuda).manual_seed(20260101), ).images[0] image.save(first_try.png)这段代码里有几个细节值得解释。torch_dtypetorch.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 时素材的分辨率和长宽比尽量统一并且裁掉背景干扰。我早期用过一批随手拍的照片构图乱七八糟结果训练出来的模型对构图极度敏感稍微换个视角就跑偏。后来把所有素材统一裁成方形去掉干扰元素同样的参数下效果提升非常明显。这个经验看起来和原理无关但它影响的其实是数据分布——你喂进去什么分布模型就学什么分布这一点在扩散模型上体现得格外直接。
返回列表