在 Ubuntu 22.04 上用 GAN 做图像生成,踩坑最多的地方往往不是模型本身,而是环境、数据和训练稳定性。我最近跑通了一个 128×128 人像生成项目,从装驱动到最终让生成图像达到可用的清晰度,花了大概两周。回过头看,很多问题其实都有固定解法,比如 CUDA 和 PyTorch 版本匹配、损失函数选型、数据增强策略,以及最重要的“怎样判断训练有没有正常收敛”。这篇文章想把我在这条路上摸出来的细节和教训完整整理出来,特别是针对 Ubuntu 22.04 的配置、GAN 训练参数调整和常见故障排查,给你一套可以直接复用的实操方案。
1. 环境准备:Ubuntu 22.04 上的深度学习底座
1.1 为什么选 Ubuntu 22.04 LTS
GAN 训练对 Linux 环境的依赖程度远高于普通 Web 开发。Ubuntu 22.04 LTS 的长期支持周期、软件源更新稳定性和 NVIDIA 驱动兼容性,让它成为目前跑深度学习项目最省心的发行版之一。相比手动编译内核或者折腾依赖,Ubuntu 的 apt 体系能让你把时间花在模型调试上,而不是花在“为什么库又编译不过”。
另外 22.04 对较新硬件的支持也更好。比如 RTX 30 系、40 系显卡配合较新的 NVIDIA 驱动,可以比较顺利启用 CUDA 12.x,PyTorch 官方 wheel 包已经自带了 CUDA 运行时,不需要额外装完整版 CUDA Toolkit 就能跑起来。这对新手非常友好。
如果你手头机器比较老,用 20.04 也完全可以,但很多新库的预编译包会默认优先支持 22.04,所以新的服务器或工作站建议直接上 22.04。
1.2 驱动、CUDA 与 PyTorch 的版本匹配
环境配置里最容易翻车的点就是驱动、CUDA、cuDNN 和 PyTorch 四者版本不对齐。我建议按“先驱动,再 PyTorch,最后补 cuDNN”的顺序来处理。
第一步,安装 NVIDIA 驱动。Ubuntu 22.04 的软件源里已经包含了 NVIDIA 驱动包,最简单的方式是用 udo apt 直接装:
sudo apt update sudo apt install nvidia-driver-535装完重启后运行nvidia-smi,应该能看到类似下面的输出,注意右上角有 “CUDA Version: 12.x”,代表当前驱动支持的 CUDA 最大版本。
第二步,安装 PyTorch。最省事的方式是直接用 pip 安装带 CUDA 支持的 wheel 包。比如 CUDA 12.1 版本:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121为什么要强调用官方 wheel 而不是pip install torch默认源?因为默认源装的是 CPU 版,你调用torch.cuda.is_available()会返回 False。这一步卡住的人很多。
第三步,验证环境。运行下面这段命令:
import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果都正常,环境基础就打通了。cuDNN 的话,PyTorch 官方 wheel 内部已经捆绑了适用的 cuDNN 动态库,一般情况下不需要手动安装。除非你打算自己编译扩展算子,否则可以暂时忽略。
1.3 GAN 训练的数据集准备
环境只是开始,GAN 训练的数据质量直接影响最终效果。你当然可以用 CIFAR-10 或 CelebA 这样的公开数据集试跑,但如果想生成自己的图像,建议把训练图片统一整理到一个文件夹里,用torchvision.datasets.ImageFolder直接读取。
我的做法是建一个data/train/目录,下面按类别放不同子目录,类别名称不重要,GAN 做无监督生成时用不到标签。但图片质量很关键:
- 图片不要有严重压缩痕迹,尤其是低分辨率社交媒体保存图,会强行把噪声和伪影学进去。
- 统一裁剪到正方形,再缩放到目标分辨率。比如用中心裁剪到 256×256,再 resize 到 128×128。
- 去除重复图、带水印的图、人脸几乎被遮挡的图,否则生成结果会“混入”这些特征。
数据量方面,如果是 DCGAN 练 128×128,建议至少上万张图。如果是 StyleGAN2 这类大模型,数据量越大越好,但单卡用户往往受限于显存和训练时间,2 万张图就是比较友好的起点。数据太少,很容易在第一次过拟合后就陷入模式崩溃。
2. GAN 核心原理与模型选型
2.1 生成器和判别器到底在博弈什么
GAN 的本质是让生成网络和判别网络玩一个零和博弈游戏。生成器 G 的输入是随机噪声 z,输出是假图;判别器 D 的输入是图片,输出是真伪概率。训练时,G 想骗过 D,D 想识破 G。两者不断对抗,最终达到纳什平衡——此时 G 生成的数据分布逼近真实数据分布。
这个对抗过程听起来简单,实际训练时特别容易一边倒。如果 D 太强,G 的梯度会变得很平缓,甚至消失,生成器怎么练都不变;如果 G 太强,D 被彻底骗过,loss 变成 0,同样无法提供有效反馈。所以整个优化过程不是在追求 loss 越小越好,而是在找平衡。
我习惯盯两件事:一是 D 的 loss 不要掉到接近 0,二是 G 的 loss 要平滑下降。只要这两个信号正常,训练就没有大崩盘。
2.2 DCGAN、WGAN-GP 还是 StyleGAN2
不同模型适合不同场景和硬件。在你的显卡显存有限、只想快速跑通一个生成管线时,我推荐先上 DCGAN 或 WGAN-GP,因为他们结构简单、参数量小、调参成本低。其中 WGAN-GP 额外引入了梯度惩罚,训练稳定性明显优于原始 DCGAN。
但如果追求“高质量图像”,那就绕不开 StyleGAN2。它通过风格调制和跳跃连接,能生成 1024×1024 的高清人脸图,效果是 DCGAN 完全没法比的。缺点是显存占用大、训练时间长,单张 24G 显存卡跑 256×256 也要比较长的时间。
我的建议是分两步走:先用 DCGAN 或 WGAN-GP 在较低分辨率(如 64×64)把环境、代码、数据管线跑通,再去尝试 StyleGAN2 的高分辨率版本。不要一上来就追求高清,否则光排环境问题就能劝退不少人。
2.3 损失函数选择与训练不稳定的根源
原始 GAN 用的是二进制交叉熵损失。实践下来这玩意太“脆”,判别器一旦太强,生成器梯度就消失;而 WGAN-GP 用的是 Wasserstein 距离加梯度惩罚,理论上有更平滑的梯度,训练相对稳定。
我在实验里对比过,同样环境下 WGAN-GP 的崩溃概率确实比原始 GAN 低很多。原因在于原始 GAN 的判别器输出是概率,存在“饱和区”,而 WGAN-GP 的判别器输出是一个评分,可以理解为“图像真实程度”,梯度分布更合理。
所以如果你不想花太多时间在“救不收敛”上,直接选择 WGAN-GP 或带 WGAN-GP 训练的 StyleGAN2 变体会省心很多。模型代码建议基于成熟的 PyTorch 复现仓库修改,不要自己从零造轮子。
3. 训练优化实操:让图像质量上一个台阶
3.1 数据增强与归一化细节
GAN 训练对数据预处理比分类任务更敏感。我踩过的第一个坑就是用常规的 ImageNet 归一化(mean/std 归一化)把像素值缩到 [0,1] 之间,结果生成图像灰蒙蒙一片。后来才意识到,GAN 的生成器一般都用 tanh 输出,像素范围应该在 [-1,1],所以数据也要同步缩放到 [-1,1] 才匹配。
正确做法是:
transform = transforms.Compose([ transforms.Resize((128, 128)), transforms.RandomHorizontalFlip(), transforms.CenterCrop((128, 128)), transforms.ToTensor(), transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) ])这里mean=0.5, std=0.5会把 0~1 的像素值转换到 -1~1 区间,正好对齐生成器的 tanh 输出。
数据增强方面,我建议只做轻度的随机翻转和随机裁剪。不要像分类网络那样用颜色抖动、旋转和 Cutout。GAN 本身就是要学数据分布,过度增强会让分布被扭曲,生成结果容易出现“诡异变形”。比如人脸训练集里如果加入水平翻转,生成的人脸画质不会受影响;但如果加了随机旋转 30°,模型就很容易把人脸画歪。
3.2 学习率、批大小与 Adam 参数调整
GAN 训练最常用的优化器是 Adam,但默认参数并不适合 GAN。PyTorch 的 Adam 默认学习率是 1e-3,beta1=0.9,直接用于 GAN 往往会导致训练震荡。业界常用设置是:
lr = 2e-4 betas = (0.5, 0.999)为什么 beta1 要调低到 0.5?因为 beta1 控制着一阶动量(历史梯度方向)的权重,调低之后,梯度方向更新更快,能避免优化器“惯性”太大,让模型在对抗过程中反应更敏捷。这个经验值最早来自 DCGAN 论文,之后很多 GAN 实现都沿用。
批大小方面,我试过 16 和 64,视觉质量差异不大,但显存占用差距明显。如果你的显卡只有 8G 显存,跑 128×128 的 DCGAN 用批大小 32 比较合适。批大小过小会导致批归一化统计不稳定,生成图像出现闪烁斑块;过大会让训练变慢且容易造成判别器过拟合。建议在 16~64 之间调,效果最好。
3.3 使用标签平滑、梯度惩罚与谱归一化
除了调优化器参数,三种技巧能有效提升训练稳定性:
第一是标签平滑。不要用 0 和 1 作为判别器真伪标签,改用 0.1~0.2 的平滑值。例如真实图片标签用 0.9,伪造图片标签用 0.1,这样判别器的输出不会过度自信,能保持梯度持续存在。实现起来很简单:
real_labels = torch.full((batch_size,), 0.9, device=device) fake_labels = torch.full((batch_size,), 0.1, device=device)第二是梯度惩罚。WGAN-GP 在计算判别器损失时,会对真实样本和伪造样本之间的插值样本计算梯度,并惩罚其 L2 范数偏离 1 的程度。这个操作能强制判别器满足 Lipschitz 约束,理论上是 WGAN 能稳定训练的关键。我每次训练损失里加上这一项后,模式崩溃的概率大幅降低。
第三是谱归一化。它限制每层权重矩阵的谱范数不超过 1,也能约束判别器的 Lipschitz 常数。PyTorch 里用起来非常方便:
import torch.nn as nn nn.utils.spectral_norm(conv_layer)我个人更推荐用谱归一化 + 分层梯度惩罚的组合,比只用其中一种效果好。
3.4 训练过程监控与调参思路
GAN 训练的一个典型误区是只看 loss 数值。实际上 loss 绝对值参考意义不大,因为 D 和 G 的对抗导致它们会波动。我建议盯三个信号:
- 一是生成器输出的图片在训练早期(比如 10 个 epoch 后)应该出现明显的物体轮廓,如果整张图片还是纯噪声,说明学习率太低或模型容量不够。
- 二是判别器对真图与假图的平均输出分数,两者应该逐渐分开,但不能急速分离到 1 和 0,否则判死速度过快,生成器会崩。
- 三是每隔 1000 步保存一组生成样本图,拼成大图定期看变化趋势,肉眼比 loss 曲线更敏锐。
调参时一次只改一个变量。比如先固定学习率 2e-4 和批大小 32,把损失函数从交叉熵换成 WGAN-GP,观察是否解决模式崩溃;然后再试标签平滑,再试谱归一化。不要同时改三个,否则出了问题不知道是谁引起的。
4. 常见问题与排查技巧实录
4.1 常见故障速查表
| 现象 | 可能原因 | 解决思路 |
|---|---|---|
| 生成图片始终是模糊色块 | 数据归一化错误,或生成器输出与输入像素范围不匹配 | 检查 transform 的 Normalize 参数,确认生成器使用 tanh 输出 |
| 训练到一半生成图片大量重复 | 模式崩溃,判别器过强或生成器分布坍缩 | 降低学习率,加入梯度惩罚或标签平滑 |
| 判别器 loss 迅速降到 0 | 判别器过强,生成器梯度消失 | 减小判别器网络容量,增加生成器容量,调低 learning rate |
| 显存不足 OOM | 输入分辨率或 batch size 过大 | 降低 batch size,或改用梯度累积方式模拟大批次 |
| 训练时 GPU 利用率低 | 数据加载瓶颈,或 CPU 预处理太慢 | 使用DataLoader(num_workers=4, pin_memory=True) |
| 训练 loss 非常平稳但不下降 | 学习率太低或网络太深梯度消失 | 适当增大学习率,或把生成器激活函数换成 LeakyReLU |
4.2 模式崩溃的定位与修复
模式崩溃是最常见又最恼人的问题。它的典型表现是生成器输出只有两三种模板图案,像“复制粘贴”一样。我遇到过的案例中,一半归因于学习率过大,另一半归因于判别器收敛太快。
一个有效的定位方法:分别单独训练判别器和生成器,观察判别器对真图和假图的输出分数差异。如果真图分数与假图分数在 100 步内就拉开到 0.9 与 0.05,说明判别器太强,需要削弱它。削弱方法包括减少判别器卷积层数量、增加判别器权重衰减、或者单纯降低判别器的更新频率(每 2 步更新一次判别器,每 1 步更新一次生成器)。
4.3 从头训练与迁移学习的选择
如果你在公共数据集上验证了模型,但想生成完全不同的图像风格,建议用预训练模型做迁移学习,而不是从头训练。
具体做法是:加载一个预训练好的 StyleGAN2 生成器,冻结低层(保留通用图像特征),只训练高层(调整风格细节),然后在小规模自有数据集上调优。这样可以大大缩短训练时间,同时避免数据量太少导致模式崩溃。
如果没有合适的预训练模型,那就老老实实用好 WGAN-GP 加梯度惩罚,在 128×128 的小分辨率上先跑出稳定的结果,再逐步提高分辨率。我习惯把分辨率每次翻倍,同时在翻倍时把批大小减半,这样显存比较稳定,效果也最好。
4.4 影响训练速度的隐藏因素
很多人以为训练速度只取决于显卡型号,其实还有几个容易被忽略的瓶颈:
- 数据加载:图片是小文件的话,用默认 DataLoader 会有严重的 I/O 瓶颈。建议把图片提前打包成 LMDB 或使用 FFHQ 类似的 data loader,能明显提速。
- 损失计算设备:别把张量反复搬运到 CPU 再回到 GPU,例如计算 FID 时不要边训练边频繁评估,否则 GPU 会进入等待状态。
- CPU 进程数:
num_workers不是越大越好,过大会导致频繁切换,建议设置为 CPU 核心数的一半,并加pin_memory=True。
我实测过一次:把num_workers从 0 换成 4,训练速度提升了接近两倍。对于长周期 GAN 训练来说,优化数据管线往往是“免费”的速度提升。
5. 一些值得坚持的实践习惯
如果你打算长期做 GAN 图像生成,我强烈建议坚持三个习惯。第一是每次实验都用固定随机种子,并且保存完整的超参配置和代码版本,否则你很难知道自己上一次“效果爆好”是怎么跑出来的。第二是定期保存权重 checkpoint,不只是保存最后一版,因为 GAN 训练中途可能出现短期质量峰值,过了这个点反而又开始崩坏。第三是养成写训练日志的习惯,记录每个 epoch 的 loss、学习率、生成样本路径、异常现象,这些信息在后续排查问题时非常宝贵。
GAN 这个东西,凭空调参非常容易让人崩溃。但只要形成了自己的固定训练流程——环境匹配、数据对齐、模型选型、损失稳定、监控调参——你会发现它其实比很多监督学习模型更可预测。希望我上面整理的这些踩坑经验和复现步骤,能帮你在 Ubuntu 22.04 上少走几天弯路,早日跑出自己满意的生成图。