Diffusers 实战:用 InstructPix2Pix 微调 Stable Diffusion 实现指令驱动图像编辑
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
本文基于 diffusers 仓库中的 train_instruct_pix2pix.py 训练脚本,系统讲解如何对 Stable Diffusion 进行 InstructPix2Pix 微调,使其能根据一句自然语言编辑指令修改输入图片。读完本文,你将掌握从依赖安装、数据准备、单卡与多卡训练,到参数调优与推理部署的完整闭环,并能理解训练脚本与推理管线背后的关键实现原理。
InstructPix2Pix 方法概述
InstructPix2Pix(对应论文 arXiv 2211.09800)是一种对文本条件扩散模型进行微调的方法,目标是让模型能够遵循针对单张输入图像的编辑指令完成图像编辑。经过该方法微调后的模型,其输入由两部分组成:
- 原始输入图像:待编辑的目标图片;
- 编辑指令(edit prompt):一段描述期望编辑效果的自然语言,例如 "把雪山变成草地"、"擦除湖面"。
模型的输出则是应用了该编辑指令后的"已编辑"图像,即输入图像与编辑指令语义对齐的新图片。
在该训练框架中,训练数据由三元组构成:input_image(原始图)、edit_prompt(编辑指令)、edited_image(编辑后的目标图)。训练目标是让模型学会从"原始图 + 指令"出发去噪出"编辑后的图",从而实现指令驱动(instruction-driven)的编辑能力。
环境准备与依赖安装
从源码安装 diffusers
官方强烈建议从源码(source)安装 diffusers,因为示例脚本更新频繁,且带有示例专属依赖。请在全新虚拟环境中执行:
git clone https://github.com/huggingface/diffusers cd diffusers pip install -e .说明:上述命令将仓库克隆到本地并以可编辑模式安装。本文所分析的当前仓库即为该示例脚本的所在项目,训练脚本位于 examples/instruct_pix2pix/train_instruct_pix2pix.py。
安装示例专属依赖
进入示例目录并安装 requirements.txt 中声明的依赖:
cd examples/instruct_pix2pix pip install -r requirements.txt该文件声明的核心依赖如下:
| 依赖 | 版本要求 | 用途 |
|---|---|---|
accelerate | >=0.16.0 | 分布式训练、混合精度与训练基础设施 |
torchvision | — | 图像变换(transforms)与数据增强 |
transformers | >=4.25.1 | CLIP 文本编码器与分词器 |
datasets | — | 加载 Hugging Face Hub 数据集或本地imagefolder |
ftfy | — | 文本清洗(修正 Unicode 编码问题) |
tensorboard | — | 默认训练日志记录 |
如需在训练中启用验证推理可视化(report_to="wandb"),还需额外安装pip install wandb。
初始化 Accelerate 环境
训练依赖 🤗 Accelerate 管理设备、混合精度与分布式环境。三种初始化方式任选其一:
交互式配置(推荐,可按环境定制):
accelerate config使用默认配置(无需回答环境问题):
accelerate config default非交互式环境(如 notebook 无法使用交互式 shell):
from accelerate.utils import write_basic_config write_basic_config()从源码实现看,脚本通过Accelerator统一管理梯度累积步数、混合精度与日志后端(见 train_instruct_pix2pix.py 中accelerator = Accelerator(gradient_accumulation_steps=..., mixed_precision=..., log_with=..., project_config=...)的初始化逻辑)。
数据集准备
使用公开玩具数据集快速验证
脚本作者已声明:train_instruct_pix2pix.py忠实实现了 InstructPix2Pix 的训练流程,但仅在小型数据集上测试过,这可能会影响最终效果。为了更好的结果,建议使用更大的数据集、训练更长时间。
本文示例使用小型玩具数据集fusing/instructpix2pix-1000-samples,它是论文原始数据集(CLIP 过滤版)的缩小版本。脚本内置了该数据集的三元组列名映射:
DATASET_NAME_MAPPING = { "fusing/instructpix2pix-1000-samples": ("input_image", "edit_prompt", "edited_image"), }即默认约定:input_image列为原始图、edit_prompt列为编辑指令、edited_image列为编辑后的目标图。若使用自定义数据集,可通过--original_image_column、--edit_prompt_column、--edited_image_column三个参数覆盖默认列名;数据加载支持两种方式:
--dataset_name:Hugging Face Hub 上的数据集标识符,或本地数据集路径;--train_data_dir:本地文件夹(需遵循imagefolder结构,含metadata.jsonl提供标注)。
关于如何构建自己的训练数据集,可参考仓库中的 create_dataset 指南。
设置环境变量
export MODEL_NAME="stable-diffusion-v1-5/stable-diffusion-v1-5" export DATASET_ID="fusing/instructpix2pix-1000-samples"MODEL_NAME作为--pretrained_model_name_or_path传入,可以是 Hub 上的模型仓库标识符,也可以是包含模型权重的本地文件夹路径;DATASET_ID作为--dataset_name传入,指定训练数据集。
启动训练:完整命令与参数解析
执行以下命令启动训练。脚本会把所有组件(feature_extractor、scheduler、text_encoder、unet、vae等)以 diffusers 格式保存到输出目录的子文件夹中:
accelerate launch --mixed_precision="fp16" train_instruct_pix2pix.py \ --pretrained_model_name_or_path=$MODEL_NAME \ --dataset_name=$DATASET_ID \ --enable_xformers_memory_efficient_attention \ --resolution=256 --random_flip \ --train_batch_size=4 --gradient_accumulation_steps=4 --gradient_checkpointing \ --max_train_steps=15000 \ --checkpointing_steps=5000 --checkpoints_total_limit=1 \ --learning_rate=5e-05 --max_grad_norm=1 --lr_warmup_steps=0 \ --conditioning_dropout_prob=0.05 \ --mixed_precision=fp16 \ --seed=42 \ --push_to_hub关键参数详解
结合 train_instruct_pix2pix.py 中的参数定义,逐项说明:
| 参数 | 示例值 | 说明 |
|---|---|---|
--pretrained_model_name_or_path | 必填 | 预训练模型路径或 Hub 标识符,脚本会分别加载scheduler、tokenizer、text_encoder、vae、unet子目录 |
--revision/--variant | — | 模型仓库的分支/标签版本(如fp16variant) |
--dataset_name | fusing/instructpix2pix-1000-samples | 训练数据集;与--train_data_dir二选一,二者皆空会直接报错 |
--resolution | 256 | 输入图像统一缩放分辨率;论文作者观察到 256 分辨率训练的模型可良好泛化到 512 等更大分辨率(得益于其大训练数据集) |
--center_crop/--random_flip | — | 中心裁剪(默认随机裁剪)/ 随机水平翻转增强 |
--train_batch_size | 4 | 每设备 batch 大小(默认 16) |
--gradient_accumulation_steps | 4 | 累积多少步后再执行一次参数更新(默认 1) |
--gradient_checkpointing | 开 | 以更慢的反向传播换取显存节省 |
--max_train_steps | 15000 | 总训练步数,指定后覆盖--num_train_epochs(默认 100 轮) |
--checkpointing_steps | 5000 | 每多少步保存一次训练状态(默认 500),可用于--resume_from_checkpoint恢复训练 |
--checkpoints_total_limit | 1 | 最多保留的 checkpoint 数量,超限时自动删除最旧的 |
--learning_rate | 5e-05 | 学习率(默认1e-4),可用--scale_lr按 GPU 数、batch、累积步数缩放 |
--lr_scheduler/--lr_warmup_steps | constant/0 | 调度器类型(支持linear、cosine、cosine_with_restarts、polynomial、constant、constant_with_warmup)与预热步数(默认 500) |
--max_grad_norm | 1 | 梯度裁剪范数上限 |
--conditioning_dropout_prob | 0.05 | 条件丢弃概率,见下文原理章节 |
--mixed_precision | fp16 | 混合精度,可选no/fp16/bf16(bf16 需 PyTorch >= 1.10 与 Ampere GPU) |
--seed | 42 | 训练随机种子 |
--push_to_hub | 开 | 训练结束后把模型推送至 Hub |
--use_ema | — | 为 UNet 维护指数移动平均(EMA)模型,通常可提升生成质量 |
--use_8bit_adam | — | 使用 bitsandbytes 的 8-bit AdamW 节省显存 |
--allow_tf32 | — | 在 Ampere GPU 上允许 TF32 加速训练 |
--report_to | tensorboard | 日志后端,支持tensorboard(默认)、wandb、comet_ml、all |
--resume_from_checkpoint | — | 传入 checkpoint 路径或"latest"自动恢复最近一次训练状态 |
训练数据预处理要点
脚本对数据集的预处理逻辑值得注意(对应preprocess_images/preprocess_train函数):
- 原始图与编辑后的目标图会被拼接在一起后施加相同的数据增强(随机裁剪或中心裁剪、随机水平翻转),以保证两张图经过完全一致的几何变换;
- 像素值归一化到
[-1, 1]区间(2 * (images / 255) - 1); - 编辑指令通过 CLIP tokenizer 以
max_length填充并截断为定长序列。
用 Weights & Biases 监控训练与验证推理
脚本额外支持在训练过程中执行验证推理,将"原始图 + 验证提示词"送入当前训练状态的模型,生成编辑结果并上传到 W&B,便于实时观察模型是否在学会遵循指令(对模型调试非常有用)。启用方式为--report_to="wandb":
accelerate launch --mixed_precision="fp16" train_instruct_pix2pix.py \ --pretrained_model_name_or_path=$MODEL_NAME \ --dataset_name=$DATASET_ID \ --enable_xformers_memory_efficient_attention \ --resolution=256 --random_flip \ --train_batch_size=4 --gradient_accumulation_steps=4 --gradient_checkpointing \ --max_train_steps=15000 \ --checkpointing_steps=5000 --checkpoints_total_limit=1 \ --learning_rate=5e-05 --max_grad_norm=1 --lr_warmup_steps=0 \ --conditioning_dropout_prob=0.05 \ --mixed_precision=fp16 \ --val_image_url="https://hf.co/datasets/diffusers/diffusers-images-docs/resolve/main/mountain.png" \ --validation_prompt="make the mountains snowy" \ --seed=42 \ --report_to=wandb \ --push_to_hub相关参数:
--val_image_url:验证用的原始图片 URL(用于调试推理);--validation_prompt:验证提示词,例如上例的 "make the mountains snowy"(让山脉积雪);--num_validation_images:验证生成张数(默认 4);--validation_epochs:每多少轮执行一次验证(默认 1)。
从源码看,验证在log_validation函数中实现:每轮结束时(若指定了验证 URL 与提示词),脚本会用当前(或 EMA 的)UNet 重建StableDiffusionInstructPix2PixPipeline,以num_inference_steps=20、image_guidance_scale=1.5、guidance_scale=7的参数生成编辑图,并将"原始图、编辑图、编辑指令"以 W&B Table 的形式记录。注意:--report_to=wandb与--hub_token不能同时使用(存在 token 泄露风险),Hub 认证请使用hf auth login。
注:论文作者发现,用 256×256 分辨率训练得到的模型可以很好地泛化到 512×512 等更高分辨率,这主要归功于其训练所用的大规模数据集。
多 GPU 分布式训练
accelerate提供了无缝的多 GPU 训练支持。先通过accelerate config完成分布式环境配置(或阅读 Accelerate 的 launch 教程),再使用--multi_gpu启动。示例命令:
accelerate launch --mixed_precision="fp16" --multi_gpu train_instruct_pix2pix.py \ --pretrained_model_name_or_path=stable-diffusion-v1-5/stable-diffusion-v1-5 \ --dataset_name=sayakpaul/instructpix2pix-1000-samples \ --use_ema \ --enable_xformers_memory_efficient_attention \ --resolution=512 --random_flip \ --train_batch_size=4 --gradient_accumulation_steps=4 --gradient_checkpointing \ --max_train_steps=15000 \ --checkpointing_steps=5000 --checkpoints_total_limit=1 \ --learning_rate=5e-05 --lr_warmup_steps=0 \ --conditioning_dropout_prob=0.05 \ --mixed_precision=fp16 \ --seed=42 \ --push_to_hub与单卡命令相比,此例额外开启了--use_ema并将分辨率提升至 512。脚本内部对分布式做了多处适配,例如:
- 用
accelerator.prepare统一包装 UNet、优化器、数据加载器与 LR 调度器; - 学习率调度器的 warmup 步数与总步数会乘以进程数(
num_warmup_steps_for_scheduler = args.lr_warmup_steps * accelerator.num_processes); - checkpoint 的保存与加载通过
save_model_hook/load_model_hook以 diffusers 格式序列化 UNet 与 EMA 权重; - 损失值通过
accelerator.gather跨进程聚合后用于日志记录。
训练核心实现原理(源码级解析)
8 通道 UNet:为双条件输入扩展输入层
InstructPix2Pix 使用额外的图像(原始图)作为条件。为此,UNet 第一个卷积层的输入通道数从 4 扩为8:其中 4 个通道承载加噪后的编辑目标图潜在表示,另外 4 个通道承载原始输入图的潜在表示。脚本的关键初始化代码如下:
in_channels = 8 out_channels = unet.conv_in.out_channels unet.register_to_config(in_channels=in_channels) with torch.no_grad(): new_conv_in = nn.Conv2d( in_channels, out_channels, unet.conv_in.kernel_size, unet.conv_in.stride, unet.conv_in.padding ) new_conv_in.weight.zero_() new_conv_in.weight[:, :4, :, :].copy_(unet.conv_in.weight) unet.conv_in = new_conv_in新增的 4 个通道权重初始化为零,前 4 个通道则从预训练 checkpoint 复制,从而在保留预训练能力的同时引入图像条件输入。其余组件保持冻结:vae与text_encoder均requires_grad_(False),只有 UNet 参与训练(EMA 可选地维护 UNet 权重副本)。
前向扩散与损失计算
每个训练步的核心流程(对应主训练循环):
- 将编辑目标图编码到潜在空间并采样:
latents = vae.encode(edited_pixel_values).latent_dist.sample(),再乘以vae.config.scaling_factor; - 为每个样本采样随机时间步,按 DDPM 调度器执行前向加噪:
noisy_latents = noise_scheduler.add_noise(latents, noise, timesteps); - 文本条件:
encoder_hidden_states = text_encoder(input_ids)[0]; - 图像条件:取原始图潜在表示的**众数(mode)**而非随机采样:
original_image_embeds = vae.encode(original_pixel_values).latent_dist.mode(); - 将加噪潜在表示与原始图条件在通道维拼接:
torch.cat([noisy_latents, original_image_embeds], dim=1); - 依据
prediction_type确定回归目标:epsilon类型直接以噪声为 target,v_prediction类型则用get_velocity计算; - UNet 前向得到预测值,与目标计算 MSE 损失:
F.mse_loss(model_pred.float(), target.float(), reduction="mean"); - 梯度裁剪(
max_grad_norm)、优化器与调度器步进、EMA 更新(若启用)。
Conditioning Dropout:支撑推理时的 Classifier-Free Guidance
训练中的--conditioning_dropout_prob(论文第 3.2.1 节)是让模型在推理时支持无分类器引导的关键。实现逻辑为:以2 * conditioning_dropout_prob的概率把文本条件替换为空文本嵌入(用空字符串 tokenize 得到),以conditioning_dropout_prob的概率把图像条件置零:
if args.conditioning_dropout_prob is not None: random_p = torch.rand(bsz, device=latents.device, generator=generator) # 文本条件 mask:以 2*p 概率替换为空条件 prompt_mask = random_p < 2 * args.conditioning_dropout_prob encoder_hidden_states = torch.where(prompt_mask, null_conditioning, encoder_hidden_states) # 图像条件 mask:以 p 概率置零 image_mask = 1 - ((random_p >= p) * (random_p < 3 * p)) original_image_embeds = image_mask * original_image_embeds这样训练出的模型同时见过"有/无"文本条件与"有/无"图像条件,推理时即可通过引导尺度在"忠实指令"与"忠实原图"之间自由权衡。
推理:加载模型并编辑图像
训练完成后即可用StableDiffusionInstructPix2PixPipeline进行推理:
import PIL import requests import torch from diffusers import StableDiffusionInstructPix2PixPipeline model_id = "your_model_id" # <- 替换为你的模型 ID pipe = StableDiffusionInstructPix2PixPipeline.from_pretrained(model_id, dtype=torch.float16).to("cuda") generator = torch.Generator("cuda").manual_seed(0) url = "https://huggingface.co/datasets/sayakpaul/sample-datasets/resolve/main/test_pix2pix_4.png" def download_image(url): image = PIL.Image.open(requests.get(url, stream=True).raw) image = PIL.ImageOps.exif_transpose(image) image = image.convert("RGB") return image image = download_image(url) prompt = "wipe out the lake" num_inference_steps = 20 image_guidance_scale = 1.5 guidance_scale = 10 edited_image = pipe( prompt, image=image, num_inference_steps=num_inference_steps, image_guidance_scale=image_guidance_scale, guidance_scale=guidance_scale, generator=generator, ).images[0] edited_image.save("edited_image.png")注意model_id需要替换为你自己的模型标识符;该训练脚本产出的示例模型仓库可参考社区公开示例(sayakpaul/instruct-pix2pix)。
三个关键推理参数
官方建议重点调节以下三个参数来控制生成速度与质量:
| 参数 | 默认值 | 作用 |
|---|---|---|
num_inference_steps | 20(示例) | 去噪步数,步数越多质量越高但耗时越长 |
guidance_scale | 10(示例) | 文本指令引导强度,越大越忠实于指令文本 |
image_guidance_scale | 1.5(示例) | 原图引导强度,越大越接近输入原图 |
其中image_guidance_scale与guidance_scale对生成的("已编辑")图像影响尤为显著,值得重点调参。
双引导的数学实现
在 pipeline_stable_diffusion_instruct_pix2pix.py 的推理循环中,无分类器引导被同时应用于文本与输入图像两个条件。因为存在两类条件,潜变量被复制为 3 份,分别得到:纯文本引导预测noise_pred_text、文本+图像引导预测noise_pred_image、无条件预测noise_pred_uncond,最终融合公式为:
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_image) + image_guidance_scale * (noise_pred_image - noise_pred_uncond)该公式(见管线源码第 441-448 行)直观体现了两个尺度的语义:guidance_scale控制"编辑指令"的影响力,image_guidance_scale控制"原图"的保持程度。管线还通过do_classifier_free_guidance属性(guidance_scale > 1.0 且 image_guidance_scale >= 1.0时为真)判断是否启用双引导,并对 UNet 输入通道进行校验:加噪潜变量通道数与原图潜变量通道数之和必须等于 UNet 配置的in_channels(即 8),否则报错提示配置不一致。
进阶:Stable Diffusion XL 变体
仓库还提供了等价的 train_instruct_pix2pix_sdxl.py 脚本(针对 Stable Diffusion XL),详见 README_sdxl.md。SDXL 变体的训练范式与本例一致,仅在基础模型与部分组件上有所差异。
常见问题与实用建议
- 效果不佳时优先增大数据与训练时长:脚本仅在 1000 样本的小数据集上验证过,官方明确提示小数据集会直接影响最终效果;论文原版使用 CLIP 过滤的大规模数据集,建议获取更大数据集并延长训练;
- 显存紧张:组合使用
--gradient_checkpointing、--use_8bit_adam、--mixed_precision=fp16、--enable_xformers_memory_efficient_attention(需安装 xformers,注意 0.0.16 版本在部分 GPU 上有已知问题,建议升级到 0.0.17+)可显著降低占用; - 想要更稳定的生成质量:开启
--use_ema维护 EMA 权重,验证与最终保存时脚本会自动把 EMA 参数复制回 UNet; - 断点续训:用
--checkpointing_steps定期保存、--resume_from_checkpoint="latest"自动恢复最近一次训练进度; - 灵活的实验数据集:如需快速验证脚本流程,可用
--max_train_samples截断训练样本数进行小规模调试。
如果希望探索 InstructPix2Pix 训练方法论在更多场景中的应用,可进一步阅读社区关于"Instruction-tuning Stable Diffusion with InstructPix2Pix"的相关技术文章(官方博客与社区示例均有讨论)。
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考