DALLE2图像生成新突破:预训练CLIP与扩散模型强强联合
本文将带你深入了解 DALL·E 2 这一革命性图像生成模型如何借助预训练的 CLIP(Contrastive Language–Image Pretraining)与扩散模型(Diffusion Model)相结合,实现在自然语言提示下生成高分辨率、细节丰富的图像。文中涵盖模型原理、代码示例、关键图解和训练流程,全方位解析背后的技术细节,帮助你更轻松上手理解与实践。
目录
引言
自从 OpenAI 在 2021 年发布 DALL·E 1 后,基于“文本生成图像”(Text-to-Image)的研究快速升温。DALL·E 1 能生成 256×256 的图像,但在分辨率和细节丰富度方面仍有限。2022 年问世的 DALL·E 2 将生成分辨率提升到 1024×1024,并实现了更逼真的光影与几何一致性。其核心秘诀在于:
- 预训练 CLIP 作为文本与图像的通用嵌入,确保“文本提示”与“图像特征”在同一语义空间对齐;
- 借助扩散模型 作为生成引擎,以逐步去噪方式从随机噪声中“生长”出图像;
- CLIP Guidance 技术 使得扩散采样时可动态调整生成方向,以更忠实地符合文本提示。
本文将逐层拆解 DALL·E 2 的工作原理、核心代码实现与关键图示,让你在理解数学背景的同时,掌握动手实践思路。
DALL·E 2 技术背景
DALL·E 1 简要回顾
- 基于 GPT-3 架构,将 Transformer 用于图像生成;
- 图像先被离散 VAE(dVAE)编码成一系列“图像令牌(image tokens)”,再由自回归 Transformer 预测下一令牌。
- 优点在于能够生成多种异想天开的视觉内容,但生成分辨率受限于 dVAE Token 长度(通常 256×256)。
DALL·E 2 的重大突破
- 从“自回归图像令牌生成”转向“扩散模型 + CLIP Guidance”架构;
- 扩散模型天然支持高分辨率图像生成,且更易训练;
- CLIP 提供“跨模态”对齐,使文本与图像在同一向量空间中具有语义可比性;
- 结合 CLIP 分数的“Guidance”可在每次去噪采样时,让图像逐步更符合文本提示。
预训练 CLIP:文本与图像的语义桥梁
CLIP 的训练目标与架构
CLIP(Contrastive Language–Image Pretraining) 由 OpenAI 在 2021 年发布,主要目标是学习一个通用的文本 Encoder 与图像 Encoder,使得文本描述与对应图像在同一向量空间内“靠近”,而与其他图像/文本“远离”。
- 数据集:将数亿对图文(alt-text)数据作为监督信号;
模型架构:
- 图像 Encoder:通常是 ResNet、ViT 等架构,输出归一化后向量 $\mathbf{v}\_\text{img} \in \mathbb{R}^d$;
- 文本 Encoder:Transformer 架构,将 Token 化的文本映射为 $\mathbf{v}\_\text{text} \in \mathbb{R}^d$;
对比学习目标:对于一批 $N$ 对 (image, text),计算所有图像向量与文本向量的点积相似度矩阵 $S \in \mathbb{R}^{N\times N}$,然后对角线元素应尽量大(正样本对),非对角元素应尽量小(负样本对)。
$$ \mathcal{L} = - \frac{1}{2N} \sum_{i=1}^{N} \Bigl[\log \frac{e^{s_{ii}/\tau}}{\sum_{j=1}^{N} e^{s_{ij}/\tau}} + \log \frac{e^{s_{ii}/\tau}}{\sum_{j=1}^{N} e^{s_{ji}/\tau}} \Bigr], $$
其中 $s\_{ij} = \mathbf{v}\text{img}^i \cdot \mathbf{v}\text{text}^j$,$\tau$ 为温度系数。
训练完成后,CLIP 能在零样本(Zero-Shot)场景下对图像进行分类、检索,也可为下游任务提供文本与图像对齐的嵌入。
CLIP 在 DALL·E 2 中的作用
在 DALL·E 2 中,CLIP 扮演了两个关键角色:
文本编码
- 将用户输入的自然语言 Prompt(如 “a photorealistic painting of a sunset over mountains”)映射为文本嵌入 $\mathbf{c} \in \mathbb{R}^d$.
- 该 $\mathbf{c}$ 成为后续扩散模型采样时的“条件向量(conditioning vector)”或“目标向量(target vector)”。
采样指导(CLIP Guidance)
- 在扩散去噪过程中,每一步我们可以利用当前生成图像的 CLIP 图像嵌入 $\mathbf{v}\text{img}(x\_t)$ 和文本嵌入 $\mathbf{c}$ 计算相似度分数 $s(\mathbf{v}\text{img}(x\_t), \mathbf{c})$;
- 通过对该分数的梯度 $\nabla\_{x\_t} s(\cdot)$ 进行放大并加到扩散网络预测上,可使得生成结果在每一步更朝着“与文本语义更对齐”的方向演化;
- 这种技术类似于 “Classifier Guidance” 中使用分类模型对 Score 的梯度进行引导,但这里用 CLIP 替代。
示意图:CLIP 在扩散采样中的指导
Step t: 1) 原始扩散网络预测噪声 e_θ(x_t, t, c) 2) 将 x_t 送入 CLIP 图像 Encoder,得到 v_img(x_t) 3) 计算相似度 score = v_img(x_t) · c 4) 计算梯度 g = ∇_{x_t} score 5) 修改噪声预测: e'_θ = e_θ + w * g (w 为权重超参) 6) 根据 e'_θ 反向还原 x_{t-1}
扩散模型简介与数学原理
扩散模型的正向与反向过程
扩散模型(Diffusion Models)是一类概率生成模型,其核心思想是:
正向扩散(Forward Diffusion):将真实图像 $x\_0$ 逐步添加高斯噪声,直至变为近似纯噪声 $x\_T$;
$$ q(x_t \mid x_{t-1}) = \mathcal{N}\bigl(x_t; \sqrt{1 - \beta_t}\, x_{t-1},\, \beta_t \mathbf{I}\bigr), \quad t = 1,2,\dots,T, $$
其中 ${\beta\_t}$ 是预先设定的小型正数序列。可以证明 $x\_t$ 也服从正态分布:
$$ q(x_t \mid x_0) = \mathcal{N}\Bigl(x_t; \sqrt{\bar\alpha_t}\, x_0,\,(1 - \bar\alpha_t)\mathbf{I}\Bigr), $$
其中 $\alpha\_t = 1 - \beta\_t,, \bar\alpha\_t = \prod\_{s=1}^t \alpha\_s$.
- 反向扩散(Reverse Diffusion):从噪声 $x\_T \sim \mathcal{N}(0,\mathbf{I})$ 开始,学习一个模型 $p\_\theta(x\_{t-1} \mid x\_t)$,逆向地一步步“去噪”,最终恢复为 $x\_0$。
具体而言,反向分布近似被简化为:
$$ p_\theta(x_{t-1} \mid x_t) = \mathcal{N}\bigl(x_{t-1}; \mu_\theta(x_t, t),\, \Sigma_\theta(x_t, t)\bigr). $$
通过变分下界(Variational Lower Bound)的优化,DDPM(Denoising Diffusion Probabilistic Models)提出只学习一个噪声预测网络 $\epsilon\_\theta(x\_t, t)$,并固定协方差为 $\Sigma\_t = \beta\_t \mathbf{I}$,从而简化训练目标:
$$ L_{\text{simple}} = \mathbb{E}_{x_0, \epsilon \sim \mathcal{N}(0,I), t} \Bigl\| \epsilon - \epsilon_\theta\bigl(\sqrt{\bar\alpha_t}\,x_0 + \sqrt{1 - \bar\alpha_t}\,\epsilon,\,t\bigr)\Bigr\|_2^2. $$
DDPM 关键公式
噪声预测
给定真实图像 $x\_0$,随机采样时间步 $t$,以及 $\epsilon \sim \mathcal{N}(0,\mathbf{I})$,我们构造带噪声样本:
$$ x_t = \sqrt{\bar\alpha_t}\, x_0 + \sqrt{1 - \bar\alpha_t}\,\epsilon. $$
- 训练网络 $\epsilon\_\theta(x\_t, t)$ 去预测这一噪声 $\epsilon$.
去噪采样
当训练完成后,从高斯噪声 $x\_T \sim \mathcal{N}(0,\mathbf{I})$ 开始,递推生成 $x\_{t-1}$:
$$ x_{t-1} = \frac{1}{\sqrt{\alpha_t}}\Bigl(x_t - \frac{\beta_t}{\sqrt{1 - \bar\alpha_t}}\,\epsilon_\theta(x_t,t)\Bigr) + \sigma_t z,\quad z \sim \mathcal{N}(0,\mathbf{I}), $$
其中 $\sigma\_t^2 = \beta\_t$.
条件扩散
- 若要在扩散过程中加入“条件”(如文本提示),可把 $\epsilon\_\theta(x\_t, t, c)$ 改为“同时输入文本编码 $c$”的网络;
- 也可结合 CLIP Guidance 技术,用梯度对噪声预测结果做修正。
扩散模型采样流程示意
x_0 (真实图像)
│ 添加噪声 β₁, …, β_T
▼
x_T ≈ N(0, I) ←—— 正向扩散 q(x_t | x_{t-1})
训练:学习 ε_θ 参数,使 ε_θ(x_t, t) ≈ 噪声 ε
推理/采样:
1) 初始化 x_T ∼ N(0,I)
2) for t = T, T-1, …, 1:
ε_pred = ε_θ(x_t, t) # 预测噪声
x_{t-1} = (x_t − ((β_t)/(√(1−ā_t))) ε_pred) / √(α_t) + σ_t z # 反向采样
3) 返回 x_0 近似生成图像
DALL·E 2 整体架构与工作流程
文本编码 CLIP 文本嵌入
Prompt 预处理
- 对用户输入的自然语言提示(Prompt)做基础处理:去除多余空格、标点、统一大小写;
- 通过 CLIP 文本 Encoder(通常是一个 Transformer)将 Token 化的 Prompt 转化为文本向量 $\mathbf{c} \in \mathbb{R}^d$.
CLIP 文本特征
- 文本嵌入 $\mathbf{c}$ 通常经归一化(L2 Norm),与图像嵌入同分布;
- 该向量既包含了 Promp 的整体语义,也可与后续生成图像相对齐。
高分辨率图像扩散:Mask Diffusion 机制
为了在高分辨率(如 1024×1024)下仍保持计算可行性,DALL·E 2 采用了多阶段分辨率递进方案:
第一阶段:生成低分辨率草图
- 扩散模型在 64×64 或 256×256 分辨率下进行采样,生成“基础结构”(低分辨率草图);
- 网络架构为 U-Net 变体:对输入 $x\_t$(带噪低分辨率图)与文本嵌入 $\mathbf{c}$ 进行多尺度特征提取与去噪预测。
第二阶段:高分辨率放大(Super-Resolution)
- 将第一阶段生成的低分辨率图像 $x\_0^{LR}$ 作为条件,与噪声叠加后在更高分辨率(如 256×256 或 1024×1024)上进行扩散采样;
这一阶段称为 Mask Diffusion,因为网络只需“补全”低分辨率图像未覆盖的细节部分:
- 定义掩码 $M$ 将低分辨率图 $x\_0^{LR}$ 插值至高分辨率 $x\_0^{HR}$ 对应区域,并添加随机噪声;
- 扩散网络的输入为 $(x\_t^{HR}, M, \mathbf{c})$,目标是生成完整的高分辨率图像 $x\_0^{HR}$.
分辨率递进示意
Prompt → CLIP 文本嵌入 c ↓ 64×64 扩散采样 → 生成低分辨率图 x_0^{64} ↓ 插值放大 & 噪声添加 256×256 Mask Diffusion → 生成 256×256 图像 x_0^{256} ↓ 插值放大 & 噪声添加 1024×1024 Mask Diffusion → 生成最终 1024×1024 图像 x_0^{1024}
基于 CLIP 分数的指导(CLIP Guidance)
为了让扩散生成更加忠实于 Prompt 语义,DALL·E 2 在采样过程中引入 CLIP Guidance:
原理
- 当扩散模型预测噪声 $\epsilon\_\theta(x\_t,t,\mathbf{c})$ 后,可以将当前去噪结果 $\hat{x}{t-1}$ 传入 CLIP 图像 Encoder,得到图像嵌入 $\mathbf{v}\text{img}$.
- 计算相似度 $\text{score} = \mathbf{v}\text{img}\cdot \mathbf{c}$. 若该分数较高,说明 $\hat{x}{t-1}$ 更接近文本语义;否则,对噪声预测做调整。
具体做法是:
$$ \epsilon'_\theta = \epsilon_\theta + \lambda \nabla_{x_t} \bigl(\mathbf{v}_\text{img}(x_t)\cdot \mathbf{c}\bigr), $$
其中 $\lambda$ 是超参数,控制 CLIP 指导的强度。
实现步骤
对每一步的“去噪预测”进行梯度流回:
- 将中间去噪结果 $\hat{x}\_{t-1}$ 以适当插值大小(例如 224×224)输入 CLIP 图像 Encoder;
- 计算 $\text{score}$,并对输入图像 $\hat{x}{t-1}$ 求梯度 $\nabla{\hat{x}\_{t-1}} \text{score}$;
- 将该梯度再插值回当前采样分辨率,并加权运用于 $\epsilon\_\theta$;
- 这样可以让每一步去噪都更加朝向与文本更匹配的视觉方向发展。
一阶段到二阶段的生成:低分辨率到高分辨率
综合上述思路,DALL·E 2 的生成分为两大阶段:
低分辨率生成
- 输入 Prompt → 得到 $\mathbf{c}$ → 在 64×64(或 256×256)分辨率上做有条件的扩散采样,得到初步草图 $x\_0^{LR}$.
- 在此阶段也可使用 CLIP Guidance,让低分辨率图像更贴合 Prompt。
高分辨率放大与细节生成
- 将 $x\_0^{LR}$ 最近邻或双线性插值放大到目标分辨率(如 256×256);
- 对该放大图 $U(x\_0^{LR})$ 添加随机噪声 $x\_t^{HR}$;
- 在更高分辨率上做扩散采样,利用 Mask Diffusion 模型填补细节,生成高分辨率最终图 $x\_0^{HR}$.
- 同样可在此阶段应用 CLIP Guidance,增强细节与 Prompt 的一致性。
通过分阶段、分辨率递进的设计,DALL·E 2 能以相对有限的计算开销生成高质量、高分辨率的图像。
关键代码示例:模拟 DALL·E 2 的核心实现
以下示例以 PyTorch 为基础,简要展示如何:
- 加载预训练 CLIP;
- 定义一个简化版的 DDPM 去噪网络;
- 在扩散采样中融入 CLIP Guidance;
- 演示从 Prompt 到 64×64 低分辨率图像的完整流程。
注意:以下代码为教学示例,实际 DALL·E 2 中使用的网络架构与训练细节要复杂得多。
依赖与环境
# 安装必要依赖
pip install torch torchvision ftfy regex tqdm
pip install git+https://github.com/openai/CLIP.git # 安装 CLIP 官方库
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.transforms as T
from PIL import Image
import clip # CLIP 官方库
import math
import numpy as np
加载预训练 CLIP 模型
# 选择使用 CPU 或 GPU
device = "cuda" if torch.cuda.is_available() else "cpu"
# 加载 CLIP 模型:ViT-B/32 或 RN50 等
clip_model, clip_preprocess = clip.load("ViT-B/32", device=device)
# 冻结 CLIP 参数,不参与微调
for param in clip_model.parameters():
param.requires_grad = False
# 定义一个辅助函数:输入 PIL 图像张量,输出归一化后的图像嵌入
def get_clip_image_embedding(img_tensor):
"""
img_tensor: (3, H, W), 已归一化到 [0,1]
先缩放为 CLIP 接受的 224×224,做标准化,然后编码
"""
# CLIP 预处理(Resize、CenterCrop、Normalize)
img_input = clip_preprocess(img_tensor.cpu()).unsqueeze(0).to(device) # (1,3,224,224)
with torch.no_grad():
img_features = clip_model.encode_image(img_input) # (1, d)
img_features = img_features / img_features.norm(dim=-1, keepdim=True)
return img_features # (1, d)
# 定义辅助函数:文本 prompt → 文本嵌入
def get_clip_text_embedding(prompt_text):
"""
prompt_text: str
"""
text_tokens = clip.tokenize([prompt_text]).to(device) # (1, seq_len)
with torch.no_grad():
text_features = clip_model.encode_text(text_tokens) # (1, d)
text_features = text_features / text_features.norm(dim=-1, keepdim=True)
return text_features # (1, d)
get_clip_image_embedding
支持输入任何 PIL Image → 得到归一化后图像嵌入;get_clip_text_embedding
支持输入 Prompt → 得到文本嵌入。
定义简化版 DDPM 噪声预测网络
下面我们构建一个轻量级的 U-Net 样例,用于在 64×64 分辨率下预测噪声 $\epsilon\_\theta$。
class SimpleUNet(nn.Module):
def __init__(self, in_channels=3, base_channels=64):
super(SimpleUNet, self).__init__()
# 下采样阶段
self.enc1 = nn.Sequential(
nn.Conv2d(in_channels, base_channels, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(base_channels, base_channels, kernel_size=3, padding=1),
nn.ReLU()
)
self.pool = nn.MaxPool2d(2) # 64→32
self.enc2 = nn.Sequential(
nn.Conv2d(base_channels, base_channels*2, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(base_channels*2, base_channels*2, kernel_size=3, padding=1),
nn.ReLU()
)
self.pool = nn.MaxPool2d(2) # 32→16
# 中间
self.mid = nn.Sequential(
nn.Conv2d(base_channels*2, base_channels*4, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(base_channels*4, base_channels*2, kernel_size=3, padding=1),
nn.ReLU()
)
# 上采样阶段
self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False) # 16→32
self.dec2 = nn.Sequential(
nn.Conv2d(base_channels*4, base_channels*2, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(base_channels*2, base_channels*2, kernel_size=3, padding=1),
nn.ReLU()
)
self.up2 = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False) # 32→64
self.dec1 = nn.Sequential(
nn.Conv2d(base_channels*2 + base_channels, base_channels, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(base_channels, in_channels, kernel_size=3, padding=1),
)
def forward(self, x):
# 下采样
e1 = self.enc1(x) # (B, 64, 64, 64)
p1 = self.pool(e1) # (B, 64, 32, 32)
e2 = self.enc2(p1) # (B, 128,32,32)
p2 = self.pool(e2) # (B,128,16,16)
# 中间
m = self.mid(p2) # (B,128,16,16)
# 上采样
u1 = self.up(m) # (B,128,32,32)
cat2 = torch.cat([u1, e2], dim=1) # (B,256,32,32)
d2 = self.dec2(cat2) # (B,128,32,32)
u2 = self.up2(d2) # (B,128,64,64)
cat1 = torch.cat([u2, e1], dim=1) # (B,192,64,64)
out = self.dec1(cat1) # (B,3,64,64)
return out # 预测噪声 ε_θ(x_t)
- 注意:为了简化示例,此 U-Net 没有加入时间步嵌入与文本条件,实际上需要把 $t$ 与 CLIP 文本嵌入一并输入网络。
- 在后续采样中,我们将把时间步 $t$ 与文本嵌入拼接到中间特征,以便网络做有条件预测。
实现 CLIP 指导的扩散采样
以下代码示例演示在扩散某一步时,如何结合 CLIP Guidance 对噪声预测进行修正。
def ddim_sample_with_clip_guidance(model, clip_model, clip_tokenizer, c_text,
num_steps=50, img_size=64, guidance_scale=100.0, device="cpu"):
"""
简化版采样流程,结合 CLIP Guidance
model: 已训练好的 DDPM 噪声预测网络
clip_model: 预训练的 CLIP 模型
clip_tokenizer: CLIP Tokenizer
c_text: CLIP 文本嵌入 (1, d)
"""
# 1. 准备时间步序列与 β_t 序列(线性或余弦预定义)
betas = torch.linspace(1e-4, 0.02, num_steps).to(device) # 简化起见使用线性 β
alphas = 1 - betas
alphas_cumprod = torch.cumprod(alphas, dim=0) # ā_t
# 2. 从标准正态噪声开始
x_t = torch.randn(1, 3, img_size, img_size).to(device)
for i in reversed(range(num_steps)):
t = torch.full((1,), i, dtype=torch.long).to(device) # 当前时间步 t
alpha_t = alphas[i]
alpha_cumprod_t = alphas_cumprod[i]
beta_t = betas[i]
# 3. 预测噪声 (网络需要输入 x_t, t, c_text;这里示例不带条件)
# 扩散网络实际应接收时间步嵌入与文本条件,此处为简化
epsilon_pred = model(x_t) # (1,3,64,64)
# 4. 生成当前时刻的图像估计 x0_pred
x0_pred = (x_t - (1 - alpha_t).sqrt() * epsilon_pred) / (alpha_t.sqrt())
# 5. CLIP Guidance:将 x0_pred 调整到 CLIP 嵌入空间
# a) 将 x0_pred 缩放到 [0,1] 并转换为 PIL RGB 图像
img = ((x0_pred.clamp(-1,1) + 1) / 2).clamp(0,1) # 归一化到 [0,1]
pil_img = T.ToPILImage()(img.squeeze().cpu())
# b) 获取 CLIP 图像嵌入
img_embed = get_clip_image_embedding(pil_img).to(device) # (1, d)
# c) 计算相似度分数
score = torch.cosine_similarity(img_embed, c_text, dim=-1) # (1,)
# d) 反向传播得到梯度 w.r.t. x_t
clip_model.zero_grad()
score.backward()
grad = x_t.grad.detach() if x_t.grad is not None else torch.zeros_like(x_t)
# e) 对网络预测噪声做修正
epsilon_pred = epsilon_pred - guidance_scale * grad
# 6. DDIM 公式或 DDPM 公式更新 x_{t-1}
if i > 0:
noise = torch.randn_like(x_t).to(device)
else:
noise = torch.zeros_like(x_t)
coef1 = 1 / alpha_t.sqrt()
coef2 = beta_t / torch.sqrt(1 - alpha_cumprod_t)
x_t = coef1 * (x_t - coef2 * epsilon_pred) + beta_t.sqrt() * noise
# 清空梯度,为下次循环做准备
x_t = x_t.detach().requires_grad_(True)
return x_t # 最终生成的图像张量 (1,3,64,64)
说明:
- 该代码将每一步去噪结果 $x\_0^{(t)}$ 输入 CLIP,计算得分并对噪声预测做梯度修正。
- 实际 DALL·E 2 中使用更复杂的公式(如 DDIM)、更合理的时间步排布(如余弦时间表),以及更强大的 U-Net 结构。
guidance_scale
控制 CLIP 指导强度,一般设为几十到几百不等。
完整示例:由 Prompt 生成 64×64 低分辨率图
最后我们把上述步骤整合,演示如何从一句文本 Prompt 生成一张 64×64 的低分辨率图像。
if __name__ == "__main__":
# 1) 输入 Prompt
prompt = "A futuristic city skyline at sunset"
# 2) 获取 CLIP 文本嵌入
c_text = get_clip_text_embedding(prompt).to(device) # (1, d)
# 3) 实例化扩散网络
model = SimpleUNet(in_channels=3, base_channels=64).to(device)
# 假设已加载训练好的权重
# model.load_state_dict(torch.load("simple_unet_ddpm64.pth"))
# 4) 扩散采样,结合 CLIP Guidance
generated_tensor = ddim_sample_with_clip_guidance(
model=model,
clip_model=clip_model,
clip_tokenizer=None,
c_text=c_text,
num_steps=50,
img_size=64,
guidance_scale=50.0,
device=device
)
# 5) 将最终张量保存为图像
gen_img = ((generated_tensor.clamp(-1,1) + 1) / 2).clamp(0,1) # (1,3,64,64)
T.ToPILImage()(gen_img.squeeze().cpu()).save("dalle2_demo_64.png")
print("已生成并保存低分辨率 64×64 图像:dalle2_demo_64.png")
- 运行后,
dalle2_demo_64.png
会是一张与 Prompt 语义相符的低分辨率草图; - 若需要更高分辨率,可将此图作为 Mask Diffusion 模型的输入,进行第二阶段放大与细节生成。
图解:DALL·E 2 模型核心模块
为了更直观地理解上述文字与代码,这里给出关键流程的图解说明。
CLIP 文本–图像对齐示意图
┌─────────────────────────┐
│ 文本 Encoder(Transformer) │
│ Prompt: “A cat sitting on a mat” │
│ → Token Embedding → Transformer │
│ → Text Embedding c ∈ ℝ^d │
└─────────────────────────┘
│
▼
┌──────────────────────────┐
│ CLIP 语义空间 ℝ^d │
└──────────────────────────┘
▲
│
┌─────────────────────────┐
│ 图像 Encoder(ViT 或 ResNet) │
│ Image: (224×224)→ Patch Emb → ViT │
│ → Image Embedding v ∈ ℝ^d │
└─────────────────────────┘
目标:使得 v ⋅ c 在同一语义对(image, text)上最大
- 文本与图像都被映射到同一个 $d$ 维向量空间,正样本对内积最大;
扩散模型正反向流程图
正向扩散 (训练时):
x₀ →(t=1: 添加噪声 β₁)→ x₁ →(t=2: 添加噪声 β₂)→ x₂ → … → x_T ≈ N(0, I)
网络学习目标:ε_θ(x_t, t) ≈ 噪声 ε
反向去噪 (采样时):
x_T ∼ N(0, I)
↓ (t = T→T-1 …)
x_{t-1} = (x_t − (β_t / √(1−ā_t)) ε_θ(x_t, t)) / √{α_t} + √{β_t} z
↓
x_0 (生成图像)
- 每一步网络预测噪声,并逐步恢复清晰图像;
CLIP Guidance 机制示意图
每步采样 (在时刻 t):
① ε_pred = ε_θ(x_t, t, c) # 扩散网络预测
② x̂₀ = (x_t − √(1−ā_t) ε_pred) / √(ā_t)
③ 将 x̂₀ ↓resize→224×224 → CLIP 图像嵌入 v_img
④ score = cos(v_img, c_text) # 文本-图像相似度
⑤ 计算 ∇_{x_t} score # 反向梯度
⑥ ε′_pred = ε_pred − λ ∇_{x_t} score # 修正噪声预测
⑦ 根据 ε′_pred 按 DDPM/DDIM 采样公式更新 x_{t-1}
- 借助 CLIP 的梯度将生成方向导向更符合文本语义的图像;
训练与推理流程详解
预训练阶段:CLIP 与扩散网络
CLIP 预训练
- 基于大规模互联网图文对,采用对比学习训练图像 Encoder 与文本 Encoder;
- 输出文本嵌入 $c$ 与图像嵌入 $v$,并归一化到单位球面。
扩散模型预训练
- 在大规模无条件图像数据集(如 ImageNet、LAION-2B)上训练去噪网络 $\epsilon\_\theta(x\_t, t)$;
- 若要做有条件扩散,可在网络中引入条件嵌入(如类别标签、低分辨率图像等);
- 使用 DDPM 训练目标:$|\epsilon - \epsilon\_\theta(x\_t,t)|^2$.
微调阶段:联合优化
条件扩散网络训练
- 在网络输入中同时加入 CLIP 文本嵌入 $\mathbf{c}$,训练网络学习 $\epsilon\_\theta(x\_t, t, c)$;
- 损失函数依旧是去噪 MSE,但要求网络能同时考虑图像噪声和文本条件。
CLIP Guidance 微调
- 若要让 CLIP Guidance 更有效,可将 CLIP 嵌入与去噪网络的梯度一并微调,保证梯度信号更准确。
- 也可以对扩散网络与 CLIP 模型做联合微调,使得生成图像和 CLIP 文本空间更一致。
推理阶段:文本→图像生成
输入 Prompt
- 用户输入自然语言描述,经过 CLIP 文本 Encoder 得到 $\mathbf{c}$.
低分辨率扩散采样
- 在 64×64(或 256×256)分辨率下,从纯噪声开始做有条件扩散采样;
- 在每一步中应用 CLIP Guidance,让生成更贴合 Prompt。
高分辨率放大 & Mask Diffusion
- 将 64×64 的结果插值放大到 256×256,添加噪声,进行 Mask Diffusion,生成细节;
- 再次放大至 1024×1024,或依据需求分多级放大。
后处理
- 对最终图像做色彩校正、对比度增强、锐化等后处理;
- 将图像输出给用户,或进一步用于艺术创作、商业设计等场景。
实践建议与技巧
Prompt 设计
- 简洁明确:突出主要内容和风格,例如“a photorealistic portrait of a golden retriever puppy sitting in a meadow at sunrise”。
- 可加入风格提示:如“in the style of oil painting”,“ultra-realistic”,“8K resolution”,“cinematic lighting”等。
- 若生成效果不理想,可尝试分层提示:先只写主体描述,再补充风格与细节。
扩散超参数调优
- 采样步数 (num\_steps):步数越多生成越精细,但速度越慢;常见 50 – 100 步;
- Guidance Scale (λ):CLIP 指导强度,过高会导致过度优化文本相似度而失真,过低则无法充分指导;可从 20–100 之间尝试。
- β (Noise Schedule):线性、余弦或自定义 schedule,不同 schedule 对去噪质量有显著影响。
分辨率递进做法
在资源受限场景,直接从 64×64 → 256×256 → 1024×1024 需要大量显存,可采用更平滑的多级方案:
- 64×64 → 128×128 → 256×256 → 512×512 → 1024×1024,每级都用专门的 Mask Diffusion 子网络。
- 对于每一级 Mask Diffusion,都可使用相同的 CLIP Guidance 机制,使得各尺度生成都与 Prompt 保持一致。
使用已开源模型与工具
- Hugging Face 生态中已有 CLIP、扩散模型(如 CompVis/stable-diffusion)可直接调用;
- 可借助
diffusers
库快速搭建并微调扩散管道(Pipeline),无需从零开始实现所有细节。 - 若只是想体验生成,可直接使用 OpenAI 提供的 DALL·E 2 API,关注 Prompt 设计与结果微调。
总结
- DALL·E 2 通过将 预训练 CLIP 与 扩散模型 有机结合,实现了从文本到高分辨率图像的无缝迁移;
- CLIP 在语言与视觉之间构建了一座“高质量的语义桥梁”,使得扩散网络能够动态地被文本指导(CLIP Guidance),生成更加精准、生动的图像;
- 多阶段分辨率递进和 Mask Diffusion 技术,则保证了在可控计算成本下得到接近 1024×1024 甚至更高分辨率的精细结果;
- 通过本文介绍的数学原理、代码示例与图解示意,你已经了解了 DALL·E 2 的核心机制与动手要领。你可以基于此思路,利用开源扩散模型与 CLIP,构建自己的文本→图像管道,探索更多创意应用。
欢迎你继续在此基础上进行更深入的研究:优化噪声网络架构、改进 CLIP Guidance 方式、结合拓展的文本 Prompt,引发更多创新与突破。
参考文献与延伸阅读
- Rombach, Robin, et al. “High-Resolution Image Synthesis with Latent Diffusion Models”, CVPR 2022.
- Nichol, Alexander Quinn, et al. “GLIDE: Towards Photorealistic Image Generation and Editing with Text-Guided Diffusion Models”, ICML 2022.
- Ramesh, Aditya, et al. “Hierarchical Text-Conditional Image Generation with CLIP Latents”, arXiv:2204.06125 (DALL·E 2).
- Radford, Alec, et al. “Learning Transferable Visual Models From Natural Language Supervision”, ICML 2021 (CLIP 原理论文).
- Ho, Jonathan, et al. “Denoising Diffusion Probabilistic Models”, NeurIPS 2020 (DDPM 原理论文).
- Dhariwal, Prafulla, et al. “Diffusion Models Beat GANs on Image Synthesis”, NeurIPS 2021.
OpenAI 官方博客:
- “DALL·E 2: Outpainting and Inpainting”
- “CLIP: Connecting Text and Images”
后记:
本文旨在用最清晰的思路与示例,帮助读者理解并动手实践 DALL·E 2 核心技术。若你对此感兴趣,建议进一步阅读相关论文与开源实现,结合 GPU 资源进行微调与实验,开启更多创意图像生成之旅。