1.作者介绍
董柯帆,男,西安工程大学电子信息学院,2025级研究生,张宏伟人工智能课题组
研究方向:机器视觉与人工智能
电子邮件:867068473@qq.com
2.算法介绍
2.1扩散模型基本思想
Stable Diffusion 是一种基于扩散模型的文本到图像生成模型,可以根据自然语言提示词生成具有对应语义内容的图像。扩散模型的核心思想可以概括为两个互逆的关键过程:正向加噪过程与反向去噪过程。通过模拟“破坏”与“重建”的动态循环,实现从纯噪声到真实图像的生成。
训练阶段:正向加噪(破坏信息) 模型在训练时不断向清晰图像中叠加高斯噪声, 让图像信息逐渐流失,最终退化为完全随机的纯噪声。
流程:清晰图像 → 含少量噪声图像 → 含大量噪声图像 → 纯随机噪声
生成阶段:反向去噪(重建信息) 生成时,算法从纯噪声出发,利用训练好的 模型逐步预测并减去噪声,从模糊到清晰, 逐步还原出具备细节和语义的图像。 流程:纯随机噪声 → 模糊轮廓 → 局部纹理结构 → 高质量清晰图像。
2.2模型结构解析
CLIP Tokenizer 负责将输入的文本提示词(Prompt)切分为模型可识别的离散Token序列,是文本处理的第一步。
CLIP Text Encoder 将分词后的Token编码为高维的语义向量,让模型理解文本的含义,为图像生成提供语义指导。
UNet (核心去噪器) 在Latent(潜)空间中,结合文本语义向量,预测当前噪声图中的噪声残差,是生成过程的核心计算单元。
Scheduler (调度器) 定义每一步的去噪算法和更新规则,控制去噪的节奏与步数,直接影响生成图像的质量和速度。
VAE (变分自编码器) 负责Latent空间与像素空间的双向转换。最后将去噪完成的Latent映射为最终可见的像素图像。
2.3无分类器引导 (CFG) 原理
本实验的重点是 Classifier-Free Guidance,CFG,无分类器引导。CFG 的作用是增强文本提示词对生成结果的控制能力,使图像更加符合 prompt。 传统的分类器引导方法需要额外训练一个分类器,用分类器梯度指导扩散模型生成目标类别图像。而 CFG 不需要额外分类器,它通过同时计算“有条件预测”和“无条件预测”,再将二者进行组合,达到增强条件控制的目的。
下面是论文中的一个例子。假设有3组带有标签的图像:“猫”、“狗”和“人类”。如果扩散过程没有引导,模型将从每个组的总体中抽取样本,但有时它可能会绘制适合两个标签的图像,例如,一个抚摸狗的男孩。
无引导 小尺度引导 大尺度引导
3,数据集介绍
本实验直接使用预训练模型runwayml/stable-diffusion-v1-5,因此本页介绍的“数据集”并非我们实验阶段自行构建,而是指该模型在预训练阶段进行“学习”时所使用的海量数据来源。
Stable Diffusion v1.5 的核心训练数据主要来源于开源的 LAION (Large-scale Artificial Intelligence Open Network) 数据集。这是一个包含了数以亿计的高质量“图像-文本” 配对数据的庞大集合。数据集中的每一条记录都包含一张 图片和对应的一段自然语言描述,模型正是通过学习这两 者之间的强关联映射关系,最终获得了理解用户的文本指 令并生成对应视觉图像的核心能力。
数据集核心特点 (Key Features)
• 海量规模与多概念覆盖:数据量级巨大,广泛覆盖了人物肖像、自然风景、动物、抽象艺术风格等各类视觉概念,丰富了模型的知识边界。
• 高质量图文配对:将图像与对应的文本描述进行精准匹配,为模型学习“语言-视觉”映射关系提供了核心基础,实现文本理解与生成。
• 英文语料主导优势:训练数据中以英文描述为主,模型对英文Prompt的理解能力和生成效果显著优于其他语言。
• 美学筛选优化 (v1.5+):后期版本引入了对美学质量的人工与算法双重筛选,使用更高质量的图像训练,大幅提升了生成作品的整体美观度。
数据集潜在局限 (Limitations)
• 潜在社会偏见风险:数据源于互联网,可能隐含并强化社会固有的刻板印象或偏见;同时对非西方文化、小众群体的覆盖度不足,导致生成内容的文化视角单一。
• 安全与合规挑战:海量数据难以做到完全清洗,可能包含低俗、暴力等不当内容,因此必须配合严格的内容安全过滤机制使用。
• 真实性无法保障:训练目标是生成“视觉合理”的图像而非“客观真实”的信息。生成的人物、场景可能在现实中并不存在,不能作为事实依据。
4.代码实现详解
本实验代码没有直接调用封装好的StableDiffusionPipeline,而是手动加载 Stable Diffusion 的各个核心模块,包括 tokenizer、text encoder、VAE、UNet 和 scheduler。这种写法更适合理解 Stable Diffusion 底层推理过程,尤其是 CFG(Classifier-Free Guidance)的实现细节。
4.1构造文本向量 (CFG 关键步骤)
这是实现 CFG (Classifier-Free Guidance) 的核心代码部分,通过循环迭代逐步去除噪声,最终生成符合文本描述的图像:
fori,tinenumerate(scheduler.timesteps): # 复制latent,用于同时预测有条件/无条件噪声 latent_model_input= torch.cat([latents] * 2) # UNet 预测噪声,结合文本向量进行去噪 withtorch.no_grad(): noise_pred= unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample # 拆分无条件/有条件预测结果,应用CFG公式 noise_pred_uncond,noise_pred_text= noise_pred.chunk(2) noise_pred= noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) # 更新 latent 状态,逐步降低噪声水平 latents= scheduler.step(noise_pred, t, latents).prev_sample完整代码:
import os os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com' import torch from transformers import CLIPTextModel, CLIPTokenizer from diffusers import AutoencoderKL, UNet2DConditionModel, LMSDiscreteScheduler from PIL import Image def main(): device = "cuda" if torch.cuda.is_available() else "cpu" print(f"当前使用的计算设备: {device}") # ========================================== # 1. 加载核心组件 (Tokenizer, Text Encoder, VAE, UNet, Scheduler) # ========================================== model_id = "runwayml/stable-diffusion-v1-5" print("正在加载模型组件...") # CLIP 文本编码器:负责将人类语言转化为模型能理解的特征向量 tokenizer = CLIPTokenizer.from_pretrained(model_id, subfolder="tokenizer") text_encoder = CLIPTextModel.from_pretrained(model_id, subfolder="text_encoder").to(device) # VAE:负责在像素空间和隐空间之间转换 vae = AutoencoderKL.from_pretrained(model_id, subfolder="vae").to(device) # UNet:条件生成的去噪主力 unet = UNet2DConditionModel.from_pretrained(model_id, subfolder="unet").to(device) scheduler = LMSDiscreteScheduler.from_pretrained(model_id, subfolder="scheduler") # ========================================== # 2. 准备条件 (Condition) 与无条件 (Uncondition) 向量 # ========================================== prompt = ["A high quality photo of a cute corgi dog"] # 你的条件 # 获取条件文本的 Embedding text_inputs = tokenizer(prompt, padding="max_length", max_length=tokenizer.model_max_length, truncation=True, return_tensors="pt") with torch.no_grad(): text_embeddings = text_encoder(text_inputs.input_ids.to(device))[0] # 获取无条件(空文本)的 Embedding,用于 CFG 计算 uncond_inputs = tokenizer([""], padding="max_length", max_length=tokenizer.model_max_length, return_tensors="pt") with torch.no_grad(): uncond_embeddings = text_encoder(uncond_inputs.input_ids.to(device))[0] # 将两者拼接,方便后续在 UNet 中一次性前向传播以节省时间 text_embeddings = torch.cat([uncond_embeddings, text_embeddings]) # ========================================== # 3. 初始化随机噪声与参数 # ========================================== torch.manual_seed(1024) # 这里的种子决定了画面的初始构图布局 batch_size = 1 # SD 1.5 的隐空间分辨率是 64x64 (对应像素空间 512x512) latents = torch.randn((batch_size, unet.config.in_channels, 64, 64)).to(device) # 缩放初始噪声以匹配调度器所需的标准差 scheduler.set_timesteps(50) latents = latents * scheduler.init_noise_sigma # CFG 引导系数 (Guidance Scale) # 值越大,越严格遵循文本条件;值越小,画面越有随机性但可能不贴合提示词 guidance_scale = 7.5 save_dir = "cfg_demo_steps" os.makedirs(save_dir, exist_ok=True) # ========================================== # 4. 核心去噪循环 (Classifier-Free Guidance 演示) # ========================================== print(f"开始去噪,提示词: '{prompt[0]}'") for i, t in enumerate(scheduler.timesteps): # 将 latents 复制一份,一半用于无条件预测,一半用于有条件预测 latent_model_input = torch.cat([latents] * 2) latent_model_input = scheduler.scale_model_input(latent_model_input, t) # UNet 预测噪声残差 with torch.no_grad(): noise_pred = unet( latent_model_input, t, encoder_hidden_states=text_embeddings # 通过 Cross-Attention 注入特征 ).sample # 拆分预测结果 noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) # ⭐️ CFG 核心公式 ⭐️ # 最终方向 = 无条件方向 + 引导系数 * (有条件方向 - 无条件方向) noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) # 调度器计算上一步的潜变量 latents = scheduler.step(noise_pred, t, latents).prev_sample # ========================================== # 5. VAE 解码并保存最终结果 # ========================================== print("去噪完成,正在使用 VAE 解码回像素空间...") # 潜变量需要按比例缩放回 VAE 的期望范围 latents = 1 / 0.18215 * latents with torch.no_grad(): image = vae.decode(latents).sample # 归一化并转换为图片 image = (image / 2 + 0.5).clamp(0, 1) image = image.cpu().permute(0, 2, 3, 1).numpy()[0] img_pil = Image.fromarray((image * 255).astype("uint8")) save_path = "conditional_output.png" img_pil.save(save_path) print(f"演示成功!图像已保存至 {save_path}") if __name__ == "__main__": main()结果:
生成质量评估: 模型成功生成了一只特征明显的柯基犬(短腿、立耳),整体风格与“高质量照片 (High Quality Photo)”的描述高度一致。生成图像内容准确响应了文本条件约束,验证了算法的有效性。
5.存在的问题
问题1:缺少diffusers库和transfomer库
用命令安装:
问题2:国内网络直连 Hugging Face 官方服务器超时
在代码最前面添加这行代码,强制使用国内镜像下载模型
6.参考链接
Stable Diffusion模型概述-CSDN博客