简介:本资源面向计算机视觉与深度学习方向的学习者、毕业设计选题者及科研工程人员,提供CycleGAN风格变换的完整实战方案,帮助快速将模型跑通在自己的数据集上。包内共75个文件,以36个Python脚本与14个Shell脚本为核心,覆盖训练、测试、数据加载与模型定义等模块,另含7份Markdown说明、5份LaTeX论文源文件、2个Jupyter Notebook及配置文件、Dockerfile等,压缩包约41.87MB,结构清晰便于按模块查阅。资源同时附带论文与教程博客,可对照理解循环一致性损失、域迁移与跨模态图像生成等关键机制,并支持在医疗图像、风景画风迁移等场景中直接复用。目前已有1258人学习下载,适合希望从零上手CycleGAN、快速完成实验复现与工程落地的读者参考。
1. 风格变换用Cycle GAN跑自己的数据集:从零到能出图的完整路径
手里有一批自己拍的照片,想让它们变成某种画风、某种色调、某种材质感,但既没有成对的训练数据,也不想从零推导公式——Cycle GAN 就是为这个场景准备的。它的核心价值在于不需要成对样本:你只要准备两个文件夹,一个放源域图片(比如真实照片),一个放目标域图片(比如某类画作或某种风格图),它就能自己学会两个方向的双向映射。这篇笔记面向的是想拿自己的数据跑通风格变换的工程师和爱好者,不要求你读过原论文,但要求你能装环境、能改配置文件、能看损失曲线。我会把数据准备、训练命令、参数含义、常见翻车点全部拆开讲,最后给一个判断“这锅到底能不能用”的验证方法。整套流程在一张消费级显卡上就能跑,数据量几百到几千张都行。
2. Cycle GAN 跑自己数据集前的环境与数据准备
2.1 为什么选 Cycle GAN 而不是 pix2pix
风格变换这个任务,最怕的就是“没有配对数据”。pix2pix 要求每张输入图都有一张对应的目标图,比如同一场景的线稿和上色版,这种数据几乎只能靠人工画或者用工具批量生成,成本高且不自然。Cycle GAN 的聪明之处是引入循环一致性损失:A→B 再 B→A 要能回到原图,B→A 再 A→B 同理。这样只要两个域各自有一堆图,不需要它们一一对应。
从落地角度看,这意味着你可以直接拿手机拍的风景照当域 A,拿网上收集的某类风格图当域 B,丢进去就能训。代价是训练更慢、更容易不稳定,因为约束变弱了。我一般会先判断:如果手头确实有成对数据,优先 pix2pix 或 pix2pixHD;如果没有,Cycle GAN 是默认选择。另一个常见替代是 CUT,它用对比学习替代循环一致性,单侧映射更快,但风格保持上 Cycle GAN 更稳,尤其是目标域风格比较“远”的时候。
2.2 目录结构与数据格式的硬性要求
Cycle GAN 的官方实现(以及绝大多数复现版)对目录结构有固定约定。假设你的项目根目录叫cyclegan_project,数据放在datasets下:
cyclegan_project/ ├── datasets/ │ └── my_style/ │ ├── trainA/ # 源域训练图,比如真实照片 │ ├── trainB/ # 目标域训练图,比如风格图 │ ├── testA/ # 源域测试图,训练时只做推理不参与更新 │ └── testB/ # 目标域测试图 ├── checkpoints/ ├── models/ └── options/每张图必须是 RGB 三通道,格式 jpg/png 都行,但不要混入灰度图或带 alpha 通道的 PNG,否则会在数据加载时报通道数不匹配。图片尺寸不要求统一,训练时会随机裁剪到crop_size,但建议短边至少 256,否则裁剪后信息太少。我一般会先跑一个清洗脚本:
import os from PIL import Image def clean_images(folder, min_side=256): removed = 0 for name in os.listdir(folder): path = os.path.join(folder, name) try: img = Image.open(path).convert("RGB") except Exception: os.remove(path) removed += 1 continue w, h = img.size if min(w, h) < min_side: os.remove(path) removed += 1 else: img.save(path) # 统一转成 RGB 并覆盖 print(f"{folder}: removed {removed} bad images") for sub in ["trainA", "trainB", "testA", "testB"]: clean_images(os.path.join("datasets/my_style", sub))这段脚本做三件事:强制转 RGB、剔除短边小于 256 的图、删掉打不开的损坏文件。参数min_side按你的显存调整,显存小可以降到 128,但风格细节会明显变糊。清洗完统计一下数量,trainA 和 trainB 不需要数量相等,但最好都在 300 张以上,低于 100 张时判别器会过拟合,生成器学不到多样风格。
2.3 环境安装与依赖版本
官方 PyTorch 版依赖比较轻,但版本不对会出各种玄学错误。我一般用 conda 建一个干净环境:
conda create -n cyclegan python=3.8 -y conda activate cyclegan pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html pip install dominate visdom wandb pillow numpy关键点是torch 和 torchvision 版本要匹配,且 CUDA 版本和驱动兼容。如果你用 30 系卡,cu113 以上;20 系卡 cu102 也行。dominate用于生成训练过程的 HTML 可视化,visdom可选,不用可视化可以不装。装完跑一句python -c "import torch; print(torch.cuda.is_available())",输出 True 再往下走。如果 False,先解决驱动和 CUDA 匹配,不要硬跑 CPU,Cycle GAN 在 CPU 上训到能看需要几周。
3. 训练脚本与关键参数:把 Cycle GAN 跑起来
3.1 最小可运行训练命令
假设你用的是官方 pytorch-CycleGAN-and-pix2pix 那套代码结构,训练命令长这样:
python train.py \ --dataroot ./datasets/my_style \ --name my_style_cyclegan \ --model cycle_gan \ --batch_size 1 \ --load_size 286 \ --crop_size 256 \ --n_epochs 100 \ --n_epochs_decay 100 \ --lr 0.0002 \ --lambda_A 10.0 \ --lambda_B 10.0 \ --pool_size 50 \ --display_id 0 \ --gpu_ids 0这条命令里每个参数都影响结果。--batch_size 1是 Cycle GAN 的常见设置,因为要同时加载 A 和 B 两组图,显存占用翻倍;如果你显存够,可以试 2 或 4,但学习率要相应调大。--load_size 286是先把图短边缩放到 286,再随机裁到 256,这样每次 epoch 看到的裁剪区域不同,相当于数据增强。--n_epochs 100保持固定学习率,后 100 个 epoch 线性衰减到 0,总共 200 个 epoch。--lambda_A和--lambda_B是循环一致性损失的权重,默认 10,调大更强调“回到原图”,风格化程度会减弱;调小风格更猛但内容容易崩。
3.2 生成器与判别器结构的选择
Cycle GAN 默认生成器是 ResNet 结构:6 个残差块用于 256 尺寸,9 个用于 512 及以上。判别器是 PatchGAN,70×70 的感受野,输出一个矩阵而不是单个标量,这样能关注局部纹理。对于风格变换,我一般会改两个地方:
第一,如果目标风格是笔触感很强的画风,把生成器的残差块数从 6 加到 9,--netG resnet_9blocks,能保留更多高频细节。第二,如果源域和目标域差异极大(比如照片转某种极简线条),判别器用--netD n_layers --n_layers_D 3加深一层,否则判别器太弱,生成器会偷懒。
# 加深生成器和判别器的训练命令片段 --netG resnet_9blocks \ --netD n_layers \ --n_layers_D 3 \ --norm instance \ --no_dropout--norm instance是实例归一化,风格变换任务里比 batch norm 更稳,因为 batch size 通常很小。--no_dropout在测试时关闭 dropout,训练时仍保留。这些改动会让显存增加约 30%,但风格迁移的质感提升明显。
3.3 损失曲线怎么看:三个关键信号
训练启动后,终端会打印每轮的损失。Cycle GAN 的损失有四项:G_A(A→B 生成器)、G_B(B→A 生成器)、D_A(判别 A 域)、D_B(判别 B 域),外加 cycle 损失和 identity 损失。你不需要逐项精读,但要看三个信号:
- D_A 和 D_B 的损失:理想情况在 0.3~0.7 之间震荡。如果持续低于 0.1,说明判别器太强,生成器学不动,把
--lr降到 0.0001 或给判别器加 dropout。如果持续高于 1.5,判别器太弱,生成器会输出重复纹理。 - cycle 损失:应该稳步下降,最后稳定在 1~3 左右(取决于图像复杂度)。如果它不降反升,说明循环一致性没起作用,检查
--lambda_A/B是否被误设为 0。 - identity 损失:可选,但开启后(
--lambda_identity 0.5)能帮助保持颜色分布。如果生成图颜色整体偏移,把它调到 1.0 试试。
我一般会在第 20、50、100 个 epoch 各存一次模型,用--save_epoch_freq 20,然后拿 testA 的图跑推理,肉眼判断有没有“风格没上去”或“内容崩了”。不要只看损失,损失好看但图糊的情况太常见了。
4. 避坑与排查:Cycle GAN 训练中最容易翻车的五件事
4.1 生成图全是同一张脸或同一片纹理
现象:无论输入什么图,输出都长得差不多,或者目标风格只集中在某一种局部纹理上。原因:模式崩溃,通常是判别器太强或学习率太高,生成器找到了一个“骗过判别器”的捷径。解决:把判别器的学习率降到生成器的一半(有些实现支持--lr_D单独设置),或者把--pool_size从 50 降到 20,减少历史生成样本的缓存,让判别器看到更多样的假图。另外检查 trainB 是否多样性不足,如果目标域全是同一种构图,生成器也会偷懒。
4.2 训练到一半 loss 突然变 NaN
现象:前几十个 epoch 正常,突然某一轮 loss 变成 nan,之后全乱。原因:梯度爆炸,常见于学习率过高或 batch 里有异常值(比如全黑或全白的图)。解决:加梯度裁剪,在训练循环里加torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)。同时用 2.2 的清洗脚本再筛一遍数据,把像素方差极低的图删掉。如果已经 NaN,只能从最近的 checkpoint 恢复,把--lr乘 0.5 继续。
4.3 显存不够,报 CUDA out of memory
现象:batch_size 已经设为 1,还是 OOM。原因:--load_size和--crop_size太大,或者生成器用了 9blocks 且输入尺寸 512。解决:先把--load_size 256 --crop_size 256跑通,再逐步加。如果必须跑 512,用--netG resnet_6blocks并开启混合精度(如果代码支持--amp)。另一个技巧是把--pool_size设为 0,省掉缓存图像占的显存。
4.4 生成的图颜色整体偏绿或偏紫
现象:风格是学到了,但所有输出都蒙了一层奇怪的颜色。原因:identity 损失权重太低,或者目标域图片本身有色偏,生成器把色偏也学进去了。解决:把--lambda_identity从 0 调到 0.5~1.0,让生成器在遇到目标域图片时尽量保持原样,从而约束颜色映射。同时检查 trainB 的图片是否经过统一的白平衡处理,没有的话先做一次自动白平衡再训。
4.5 测试时生成的图比训练时差很多
现象:训练过程中 visdom 里看到的图不错,但用 testA 跑推理时效果明显下降。原因:训练时用了--no_dropout但测试时忘了加,或者模型加载的是最后一个 epoch 而不是最佳 epoch。解决:测试命令里确保--norm instance --no_dropout和训练一致。另外不要盲目用最后一个模型,我一般会拿第 100、150、200 epoch 的模型各跑一批测试图,选肉眼最稳的那个。如果代码支持--eval模式,记得加上,它会关闭数据增强的随机裁剪。
5. 进阶技巧:用 FID 和人工盲测判断这锅到底能不能用
训练完不是终点,你得能回答“这个风格变换到底能不能交付”。光看几张图容易自欺,我一般会做两件事:算 FID 和做人工盲测。
FID(Fréchet Inception Distance)衡量生成图分布和真实目标域分布的差距,值越低越好。但注意,FID 在风格变换里只能做参考,因为你的目标域图片和生成图本来就不是同一内容。更实用的做法是:把 testA 的图用生成器转成风格图,然后和 trainB 的图混在一起,找几个人(或者你自己隔一天)做盲测,看能不能分辨出来。如果分辨准确率接近 50%,说明风格已经骗过人眼;如果一眼就能挑出生成图,说明还有明显伪影。
# 用 pytorch-fid 计算 FID 的示例 # 先把生成图存到 fakeB 文件夹,真实目标域图在 trainB import os os.system("python -m pytorch_fid datasets/my_style/trainB generated/fakeB --device cuda:0")这个命令会输出一个 FID 值。根据经验,256 尺寸的风格变换任务,FID 低于 80 算可用,低于 50 算不错。但别死磕数字,我见过 FID 很低但图明显发灰的情况,也见过 FID 一般但肉眼很舒服的。所以最终判断标准是:随机抽 20 张 testA,生成后和原图并排看,内容结构没崩、风格统一、没有大面积伪影,就算过关。
另一个进阶技巧是控制风格强度。Cycle GAN 本身没有强度旋钮,但你可以插值:把输入图和生成图按比例混合,output = alpha * fake + (1 - alpha) * real,alpha 从 0.3 到 1.0 调,找到风格和内容保留的平衡点。这个技巧在交付给非技术用户时特别有用,因为他们往往觉得“风格太猛了”。
最后说一个我踩过的坑:不要用训练集里的图去评估效果,那只会给你虚假的自信。一定留出独立的 testA,而且 testA 的拍摄条件、内容分布要和 trainA 有差异,这样才能看出模型是不是真的学到了风格映射,而不是记住了训练样本。希望帮到你。
本文还有配套的精品资源,点击获取