合成数据:用 GAN 或扩散模型生成训练样本
min_G max_D V(D,G) = E_{x~p_data}[log D(x)] + E_{z~p_z}[log(1 - D(G(z)))]
这一公式驱动着两个网络的对抗学习。
### 2.2 用 DCGAN 生成 CIFAR-10 图像
推荐从**深度卷积生成对抗网络(DCGAN)**入手,它将卷积层的归纳偏置引入 GAN,稳定易训练。我们直接基于 PyTorch 实现。
**环境准备**:安装 `torch`, `torchvision`, `matplotlib`, `numpy`。
**生成器架构**:输入一个 100 维的随机噪声向量,经过转置卷积层逐步上采样,最终输出 3×32×32 的彩色图像。
```python
import torch.nn as nn
class Generator(nn.Module):
def __init__(self, nz=100, ngf=64, nc=3):
super().__init__()
self.main = nn.Sequential(
# 输入 (nz) 映射到 (ngf*8)*4*4
nn.ConvTranspose2d(nz, ngf * 8, 4, 1, 0, bias=False),
nn.BatchNorm2d(ngf * 8), nn.ReLU(True),
# 状态: (ngf*8) x 4 x 4
nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf * 4), nn.ReLU(True),
# 状态: (ngf*4) x 8 x 8
nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf * 2), nn.ReLU(True),
# 状态: (ngf*2) x 16 x 16
nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf), nn.ReLU(True),
# 状态: (ngf) x 32 x 32
nn.ConvTranspose2d(ngf, nc, 4, 2, 1, bias=False),
nn.Tanh()
# 输出: (nc) x 64 x 64 (CIFAR-10大小为32,需调整。此处仅为示例架构,实际需适配)
)
def forward(self, input):
return self.main(input)
注意:针对 32×32 的 CIFAR-10,只需将最后一层输出尺寸适配为 32×32,移除一层上采样即可。实践时可在官方 DCGAN 代码基础上调整。
判别器架构:标准 CNN 分类器,输出输入图像为真的概率。
class Discriminator(nn.Module):
def __init__(self, nc=3, ndf=64):
super().__init__()
self.main = nn.Sequential(
# 输入 (nc) x 32 x 32
nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),
nn.LeakyReLU(0.2, inplace=True),
# 状态: (ndf) x 16 x 16
nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf * 2),
nn.LeakyReLU(0.2, inplace=True),
# 状态: (ndf*2) x 8 x 8
nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf * 4),
nn.LeakyReLU(0.2, inplace=True),
# 状态: (ndf*4) x 4 x 4
nn.Conv2d(ndf * 4, 1, 4, 1, 0, bias=False),
nn.Sigmoid()
)
def forward(self, input):
return self.main(input)
训练循环要点:
- 固定判别器,更新生成器;固定生成器,更新判别器。
- 使用标签平滑(真实样本标签设为 0.9,而非 1.0)和随机噪声增强判别器鲁棒性。
- 使用 Adam 优化器,学习率推荐 0.0002,beta1=0.5。
- 监控生成器与判别器损失,避免一方压制另一方。
采样合成数据:
训练完成后,只需从标准正态分布采样一批随机噪声 z,喂给生成器,即可获得一批合成图像。可以将这些图像保存为 .png 文件或直接拼入训练集。
import torch
with torch.no_grad():
fixed_noise = torch.randn(64, nz, 1, 1, device=device)
fake_images = generator(fixed_noise).detach().cpu()
2.3 提高合成质量的实用技巧
- 特征匹配:在判别器中间层计算生成样本与真实样本的统计距离,稳定训练。
- 渐进增长:从低分辨率开始训练,逐步增加层数生成更高分辨率图像(ProGAN 思路)。
- 数据增强的适应性:使用 Differentiable Augmentation 同时增强真实与生成样本,防止判别器过拟合。
- 条件生成:在生成器和判别器输入中拼接类别标签,实现可控类别的合成(CGAN)。这对平衡类别分布特别有用。
3. 扩散模型合成数据实战
扩散模型是当前图像合成质量最高的范式,它通过逐步向数据添加噪声,然后学习逆过程从噪声中恢复出清晰图像。Denoising Diffusion Probabilistic Models (DDPM) 是其典型代表。
3.1 扩散模型直觉
想象一滴墨水滴入清水,扩散过程让墨水分子逐渐遍布整个杯子。扩散模型的前向过程就是不断给图像加高斯噪声,直至变成纯噪声。反向过程则训练一个神经网络从噪声中一步步去噪,重现原始图像。一旦学会这个去噪过程,我们就可以从随机噪声出发,生成全新的图像。
3.2 使用预训练扩散模型快速生成数据
从头训练扩散模型计算量极大,推荐直接使用 Hugging Face 的 diffusers 库调用已经训练好的模型,一步到位生成高质量合成图像。这里以 Stable Diffusion 为例,它使用潜在空间扩散,效率更高。
安装依赖:
pip install diffusers transformers accelerate
基础文本到图像生成:
from diffusers import StableDiffusionPipeline
import torch
model_id = "runwayml/stable-diffusion-v1-5"
pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float16)
pipe = pipe.to("cuda")
prompt = "a realistic photo of a cat wearing a hat, high quality"
image = pipe(prompt).images[0]
image.save("syn_cat.png")
批量生成特定类别的训练数据: 通过编写包含类别描述、背景、风格变化的提示词列表,循环生成即可。例如生成“不同颜色的汽车在街道”:
prompts = [
"a red sedan driving on a rainy street",
"a blue SUV parked in front of a house, sunny day",
"a yellow sports car on a mountain road, dramatic lighting",
...
]
for i, prompt in enumerate(prompts):
image = pipe(prompt).images[0]
image.save(f"syn_car_{i}.jpg")
3.3 用 DDPM 自训练小规模数据(概念演示)
如果你希望在自己的数据集上从零训练一个扩散模型,可以基于简化的 DDPM 代码理解整个过程。以下是核心组件的伪代码,帮助你把握脉络。
前向扩散:定义固定调度器 betas,根据时间步 t 直接采样加噪图像:
def q_sample(x_start, t, noise=None):
# alpha_t = 1 - beta_t ; alpha_bar_t = product_{s=1..t} alpha_s
sqrt_alphas_bar = torch.sqrt(alphas_bar[t])
sqrt_one_minus_alphas_bar = torch.sqrt(1 - alphas_bar[t])
return sqrt_alphas_bar * x_start + sqrt_one_minus_alphas_bar * noise
模型:一个 U-Net,输入加噪图像与时间步 t,输出预测的噪声。
unet = Unet(
dim=64,
channels=3,
dim_mults=(1,2,4,8)
).to(device)
简化训练步:
- 从数据集中取出一批真实图像
x0。 - 随机采样时间步
t和标准高斯噪声noise。 - 用
q_sample生成加噪图像x_t。 - 让 U-Net 预测添加的噪声
noise_pred = unet(x_t, t)。 - 损失函数为预测噪声与真实噪声的 MSE:
loss = mse(noise_pred, noise)。
采样生成:从纯噪声 x_T 开始,逐步迭代去噪:
@torch.no_grad()
def p_sample(model, x, t):
predicted_noise = model(x, t)
alpha = alphas[t]
alpha_bar = alphas_bar[t]
# 反向过程公式
x = 1 / sqrt(alpha) * (x - (1-alpha)/sqrt(1-alpha_bar) * predicted_noise)
if t > 0:
x = x + sqrt(beta[t]) * torch.randn_like(x)
return x