Stable Diffusion 核心原理拆解:CLIP、扩散模型、UNet 与文生图实战 很多人第一次接触 Stable Diffusion是从 WebUI 或者 ComfyUI 开始的输入一句英文提示词选一个模型点一下生成几十秒后得到一张图。这套流程很顺但一旦你开始认真调参、追求稳定出图、甚至想自己做 LoRA 或改流程就会发现自己只是在一个黑盒外面点按钮。我这次把 Stable Diffusion 拆成四集来讲CLIP 文本编码、扩散模型原理、UNet 网络结构、文生图代码实战。这套笔记最初是我给团队算法实习生做入门培训用的后来发现不少已经用 SD 出了几千张图的老手也经常卡在几个基本问题上——CLIP 到底能不能换扩散模型为什么不直接学预测图像UNet 这个分割网络怎么就变成生成模型主力了为什么同一个 seed 换个调度器结果完全不一样这些问题的答案都在模型内部。如果你已经用过至少一种 AI 绘画工具知道采样步数和提示词是什么概念那这份笔记就刚好适合你。我会尽量把原理讲得像工程师之间聊天一样直接该上公式的地方上公式该给代码的地方给完整可跑的代码保证你读完之后能用自己的话解释 Stable Diffusion 的完整链路。1. 第一集CLIP——把一句提示词变成 UNet 能读懂的 77 个向量1.1 为什么偏偏是 CLIP双塔结构的对比学习CLIP 全称 Contrastive Language-Image Pre-training是 OpenAI 在 2021 年发布的模型。它既不是一个单纯看图模型也不是一个文本模型而是把图像和文本拉进同一个向量空间的桥梁。训练数据是大约 4 亿个图像-文本配对训练方法是对比学习。对比学习怎么理解想象一个 batch 里有 N 对图文模型把每张图编码成一个向量把每条文本也编码成一个向量然后要做一件事让每一对正确配对的图文向量距离更近让其他 N²-N 对错误配对的向量距离更远。说得直白点这就像你拿到 50 张动物照片和 50 条英文描述不给你标准答案只让你通过反复对比猜哪句描述对应哪张图猜多了模型自然会发现cat这个词和猫的照片有很强的对应关系。这种训练方式和看图生成文字那种预测式模型有本质区别。预测式模型学到的表示会偏向文本生成而对比式模型学到的是更纯粹的语义对齐。对 Stable Diffusion 来说我们需要的是一个能把自然语言转换成高质量语义向量的文本塔CLIP 恰好提供了这个能力。而且它的文本塔是 Transformer 结构对上下文的理解能力远超传统词向量。1.2 Stable Diffusion 只偷走了文本塔文本塔输出什么这里有一个很常见的误解很多人以为 Stable Diffusion 在推理时会让 CLIP 双塔同时工作——既理解文本也理解图像。实际上推理时 CLIP 的图像塔完全不出场只有文本编码器在起作用。以 SD 1.x 为例你输入的 prompt 会先被 CLIP 的 tokenizer 切词、转成 token id 序列。最大长度是 77 个 token其中还包含开头和结尾的特殊 token。也就是说SD 1.x 从设计上就只能读完约 75 个真正有语义的词元。超过 77 的部分不是被弱化而是直接被截断丢弃。然后这个 token 序列进入 CLIP 文本编码器一个 Transformer输出形状是[1, 77, 768]。注意模型并没有把整句话压缩成一个向量而是保留了 77 个位置、每个位置 768 维的向量序列。这 77 个向量就是在后续 UNet 里和你图像特征做 CrossAttention 的文本条件。这里我要给一个非常实用的建议写 prompt 时最重要的关键词一定要放在前面。因为 SD 1.x 对后段 token 的注意力通常更弱越靠后的词越容易被遗忘。很多人发现换一个词序出图差异巨大很大程度上就是这个原因。1.3 为什么不直接查词表CLIP 处理的是上下文语义如果只是把单词映射成固定向量那 CLIP 文本塔就没什么特别了——这本质上就是传统词向量Word2Vec的能力。CLIP 文本编码器厉害在它是一个带位置编码的 Transformer同一个词在不同上下文里会得到不同的向量表示。bank在river bank和money bank里向量会向不同方向偏移a cat on the moon也不是简单地把 cat 的向量和 moon 的向量拼在一起而是每个 token 都会通过自注意力机制和其他 token 交换信息最终每个 token 的向量都隐含了整个句子的语境。这就是为什么 Stable Diffusion 能理解A 在 B 上面这类空间关系的一部分原因。如果你的文本编码器是静态词向量那狗追猫和猫追狗就完全没区别了生成出来的图大概率会乱套。1.4 换 CLIP 的进阶玩法微调、中文 CLIP 与生态工具CLIP 文本塔虽然强大但有一个天然短板训练语料以英文为主。所以很多中文用户想直接写中文 prompt最核心的改动就是替换或者增强这个文本编码器。社区里有把 Chinese-CLIP 的文本塔接到 SD 的案例。做法不复杂但有一个关键约束文本塔输出维度必须能对齐 UNet 的 cross_attention_dim。SD 1.x 是 768 维SDXL 是两个文本塔拼接成 2048 维。如果换了中文 CLIP 后维度不一样需要加一层线性映射做对齐。这种改动属于换编码器级别的改造会影响出图风格做之前一定要备份原权重。另外CLIP 也经常被当成评分器用生成图片后把图和 prompt 分别过一遍 CLIP 双塔计算两者相似度作为图文一致性的参考分数。很多自动抽卡、批量出图筛选的脚本底层就是这套 CLIP Score 逻辑。在 ComfyUI 里也能找到对应的 CLIP 节点做类似的事情。讲完 CLIP我们应该很清楚一件事prompt 进入模型后被变成了 77 个带语义的向量。但这 77 个向量还不足以直接生成图像它只是条件。真正的图像生成靠的是接下来要讲的扩散模型。2. 第二集扩散模型——从纯噪声里一步步退回去的概率算法2.1 前向过程加噪是一套设计好的确定性流程扩散模型Diffusion Model的正式框架是 2020 年 DDPMDenoising Diffusion Probabilistic Models那篇论文确立的。它包含两个过程前向加噪和反向去噪。前向过程看起来像是故意破坏图像从一个干净的图像 x0 出发每一步加入一点高斯噪声经过 T 步训练时 T 通常取 1000图像逐渐变成纯噪声。如果每一步都要重新模拟深度学习训练根本跑不动。所以 DDPM 用了重参数化技巧直接推导出任意时刻 t 的解析式x_t √(ᾱ_t) · x0 √(1 - ᾱ_t) · ε这里的 ᾱ_t 是一个从 1 逐渐衰减到接近 0 的系数序列ε 是从标准正态分布里采样出来的噪声。这个式子的含义是在第 t 步图像 原始图的信号部分 纯噪声部分两者的比例由 ᾱ_t 控制。t 越大信号越少噪声越多。你可以把前向过程想象成往一杯浓缩咖啡里不断掺水。一开始你还能尝出咖啡味图像语义清晰掺到第 1000 次时基本就是一杯白水纯噪声。而训练时的做法是每次随机挑一个 t按公式直接算出掺了对应比例水的那杯咖啡让网络学会从它还原出原来的浓缩咖啡。2.2 反向去噪网络学习的不是预测图像而是预测噪声反向过程是真正的主角。我们需要训练一个去噪网络 ε_θ(x_t, t)输入是带噪图像 x_t 和时间步 t输出是当初加进去的噪声 ε。训练损失很简单就是预测噪声和真实噪声之间的 L2 距离L || ε - ε_θ(x_t, t) ||²这里有个新手很容易问的问题为什么不直接让网络预测干净的 x0原因主要有两个。一是直接预测图像在数学上等价于一个更强的不适定问题图像细节太多网络很难在任意噪声程度下都稳定还原。二是预测噪声的本质是告诉网络接下来怎么修正对于同一个输入 x_t噪声预测得越准反向采样公式就能越精确地得到 x_{t-1}。再用修旧照片打比方一个合格的修复师得先判断照片破损程度对应 t才知道该做大幅修复还是轻微锐化。如果拿着一张只是有点褪色的照片结果上来就猛涂猛补那一定过犹不及。正因为网络必须区分这张图是被加了 100 步噪声还是 900 步噪声的版本UNet 才必须接收时间步 t 作为额外条件这个点在第三集会展开。2.3 从噪声到图像采样循环里的数学直觉推理阶段和训练阶段截然不同。训练时我们随机挑 t 练习推理时则是从 tT 开始逐步去掉噪声一直走到 t0。每一步的逻辑是用 UNet 预测当前噪声根据公式算出去了一部分噪声后的图像 x_{t-1}重复直到 t0。DDPM 原始采样公式里每一步还会注入一点随机扰动 σ_t·z用来维持采样过程的多样性。这导致一个问题如果想得到高质量图像需要走完接近 1000 步太慢了。DDIMDenoising Diffusion Implicit Models的出现解决了这个问题。DDIM 把采样过程变成确定性轨道不再依赖额外的随机扰动并且允许跳步——可以每 50 步采样一次直接从 1000 步压缩到 20~50 步画质几乎没有损失。后面出现的更快的调度器如 DPM-Solver、UniPC本质上都是对去噪 ODE 的高阶数值解法可以理解成聪明的跳跃策略。这就是为什么同一个模型用不同调度器、不同步数出图会有差异采样轨迹不同落点自然不同。2.4 潜在扩散Stable Diffusion 真正能跑起来的关键如果你在 512×512×3 的像素空间里跑扩散模型每个 batch 的运算量会直接劝退消费级显卡。Stable Diffusion 的解决方案来自 2022 年 Rombach 等人的论文《High-Resolution Image Synthesis with Latent Diffusion Models》思路非常简单先用一个自编码器 VAE 把图像压缩到低维潜在空间然后在潜在空间里做扩散最后再解码回像素图。具体来说一张 512×512×3 的真实图像经 VAE 编码器压缩后变成 64×64×4 的 latent。空间分辨率变成原来的 1/8面积变成 1/64通道数还只有 4。扩散模型处理的张量大小直接从 512×512×3 降到了 64×64×4计算量节省了一个数量级以上。这是 SD 能在普通显卡上推理甚至能做 LoRA 训练的根本原因。这里的 4 个 latent 通道不像 RGB 那样直观你可以把它们理解为 VAE 从图像中提炼出的浓缩语义包含了颜色、轮廓、纹理等 decoder 重建时所需的信息。扩散模型从头到尾都在这个浓缩空间里操作最后用 VAE decoder 把去噪后的 latent翻译成 RGB 像素图。这也是为什么你把 DAE 或者 VAE 换掉出图色彩会明显变化。2.5 步数、调度器和 CFG 的三角关系推理阶段有三个参数直接影响出图效果采样步数、调度器、CFG 引导强度。先说步数。训练时 T1000但推理时用现代调度器根本不需要那么多步。我的经验是5 步以下轮廓能看但细节糊画面容易崩坏10 步能出图但光影和结构比较粗糙20~30 步性价比最高的区间细节和速度平衡最好50 步以上收益递减适合在固定 seed 下微调细节不建议日常使用。再说 CFG全称 Classifier-Free Guidance无分类器引导。它在推理时会同时计算两个分支一个是空文本无条件的噪声预测 ε_uncond一个是实际 prompt条件的噪声预测 ε_cond。最终的预测噪声用这个公式合成ε ε_uncond guidance_scale × (ε_cond - ε_uncond)当 guidance_scale1 时等于完全不做引导图像很自由散漫当 guidance_scale 增大到 7~9 时图像会更贴近 prompt 语义但颜色也会更饱和、对比度更强如果调到 15 以上画面容易出现过曝、塑料感和文字崩坏。这个参数不是越大越好需要找到你模型和 prompt 的平衡点。理解了第二集我们知道图像其实是从一个随机噪声退火出来的。但扩散模型里的主力网络——UNet——还没有讲。它是怎么做到既知道全局语义、又保住细节的第三集解开。3. 第三集UNet——一路下采样又一路还原的降噪骨架3.1 为什么降噪网络选了 UNet 而不是普通 CNNUNet 最早是 2015 年提出的医学图像分割网络结构是编码器-解码器对称外加跳跃连接。它之所以成为扩散模型的主力是因为扩散模型的去噪任务和分割任务有共同点输入和输出必须保持相同尺寸的空间结构。普通 CNN 如果用一堆卷积不断下采样最后接全连接层输出类别那它只能做分类或者压缩到单个向量这类任务。但去噪任务要求网络对每一个空间位置都给出一个预测值——像素级输出。UNet 的编码器负责提取多尺度特征解码器负责逐步恢复分辨率而跳跃连接把编码器各层的空间信息直接拼到解码器对应层让网络在放大时能参考原始细节。你可以这样想UNet 的下采样部分像在读图——理解图像里哪里是猫、哪里是天空、哪里是边界上采样部分像在改写——带着全局理解把每个像素修正一遍。如果只有下采样没有跳跃连接细节会丢得七七八八生成出来的图就会看起来大概对但线条和纹理全糊了。3.2 在潜在空间里UNet 内部长什么样Stable Diffusion 的 UNet 处理的不是像素图而是 64×64×4 的 latent。整体结构可以分为四个部分编码器down经过若干阶段的 ResNetBlock 和 Transformer 2D 块分辨率逐步减半通道数逐步增加典型路径是 64×64 → 32×32 → 16×16 → 8×8中段mid在最低分辨率8×8处继续做全局建模这里是信息最浓缩的地方解码器up对称地上采样从 8×8 一路回到 64×64跳跃连接把编码器每阶段的特征保存下来与解码器上采样后的结果拼接。你会在每个基础块里看到卷积、GroupNorm、SiLU 激活和注意力模块的组合。这里特别提一下SD 的 UNet 用的是 GroupNorm 而不是 BatchNorm是因为扩散模型训练和推理时 batch size 通常很小很多时候就是 1BatchNorm 依赖的 batch 统计量不稳定而 GroupNorm 不依赖 batch 维度对单样本更友好。3.3 时间步条件UNet 怎么知道自己在第几步在前面讲反向过程时我说过网络必须知道自己正处在去噪的哪一步。实现方式是将时间步 t 作为标量输入先用正弦位置编码转成向量再经过 MLP 映射成 high-dimension 的时间嵌入注入到每个 ResNetBlock 中。时间步条件的作用非常直观当 t 接近 1000 时输入图噪声极大网络要重点恢复整体结构当 t 接近 0 时输入图已经很干净网络只需要做微小修正。同一个 UNet 要同时胜任这两种截然不同的任务没有时间嵌入几乎不可能做到。这有点像面试官问你你工作到第几年了——你给的建议深度完全取决于这个年限。UNet 也一样缺了时间信号它就无法决定该用多大的力度去噪。3.4 CrossAttention文本和图像真正汇合的地方UNet 的 CrossAttention交叉注意力是实现文本控制图像的核心模块。注意力机制在 UNet 里有两种SelfAttention 和 CrossAttention。SelfAttention 让图像内部不同区域互相参考保证整体一致性CrossAttention 则负责把文本信息注入图像特征。具体机制是QQuery来自 UNet 的图像特征图把空间位置展开成向量KKey和 VValue来自 CLIP 文本编码器输出的 77 个 token 向量注意力矩阵大小大约是 [H×W, 77]代表图像每个位置与每个 token 的相关性经过 Softmax 加权求和后把文本语义融入到图像特征中。这也是很多人在做注意力编辑的切入点如果想知道 cat 这个词具体影响了图像哪个区域可以直接读 CrossAttention 的注意力热力图。某个区域对 cat token 的权重越高生成时就越是猫的地方。LoRA 和 ControlNet 等工作也都围绕这些注意力模块做文章。3.5 基于 UNet 的生态LoRA、ControlNet 与蒸馏模型理解了 UNet 结构你就会明白为什么 LoRA 文件通常只有几十到几百 MB。LoRA 本质上是给注意力层的 Q/K/V 和 FF 线性层添加低秩分解矩阵训练时冻结原权重只更新这些小型旁路矩阵。它作用的是 UNet 内部注意力投影的方向所以不同的 LoRA 能改变出图风格或人物长相。ControlNet 的思路更巧妙复制一份 UNet 的可训练分支通过零卷积zero convolution让这个分支初始输出为 0不会破坏原模型然后逐步加载姿态、深度、边缘等条件。因为条件图能通过这个旁路影响 UNet 的特征最终的生成结果就严格受外部结构控制。近几年流行的 LCM、Turbo 等蒸馏模型本质上是让 UNet 学会用更少步数去噪或者配合新的调度器把采样步数压到 1~8 步。这些模型往往也要修改 UNet 的某些层和时间嵌入但核心骨架依然是 U 型结构。现在原理层面的三块拼图已经齐全CLIP 负责理解文本扩散模型负责定义如何从噪声还原图像UNet 负责实际执行去噪。接下来把它们串起来跑一段真实代码。4. 第四集文生图代码实战——从加载模型到保存第一张图4.1 环境准备与依赖版本我建议在 Python 3.10 或 3.11 环境下运行PyTorch 使用 2.x 版本。核心依赖是 Hugging Face 的 diffusers、transformers、accelerate以及 safetensors。安装命令pip install diffusers transformers accelerate safetensors模型权重直接从 Hugging Face 加载即可前提是当前环境可以正常访问该站点。模型本体大约 4GB 左右如果显卡显存有限我会建议下载 fp16 版本权重能用较小显存跑。显存方面SD 1.5 的 fp16 推理在 4GB 显存上已经能勉强运行开启 attention slicing 之后更稳8GB 显存可以比较流畅地出图。如果你的机器是 Mac也可以走 MPS 后端但注意 PyTorch 版本必须和 macOS 架构匹配否则启动时会遇到ImportError: dlopen ...这类经典报错。可以用下面这行代码先验证环境python -c import torch; print(torch.backends.mps.is_available())如果输出True说明 MPS 后端可用如果报 dylib 相关错误一般是 torch 装成了 x86 版本或系统 Python 环境不干净建议用干净的虚拟环境重新安装 arm64 版 PyTorch。4.2 最短路径用 diffusers 的 pipeline 一行流出图如果是快速验证效果我强烈推荐先用StableDiffusionPipeline代码量非常少from diffusers import StableDiffusionPipeline import torch model_id runwayml/stable-diffusion-v1-5 pipe StableDiffusionPipeline.from_pretrained( model_id, torch_dtypetorch.float16, safety_checkerNone, ) pipe pipe.to(cuda) # 固定随机种子便于复现 generator torch.Generator(devicecuda).manual_seed(42) prompt a cute corgi walking on the beach, golden hour, highly detailed negative_prompt lowres, blurry, bad anatomy image pipe( prompt, negative_promptnegative_prompt, num_inference_steps20, guidance_scale7.5, generatorgenerator, ).images[0] image.save(corgi.png)这里把safety_checker设为None是为了省显存、提速实际产品中是否保留检查器取决于你的场景。num_inference_steps20是采样步数guidance_scale7.5是 CFG 强度。只要模型下载完成这一段代码就能出图。4.3 手写采样循环真正理解每一步去噪Pipeline 封装得太好反而不利于理解。所以我把它的内部流程拆开手动加载各个组件自己写采样循环。这一步强烈建议照着敲一遍你会对前几集讲的所有概念建立真正的直觉。import torch from transformers import CLIPTextModel, CLIPTokenizer from diffusers import AutoencoderKL, UNet2DConditionModel, DPMSolverMultistepScheduler model_id runwayml/stable-diffusion-v1-5 device cuda # 1. 加载四个核心组件 tokenizer CLIPTokenizer.from_pretrained(model_id, subfoldertokenizer) text_encoder CLIPTextModel.from_pretrained( model_id, subfoldertext_encoder, torch_dtypetorch.float16 ) vae AutoencoderKL.from_pretrained( model_id, subfoldervae, torch_dtypetorch.float16 ) unet UNet2DConditionModel.from_pretrained( model_id, subfolderunet, torch_dtypetorch.float16 ) scheduler DPMSolverMultistepScheduler.from_pretrained( model_id, subfolderscheduler ) text_encoder.to(device) vae.to(device) unet.to(device)然后是 prompt 编码。这一部分对应第一集讲的内容文本塔把 prompt 变成[1, 77, 768]的向量序列。注意空文本也要编码因为 CFG 需要条件分支和无条件分支两个预测。def encode_prompt(prompt: str): tokens tokenizer( prompt, paddingmax_length, max_lengthtokenizer.model_max_length, truncationTrue, return_tensorspt, ).input_ids.to(device) return text_encoder(tokens)[0] text_emb encode_prompt(a cute corgi walking on the beach, golden hour) uncond_emb encode_prompt()初始化随机噪声 latent这对应前向过程里的纯噪声起点。然后进入去噪循环每一步用 UNet 同时预测条件分支和无条件分支的噪声再按 CFG 公式合成generator torch.Generator(devicedevice).manual_seed(42) latents torch.randn( (1, 4, 64, 64), generatorgenerator, devicedevice, dtypetorch.float16, ) latents latents * scheduler.init_noise_sigma for t in scheduler.timesteps: # 把无条件分支和条件分支拼接成一个 batch latent_model_input torch.cat([latents] * 2) latent_model_input scheduler.scale_model_input(latent_model_input, t) with torch.no_grad(): noise_pred unet( latent_model_input, t, encoder_hidden_statestorch.cat([uncond_emb, text_emb]), ).sample noise_pred_uncond, noise_pred_text noise_pred.chunk(2) noise_pred noise_pred_uncond 7.5 * (noise_pred_text - noise_pred_uncond) latents scheduler.step(noise_pred, t, latents).prev_sample循环结束之后latents 已经是干净的潜在表示但还不是 RGB 图像。最后用 VAE 解码器把它翻译回像素图latents latents / vae.config.scaling_factor with torch.no_grad(): image vae.decode(latents.to(torch.float32)).sample image (image / 2 0.5).clamp(0, 1).permute(0, 2, 3, 1).cpu().numpy()这一段代码走完你就亲手复现了 Stable Diffusion 文生图的完整链路CLIP 编码文本 → 随机初始化 latent → UNet 逐步去噪含 CFG 引导→ VAE 解码成图。以后无论用 WebUI、ComfyUI 还是其他封装方案你都能在脑中把它映射成这条流程。4.4 常用参数的调参心法下面这张表是我在实际使用中总结的常见参数变化影响你可以直接拿来当参考参数常用区间倾向个人建议采样步数10~50越少越快越多越精细20~30 步最佳平衡CFG 引导强度1~15越高越贴 prompt越高越容易过饱和7~9 是大多数模型的甜点区调度器Euler / DPM / UniPC 等决定采样轨迹DPM 2M Karras 20 步很稳Seed任意整数决定初始噪声先固定再调其他参数分辨率SD 1.x 建议 512 的倍数过高会崩或重复构图512×768 比较常见关于步数我要多说一句很多人以为步数越多画质越高实际上超过 50 步之后画面细节提升微乎其微反而让生成时间翻倍。如果你的 prompt 和 CFG 都不变固定 seed 后把 20 步换成 30 步会发现画面只是微调而非本质改变。调度器方面我现在最常用的组合是 DPM 2M Karras 20 步。Karras 变体使用了更合理的噪声调度出来的图比老式 Euler 更干净色彩也更自然。如果你追求最大细节可以试试 DPM SDE Karras 30 步但速度会慢不少。4.5 实际踩坑记录显存、精度、复现与 Mac 启动问题实践过程中总有一些文档里不写的问题这里挑几个最常遇到的。第一个坑是 fp16 下的 VAE 解码发灰。SD 的 VAE 在 fp16 精度下解码时输出容易偏灰、饱和度低。这是精度问题不是模型问题。解决办法有两种一种是像我在上面代码里那样把 latent 转成 float32 再解码另一种是使用社区里修复过的 fp16 VAE。至少在 diffusers 里decode 前转 float32 是最省事的。第二个坑是显存不足。如果你在 4GB 显存上运行建议在 pipeline 里开启pipe.enable_attention_slicing()或者开启模型层级的 CPU offload。不要一上来就把 batch size 调大更不要直接跑 1024×1024 分辨率。潜在扩散对分辨率的容忍度比像素扩散高但也不是完全无上限超采样很容易出现重复构图或结构崩坏。第三个坑是复现问题。很多人固定了 seed 却发现每次结果不一样原因通常是调度器不一致。StableDiffusionPipeline默认会读取模型仓库里的 scheduler 配置如果你手动换了pipe.scheduler必须连调度器、步数、CFG 一起固定才能复现同一张图。另外PyTorch 版本差异也会带来极细微的数值误差如果做严格复现最好连运行环境一起固定。第四个坑是 Mac 上启动时的ImportError: dlopen报错。这个问题大多不是模型问题而是 PyTorch 安装包与系统架构不匹配。请先把环境里的 torch 卸载重新安装和你的 MacApple Silicon匹配的 arm64 版本再检查torch.backends.mps.is_available()。MPS 后端目前已经能稳定跑 SD 1.5 的推理只是比 NVIDIA 显卡慢一些。最后再分享一个我自己的调参习惯。我会先写一个小而精的 prompt把最重要的核心词放在最前面然后固定 seed 和调度器只调整 CFG观察 6、7、8、9 四档出图的差异找到最贴合意图的档位。之后再试着把步数从 20 提到 30看细节是否值得额外的等待时间。最后才换调度器或者换模型对比。这样一轮下来你基本就知道这个模型在你这台机器上的脾气了。这套方法的思路也适用于后面你想玩的 LoRA、ControlNet 和更快的蒸馏模型——它们再怎么进阶底层还是 CLIP、扩散模型和 UNet 这三个老搭档。