
手头这套四集精讲笔记我翻来覆去看了好几遍每一集都对应了一个核心模块CLIP 文本编码、扩散过程、UNet 去噪骨干、以及最后从零开始的文生图代码实战。如果你正准备入门 Stable Diffusion但又不想一直停留在抄提示词、点生成按钮的层面那这套笔记应该能帮你把底层的逻辑脉络捋清楚。这篇文章我会顺着四集的思路把 CLIP、扩散模型、UNet 和代码实战这四条线全部串起来讲同时补上很多视频里不会细说的背景知识和实操心得。先说清楚这个内容适合谁。已经用过 WebUI 或 ComfyUI 的朋友平时生成图片没什么问题但面对像为什么 Lora 训练要冻结 CLIP、扩散采样器到底在采什么、手部结构为什么总崩这类问题会觉得心里没底。这套笔记正是给这类人群准备的。不夸张地说把这几块原理吃透之后你再调参数、换模型、甚至自己改 pipeline都会有明确的方向感而不是靠运气抽卡。1. 整体架构与四集编排思路1.1 Stable Diffusion 并不是单一模型而是一条组装流水线很多人初次接触 Stable Diffusion 时会下意识地把它当成一个类似输入文字输出图片的巨型黑盒模型。实际上它是由三个独立组件协作完成任务的整套系统文本编码器把用户输入的自然语言提示词转换成向量大多数常用版本用的是 CLIP 的文本分支。扩散模型主体通常指 UNet 结构负责在潜在空间里做去噪是画面内容真正的绘制者。变分自编码器VAE负责把像素空间的图片压缩到潜在空间以及最后把生成好的潜在表示解码回可视图像。打个生活化的比方CLIP 文本编码器像一个理解需求的翻译官把一只戴帽子的柴犬翻译成模型能读懂的数字向量UNet 是主画师根据翻译官转述的需求一遍遍抹去噪点逐渐勾勒出画面VAE 的解码器则是装裱师傅把主画师在草稿纸上画的紧凑编码恢复成一张完整的高清图片。这套笔记里四集的内容恰好就是围绕这三个组件再加一条代码实操链展开的。之所以把架构拆成多段来讲而不是一口气糊上去是因为这三个组件的训练逻辑、参数量级、运行方式差异巨大。混在一起讲新手很容易把文本信息怎么注入 UNet和噪声是怎么一步步被去掉的搅成一锅粥。分模块逐个击破是学习这类大型生成模型最有效的路径。1.2 为什么选择在潜在空间里做扩散第一集讲到扩散模型的时候会特别强调一个概念——潜在扩散模型LDM。原始的 Denoising Diffusion Probabilistic ModelsDDPM是在像素空间里工作的每一步都要处理 512×512×3 这种量级的数据训练和推理开销都相当惊人。Stable Diffusion 的聪明之处在于先用 VAE 的编码器把 512×512 的图片压缩成 64×64 的潜在表示通道数也大幅减少扩散过程在这个小得多的空间里进行。这里有个很关键的收益计算量直接降了一个量级。原本在像素空间里跑 1000 步扩散在潜在空间可能只需要同样的步数但单步的计算代价小得多。这也是为什么 Stable Diffusion 能在普通消费级显卡上跑起来而不是只能存在于云端服务器集群里。所以整套笔记反复强调潜在空间这个概念它是理解 Stable Diffusion 性能优势的钥匙。四集笔记的编排还有一个很贴心的逻辑每一集都会在最后留下一道思考题或者一个指向下一集的问题。看的时候我建议你顺着这个设计走比如第一集讲完文本编码后思考如果没有这一层模型还能生成指定内容的图片吗这会为第二集理解扩散模型的条件机制打下很好的基础。2. CLIP 文本编码器提示词如何变成模型能懂的语言2.1 CLIP 的基本机制图文对比学习第二集开篇讲 CLIP 时很多人会误以为 CLIP 是一个 NLP 模型。其实 CLIP 是一个多模态模型它的核心思路是用对比学习把图片和文本映射到同一个向量空间。简单来说训练时给它看一批图文对让模型学会把匹配的图片和文字在向量空间里拉近把不匹配的推远。训练完成后CLIP 就有了两项能力图片编码和文本编码Stable Diffusion 用的是它的文本编码分支。为什么这事很重要因为 Stable Diffusion 本身并不理解英文单词的含义。它只知道一个向量。假设提示词是a red carCLIP 文本编码器会把这四个 token 编码成一串高维浮点数。这个浮点向量携带了红色、汽车、单数等语义信息。后续 UNet 在去噪时会通过交叉注意力机制把这个向量里的信息逐步画到潜在表示上。视频里有个很精准的说法提示词不是被读懂的而是被映射成了一种机器可以施加影响的数值条件。2.2 文本条件如何进入生成过程这里需要澄清一个容易踩坑的细节。在代码实战那一集你会发现调用 pipeline 时传入的 text prompt 并不会直接以原始字符串的形式进入 UNet。流程是先由 CLIP 的 tokenizer 把字符串切成 token再通过 CLIP Text Encoder 得到 77 个 token 长度的嵌入序列超出部分会被截断。这 77 个 token 的向量序列会被 UNet 的交叉注意力层用作 key 和 value 的来源而 query 来自 UNet 自身的中间特征。如果你把这段过程类比成绘画时参考一本文字分镜脚本CLIP 编码器负责把脚本的每一页都转换成具体的画面提示UNet 每一层去噪时都会翻看这些提示决定当前该加强哪个细节。这也是为什么提示词的顺序、措辞会影响出图结果——不同 token 在嵌入空间的远近关系不同交叉注意力赋予它们的权重也不同。你越是想让某个元素出现越要把对应的描述写清楚因为 UNet 在注意力计算中会更倾向于利用那些语义明确的词条。2.3 CLIP 的实际局限与微调方向热词里经常出现clip 模型应用、clip 模型微调、clip 知乎这类搜索说明大家在实际使用中发现 CLIP 并不完美。它有几个非常明显的短板token 长度限制CLIP 一次只能处理 77 个 token。长提示词会被无情截断尾部信息直接丢失。这也是为什么很多进阶玩家会用提示词合并或者加权语法来规避但本质上 77 token 的上限依然存在。语义纠缠问题CLIP 在很多抽象概念、否定表达、空间关系上表现不佳。没有帽子这种否定语义往往会失效因为训练数据里很难覆盖所有否定场景。手部细节不敏感CLIP 编码对精细结构比如手指的数量和关节方向不敏感加上 UNet 的交叉注意力分辨率限制导致生成的手部经常穿模。这其实不是某一个模块单独的问题而是整套 pipeline 在细节感知上的系统性短板。针对 CLIP 的微调社区里已经有比较成熟的方案比如自定义 CLIP 模型训练、给 CLIP 增加额外的视觉分支约束等。但普通人更实际的做法是在提示词层面做规避比如直接写明five fingers或者用负面提示词把bad hands排除。如果你打算深入训练自己的 LoRA建议先弄清楚 CLIP 底层嵌入空间的风格偏置——不同行业、不同风格的图片集对同一段描述词的响应路径完全不同微调时最好先做小范围实验。3. 扩散模型核心原理噪声与去噪的双向奔赴3.1 前向过程给图片一点点加噪声扩散模型的前向过程本质上是一个逐步破坏数据的过程。给定一张干净图片我们按预设的噪声调度表逐步往里面加高斯噪声。经过足够多步之后图片会变成一片完全随机的噪点也就是纯高斯噪声。这个过程不需要训练只是一个确定性的数学流程。记作[ q(x_t | x_{t-1}) \mathcal{N}(x_t; \sqrt{1 - \beta_t} x_{t-1}, \beta_t I) ]这里的 (\beta_t) 是每一步的噪声强度。在 DDPM 原始论文里(\beta) 是逐步递增的早期步骤加少量噪声后期步骤加更多噪声。视频笔记里把前向过程比作把一幅油画放进雨里淋雨点越来越密画面细节逐渐褪去最终只剩一片水渍。这个比喻非常形象因为接下来的反向过程就是把雨点一滴一滴从画布上抹掉。3.2 反向过程从噪声中逐步还原画面反向过程是扩散模型的核心难点。我们想要学习一个神经网络让它能够逐步预测并去除前向过程添加的噪声。给定当前时间步 (t) 的噪声图模型要预测出噪声分量然后据此推算出 (t-1) 时刻的较干净图像。在 Stable Diffusion 里这个负责预测噪声的网络就是 UNet。这里有一个常见的困惑为什么会预测噪声而不是直接预测原图从数学上看给定 (x_t)通过预测噪声 (\epsilon)我们能根据 (x_{t-1} \frac{1}{\sqrt{\alpha_t}}(x_t - \frac{\beta_t}{\sqrt{1-\bar{\alpha}_t}}\epsilon)) 来估计前一步的图像。预测噪声比直接回归原图更容易因为噪声的分布是已知的梯度信号更平稳。这套笔记里在讲 UNet 训练目标时也强调了这一点模型输出的是噪声残差而非像素本身。反向过程不是一步到位的。你设置的采样步数决定它需要执行多少轮。常见的步数范围是 20 到 50 步步数太少会丢失细节太多则会增加推理时间。这也是采样器算法被引入的原因——不同采样器用不同的数学策略来估计这个逆过程的跳步方式。3.3 训练目标与损失函数扩散模型的训练目标可以写成下面这种期望形式[ L \mathbb{E}{x, \epsilon, t} \left[ | \epsilon - \epsilon\theta(x_t, t) |^2 \right] ]大意是让 UNet 在给定加噪后的图像 (x_t) 和时间步 (t) 时能够尽量精确地预测原始噪声 (\epsilon)。这个目标非常简洁没有对抗损失没有感知损失就是一个 L2 距离。你能看出为什么扩散模型的训练相对稳定——它的优化目标非常清晰。在训练的时候还会随机采样时间步 (t)并且在推理时对时间嵌入做条件注入。这意味着 UNet 要理解我现在处于去噪过程的哪一步。所以 UNet 的结构里会有时间步的嵌入层也就是 SinaCos 位置编码加上 MLP 映射。如果没有这个时间信息模型就无法区分早期去噪和晚期去噪的不同行为要求。3.4 采样器选择为什么有些人偏爱 DPM当前流行的采样器有很多种DDIM、Euler、DPM 2M、UniPC 等。它们本质上都是对上述反向过程的重建策略。DDIM 是经典的确定性采样器速度快且可复现性好Euler 更简单适合快速预览DPM 系列在步数较少时依然能保持较好的图像质量是目前社区的主流选择。选采样器要考虑两个维度速度和质量。如果你只是出草图、做灵感预览用 20 步 Euler 完全够用如果你要高清出图或者做精细修复建议用 DPM 2M Karras 配 30 步效果通常更稳定。CDMS 在 4 到 8 步的情况下表现相当诱人但工程稳定性还得看显卡和模型匹配度。实战时建议你在固定 seed 的情况下用不同采样器跑同一组提示词观察细节差异这样很快能积累出你自己的采样器偏好。4. UNet 架构深度解读去噪主画师的工作台4.1 为什么 UNet 能在扩散模型中占据核心位置UNet 源自生物医学图像分割任务它的名字来源于U形的对称编码器-解码器结构。在扩散模型里它的工作不是分割而是去噪。之所以沿用 UNet 结构是因为它天然适合多尺度特征提取——下采样路径逐步捕获全局语义上采样路径逐步恢复空间细节跳连接则保留了不同尺度的纹理信息。去噪任务正好需要理解整体内容 恢复局部细节两方面的能力UNet 的结构与这一需求高度契合。如果你看过 UNet 的层结构图会发现左侧是降采样阶段逐层减少空间分辨率、增加通道数右侧是上采样阶段逐层恢复分辨率、减少通道数中间是瓶颈层。每一层都包含 ResNet 残差块而每隔几个 stage 还会加入注意力模块。这些注意力模块在文生图任务里承担着接收文本条件的关键职责。4.2 架构组件拆解残差块、注意力块、时间嵌入整个 UNet 内部包含三类核心组件ResNet 残差块用两条路径恒等映射 卷积堆叠叠加输出保证梯度流通顺畅同时提高特征表达能力。自注意力模块让特征图内部的不同位置互相交换信息有助于生成风格统一、结构协调的画面。交叉注意力模块以文本嵌入向量为条件让图像特征按文字语义对齐。这是文生图的核心机制。时间嵌入同样是必需组件它通过一个简单的正弦编码 MLP 映射把当前去噪步数转换成可学习的向量再注入到 ResNet 块中。没有它UNet 就不知道当前处于去噪的哪个阶段也就无法动态调整细节强度。4.3 交叉注意力文本条件如何画进图像交叉注意力是 Stable Diffusion 理解提示词的关键机制。可以把注意力计算看作一个信息检索过程UNet 的特征图产生 Query当前要画的东西CLIP 文本嵌入产生 Key 和 Value文本里提到的要素集合。通过点积相似度模型决定当前这个区域更应该关注哪些文本 token。在代码实战里你会看到 UNet 的 forward 函数中有一个 text_prompt 的输入这个输入会经过一个投影层映射到与图像特征相同的维度然后进入交叉注意力层。这个过程通常被描述为把文字信息与视觉特征做对齐。当模型生成一只戴帽子的柴犬时交叉注意力会在去噪的早期阶段把帽子的语义绑在狗头的位置附近后续去噪步再逐步细化。4.4 UNet 模型的改进方向社区里很多魔改模型本质上就是换掉了 UNet 的不同组件。常见的方向包括增加模块深度或宽度让 UNet 能捕获更复杂的特征但训练的显存开销大幅增加。引入稀疏注意力在注意力计算中只让特定区域交互减少全局注意力的计算量。适合高分辨率生成。混合专家机制把 UNet 的部分层替换为 MoE 结构提升模型容量而不显著增加推理成本。如果你对这些改进感兴趣可以先读几篇关于 Attention 变体的论文再对照代码库里的 UNet 实现看改动点。这个思路比盲目套用模型更有长期价值。5. 文生图代码实战从零开始调用扩散 Pipeline5.1 环境准备与依赖安装第三集和第四集是纯代码实战第一件事就是搭环境。我推荐使用 Python 3.10 及以上版本、PyTorch 2.x、以及 Hugging Face 的 diffusers 库。安装命令极其简单pip install torch torchvision diffusers transformers accelerate如果你用的是 NVIDIA 显卡建议先装对应版本的 CUDA 版 PyTorch再安装 diffusers。AMD 用户可以考虑偏底层的方向或者装好 DirectML 后跑简化版。注意最好不要直接装最新版 PyTorch 而不看显卡 CUDA 版本踩过坑的人都知道版本不匹配会导致一堆张量运算报错。为了节省网络负担建议预先设置模型缓存目录环境变量export HF_HOME/your/cache/dir5.2 用 diffusers 的高阶接口快速出图如果只想快速验证流程使用StableDiffusionPipeline非常直接from diffusers import StableDiffusionPipeline import torch pipe StableDiffusionPipeline.from_pretrained( runwayml/stable-diffusion-v1-5, torch_dtypetorch.float16, safety_checkerNone ) pipe pipe.to(cuda) prompt a cute corgi wearing a hat, studio lighting, high detail image pipe(prompt, num_inference_steps30, guidance_scale7.5).images[0] image.save(output.png)这段代码虽然简洁背后包含的步骤其实就是前文讲过的完整流程CLIP 编码提示词 → 生成初始噪声 → UNet 按步去噪 → VAE 解码 → 输出图像。这里guidance_scale即 CFG 强度控制模型对文本条件的服从程度7.5 是经验值设置太低会导致画面与提示词关联弱太高则容易过饱和或细节失真。如果你想要更可控的复现效果记得固定随机种子generator torch.Generator(cuda).manual_seed(42) image pipe(prompt, num_inference_steps30, guidance_scale7.5, generatorgenerator).images[0]5.3 手写一个精简版采样循环别急着直接跳进全功能 pipeline自己写一遍采样循环会极大地加深理解。以下是一个极度精简的推理流程伪结构便于对照笔记中的公式理解实际运作方式# 以随机噪声 x_T 开始 x torch.randn(...) # [1, 4, 64, 64] 潜在空间尺寸 # 从 T 到 1 循环 for t in range(num_timesteps, 0, -1): timestep torch.tensor([t] * batch_size) noise_pred unet(x, timestep, encoder_hidden_statestext_emb).sample x scheduler.step(noise_pred, t, x).prev_sample # 最后用 VAE 解码 image vae.decode(x / scale).sample这里的scheduler.step就是采样器核心逻辑的封装。如果你有精力深挖建议去读 diffusers 里 DDIMScheduler 的源代码你会发现它内部就是几个张量乘加操作并不神秘。把采样循环手写一遍之后再看教材里的公式每个符号都有了对应物不会再觉得抽象。5.4 参数调整的实操套路实战中最重要的一批参数是采样步数20 到 30 步之间曲线效果最佳。低步数适合快速探索高步数适合精细输出。CFG 强度7 到 9 是文生图的甜区具体值随模型不同而浮动。如果你用负面提示词建议不要超过 12。图像尺寸如果以 512 为基础不要突然输出 1024×1024容易产生重复结构和异常畸变。最好从 512×512 开始再逐步放大。seed发现好结果时立刻记录对应的 seed这是社群交流出图的通用语言。6. 常见问题与排查实录6.1 CLIP 相关的坑截断、语义偏移、条件注入失败提示词被截断CLIP 只接受 77 个 token超出部分不会报错但会静默丢弃。当你发现提示词里某个重要元素死活不出现先检查它是不是排在 77 token 之后。条件注入失败在代码里如果encoder_hidden_states的传递出错UNet 不会报错但生成的图片会跟提示词毫无关联变成随机风格迁移。这种情况要先检查 text encoder 的输出维度是否为 (batch, 77, 768)再看有没有正确传入。6.2 手部修复三招手部结构崩坏是 Stable Diffusion 老生常谈的问题实际应对有三招比较好用负面提示词兜底在 negative prompt 里写入bad hands, missing fingers, extra fingers, malformed hands这能显著降低手部畸形率但并不能根治。原因在于负面提示词也只是在交叉注意力层面做排除底层能力缺失照样崩。后处理重绘先用低分辨率、粗糙整体生成然后用局部重绘inpainting或 img2img 聚焦到手部区域配合 prompt 强调detailed hand with five fingers再重绘这块区域。这是目前手动修复最稳定可靠的方法。专用模型与 LoRA换用手部质量优化的 checkpoint 或专门训练的手部 LoRA。这其实是绕开短板的最快路径。如果你频繁出人像建议常备一个手部 LoRA关键时候能少掉很多头发。另外有一个细节常被忽略当生成的人物较小或手部处于图片边缘时手部崩坏率会明显提升。原因一目了然——交叉注意力在低分辨率特征上对微小结构的感知能力天然不足加上边缘位置缺乏上下文依赖。6.3 ComfyUI 中的提示词优化思路在 ComfyUI 里很多用户会碰到clip 询问机或者提示词相关的节点连接问题。其实 ComfyUI 的底层逻辑和 WebUI 没有本质区别只是把 pipeline 的所有子步骤都显式地做成节点。新手容易犯的错误是把CLIP Text Encode节点的输出直接接到模型加载器却忘了 connect 到 UNet 的 cross attention 条件输入。遇到此类问题建议一步步检查节点连接关系另外注意两个 Text Encode 输入正向/负向都要有对应的conditioning输出否则 CFG 会退化为非条件生成画面基本不可控。6.4 稳定复现的参数习惯固定 pipeline 内部的随机种子同时把scheduler设定为固定的策略。不同采样器在相同 seed 下结果不同这是正常现象不算 bug。如果你用了torch.compile请注意部分调试场景下它会改变采样结果通常仅影响性能而非正确性但如果你在做精确复现实验建议关掉它。做对比实验时尽量保持同一显卡、同一批次大小。浮点数精度与 batch 大小在某些实现中会影响结果中的微小细节影响大图评估的整体印象。最后再分享一条个人经验这四集笔记的价值在于它把 Stable Diffusion 从神秘的 AI 绘图工具还原成了一个可理解、可调试的工程系统。真正常用的核心公式和形态其实不多难点在于把这些组件如何串联成一条完整流水线理解透。建议你在看完笔记后亲自把第五节的采样循环改写成一个自定义版本再往里面加一个自定义 LoRA 注入模块。走完这一遍绝大多数关于 Stable Diffusion 的技术讨论你都能跟上节奏不会再觉得这些概念只是漂浮在抽象层面的流行词。