6.md 31.0 KB
Newer Older
W
wizardforcel 已提交
1 2
# 探索生成对抗网络

W
wizardforcel 已提交
3
**生成对抗网络****GAN**)是一种机器学习技术,其中同时训练两种模型:一种专门用于创建伪造数据,另一种专门用于区分伪造数据和伪造数据。 真实数据。 术语*生成*反映了以下事实:这些神经网络用于创建新数据,而术语*对抗*来自以下事实:两个模型相互竞争,从而提高了质量 生成的数据。
W
wizardforcel 已提交
4

W
wizardforcel 已提交
5
GAN 中的两个模型称为生成器和判别器,其中生成器负责创建数据,判别器接收数据并将其分类为真实数据或由生成器生成。 生成器的目标是创建与训练集中的真实数据没有区别的数据样本。
W
wizardforcel 已提交
6 7 8

我们可以用一个类比来理解 GAN 的概念,即犯罪分子(产生者)想要伪造金钱,而侦探(鉴别者)则试图抓住他。 假币的外观越真实,侦探在检测到假币时就必须越好,越高效,这意味着伪钞的质量必须提高到足以使侦探无法发现。

W
wizardforcel 已提交
9
生成器从判别器的分类反馈中学习。 判别器的目标是确定其输入是真实的(来自训练数据集)还是伪造的(来自生成器),因此,每当辨别器将虚假图像分类为真实的错误时,生成器都会获得正反馈 一份好工作。 相反,每当判别器正确捕捉到生成器生成的图像为伪造图像时,生成器就会收到需要改进的反馈。
W
wizardforcel 已提交
10

W
wizardforcel 已提交
11
判别器基本上是一个分类器,并且像任何分类器一样,它从真实标签(它在这种情况下是真实的或假的)中了解到其预测有多远。 因此,随着生成器在生成逼真的数据方面变得更好,判别器在从真实标签中辨别伪造品方面也必须变得更好。 这样,两个网络都可以同时改善。
W
wizardforcel 已提交
12 13 14 15 16 17 18

从概念上讲,生成器必须能够从训练示例中捕获真实数据的特征,以使其生成的样本与真实数据无法区分。 生成器自己学习创建模式(而不是识别图像分类问题中的模式)。 通常,生成器的输入通常是随机数的向量。

让我们看一下 GAN 的以下架构图:

![](img/13480561-764e-43be-b39a-07d7656540db.png)

W
wizardforcel 已提交
19
在此图中,有一个数据源,其中包含训练图像`x`,生成器必须捕获其属性并重新创建。 生成器接收随机向量`z`,该向量充当生成器创建伪图像的种子。 生成器获取种子并生成`x*`图像,判别器从真实和虚假图像中获取图像,并输出给定​​输入为真实的概率(假设 图像用 1 表示,伪图像用 0 表示)。 然后,我们获得分类误差,并使用它来迭代训练判别器和生成器。 判别器的目的是使分类误差最小,而发生器的目的是使分类误差最大化。
W
wizardforcel 已提交
20

W
wizardforcel 已提交
21
从理论上讲,生成器和判别器达到平衡,其中生成器已捕获了生成的伪图像中真实图像的所有特征,而从进一步的训练中没有任何收获。 类似地,鉴别者只能以 50% 的概率猜测图像是伪造的还是真实的,因为这两个图像就其性质而言完全无法区分。 在那个状态下,GAN 已经收敛。 然而,实际上,这种状态很难实现。 在本章中,我们将探讨 GAN 的概念以及 PyTorch 中各种 GAN 的实现。
W
wizardforcel 已提交
22 23 24 25

在本章中,我们将介绍以下食谱:

*   创建一个 DCGAN 生成器
W
wizardforcel 已提交
26
*   创建 DCGAN 判别器
W
wizardforcel 已提交
27 28 29 30 31 32 33 34 35 36
*   训练 DCGAN 模型
*   可视化 DCGAN 结果
*   使用 PyTorch 集线器运行 PGGAN

# 技术要求

强烈建议,对于本章中实现的配方,代码应在具有 NVIDIA GPU 且启用了 CUDA 和 CUDNN 的计算机上运行,​​因为本章中的配方需要大量计算。

# 创建一个 DCGAN 生成器

W
wizardforcel 已提交
37
在本食谱及其后续食谱中,我们将实现 DCGAN。 DCGAN 代表“深度卷积 GAN”; 与香草 GAN 相比,它们有很大的改进。 在 DCGAN 中,我们使用卷积神经网络,而不是香草 GAN 中的全连接网络。 在第 3 章,“用于计算机视觉的卷积神经网络”中,我们看到了第 2 章“处理神经网络”中全连接分类器是什么样,是该领域的一项改进; DCGAN 与香草 GAN 的情况相同。 在 DCGAN 中,我们将使用批量归一化,这是一种技术,它可以将作为输入输入到下一层的层的输出归一化。 批量归一化允许网络的每一层独立于其他层进行学习,从而减少了协变量偏移。
W
wizardforcel 已提交
38

W
wizardforcel 已提交
39
批量归一化是通过缩放来实现的,以使平均值为 0,方差为 1。在此配方中,我们将生成类似于 MNIST 数据集的手写数字,其中包含来自噪声向量的数据。 我们将扩展此噪声向量,将其转换为 2D 矩阵,最后将其转换为`28 x 28`黑白图像。 为了增加高度和宽度,我们必须执行与卷积操作相反的操作,这称为反卷积。 我们将在使用卷积执行分类任务时执行此操作。 在执行反卷积时,我们将增加高度和宽度,同时减少通道数。
W
wizardforcel 已提交
40 41 42 43 44

以下是我们的 DCGAN 生成器的架构图:

![](img/89189246-b3a7-49dc-acc5-78bc00c5f9b7.png)

W
wizardforcel 已提交
45
请注意,我们将使用第 3 章,“用于计算机视觉的卷积神经网络”中的概念,因此再次阅读这些食谱将非常有用。
W
wizardforcel 已提交
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116

# 怎么做...

在此配方中,我们将实现 GAN 网络的生成器端:

1.我们将从进口开始:

```py
>import torch
>>import torch.nn as nn
>>import torchvision.transforms as transforms
```

2.然后,我们将定义转换:

```py
>>transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5, ), (0.5, )),
])
```

3.然后,我们将提供该设备:

```py
>>device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
```

3.现在,我们将定义生成器类:

```py
>>class Generator_model(nn.Module):
    def __init__(self, z_dim):
         super().__init__()
```

4.然后,我们将定义生成器的单位:

```py
 self.fc = nn.Linear(z_dim, 256 * 7 * 7)
 self.gen = nn.Sequential(
            nn.ConvTranspose2d(256, 128, 4, 2, 1),
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.01),
            nn.ConvTranspose2d(128, 64, 3, 1, 1),
            nn.BatchNorm2d(64),
            nn.LeakyReLU(0.01),
            nn.ConvTranspose2d(64, 1, 4, 2, 1),
            nn.Tanh()
        )
```

5.现在我们将定义 forward 方法:

```py
def forward(self, input):
    x = self.fc(input)
    x = x.view(-1, 256, 7, 7)
    return self.gen(x)
```

6.最后,我们将为生成器模型创建对象:

```py
>>generator = Generator_model(z_dim).to(device)
```

完成此过程后,我们已经准备好 DCGAN 发生器。

# 这个怎么运作...

W
wizardforcel 已提交
117
在此食谱中,我们进行了变换以将图像转换为张量并对其进行归一化,就像在第 3 章,“用于计算机视觉的卷积神经网络”中所做的一样。 然后,我们确定了机器上的设备:CPU 或 GPU。 然后,我们定义了从`nn.Module`类继承的`Generator_model`类,就像在所有以前的架构中所做的一样。
W
wizardforcel 已提交
118

W
wizardforcel 已提交
119
在构造函数中,我们传递了`z_dim`参数,这是我们的噪声向量大小。 然后,我们定义了一个全连接单元`self.fc`,我们将噪声向量传递给该单元,并为其提供了`256 * 7 * 7`输出。 然后,我们定义了一个称为`self.gen``nn.Sequential`单元,其中包含用于定义生成器的关键组件。 我们使用 PyTorch 中提供的`nn.ConvTranspose2d``nn.BatchNorm2d``nn.LeakyReLU`使用一组反卷积,批量规范化和激活层。 `ConvTranspose2d`接受输入通道,输出通道,内核大小,步幅和填充等参数。 `BatchNorm2d`接受上一层的要素/通道数作为其参数,而 LeakyReLU 接受负斜率的角度。
W
wizardforcel 已提交
120

W
wizardforcel 已提交
121
与 ReLU 不同,LeakyReLU 允许传递小的梯度信号以获取负值。 它使来自判别器的梯度流入发生器。 我们在输出层中使用了 tanh 激活,但是从 DCGAN 论文中我们观察到,使用有界激活可以使模型学会快速饱和并覆盖训练分布的色彩空间。 tanh 的对称性在这里可能是一个优势,因为网络应该以对称方式处理较深的颜色和较浅的颜色。
W
wizardforcel 已提交
122

W
wizardforcel 已提交
123
让我们看一下`forward`方法的工作方式。 `z_dim`维度的输入噪声向量经过全连接层以提供 12544 输出。 然后,我们将 12544 输出调整为`256 x 7 x 7`,其中 256 是通道数。 `256 x 7 x 7`张量然后通过反卷积层以提供`128 x 14 x 14`输出,然后通过具有 128 个特征和泄漏 ReLU 的 Batchnorm 层。 `128 x 14 x 14`然后在第二次反卷积中转换为`64 x 14 x 14`张量,在第三次反卷积中变为`1 x 28 x 28`张量; 这些只是我们需要的尺寸。 然后,我们创建生成器对象并将其移动到设备。
W
wizardforcel 已提交
124 125 126

# 也可以看看

W
wizardforcel 已提交
127
您可以通过[这个页面](https://arxiv.org/pdf/1511.06434.pdf)了解更多有关 DCGAN 的信息。
W
wizardforcel 已提交
128

W
wizardforcel 已提交
129
# 创建 DCGAN 判别器
W
wizardforcel 已提交
130

W
wizardforcel 已提交
131
在本食谱中,我们将探讨 GAN 网络的鉴别方。 基本上,判别器是在两个类别之间进行分类的分类器,即根据给定图像是来自数据集的真实图像还是由生成器网络生成的伪图像。 正是基于来自判别器网络的反馈,生成器学会了创建更好的图像,以试图使判别器误以为来自生成器的图像是真实的。 现在,在 DCGAN 中,将使用卷积神经网络构建判别器。
W
wizardforcel 已提交
132

W
wizardforcel 已提交
133
以下是我们的判别器的架构图:
W
wizardforcel 已提交
134 135 136 137 138

![](img/af31d30e-00c9-47e1-a24a-89194da6033f.png)

# 准备好

W
wizardforcel 已提交
139
在本食谱中,我们将严重依赖第 3 章,“用于计算机视觉的卷积神经网络”的食谱,因此最好快速浏览 第 3 章,“用于计算机视觉的卷积神经网络”。
W
wizardforcel 已提交
140 141 142

# 怎么做...

W
wizardforcel 已提交
143
在此配方中,我们将构建 GAN 的判别器端:
W
wizardforcel 已提交
144 145 146 147 148 149 150

1.我们将从进口开始:

```py
>>import torch.nn.functional as F
```

W
wizardforcel 已提交
151
2.然后,我们将定义判别器类:
W
wizardforcel 已提交
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173

```py
>>class Discriminator_model(nn.Module):
    def __init__(self):
        super().__init__()
```

3.接下来,我们定义鉴别单位:

```py
self.disc = nn.Sequential(
            nn.Conv2d(1, 32, 3, 2, 1),
            nn.LeakyReLU(0.01),
            nn.Conv2d(32, 64, 3, 2, 1),
            nn.BatchNorm2d(64),
            nn.LeakyReLU(0.01),
            nn.Conv2d(64, 128, 3, 2, 1),
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.01)
        )
```

W
wizardforcel 已提交
174
4.然后定义最后一个全连接层:
W
wizardforcel 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193

```py
self.fc = nn.Linear(2048, 1)
```

5.然后定义`forward()`方法:

```py
def forward(self, input):
        x = self.disc(input)
        return F.sigmoid(self.fc(x.view(-1, 2048)))
```

6.然后我们创建鉴别对象:

```py
>>discriminator = Discriminator_model().to(device)
```

W
wizardforcel 已提交
194
现在我们已经准备好判别器。
W
wizardforcel 已提交
195 196 197

# 这个怎么运作...

W
wizardforcel 已提交
198
在本食谱中,我们定义了一个分类器; 使用`nn.Sequential()`定义卷积,激活和批量规范化单元的数组; 并且还定义了最后一个全连接层,该层采用平坦的张量并给出通过 Sigmoid层的单个输出。 由于只有两个类,因此我们最后使用了 Sigmoid层。 输入是尺寸为`1 x 28 x 28`的图像张量,并经过第一卷积单元以给出尺寸为`32 x 14 x 14`的输出张量。 第二个卷积层使它成为`64 x 7 x 7`张量,然后从那里变成`128 x 4 x 4`。 之后,我们将拉平并使张量穿过全连接层。
W
wizardforcel 已提交
199 200 201

# 也可以看看

W
wizardforcel 已提交
202
您可以在[这个页面](https://arxiv.org/pdf/1511.06434.pdf)上了解有关 DCGAN 的信息。
W
wizardforcel 已提交
203 204 205

# 训练 DCGAN 模型

W
wizardforcel 已提交
206
我们在前两个配方中定义了生成器,即“创建 DCGan 生成器”和“创建 DCGAN 判别器”。 在本食谱中,我们将继续训练 GAN 模型。 请记住,生成器的目标是创建与数据集尽可能相似的图像,而判别器的目标是区分真实图像和生成的图像。 从理论上讲,生成器将捕获数据集中图像的所有特征,并且无法学习更多信息,而判别器只能猜测图像是真实的还是生成的。 在本食谱中,我们将通过整合到目前为止已经创建的生成器和判别器来完成 DCGANs 模型的训练。
W
wizardforcel 已提交
207 208 209 210 211 212 213 214 215 216 217 218 219

# 准备好

我们将使用`torchsummary`库来查看我们的模型层,它们的输出形状和它们的参数。 为此,我们将使用以下命令安装该库:

```py
pip install torchsummary
```

准备好此安装​​后,我们将继续进行下一步。

# 怎么做...

W
wizardforcel 已提交
220
在此食谱中,我们将完成 GAN 训练:
W
wizardforcel 已提交
221 222 223 224 225 226 227 228 229

1.  一,进口:

```py
>>from torchsummary import summary
>>import torch.optim as optim
>>import torchvision.utils as vutils
```

W
wizardforcel 已提交
230
2.  然后我们初始化生成器和判别器的权重:
W
wizardforcel 已提交
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277

```py
>>def weights_init(m):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        nn.init.normal_(m.weight.data, 0.0, 0.02)
    elif classname.find('BatchNorm') != -1:
        nn.init.normal_(m.weight.data, 1.0, 0.02)
        nn.init.constant_(m.bias.data, 0)
>>generator.apply(weights_init)
>>discriminator.apply(weights_init)
```

3.  然后我们打印生成器的摘要:

```py
>>summary(generator, (100, ))
```

这给我们以下输出:

```py
----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Linear-1                [-1, 12544]       1,266,944
   ConvTranspose2d-2          [-1, 128, 14, 14]         524,416
       BatchNorm2d-3          [-1, 128, 14, 14]             256
         LeakyReLU-4          [-1, 128, 14, 14]               0
   ConvTranspose2d-5           [-1, 64, 14, 14]          73,792
       BatchNorm2d-6           [-1, 64, 14, 14]             128
         LeakyReLU-7           [-1, 64, 14, 14]               0
   ConvTranspose2d-8            [-1, 1, 28, 28]           1,025
              Tanh-9            [-1, 1, 28, 28]               0
================================================================
Total params: 1,866,561
Trainable params: 1,866,561
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.97
Params size (MB): 7.12
Estimated Total Size (MB): 8.09
----------------------------------------------------------------

```

W
wizardforcel 已提交
278
4.  然后,我们将打印判别器摘要:
W
wizardforcel 已提交
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345

```py
>>summary(discriminator, (1, 28, 28))
```

这给我们以下输出:

```py
----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv2d-1           [-1, 32, 14, 14]             320
         LeakyReLU-2           [-1, 32, 14, 14]               0
            Conv2d-3             [-1, 64, 7, 7]          18,496
       BatchNorm2d-4             [-1, 64, 7, 7]             128
         LeakyReLU-5             [-1, 64, 7, 7]               0
            Conv2d-6            [-1, 128, 4, 4]          73,856
       BatchNorm2d-7            [-1, 128, 4, 4]             256
         LeakyReLU-8            [-1, 128, 4, 4]               0
            Linear-9                    [-1, 1]           2,049
================================================================
Total params: 95,105
Trainable params: 95,105
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.21
Params size (MB): 0.36
Estimated Total Size (MB): 0.58
----------------------------------------------------------------
```

5.  接下来,我们将定义损失函数:

```py
>>criterion = nn.BCELoss()
```

6.  我们还将创建一个固定的噪声:

```py
>>fixed_noise = torch.randn(64, z_dim, device=device)
```

7.  现在,我们将定义优化器功能:

```py
>>doptimizer = optim.Adam(discriminator.parameters())
>>goptimizer = optim.Adam(generator.parameters())
```

8.  然后,我们将为鉴别符设置标签:

```py
>>real_label, fake_label = 1, 0
```

9.  我们还将准备存储我们训练中的指标:

```py
>>image_list = []
>>g_losses = []
>>d_losses = []
>>iterations = 0
>>num_epochs = 50
```

W
wizardforcel 已提交
346
0.  现在,我们开始训练循环:
W
wizardforcel 已提交
347 348 349 350 351

```py
>>for epoch in range(num_epochs):
```

W
wizardforcel 已提交
352
1.  然后我们遍历数据:
W
wizardforcel 已提交
353 354 355 356 357 358

```py
print(f'Epoch : | {epoch+1:03} / {num_epochs:03} |')
for i, data in enumerate(train_loader):
```

W
wizardforcel 已提交
359
2.  然后,我们通过清除梯度来开始训练判别器:
W
wizardforcel 已提交
360 361 362 363 364

```py
discriminator.zero_grad()
```

W
wizardforcel 已提交
365
3.  然后我们获取图像:
W
wizardforcel 已提交
366 367 368 369 370 371

```py
real_images = data[0].to(device)
size = real_images.size(0)
```

W
wizardforcel 已提交
372
4.  然后,我们为这些图像创建标签:
W
wizardforcel 已提交
373 374 375 376 377

```py
label = torch.full((size,), real_label, device=device)
```

W
wizardforcel 已提交
378
5.  接下来,我们得到判别器输出:
W
wizardforcel 已提交
379 380 381 382 383

```py
d_output = discriminator(real_images).view(-1)
```

W
wizardforcel 已提交
384
6.  然后我们计算判别器误差:
W
wizardforcel 已提交
385 386 387 388 389

```py
derror_real = criterion(d_output, label)
```

W
wizardforcel 已提交
390
7.  接下来,我们计算梯度:
W
wizardforcel 已提交
391 392 393 394 395

```py
derror_real.backward()
```

W
wizardforcel 已提交
396
8.  现在我们将创建一个噪声向量:
W
wizardforcel 已提交
397 398 399 400 401

```py
noise = torch.randn(size, z_dim, device=device)
```

W
wizardforcel 已提交
402
9.  接下来,我们将噪声向量传递给生成器:
W
wizardforcel 已提交
403 404 405 406 407

```py
fake_images = generator(noise)
```

W
wizardforcel 已提交
408
0.  然后,我们为生成的图像创建标签:
W
wizardforcel 已提交
409 410 411 412 413

```py
label.fill_(0)
```

W
wizardforcel 已提交
414
1.  然后我们将它们传递给判别器:
W
wizardforcel 已提交
415 416 417 418 419

```py
d_output = discriminator(fake_images.detach()).view(-1)
```

W
wizardforcel 已提交
420
2.  接下来,我们得到误差和梯度:
W
wizardforcel 已提交
421 422 423 424 425 426 427

```py
derror_fake = criterion(d_output, label)
derror_fake.backward()
derror_total = derror_real + derror_fake
```

W
wizardforcel 已提交
428
3.  然后,我们更新判别器权重:
W
wizardforcel 已提交
429 430 431 432 433

```py
doptimizer.step()
```

W
wizardforcel 已提交
434
4.  我们通过清除梯度开始训练生成器:
W
wizardforcel 已提交
435 436 437 438 439

```py
generator.zero_grad()
```

W
wizardforcel 已提交
440
5.  然后,我们将标签从假更改为真实:
W
wizardforcel 已提交
441 442 443 444 445

```py
label.fill_(1)
```

W
wizardforcel 已提交
446
6.  接下来,我们得到判别器输出:
W
wizardforcel 已提交
447 448 449 450 451

```py
d_output = discriminator(fake_images).view(-1)
```

W
wizardforcel 已提交
452
7.  然后我们计算生成器损耗和梯度并更新生成器权重:
W
wizardforcel 已提交
453 454 455 456 457 458 459

```py
gerror = criterion(d_output, label)
gerror.backward()
goptimizer.step()
```

W
wizardforcel 已提交
460
8.  然后我们节省了损失:
W
wizardforcel 已提交
461 462 463 464 465 466 467 468

```py
if i % 50 == 0:
    print(f'| {i:03} / {len(train_loader):03} | G Loss: {gerror.item():.3f} | D Loss: {derror_total.item():.3f} |')
    g_losses.append(gerror.item())
    d_losses.append(derror_total.item())
```

W
wizardforcel 已提交
469
9.  然后,我们从固定噪声中保存图像:
W
wizardforcel 已提交
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539

```py
if (iterations % 500 == 0) or ((epoch == num_epochs-1) and (i == len(train_loader)-1)):
    with torch.no_grad():
        fake_images = generator(fixed_noise).detach().cpu()
        image_list.append(vutils.make_grid(fake_images, padding=2, normalize=True))

iterations += 1
```

以下是示例输出:

```py
Epoch : | 001 / 050 |
| 000 / 469 | G Loss: 1.939 | D Loss: 1.432 |
| 050 / 469 | G Loss: 3.920 | D Loss: 0.266 |
| 100 / 469 | G Loss: 3.900 | D Loss: 0.406 |
| 150 / 469 | G Loss: 3.260 | D Loss: 0.230 |
| 200 / 469 | G Loss: 3.856 | D Loss: 0.556 |
| 250 / 469 | G Loss: 4.097 | D Loss: 0.123 |
| 300 / 469 | G Loss: 2.377 | D Loss: 0.416 |
| 350 / 469 | G Loss: 2.984 | D Loss: 0.416 |
| 400 / 469 | G Loss: 3.262 | D Loss: 0.140 |
| 450 / 469 | G Loss: 3.469 | D Loss: 0.849 |
Epoch : | 002 / 050 |
| 000 / 469 | G Loss: 2.057 | D Loss: 0.484 |
| 050 / 469 | G Loss: 2.108 | D Loss: 0.435 |
| 100 / 469 | G Loss: 1.714 | D Loss: 0.862 |
| 150 / 469 | G Loss: 3.902 | D Loss: 0.199 |
| 200 / 469 | G Loss: 3.869 | D Loss: 0.086 |
| 250 / 469 | G Loss: 2.390 | D Loss: 0.208 |
| 300 / 469 | G Loss: 3.008 | D Loss: 0.586 |
| 350 / 469 | G Loss: 4.662 | D Loss: 0.074 |
| 400 / 469 | G Loss: 3.353 | D Loss: 0.368 |
| 450 / 469 | G Loss: 5.080 | D Loss: 0.110 |
Epoch : | 003 / 050 |
| 000 / 469 | G Loss: 7.159 | D Loss: 0.008 |
| 050 / 469 | G Loss: 5.087 | D Loss: 0.056 |
| 100 / 469 | G Loss: 4.232 | D Loss: 0.184 |
| 150 / 469 | G Loss: 5.037 | D Loss: 0.141 |
| 200 / 469 | G Loss: 5.636 | D Loss: 0.570 |
| 250 / 469 | G Loss: 3.624 | D Loss: 0.304 |
| 300 / 469 | G Loss: 4.291 | D Loss: 0.214 |
| 350 / 469 | G Loss: 2.901 | D Loss: 0.247 |
| 400 / 469 | G Loss: 3.703 | D Loss: 0.643 |
| 450 / 469 | G Loss: 1.149 | D Loss: 1.035 |
Epoch : | 004 / 050 |
| 000 / 469 | G Loss: 3.317 | D Loss: 0.202 |
| 050 / 469 | G Loss: 2.990 | D Loss: 0.350 |
| 100 / 469 | G Loss: 2.680 | D Loss: 0.162 |
| 150 / 469 | G Loss: 2.934 | D Loss: 0.391 |
| 200 / 469 | G Loss: 3.736 | D Loss: 0.215 |
| 250 / 469 | G Loss: 3.601 | D Loss: 0.199 |
| 300 / 469 | G Loss: 4.288 | D Loss: 0.164 |
| 350 / 469 | G Loss: 2.978 | D Loss: 0.086 |
| 400 / 469 | G Loss: 3.827 | D Loss: 0.189 |
| 450 / 469 | G Loss: 4.283 | D Loss: 0.216 |
Epoch : | 005 / 050 |
| 000 / 469 | G Loss: 4.456 | D Loss: 0.250 |
| 050 / 469 | G Loss: 4.886 | D Loss: 0.160 |
| 100 / 469 | G Loss: 1.844 | D Loss: 0.447 |
| 150 / 469 | G Loss: 3.680 | D Loss: 0.505 |
| 200 / 469 | G Loss: 4.428 | D Loss: 0.200 |
| 250 / 469 | G Loss: 4.270 | D Loss: 0.222 |
| 300 / 469 | G Loss: 4.617 | D Loss: 0.102 |
| 350 / 469 | G Loss: 3.920 | D Loss: 0.092 |
| 400 / 469 | G Loss: 4.010 | D Loss: 0.392 |
| 450 / 469 | G Loss: 1.705 | D Loss: 0.651 |
```

W
wizardforcel 已提交
540
至此,我们已经完成了 DCGAN 的训练。
W
wizardforcel 已提交
541 542 543

# 这个怎么运作...

W
wizardforcel 已提交
544
我们从`weights_init`函数开始,该函数用于从均值 0 和标准差 0.02 的正态分布中随机初始化所有权重。 初始化模型后,函数将模型作为输入,并重新初始化所有卷积,卷积转置和批归一化层。
W
wizardforcel 已提交
545 546 547

然后,我们使用`torchsummary`库打印模型的摘要; 为此,我们将模型与输入维一起传递。 方便地查看我们所有的输出尺寸是否正确,并检查每一层中参数的数量和大小。 接下来,我们定义损失函数,并使用二进制交叉熵损失,因为只有一个输出具有两个可能的状态,分别表示图像是真实的 1 还是伪造的 0。

W
wizardforcel 已提交
548
我们还创建了固定噪声,用于可视化 GAN 模型在迭代过程中的改进。 我们使用 ADAM 优化器分别更新了生成器和判别器`goptimizer`和`doptimizer`的权重。 然后,我们进行了准备以存储一些模型指标,以查看模型在迭代过程中的变化,然后开始训练循环。
W
wizardforcel 已提交
549

W
wizardforcel 已提交
550
我们遍历了每个小批量并开始训练判别器。 我们仅从 MNIST 数据集中获取图像,然后使用`real_images = data[0].to(device)`将其移动到设备中; 由于图像全部来自 MNIST 数据集,因此我们知道它们是真实的,因此我们创建了与小批量相同大小的标签向量,并用真实图像标签 1 进行填充。然后,将这些真实图像传递到 预测的鉴别符,然后使用此预测从准则中得出误差`derror_real`并计算梯度。 然后,我们创建了相等数量的噪声向量,并将它们传递给生成器以生成图像,然后将这些生成的图像传递给判别器以获取预测,然后从准则`derror_fake`中获取误差。 然后,我们再次进行此操作以计算和累积梯度。 然后,我们从真实图像和伪图像中获得了误差之和,以得出总的判别器误差,并更新判别器的权重。
W
wizardforcel 已提交
551

W
wizardforcel 已提交
552
然后,我们开始训练生成器,并且生成器应该能够欺骗判别器。 生成器必须纠正鉴别者正确预测生成的图像为假的情况。 因此,只要来自判别器的预测将生成的图像标记为伪造,就会增加生成器损耗`gerror`。 然后,我们计算梯度并更新发生器权重。
W
wizardforcel 已提交
553 554 555 556 557

然后,我们定期显示模型指标,并保存固定噪声生成器生成的图像,以可视化模型在各个时期的性能。

# 还有更多...

W
wizardforcel 已提交
558
您可以利用网络的超参数发挥更多的作用-例如,您可以对判别器优化器使用与生成器不同的学习率,或者对判别器的每次更新训练生成器两次或三次。
W
wizardforcel 已提交
559 560 561

# 也可以看看

W
wizardforcel 已提交
562
您可以在 [这个页面](https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html)和[这个页面](https://iq.opengenus.org/deep-convolutional-gans-pytorch/)上看到训练和架构 DCGAN 的另一个示例。
W
wizardforcel 已提交
563 564 565

# 可视化 DCGAN 结果

W
wizardforcel 已提交
566
以前我们已经看到,使用 GAN,生成器和判别器相互竞争,并且这样做可以产生越来越好的图像。 但是,从理论上讲,它们达到了生成器已捕获真实图像的所有特征的程度,并且生成器无法学习。 同样,鉴别者只能猜测给定图像是真实的还是伪造的,成功机会为 50/50。 在这一点上,据说 GAN 已经收敛。
W
wizardforcel 已提交
567

W
wizardforcel 已提交
568
现在,任何方面的改善都会导致另一面的结果降低,这是**零和**状态或**纳什平衡**; 但是在实践中很难实现,因为生成器和判别器都在不断变化,因此检查 GAN 性能的最佳方法是通过图形和图表。 在本食谱中,我们将快速了解可视化。
W
wizardforcel 已提交
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598

# 准备好

对于此食谱,您必须具有`matplotlib`和`numpy`库,可以使用`pip`如下安装它们:

```py
pip install matplotlib
pip install numpy
```

安装这些工具后,我们将继续进行操作。

# 怎么做...

在此配方中,我们将快速绘制 GAN 中的图形和图像:

1.  我们将从进口开始:

```py
>>import matplotlib.pyplot as plt
>>import numpy as np
```

2.  然后,我们将添加图的大小和标题:

```py
>>plt.figure(figsize=(10,5))
>>plt.title("Generator and Discriminator Loss During Training")
```

W
wizardforcel 已提交
599
3.  接下来,我们添加生成器和判别器损耗:
W
wizardforcel 已提交
600 601 602 603 604 605

```py
>>plt.plot(g_losses,label="Generator")
>>plt.plot(d_losses,label="Discriminator")
```

W
wizardforcel 已提交
606
4.  然后,我们添加`x`和`y`轴标签:
W
wizardforcel 已提交
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644

```py
>>plt.xlabel("iterations")
>>plt.ylabel("Loss")
```

5.  接下来,我们为图添加图例:

```py
>>plt.legend()
```

6.  最后,我们显示该图:

```py
>>plt.show()
```

这将给出以下输出:

![](img/184af20e-79e7-4da1-b8b4-5168a57aef8d.png)

7.  然后,我们遍历`image_list`中的图像并显示它们:

```py
>>for image in image_list:
    plt.imshow(np.transpose(image,(1,2,0)))
    plt.show()
```

结果为以下输出:

![](img/3ea3ae9d-d0f9-413f-b06b-54a248081ddd.png)

在这里,我们看到了 DCGAN 生成的手写图像。

# 这个怎么运作...

W
wizardforcel 已提交
645
在本食谱中,我们使用 matplotlib 绘制图形和图像。 我们使用`figure()`和`title()`方法设置图形尺寸和标题,然后使用`plot()`方法绘制发生器和判别器损耗。 我们还使用`xlabel`和`ylabel`方法添加了`x`和`y`标签。 我们还使用`legend()`方法为图添加了图例,最后使用`show()`方法显示了图。
W
wizardforcel 已提交
646

W
wizardforcel 已提交
647
我们遍历训练期间保存在`image_list`中的图像,并使用 NumPy 的`transpose()`方法以所需顺序固定图像的尺寸。 `image_list`中的图像是使用`torchvision.util.make_grid()`方法生成的,我们根据噪声向量创建了生成图像的网格。
W
wizardforcel 已提交
648 649 650 651 652 653 654

# 还有更多...

您可以使用其他库(例如 plotly 和 seaborn)来绘制和美化图形。

# 也可以看看

W
wizardforcel 已提交
655
您可以在[这个页面](https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html#results)上看到 DCGAN 的可视化效果和动画。
W
wizardforcel 已提交
656 657 658

# 使用 PyTorch 集线器运行 PGGAN

W
wizardforcel 已提交
659
在本食谱中,我们将研究**渐进 GAN**(**PGGAN**),与 DCGAN 相比它们是高级 GAN,并且能够生成逼真的图像。 PGGAN 分多个阶段训练 GAN 网络。 它具有`z`的潜在特征,并使用两个反卷积层生成 4×4 图像。 在判别器方面,网络使用两个卷积层训练生成的`4 x 4`图像。 网络稳定后,它会在判别器中再增加两个卷积层以将图像上采样到 8 x 8,再增加两个卷积层以对图像下采样。
W
wizardforcel 已提交
660

W
wizardforcel 已提交
661
经过 9 个这样的级数后,生成器将生成`1024 x 1024`个图像。 PGGAN 的渐进式训练策略相对于常规 GAN 具有优势,因为它可以加快并稳定训练。 之所以如此,是因为大多数训练都是在较低的分辨率下进行的,而在网络达到各个阶段的稳定性之后,会逐渐发展为较高的分辨率。
W
wizardforcel 已提交
662 663 664 665 666 667 668

以下是 PGGAN 的抽象表示形式:

![](img/9cd78f4c-0241-467d-92f8-2967611136e7.png)

PGGAN 的关键创新可以总结如下:

W
wizardforcel 已提交
669
*   **高分辨率层中的逐渐增长和平滑淡化**:它从低分辨率卷积变为高分辨率卷积,而不是立即跳变分辨率,而是通过参数平滑地淡化了具有更高分辨率的 nedonew 层 alpha(α)(介于 0 和 1 之间)的大小,用于控制我们使用旧的还是放大的较大输出。
W
wizardforcel 已提交
670
*   **小批量标准差**:我们计算鉴别符的统计信息,即生成器生成或来自真实数据的小批量中所有像素的标准差。 现在,判别器需要了解,如果要评估的批量中图像的标准差较低,则该图像很可能是伪造的,因为真实数据的方差更高。 因此,生成器必须增加所生成样本的方差,以欺骗判别器。
W
wizardforcel 已提交
671 672
*   **均衡的学习率**:所有权重(`w`)被归一化(`w'`)在某个范围内,以便`w' = w / c`的常数`c`对于每一层来说都是不同的,具体取决于重量矩阵的形状。
*   **逐像素特征归一化**:对每个像素中的特征向量进行归一化,因为批量规范最适合大型微型批量,并且占用大量内存。
W
wizardforcel 已提交
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713

Nvidia 最初执行 PGGAN 的过程要花一到两个月的时间。 但是,为了让我们看到 PGGAN 的性能,我们将使用 PyTorch 集线器,它是使用 PyTorch 构建的预训练模型的存储库。

# 做好准备

为了使割炬轮毂正常工作,您需要拥有 PyTorch 版本 1.1.0 或更高版本。

# 怎么做...

在此配方中,我们将从火炬中心运行 PGGAN:

1.  首先,我们将设置进口:

```py
>>import torch
>>import matplotlib.pyplot as plt
>>import torchvision
```

2.  然后我们检查 GPU:

```py
>>use_gpu = True if torch.cuda.is_available() else False
```

3.  接下来,我们加载预训练的 PGGAN 模型:

```py
>>model = torch.hub.load('facebookresearch/pytorch_GAN_zoo:hub', 'PGAN', 
                       model_name='celebAHQ-512',
                       pretrained=True, 
                       useGPU=use_gpu)
```

4.  然后我们产生噪音:

```py
>>num_images = 5
>>noise, _ = model.buildNoiseData(num_images)
```

W
wizardforcel 已提交
714
5.  接下来,我们获得生成器输出
W
wizardforcel 已提交
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735

```py
>>with torch.no_grad():
    generated_images = model.test(noise)
```

6.  最后,我们制作图像网格并显示它:

```py
>>grid = torchvision.utils.make_grid(generated_images.clamp(min=-1, max=1), scale_each=True, normalize=True)
>>plt.imshow(grid.permute(1, 2, 0).cpu().numpy())
```

在这里,我们看到了从 PGGAN 生成的面部图像:

![](img/fbaff817-dad6-4995-b0dc-ae4d42aaf620.png)

通过前面的步骤,我们学习了如何使用 PyTorch Hub 运行 PGGAN。

# 这个怎么运作...

W
wizardforcel 已提交
736
在此配方中,我们加载了在`celebAHQ`数据集上训练的 PGGAN 预训练模型; 为此,我们使用了`torch.hub`中的`load()`方法。 然后,我们定义了创建和生成尺寸为`num_images x 512`的噪声向量所需的图像数量,因为此模型使用大小为 512 的噪声向量进行训练,所有噪声向量都由`buildNoiseData()`方法内部处理 在模型对象中可用。
W
wizardforcel 已提交
737 738 739 740 741

`model.test()`方法生成了我们用来制作网格的图像。 钳位方法将所有值限制在`min`和`max`定义的范围内。 `.cpu()`方法将生成的图像移至 CPU,我们使用`permute`固定尺寸。 最后,`plt.imshow()`显示了我们创建的网格。

# 还有更多...

W
wizardforcel 已提交
742
您可以在[这里](https://github.com/github-pengge/PyTorch-progressive_growing_of_gans)探索完整的 PGGAN 实现。
W
wizardforcel 已提交
743 744 745

# 也可以看看

W
wizardforcel 已提交
746
您可以在[这个页面](https://pytorch.org/docs/stable/hub.html)上了解有关割炬轮毂的更多信息。
W
wizardforcel 已提交
747

W
wizardforcel 已提交
748
您可以在[这里](https://github.com/tkarras/progressive_growing_of_gans)和[这里](https://research.nvidia.com/publication/2017-10_Progressive-Growing-of)。