news 2026/10/12 3:27:31

Chainer 实现 DCGAN 完整指南:从 GAN 原理到 CIFAR-10 图像生成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Chainer 实现 DCGAN 完整指南:从 GAN 原理到 CIFAR-10 图像生成
  • 深度学习
  • 机器学习

【免费下载链接】chainer

A flexible framework of neural networks for deep learning

项目地址:https://gitcode.com/gh_mirrors/ch/chainer
点击查看免费下载

本教程基于 Chainer 官方仓库中的 DCGAN 示例(examples/dcgan 目录)与配套教程文档(docs/source/examples/dcgan.rst),系统讲解生成对抗网络(GAN)的核心原理、DCGAN 的架构设计要点,以及如何在 Chainer 中从零搭建生成器、判别器、自定义训练器并完成端到端的图像生成训练。读完本文,你将掌握 GAN 的数学机理、DCGAN 的四种关键训练技巧,以及 Chainer 中Chain、Deconvolution2D、StandardUpdater、Trainer等组件的实战用法,能够独立复现并调优一个可运行的 DCGAN 图像生成任务。

1. 引言:GAN 能做什么

生成对抗网络(Generative Adversarial Networks,GAN)是一类用于**生成式建模(generative modeling)**的深度神经网络,最常见的应用场景是图像生成。GAN 系模型已经进入实际产品——例如本教程文档提到的自动上色服务 PaintsChainer 就使用了 GAN 相关技术。

通过本教程你将掌握两件事:

  1. 生成对抗网络(GAN)的核心原理与数学形式;
  2. 在 Chainer 框架中实现 DCGAN(Deep Convolutional GAN)的完整步骤。

示例代码位于仓库的 examples/dcgan 目录,包含四个核心文件:

文件职责
net.py定义Generator、Discriminator网络与add_noise辅助函数
updater.py定义交替训练判别器与生成器的DCGANUpdater
train_dcgan.py入口脚本:参数解析、数据加载、优化器/训练器装配与训练循环
visualize.py定义训练过程中周期性输出生成图像的out_generated_image扩展

2. 生成对抗网络(GAN)原理

2.1 什么是 GAN

按照 NIPS 2016 的 GAN 教程(Goodfellow 等人),生成模型大体可分为三类:

  • 全可见置信网络(Fully Visible Belief Networks,FVBNs);
  • 变分自编码器(Variational Autoencoder,VAE);
  • 生成对抗网络(GAN)。

与 FVBNs、VAE 不同,GAN不显式建模训练数据的概率分布$p(\mathbf{s})$,而是直接建模一个生成器 $G: \mathbf{z} \mapsto \mathbf{s}$。生成器 $G$ 从隐变量 $\mathbf{z}$ 出发采样得到 $\mathbf{s} \sim p(\mathbf{s})$。除了生成器 $G$ 之外,GAN 还构造一个判别器 $D(\mathbf{x})$,用来区分「来自生成器 $G$ 的样本」与「来自训练数据的真实样本」。

训练过程中:判别器 $D$ 负责分辨真假;生成器 $G$ 则尽力提高判别器 $D$ 犯错的概率,从而让生成样本看起来与训练数据服从同一分布。

GAN 的优点在于采样成本低,且在图像生成任务上表现突出;缺点是无法计算似然$p_{\mathrm{model}}(\mathbf{s})$(因为没有建模任何概率分布),也无法从样本反推隐变量 $\mathbf{z}$。

2.2 GAN 是如何工作的

GAN 依赖两个模型——生成器与判别器。训练的核心目标,是让数据分布 $p(\mathbf{s})$ 与生成器产出的分布($\mathbf{s} = G(\mathbf{z})$)逐步对齐。理想情况下,双方博弈最终会收敛到博弈论意义上的纳什均衡(Nash equilibrium)。

一个经典而直观的类比是「假币制造者与警察」:造假者努力制造足以乱真的假钞,警察努力鉴别真伪;随着警察鉴别能力提升,造假者被迫造出更逼真的假钞,双方能力在对抗中同步提升,最终造假者能造出与真钞几乎无法区分的假币。这正是生成器与判别器关系的生动写照。

数学形式。判别器 $D(\mathbf{s})$ 表示样本 $\mathbf{s}$ 来自数据分布的概率,可写成:

$$D(\mathbf{s}) = \frac{p(\mathbf{s})}{p(\mathbf{s}) + p_{\mathrm{model}}(\mathbf{s})}$$

要让生成分布匹配数据分布,等价于最小化两个分布之间的差异,通常用Jensen-Shannon 散度$D_{\mathrm{JS}}$ 度量。以 $D(\mathbf{s})$ 表达:

$$2 D_{\mathrm{JS}} = D_{\mathrm{KL}}(p(\mathbf{s}),|,\bar{p}(\mathbf{s})) + D_{\mathrm{KL}}(p_{\mathrm{model}}(\mathbf{s}),|,\bar{p}(\mathbf{s}))$$

$$= \mathbb{E}{p(\mathbf{s})} \log D(\mathbf{s}) + \mathbb{E}{p_{\mathbf{z}}} \log (1 - D(G(\mathbf{z}))) + \log 4$$

其中 $\bar{p}(\mathbf{s}) = \frac{p(\mathbf{s}) + p_{\mathrm{model}}(\mathbf{s})}{2}$。$D_{\mathrm{JS}}$ 由判别器 $D$ 最大化、由生成器 $G$(即 $p_{\mathrm{model}}$)最小化,从而让 $G$ 生成的分布 $p_{\mathrm{model}}(\mathbf{s})$ 逼近数据分布 $p(\mathbf{s})$。整个训练可以归结为如下的min-max 目标函数:

$$\min_{G} \max_{D} ; \mathbb{E}{p(\mathbf{s})} \log D(\mathbf{s}) + \mathbb{E}{p_{\mathbf{z}}} \log (1 - D(G(\mathbf{z})))$$

实际训练时,上述 min-max 问题通过交替更新判别器 $D(\mathbf{s})$ 与生成器 $G(\mathbf{z})$求解:先固定生成器、更新判别器若干步,再固定判别器、更新生成器,循环往复,直至两者达到平衡。

2.3 什么是 DCGAN

DCGAN(Deep Convolutional GAN,深度卷积生成对抗网络)由 Radford 等人提出,名字已经说明其要点——使用 CNN(卷积神经网络)作为生成器与判别器的主体结构,其典型架构如下:

GAN 以训练困难著称,而 DCGAN 论文针对这一点提出了四项至关重要的训练技巧:

  1. 将max-pooling 层替换为更大步幅(strided)的卷积层(判别器用 stride 卷积下采样,生成器用分数步幅/反卷积上采样);
  2. 判别器中用全局平均池化(global average pooling)替代全连接层,降低参数量、增强稳定性;
  3. 生成器与判别器中都使用批归一化(batch normalization)层;
  4. 判别器使用leaky ReLU激活函数,避免梯度消失。

这些技巧正是 Chainer 示例 net.py 中网络设计的直接依据,下文将逐条对照源码说明。

3. 在 Chainer 中实现 DCGAN

3.1 定义生成器模型

首先定义生成器网络。完整的Generator类代码如下(与 examples/dcgan/net.py 一致):

class Generator(chainer.Chain): def __init__(self, n_hidden, bottom_width=4, ch=512, wscale=0.02): super(Generator, self).__init__() self.n_hidden = n_hidden self.ch = ch self.bottom_width = bottom_width with self.init_scope(): w = chainer.initializers.Normal(wscale) self.l0 = L.Linear(self.n_hidden, bottom_width * bottom_width * ch, initialW=w) self.dc1 = L.Deconvolution2D(ch, ch // 2, 4, 2, 1, initialW=w) self.dc2 = L.Deconvolution2D(ch // 2, ch // 4, 4, 2, 1, initialW=w) self.dc3 = L.Deconvolution2D(ch // 4, ch // 8, 4, 2, 1, initialW=w) self.dc4 = L.Deconvolution2D(ch // 8, 3, 3, 1, 1, initialW=w) self.bn0 = L.BatchNormalization(bottom_width * bottom_width * ch) self.bn1 = L.BatchNormalization(ch // 2) self.bn2 = L.BatchNormalization(ch // 4) self.bn3 = L.BatchNormalization(ch // 8) def make_hidden(self, batchsize): dtype = chainer.get_dtype() return numpy.random.uniform(-1, 1, (batchsize, self.n_hidden, 1, 1))\ .astype(dtype) def forward(self, z): h = F.reshape(F.relu(self.bn0(self.l0(z))), (len(z), self.ch, self.bottom_width, self.bottom_width)) h = F.relu(self.bn1(self.dc1(h))) h = F.relu(self.bn2(self.dc2(h))) h = F.relu(self.bn3(self.dc3(h))) x = F.sigmoid(self.dc4(h)) return x

在 Chainer 中构建网络有三条约定,示例代码完整遵循了它们:

  1. 网络类必须继承chainer.Chain;
  2. 在__init__初始化器的init_scope():中创建chainer.links的实例(即注册可学习参数);
  3. 在__call__运算符(本示例命名为forward)中,用chainer.links实例与chainer.functions定义网络的前向连接。

从__init__可见,Generator使用反卷积层L.Deconvolution2D与批归一化层L.BatchNormalization;在forward中,每一层之后都接F.relu激活,只有最后一层(dc4)例外,输出经过F.sigmoid映射到 $[0,1]$ 区间,以匹配像素归一化后的取值范围。

通道数变化规律。L.Deconvolution2D的第一个参数是输入通道数、第二个参数是输出通道数。以默认ch=512为例:dc1从 512 → 256,dc2从 256 → 128,dc3从 128 → 64,dc4从 64 → 3(RGB 三通道),每一层通道数近似减半。若以教程文档中的ch=1024构造Generator,网络形态则与 Radford 论文的原始架构图完全一致。各反卷积层均使用kernel=4, stride=2, pad=1(除最后一层为kernel=3, stride=1, pad=1),这正对应 DCGAN 技巧中「用分数步幅卷积上采样」的做法。

注意:将全连接层输出传入卷积层时,必须保证输入具备「通道 × 高 × 宽」的额外维度。看forward的第一行:全连接层l0的输出经过F.reshape被重塑为(len(z), ch, bottom_width, bottom_width),即添加了通道、宽、高三维,这正是 DCGAN 论文中把全连接层映射为 $4\times4\times ch$ 特征图的关键一步。make_hidden则负责从 $[-1, 1]$ 均匀分布中采样隐变量 $\mathbf{z}$,形状为(batchsize, n_hidden, 1, 1)。

3.2 定义判别器模型

判别器网络是生成器的「镜像」,代码同样位于 examples/dcgan/net.py:

class Discriminator(chainer.Chain): def __init__(self, bottom_width=4, ch=512, wscale=0.02): w = chainer.initializers.Normal(wscale) super(Discriminator, self).__init__() with self.init_scope(): self.c0_0 = L.Convolution2D(3, ch // 8, 3, 1, 1, initialW=w) self.c0_1 = L.Convolution2D(ch // 8, ch // 4, 4, 2, 1, initialW=w) self.c1_0 = L.Convolution2D(ch // 4, ch // 4, 3, 1, 1, initialW=w) self.c1_1 = L.Convolution2D(ch // 4, ch // 2, 4, 2, 1, initialW=w) self.c2_0 = L.Convolution2D(ch // 2, ch // 2, 3, 1, 1, initialW=w) self.c2_1 = L.Convolution2D(ch // 2, ch // 1, 4, 2, 1, initialW=w) self.c3_0 = L.Convolution2D(ch // 1, ch // 1, 3, 1, 1, initialW=w) self.l4 = L.Linear(bottom_width * bottom_width * ch, 1, initialW=w) self.bn0_1 = L.BatchNormalization(ch // 4, use_gamma=False) self.bn1_0 = L.BatchNormalization(ch // 4, use_gamma=False) self.bn1_1 = L.BatchNormalization(ch // 2, use_gamma=False) self.bn2_0 = L.BatchNormalization(ch // 2, use_gamma=False) self.bn2_1 = L.BatchNormalization(ch // 1, use_gamma=False) self.bn3_0 = L.BatchNormalization(ch // 1, use_gamma=False)

虽然Discriminator大体上是Generator的镜像结构,但有三个关键差异:

  1. 激活函数改用F.leaky_relu(对应 DCGAN 技巧 4);
  2. 网络更深——在 32×32 输入上先用3×3, stride=1卷积细化特征,再用4×4, stride=2卷积做下采样(对应 DCGAN 技巧 1 中「用 stride 卷积替代池化」);
  3. 对每层中间输出都叠加随机噪声(见下方add_noise),提升训练的稳定性。

噪声注入函数同样定义在 examples/dcgan/net.py 中:

def add_noise(device, h, sigma=0.2): if chainer.config.train: xp = device.xp if device.xp is chainerx: fallback_device = device.fallback_device with chainer.using_device(fallback_device): randn = device.send(fallback_device.xp.random.randn(*h.shape)) else: randn = xp.random.randn(*h.shape) return h + sigma * randn else: return h

add_noise只在chainer.config.train为真(即训练阶段)时向特征图添加标准差为sigma=0.2的高斯噪声,推理阶段则原样返回。判别器的forward将每个卷积/批归一化输出先加噪、再过leaky_relu,最终由全连接层l4输出一个标量 logit。注意:这里的BatchNormalization使用了use_gamma=False,即不学习缩放参数 $\gamma$,这是判别器的一个常见简化。

3.3 准备数据集与迭代器

示例默认使用CIFAR-10数据集,通过 Chainer 的数据集工具函数chainer.datasets.get_cifar10获取(实现见 chainer/datasets/cifar.py)。CIFAR-10 是 32×32 的小尺寸自然图像集合,每个样本是一张 RGB 彩色图像;原始图像中每个像素的 R、G、B 分量用单字节无符号整数表示(0~255)。get_cifar10支持通过scale参数把像素值缩放到[0, scale]的浮点区间。

训练脚本中的数据加载代码(examples/dcgan/train_dcgan.py):

if args.dataset == '': # Load the CIFAR10 dataset if args.dataset is not specified train, _ = chainer.datasets.get_cifar10(withlabel=False, scale=255.) else: all_files = os.listdir(args.dataset) image_files = [f for f in all_files if ('png' in f or 'jpg' in f)] print('{} contains {} image files' .format(args.dataset, len(image_files))) train = chainer.datasets\ .ImageDataset(paths=image_files, root=args.dataset)
  • 默认加载 CIFAR-10 训练集,withlabel=False表示只取图像不要标签,scale=255.把像素缩放到 $[0,255]$(后续在 updater 中会再除以 255 归一到 $[0,1]$,见 3.5 节);
  • 也可以通过--dataset参数传入一个包含 png/jpg 图片的目录,改用chainer.datasets.ImageDataset训练自定义数据。需要留意的是:示例网络假定输入分辨率是32×32,若使用其他分辨率,必须相应修改 net.py 中的网络结构(这一点在 examples/dcgan/README.md 中有明确说明)。

接下来设置迭代器(examples/dcgan/train_dcgan.py):

# Setup an iterator train_iter = chainer.iterators.SerialIterator(train, args.batchsize)

这里使用SerialIterator(单进程顺序迭代),每个 minibatch 包含args.batchsize(默认 50)张图像。若需加速数据读取,可替换为MultiprocessIterator或MultithreadIterator(见 chainer/iterators 目录)。

3.4 准备模型与优化器

实例化生成器与判别器,并将模型搬运到指定设备(examples/dcgan/train_dcgan.py):

# Set up a neural network to train gen = Generator(n_hidden=args.n_hidden) dis = Discriminator() gen.to_device(device) # Copy the model to the device dis.to_device(device)

随后为两个模型分别创建优化器(examples/dcgan/train_dcgan.py):

# Setup an optimizer def make_optimizer(model, alpha=0.0002, beta1=0.5): optimizer = chainer.optimizers.Adam(alpha=alpha, beta1=beta1) optimizer.setup(model) optimizer.add_hook( chainer.optimizer_hooks.WeightDecay(0.0001), 'hook_dec') return optimizer opt_gen = make_optimizer(gen) opt_dis = make_optimizer(dis)

要点:

  • 两个模型都使用Adam 优化器(实现见 chainer/optimizers/adam.py),学习率alpha=0.0002、一阶矩衰减beta1=0.5。beta1=0.5是 DCGAN 论文针对生成器训练给出的经验值——相比默认的 0.9 更小的动量有助于稳定对抗训练;Adam还支持beta2、eps、amsgrad等超参数,可按需调整;
  • 通过optimizer.add_hook为每个模型挂载了WeightDecay(权重衰减)hook(见 chainer/optimizer_hooks/weight_decay.py),衰减系数 0.0001;
  • 参数初始化使用标准差wscale=0.02的正态分布(chainer.initializers.Normal(0.02)),这也是 DCGAN 论文推荐的初始化方式。

3.5 自定义 Updater:交替训练的关键

GAN 需要两个模型,而 Chainer 预置的默认 updater 通常只接收一个模型,因此必须为 GAN 训练自定义 updater。DCGANUpdater的定义位于 examples/dcgan/updater.py:

class DCGANUpdater(chainer.training.updaters.StandardUpdater): def __init__(self, *args, **kwargs): self.gen, self.dis = kwargs.pop('models') super(DCGANUpdater, self).__init__(*args, **kwargs) def loss_dis(self, dis, y_fake, y_real): batchsize = len(y_fake) L1 = F.sum(F.softplus(-y_real)) / batchsize L2 = F.sum(F.softplus(y_fake)) / batchsize loss = L1 + L2 chainer.report({'loss': loss}, dis) return loss def loss_gen(self, gen, y_fake): batchsize = len(y_fake) loss = F.sum(F.softplus(-y_fake)) / batchsize chainer.report({'loss': loss}, gen) return loss def update_core(self): gen_optimizer = self.get_optimizer('gen') dis_optimizer = self.get_optimizer('dis') batch = self.get_iterator('main').next() device = self.device x_real = Variable(self.converter(batch, device)) / 255. gen, dis = self.gen, self.dis batchsize = len(batch) y_real = dis(x_real) z = Variable(device.xp.asarray(gen.make_hidden(batchsize))) x_fake = gen(z) y_fake = dis(x_fake) dis_optimizer.update(self.loss_dis, dis, y_fake, y_real) gen_optimizer.update(self.loss_gen, gen, y_fake)

DCGANUpdater继承自chainer.training.updaters.StandardUpdater(框架实现见 chainer/training/updaters/standard_updater.py),几乎所有基础设施(迭代器管理、优化器管理、序列化等)都由父类提供,这里只需重写__init__与update_core两个方法:

__init__的约定。额外的关键字参数models传入(gen, dis)二元组,并在调用父类构造函数前从中弹出;同时通过关键字参数传递iterator、optimizer、device。特别地,optimizer参数必须是一个字典——两个不同的模型需要两个不同的优化器,因此传入{'gen': opt_gen, 'dis': opt_dis}。在DCGANUpdater内部,通过self.get_iterator('main')访问主迭代器,通过self.get_optimizer('gen')与self.get_optimizer('dis')分别访问两个优化器。

说明:loss_dis与loss_gen只在update_core内部被调用,无需定义为方法;这里单独抽取出来纯粹是为了提高代码可读性。

update_core的交替更新流程。具体步骤为:

  1. 取出两个优化器引用(gen_optimizer、dis_optimizer);
  2. 用self.get_iterator('main').next()获取下一个 minibatch,交给self.converter拷贝到目标设备,再包成Variable——即x_real = Variable(self.converter(batch, device)) / 255.,除以 255 是把像素值归一化到 $[0,1]$,与生成器sigmoid输出的取值范围对齐;
  3. 让判别器对真实图像打分:y_real = dis(x_real);
  4. 采样隐变量z(gen.make_hidden(batchsize)),生成假图像x_fake = gen(z),再让判别器打分y_fake = dis(x_fake);
  5. 先更新判别器:dis_optimizer.update(self.loss_dis, dis, y_fake, y_real);
  6. 再更新生成器:gen_optimizer.update(self.loss_gen, gen, y_fake)。

损失函数的形式。这里使用的是 GAN 目标函数的非饱和(non-saturating)形式,以softplus实现:

  • loss_dis:$L = \frac{1}{N}\sum \big[\mathrm{softplus}(-y_{real}) + \mathrm{softplus}(y_{fake})\big]$。由于 $\mathrm{softplus}(-y) = \log(1+e^{-y}) = -\log \sigma(y)$,等价于判别器在真实样本上输出尽量大、在假样本上输出尽量小(即最小化 $-\log D(x_{real}) - \log(1-D(G(z)))$);
  • loss_gen:$L = \frac{1}{N}\sum \mathrm{softplus}(-y_{fake})$,即最小化 $-\log D(G(z))$,推动生成器让判别器对假样本输出尽量大(骗过判别器)。

两个损失函数还通过chainer.report({'loss': loss}, ...)上报,供Trainer的LogReport、PrintReport扩展记录到日志中——这就是训练输出中gen/loss、dis/loss两列数据的来源。

关于数组模块:在自定义update_core时,常需用 numpy/cupy 操作Variable底层的array。注意 CPU 上数组类型是numpy.ndarray,GPU 上是cupy.ndarray,无需手写if分支——可用xp = chainer.backend.get_array_module(variable.array)获得正确的数组模块:variable在 GPU 上时xp为cupy,否则为numpy。本示例代码中则直接使用device.xp与self.device,效果相同。

3.6 装配 Trainer 与训练扩展

设置好 updater 后,将其交给Trainer,并挂载一系列扩展(examples/dcgan/train_dcgan.py):

# Setup a trainer trainer = training.Trainer(updater, (args.epoch, 'epoch'), out=args.out) snapshot_interval = (args.snapshot_interval, 'iteration') display_interval = (args.display_interval, 'iteration') trainer.extend( extensions.snapshot(filename='snapshot_iter_{.updater.iteration}.npz'), trigger=snapshot_interval) trainer.extend(extensions.snapshot_object( gen, 'gen_iter_{.updater.iteration}.npz'), trigger=snapshot_interval) trainer.extend(extensions.snapshot_object( dis, 'dis_iter_{.updater.iteration}.npz'), trigger=snapshot_interval) trainer.extend(extensions.LogReport(trigger=display_interval)) trainer.extend(extensions.PrintReport([ 'epoch', 'iteration', 'gen/loss', 'dis/loss', ]), trigger=display_interval) trainer.extend(extensions.ProgressBar(update_interval=10)) trainer.extend( out_generated_image( gen, dis, 10, 10, args.seed, args.out), trigger=snapshot_interval)

各扩展的作用:

  • extensions.snapshot:每snapshot_interval(默认 1000)次迭代保存整个 trainer 的快照snapshot_iter_{iteration}.npz,用于中途断点恢复;
  • extensions.snapshot_object:分别保存生成器与判别器的参数快照(gen_iter_*.npz、dis_iter_*.npz),便于事后单独加载某个模型;
  • extensions.LogReport:每display_interval(默认 100)次迭代记录日志;
  • extensions.PrintReport:在控制台打印epoch / iteration / gen/loss / dis/loss;
  • extensions.ProgressBar:显示训练进度条与预估剩余时间;
  • out_generated_image:来自 examples/dcgan/visualize.py 的自定义扩展,每个快照间隔生成一张 $10\times10$ 网格的生成图像,保存到out/preview/image{iteration:08d}.png(固定seed以保证同一隐变量在不同时期可以对比)。

visualize.py中out_generated_image的实现要点(examples/dcgan/visualize.py):设置随机种子后采样 $10\times10=100$ 个隐变量,在chainer.using_config('train', False)上下文(关闭训练模式,从而跳过add_noise)中让生成器产出图像,把输出乘 255、裁剪到[0, 255]并转成uint8,再按(rows, cols, 3, H, W)重排成网格后保存为 PNG。

另外,训练脚本支持从快照断点续训(examples/dcgan/train_dcgan.py):

if args.resume is not None: # Resume from a snapshot chainer.serializers.load_npz(args.resume, trainer) # Run the training trainer.run()

3.7 命令行参数一览

入口脚本 examples/dcgan/train_dcgan.py 通过argparse解析全部参数:

参数别名默认值说明
--batchsize-b50每个 minibatch 的图像数量
--epoch-e1000遍历数据集的训练轮数
--device-d'-1'设备说明符:ChainerX 设备描述符或整数;非负整数使用指定 id 的 CuPy 数组(GPU),负整数使用 NumPy 数组(CPU)
--dataset-i''图像文件目录;缺省时使用 CIFAR-10
--out-oresult结果输出目录
--resume-r无从快照恢复训练
--n_hidden-n100隐变量 z 的维度
--seed无0可视化阶段隐变量采样的随机种子
--snapshot_interval无1000快照保存间隔(迭代数)
--display_interval无100控制台日志打印间隔(迭代数)
--gpu-g无已废弃参数:GPU id(负数表示 CPU),实际映射到--device

注意--gpu已被标记为deprecated,推荐使用--device指定设备;--device也支持 ChainerX 设备描述符(如cuda:0、native:0)。

3.8 启动训练

在examples/dcgan目录下执行:

$ pwd /root2chainer/chainer/examples/dcgan $ python train_dcgan.py --gpu 0 GPU: 0 # Minibatch-size: 50 # n_hidden: 100 # epoch: 1000 epoch iteration gen/loss dis/loss ................] 0.01% 0 100 1.2292 1.76914 total [..................................................] 0.02% this epoch [#########.........................................] 19.00% 190 iter, 0 epoch / 1000 epochs 10.121 iters/sec. Estimated time to finish: 1 day, 3:26:26.372445.

脚本启动时会先打印设备、minibatch 大小、隐变量维度与总 epoch 数,随后由PrintReport与ProgressBar输出每 100 次迭代的gen/loss、dis/loss以及进度和预估剩余时间。教程文档中的运行环境为单 GPU(--gpu 0,等价于--device 0)。

训练结束后,所有结果保存在输出目录(默认examples/dcgan/result/,由--out指定)下,包括:

  • snapshot_iter_*.npz、gen_iter_*.npz、dis_iter_*.npz:训练快照与模型参数;
  • log:LogReport产生的训练日志;
  • preview/image*.png:周期性生成的 $10\times10$ 图像网格。

下面的结果是生成器训练 1000 个 epoch 后的输出,教程页面顶部的 GIF 则展示了每 10 个 epoch 的生成图像演变过程:

从图中可以看到,经过充分训练后,生成器产出的图像在色彩、纹理与物体结构上已接近 CIFAR-10 真实样本的分布。

4. 小结与扩展阅读

本教程完整走通了「GAN 原理 → DCGAN 架构设计 → Chainer 代码实现 → 端到端训练」的整条链路:

  • GAN 通过生成器与判别器的 min-max 博弈学习数据分布,对应目标函数 $\min_G \max_D \mathbb{E}[\log D(\mathbf{s})] + \mathbb{E}[\log(1-D(G(\mathbf{z})))]$;
  • DCGAN 用全卷积结构 + 批归一化 + leaky ReLU 克服 GAN 训练不稳定问题;
  • Chainer 侧的关键实现:Generator/Discriminator(net.py)、交替更新的DCGANUpdater(updater.py)、训练入口(train_dcgan.py)与可视化扩展(visualize.py)。

如果你想更深入:

  • 学习 Chainer 网络构建规范,可参考 docs/source/guides 中的模型创建教程;
  • 了解StandardUpdater的默认更新逻辑与序列化机制,可阅读 chainer/training/updaters/standard_updater.py;
  • 将 CIFAR-10 换成自己的图片数据集,只需用--dataset指向图片目录(注意保持 32×32 分辨率或同步修改网络结构);
  • 本示例其余相关资料见 examples/dcgan/README.md。

参考论文

  • Goodfellow 等人,NIPS 2016 教程《Generative Adversarial Networks》
  • Nash 均衡(博弈论概念)
  • Jensen-Shannon 散度(分布差异度量)
  • Goodfellow 等人,《Generative Adversarial Networks》(GAN 原始论文)
  • Radford 等人,《Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks》(DCGAN 原始论文,本文架构与训练技巧的直接来源)
  • 深度学习
  • 机器学习

【免费下载链接】chainer

A flexible framework of neural networks for deep learning

项目地址:https://gitcode.com/gh_mirrors/ch/chainer
点击查看免费下载

相关推荐

上一篇:8cc 开源项目教程
下一篇:pg_repack: 优化 PostgreSQL 数据存储的高效工具

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 3:22:13

声呐阵列信号处理——声呐阵列波束形成(第一章第三节)

一、声呐阵列模型3.接收数据模型(1)数据组成阵元的实际接收数据是信号、噪声等干扰的叠加,所以接收数据模型建立的前提需是信号模型、噪声模型的构建。对于第m个阵元,其接收数据可以表示为数据中包含期望信号,D个干扰信…

作者头像 李华
网站建设 2026/10/12 3:21:06

展讯平台Camera驱动移植:从MIPI时序到ISP通路实战指南

1. 项目概述:为什么“展讯平台手机camera驱动移植”是嵌入式系统工程师绕不开的硬核课题展讯平台手机camera驱动移植——这八个字背后,不是简单的代码搬运,而是一场横跨硬件抽象层、图像信号处理链路、Linux内核子系统与SoC私有IP核的多线程协…

作者头像 李华
网站建设 2026/10/12 3:20:52

从无状态到有状态:AGENTS.md 与 Memory 工程实战指南

1. 从无状态到有状态:AI 编程范式转换的底层逻辑1.1 为什么传统 AI 编程模式正在失效过去两年,大多数人用 AI 写代码的方式还停留在“对话式问答”:打开一个聊天窗口,把需求描述一遍,AI 吐出一段代码,复制粘…

作者头像 李华
网站建设 2026/10/12 3:18:37

物联网宠物定位与监控系统设计与落地指南

“物联网宠物定位与监控系统”这个题目,是这几年毕业设计里特别常见的一类:听着新潮,跟物联网挂钩,又有硬件有软件,做出来还能直接演示。但很多同学是从拿到任务书那一刻就开始发懵——开题报告不知道怎么写满几页纸&a…

作者头像 李华