8.md 57.8 KB
Newer Older
W
wizardforcel 已提交
1 2


W
wizardforcel 已提交
3
# 条件 GAN - 使用条件对抗网络的图像到图像翻译
W
wizardforcel 已提交
4 5 6



W
wizardforcel 已提交
7
Pix2pix 是一种**生成对抗网络****GAN**),用于图像到图像的翻译。 图像到图像转换是一种将图像的一种表示形式转换为另一种表示形式的方法。 Pix2pix 学习从输入图像到输出图像的映射。 它可用于将黑白图像转换为彩色图像,将草图转换为照片,将白天图像转换为夜间图像,将卫星图像转换为地图图像。 pix2pix 网络最早是由菲利普·伊索拉(Phillip Isola),朱俊彦,周婷慧,阿列克谢·埃弗罗斯(Alexei A. Efros)在名为《使用条件对抗网络的图像到图像转换》中引入的。 可以在[以下链接中找到](https://arxiv.org/pdf/1611.07004.pdf)
W
wizardforcel 已提交
8 9 10 11 12 13 14

在本章中,我们将介绍以下主题:

*   介绍 Pix2pix 网络
*   Pix2pix 网络的架构
*   数据收集与准备
*   Pix2pix 的 Keras 实现
W
wizardforcel 已提交
15
*   目标函数
W
wizardforcel 已提交
16
*   训练 Pix2pix
W
wizardforcel 已提交
17 18 19 20 21 22 23
*   评估训练好的模型
*   Pix2pix 网络的实际应用





W
wizardforcel 已提交
24
# Pix2pix 介绍
W
wizardforcel 已提交
25 26 27



W
wizardforcel 已提交
28
Pix2pix 是条件 GAN 的变体。 我们已经在第 3 章,“使用条件 GAN(cGAN)进行面部老化处理”中介绍。 在继续之前,请确保您了解什么是 cGAN。 一旦熟悉了 cGAN,就可以继续本章。 Pix2pix 是一种 GAN,能够使用**机器学习****ML**)的无监督方法执行图像到图像的翻译。 经过训练后,pix2pix 可以将图像从域 A 转换为域 B。朴素 CNN 也可以用于图像到图像的转换,但是它们不会生成逼真的图像。 另一方面,pix2pix 显示出巨大的潜力,能够生成逼真的图像。 我们将训练 pix2pix 将立面的标签转换为立面的图像。 让我们从了解 pix2pix 的架构开始。
W
wizardforcel 已提交
29 30 31 32 33





W
wizardforcel 已提交
34
# pix2pix 的架构
W
wizardforcel 已提交
35 36 37



W
wizardforcel 已提交
38
与其他 GAN 相似,pix2pix 由两个网络组成:生成器网络和判别器网络。 生成器网络的架构受 [U-Net](https://arxiv.org/pdf/1505.04597.pdf) 的架构的启发。 同样,判别器网络的架构也受到 [PatchGAN](https://arxiv.org/pdf/1604.04382.pdf) 架构的启发。 这两个网络都是深度卷积神经网络。 在本节中,我们将详细探讨 pix2pix。
W
wizardforcel 已提交
39 40 41 42 43





W
wizardforcel 已提交
44
# 生成器网络
W
wizardforcel 已提交
45 46 47



W
wizardforcel 已提交
48
正如我们在上一节中提到的,生成器网络在很大程度上受到 U-Net 架构的启发。 U-Net 的架构与自编码器网络的架构几乎相同。 它们之间的主要区别在于,U-Net 网络在编码器中的各层之间具有跳跃连接,并且生成器网络和自编码器的解码器部分不具有跳跃连接。 U-Net 网络由两个网络组成:编码器网络和解码器网络。 下图从基本层面说明了 U-Net 的架构:
W
wizardforcel 已提交
49 50 51

![](img/ba22d993-cf32-42a1-86b9-94c2b37311e8.png)

W
wizardforcel 已提交
52
上图应使您了解 U-Net 的架构。 如您所见,第一层的输出直接与最后一层合并。 第二层的输出与第二层的第二层合并,依此类推。 如果`n`是层的总数,则在编码器网络的第`i`层与解码器网络的第`n - i`层之间存在跳跃连接。 第`i`层可以是这些层中的任何层。 让我们一个一个地仔细研究两个网络。
W
wizardforcel 已提交
53 54 55 56 57 58 59 60 61





# 编码器网络



W
wizardforcel 已提交
62
编码器网络是生成器网络的初始网络,包含八个**卷积块**,其配置如下:
W
wizardforcel 已提交
63

W
wizardforcel 已提交
64
| **层名称** | **超参数** | **输入形状** | **输出形状** |
W
wizardforcel 已提交
65 66 67 68
| --- | --- | --- | --- |
| 第一个 2D 卷积层 | `filter= 64, kernel_size = 4, stride= 2, padding ='same'` | `(256, 256, 1)` | `(128, 128, 64)` |
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(128, 128, 64)` | `(128, 128, 64)` |
| 第二个 2D 卷积层 | `filter= 128, kernel_size = 4, stride= 2, padding ="same"` | `(128, 128, 64)` | `(64, 64, 128)` |
W
wizardforcel 已提交
69
| 批量规范化层 | 没有 | `(64, 64, 128)` | `(64, 64, 128)` |
W
wizardforcel 已提交
70 71
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(64, 64, 128)` | `(64, 64, 128)` |
| 第三个 2D 卷积层 | `filter= 256, kernel_size = 4, stride= 2, padding ='same'` | `(64, 64, 128)` | `(32, 32, 256)` |
W
wizardforcel 已提交
72
| 批量规范化层 | 没有 | `(32, 32, 256)` | `(32, 32, 256)` |
W
wizardforcel 已提交
73 74
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(32, 32, 256)` | `(32, 32, 256)` |
| 第四个 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 2, padding ="same"` | `(32, 32, 256)` | `(16, 16, 512)` |
W
wizardforcel 已提交
75
| 批量规范化层 | 没有 | `(16, 16, 512)` | `(16, 16, 512)` |
W
wizardforcel 已提交
76 77
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(16, 16, 512)` | `(16, 16, 512)` |
| 第五个 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 2, padding ="same"` | `(16, 16, 512)` | `(8, 8, 512)` |
W
wizardforcel 已提交
78
| 批量规范化层 | 没有 | `(8, 8, 512)` | `(8, 8, 512)` |
W
wizardforcel 已提交
79 80
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(8, 8, 512)` | `(8, 8, 512)` |
| 第六个 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 2, padding ="same"` | `(8, 8, 512)` | `(4, 4, 512)` |
W
wizardforcel 已提交
81
| 批量规范化层 | 没有 | `(4, 4, 512)` | `(4, 4, 512)` |
W
wizardforcel 已提交
82 83
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(4, 4, 512)` | `(4, 4, 512)` |
| 第七个 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 2, padding ="same"` | `(4, 4, 512)` | `(2, 2, 512)` |
W
wizardforcel 已提交
84
| 批量规范化层 | 没有 | `(2, 2, 512)` | `(2, 2, 512)` |
W
wizardforcel 已提交
85 86
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(2, 2, 512)` | `(2, 2, 512)` |
| 第八个 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 2, padding ="same"` | `(2, 2, 512)` | `(1, 1, 512)` |
W
wizardforcel 已提交
87
| 批量规范化层 | 没有 | `(1, 1, 512)` | `(1, 1, 512)` |
W
wizardforcel 已提交
88
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(1, 1, 512)` | `(1, 1, 512)` |
W
wizardforcel 已提交
89

W
wizardforcel 已提交
90
编码器网络之后是解码器网络。 我们将在下一节中了解解码器网络的架构。
W
wizardforcel 已提交
91 92 93 94 95 96 97 98 99





# 解码器网络



W
wizardforcel 已提交
100
生成器网络中的解码器网络还包括八个**上采样卷积块**。 八个上采样卷积块的配置如下:
W
wizardforcel 已提交
101

W
wizardforcel 已提交
102
| **层名称** | **超参数** | **输入形状** | **输出形状** |
W
wizardforcel 已提交
103 104 105
| --- | --- | --- | --- |
| 第一个 2D 上采样层 | `size=(2, 2)` | `(1, 1, 512)` | `(2, 2, 512)` |
| 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 1, padding="same"` | `(2, 2, 512)` | `(2, 2, 512)` |
W
wizardforcel 已提交
106
| 批量规范化层 | 没有 | `(2, 2, 512)` | `(2, 2, 512)` |
W
wizardforcel 已提交
107
| 丢弃层 | 丢弃= 0.5 | `(2, 2, 512)` | `(2, 2, 512)` |
W
wizardforcel 已提交
108 109 110 111
| 级联层(编码器网络中的第 7 个卷积层) | `axis=3` | `(2, 2, 512)` | `(2, 2, 1024)` |
| 激活层 | `activation="relu"` | `(2, 2, 1024)` | `(2, 2, 1024)` |
| 第二个 2D 上采样层 | `size=(2, 2)` | `(2, 2, 1024)` | `(4, 4, 1024)` |
| 2D 卷积层 | `filter= 1024, kernel_size = 4, stride= 1, padding="same"` | `(4, 4, 1024)` | `(4, 4, 1024)` |
W
wizardforcel 已提交
112
| 批量规范化层 | 没有 | `(4, 4, 1024)` | `(4, 4, 1024)` |
W
wizardforcel 已提交
113
| 丢弃层 | 丢弃= 0.5 | `(4, 4, 1024)` | `(4, 4, 1024)` |
W
wizardforcel 已提交
114 115 116 117
| 级联层(编码器网络中的第 6 个卷积层) | `axis=3` | `(4, 4, 1024)` | `(4, 4, 1536)` |
| 激活层 | `activation="relu"` | `(4, 4, 1536)` | `(4, 4, 1536)` |
| 第三个 2D 上采样层 | `size=(2, 2)` | `(4, 4, 1536)` | `(8, 8, 1536)` |
| 2D 卷积层 | `filter= 1024, kernel_size = 4, stride= 1, padding="same"` | `(8, 8, 1536)` | `(8, 8, 1024)` |
W
wizardforcel 已提交
118
| 批量规范化层 | 没有 | `(8, 8, 1024)` | `(8, 8, 1024)` |
W
wizardforcel 已提交
119
| 丢弃层 | 丢弃= 0.5 | `(8, 8, 1024)` | `(8, 8, 1024)` |
W
wizardforcel 已提交
120 121 122 123
| 级联层(编码器网络中的第 5 个卷积层) | `axis=3` | `(8, 8, 1024)` | `(8, 8, 1536)` |
| 激活层 | `activation="relu"` | `(8, 8, 1536)` | `(8, 8, 1536)` |
| 第四个 2D 上采样层 | `size=(2, 2)` | `(8, 8, 1536)` | `(16, 16, 1536)` |
| 2D 卷积层 | `filter= 1024, kernel_size = 4, stride= 1, padding="same"` | `(16, 16, 1536)` | `(16, 16, 1024)` |
W
wizardforcel 已提交
124
| 批量规范化层 | 没有 | `(16, 16, 1024)` | `(16, 16, 1024)` |
W
wizardforcel 已提交
125 126 127 128
| 连接层(编码器网络的第 4 个卷积层) | `axis=3` | `(16, 16, 1024)` | `(16, 16, 1536)` |
| 激活层 | `activation="relu"` | `(16, 16, 1536)` | `(16, 16, 1536)` |
| 第五个 2D 上采样层 | `size=(2, 2)` | `(16, 16, 1536)` | `(32, 32, 1536)` |
| 2D 卷积层 | `filter= 1024, kernel_size = 4, stride= 1, padding="same"` | `(32, 32, 1536)` | `(32, 32, 1024)` |
W
wizardforcel 已提交
129
| 批量规范化层 | 没有 | `(32, 32, 1024)` | `(32, 32, 1024)` |
W
wizardforcel 已提交
130 131 132 133
| 连接层(编码器网络的第 3 个卷积层) | `axis=3` | `(32, 32, 1024)` | `(32, 32, 1280)` |
| 激活层 | `activation="relu"` | `(32, 32, 1280)` | `(32, 32, 1280)` |
| 第六个 2D 上采样层 | `size=(2, 2)` | `(64, 64, 1280)` | `(64, 64, 1280)` |
| 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 1, padding="same"` | `(64, 64, 1280)` | `(64, 64, 512)` |
W
wizardforcel 已提交
134
| 批量规范化层 | 没有 | `(64, 64, 512)` | `(64, 64, 512)` |
W
wizardforcel 已提交
135 136 137 138
| 连接层(编码器网络中的第二个卷积层) | `axis=3` | `(64, 64, 512)` | `(64, 64, 640)` |
| 激活层 | `activation="relu"` | `(64, 64, 640)` | `(64, 64, 640)` |
| 第七个 2D 上采样层 | `size=(2, 2)` | `(64, 64, 640)` | `(128, 128, 640)` |
| 2D 卷积层 | `filter= 256, kernel_size = 4, stride= 1, padding="same"` | `(128, 128, 640)` | `(128, 128, 256)` |
W
wizardforcel 已提交
139
| 批量规范化层 | 没有 | `(128, 128, 256)` | `(128, 128, 256)` |
W
wizardforcel 已提交
140 141 142 143 144
| 连接层(编码器网络中的第一个卷积层) | `axis=3` | `(128, 128, 256)` | `(128, 128, 320)` |
| 激活层 | `activation="relu"` | `(128, 128, 320)` | `(128, 128, 320)` |
| 第八个 2D 上采样层 | `size=(2, 2)` | `(128, 128, 320)` | `(256, 256, 320)` |
| 2D 卷积层 | `filter= 1, kernel_size = 4, stride= 1, padding="same"` | `(256, 256, 320)` | `(256, 256, 1)` |
| 激活层 | `activation="tanh"` | `(256, 256, 1)` | `(256, 256, 1)` |
W
wizardforcel 已提交
145

W
wizardforcel 已提交
146
生成器网络具有**七个跳跃连接**,可以将其定义如下:
W
wizardforcel 已提交
147

W
wizardforcel 已提交
148 149 150 151 152 153 154
*   从第 1 个编码器模块到第 7 个解码器模块的输出的连接
*   从第 2 个编码器模块到第 6 个解码器模块的输出的连接
*   从第 3 个编码器模块到第 5 个解码器模块的输出的连接
*   从第 4 个编码器模块到第 4 个解码器模块的输出的连接
*   从第 5 个编码器模块到第 3 个解码器模块的输出的连接
*   从第 6 个编码器模块到第 2 个解码器模块的输出的连接
*   从第 7 个编码器模块到第 1 个解码器模块的输出的连接
W
wizardforcel 已提交
155

W
wizardforcel 已提交
156
连接沿着通道轴发生。 编码器网络的最后一层将张量传递到解码器网络的第一层。 在编码器网络的最后一块和解码器网络的最后一块没有连接。
W
wizardforcel 已提交
157

W
wizardforcel 已提交
158
生成器网络由这两个网络组成。 基本上,编码器网络是下采样器,而解码器网络是上采样器。 编码器网络将尺寸为`(256, 256, 1)`的图像采样为尺寸为`(1, 1, 1, 512)`的内部表示。 另一方面,解码器网络将尺寸为`(1, 1, 1, 512)`的内部表示采样为尺寸为`(256, 256, 1)`的输出图像。
W
wizardforcel 已提交
159

W
wizardforcel 已提交
160
在“pix2pix 的 Keras 实现”中,我们将详细介绍该架构。
W
wizardforcel 已提交
161 162


W
wizardforcel 已提交
163
# 判别器网络
W
wizardforcel 已提交
164 165 166



W
wizardforcel 已提交
167
pix2pix 中判别器网络的架构受到 PatchGAN 网络架构的启发。 PatchGAN 网络包含八个卷积块,如下所示:
W
wizardforcel 已提交
168

W
wizardforcel 已提交
169
| **层名称** | **超参数** | **输入形状** | **输出形状** |
W
wizardforcel 已提交
170
| --- | --- | --- | --- |
W
wizardforcel 已提交
171 172 173
| 第一个 2D 卷积层 | `filter= 64, kernel_size = 4, stride= 2, padding ='same'`  | `(256, 256, 1)` | `(256, 256, 64)` |
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(128, 128, 64)` | `(128, 128, 64)` |
| 第二个 2D 卷积层 | `filter= 128, kernel_size = 4, stride= 2, padding ="same"` | `(128, 128, 64)` | `(64, 64, 128)` |
W
wizardforcel 已提交
174
| 批量规范化层 | 没有 | `(64, 64, 128)` | `(64, 64, 128)` |
W
wizardforcel 已提交
175 176
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(64, 64, 128)`[ | `(64, 64, 128)` |
| 第三个 2D 卷积层 | `filter= 256, kernel_size = 4, stride= 2, padding ='same'`  | `(64, 64, 128)` | `(32, 32, 256)` |
W
wizardforcel 已提交
177
| 批量规范化层 | 没有 | `(32, 32, 256)` | `(32, 32, 256)` |
W
wizardforcel 已提交
178 179
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(32, 32, 256)` | `(32, 32, 256)` |
| 第四个 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 2, padding ="same"` | `(32, 32, 256)` | `(16, 16, 512)` |
W
wizardforcel 已提交
180
| 批量规范化层 | 没有 | `(16, 16, 512)` | `(16, 16, 512)` |
W
wizardforcel 已提交
181 182
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(16, 16, 512)` | `(16, 16, 512)` |
| 第五个 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 2, padding ="same"` | `(16, 16, 512)` | `(8, 8, 512)` |
W
wizardforcel 已提交
183
| 批量规范化层 | 没有 | `(8, 8, 512)` | `(8, 8, 512)` |
W
wizardforcel 已提交
184 185
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(8, 8, 512)` | `(8, 8, 512)` |
| 第六个 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 2, padding ="same"` | `(8, 8, 512)` | `(4, 4, 512)` |
W
wizardforcel 已提交
186
| 批量规范化层 | 没有 | `(4, 4, 512)` | `(4, 4, 512)` |
W
wizardforcel 已提交
187 188
| 激活层 |` Activation ='leakyrelu', alpha = 0.2` | `(4, 4, 512)` | `(4, 4, 512)` |
| 第七个 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 2, padding ="same"` | `(4, 4, 512)` | `(2, 2, 512)` |
W
wizardforcel 已提交
189
| 批量规范化层 | 没有 | `(2, 2, 512)` | `(2, 2, 512)` |
W
wizardforcel 已提交
190 191
| 激活层 | `Activation ='leakyrelu', alpha = 0.2` | `(2, 2, 512)` | `(2, 2, 512)` |
| 第八个 2D 卷积层 | `filter= 512, kernel_size = 4, stride= 2, padding ="same"` | `(4, 4, 512)` | `(1, 1, 512)` |
W
wizardforcel 已提交
192
| 批量规范化层 | 没有 | `(1, 1, 512)` | `(1, 1, 512)` |
W
wizardforcel 已提交
193 194 195 196 197
| 激活层 | `Activation ='leakyrelu', alpha = 0.2 `| `(1, 1, 512)` | `(1, 1, 512)` |
| 展开层 | 没有 | `(1, 1, 512)` | `(512, )` |
| 密集层 | `unit= 2, activation='softmax'` | `(1, 1, 512)` | `(2, )` |

下表突出显示了判别器网络的架构和配置。 展开层将张量展开为一维数组。
W
wizardforcel 已提交
198

W
wizardforcel 已提交
199
判别器网络中的其余层将在本章的“Keras pix2pix 实现”部分中介绍。
W
wizardforcel 已提交
200

W
wizardforcel 已提交
201
现在,我们已经探索了这两个网络的架构和配置。 现在,我们将探讨训练 pix2pix 所需的训练目标函数。
W
wizardforcel 已提交
202 203 204 205 206





W
wizardforcel 已提交
207
# 训练目标函数
W
wizardforcel 已提交
208 209 210



W
wizardforcel 已提交
211
Pix2pix 是一个条件生成对抗网络,并且用于条件 GAN 的目标函数可以表示为:
W
wizardforcel 已提交
212 213 214

![](img/a2a19bc3-64c1-48bd-b2d8-4b1303293dce.png)

W
wizardforcel 已提交
215
在这里,网络`G`(生成器)正试图使针对对手`D`(判别器)的先前功能最小化,而对手`D`则试图使先前的功能最大化。
W
wizardforcel 已提交
216

W
wizardforcel 已提交
217
如果必须比较朴素 GAN 和条件 GAN 的目标函数,则朴素 GAN 的目标函数如下:
W
wizardforcel 已提交
218 219 220 221 222 223 224

![](img/b2a6e013-9ffb-4a51-93fb-91454976c9c4.png)

为了减少图像的模糊,我们可以向目标函数添加一个 L1 损失函数。 L1 损失函数可以表示为:

![](img/e5f5e27a-31d3-4fcc-a69c-3fd555c755b4.png)

W
wizardforcel 已提交
225
在该等式中,`y`是原始图像, `G(x, z)`是生成器网络生成的图像。 L1 损失是由原始图像的所有像素值与生成的图像的所有像素值之间的所有绝对差值的总和来计算的。
W
wizardforcel 已提交
226 227 228 229 230 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 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292

pix2pix 的最终目标函数如下:

![](img/aea66002-edd3-4c8f-bea7-91ebc8e0aaf4.png)

这是条件 GAN 的损失函数和 L1 损失函数的加权和。

现在我们对 pix2pix 网络有了基本的了解。 在开始在 Keras 中实现 pix2pix 之前,让我们设置项目。





# 设置项目



如果尚未使用所有章节的完整代码克隆存储库,请立即克隆存储库。 克隆的存储库有一个名为`Chapter09`的目录,其中包含本章的全部代码。 执行以下命令来设置项目:

1.  首先,导航到父目录,如下所示:

```py
cd Generative-Adversarial-Networks-Projects
```

2.  现在,将目录从当前目录更改为  `Chapter09`

```py
cd Chapter09
```

3.  接下来,为该项目创建一个 Python 虚拟环境:

```py
virtualenv venv
virtualenv venv -p python3 # Create a virtual environment using python3 interpreter
virtualenv venv -p python2 # Create a virtual environment using python2 interpreter
```

我们将为此项目使用此新创建的虚拟环境。 每章都有其自己单独的虚拟环境。

4.  接下来,激活新创建的虚拟环境:

```py
source venv/bin/activate
```

激活虚拟环境后,所有其他命令将在此虚拟环境中执行。

5.  接下来,通过执行以下命令,安装`requirements.txt` 文件中提供的所有库:

```py
pip install -r requirements.txt
```

您可以参考  `README.md` 文件,以获取有关如何设置项目的更多说明。 开发人员经常会遇到依赖关系不匹配的问题。 为每个项目创建一个单独的虚拟环境将解决此问题。

在本节中,我们已成功设置项目并安装了所需的依赖项。 在下一部分中,我们将处理数据集。 现在,我们将探讨下载和格式化数据集所需的各个步骤。





# 准备数据



W
wizardforcel 已提交
293
在本章中,我们将使用 Facades 数据集,[该数据集可从以下链接获得](http://efrosgans.eecs.berkeley.edu/pix2pix/datasets/facades.tar.gz)
W
wizardforcel 已提交
294

W
wizardforcel 已提交
295
该数据集包含立面标签和真实立面图像。 外观通常是建筑物的正面,外观标签是外观图像的建筑标签。 下载数据集后,我们将了解有关立面的更多信息。 执行以下命令以下载和提取数据集:
W
wizardforcel 已提交
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

1.  通过执行以下命令下载数据集:

```py
# Before downloading the dataset navigate to data directory
cd data

# Download the dataset
wget http://efrosgans.eecs.berkeley.edu/pix2pix/datasets/facades.tar.gz
```

2.  下载数据集后,使用以下命令提取数据集:

```py
tar -xvzf facades.tar.gz
```

下载的数据集的文件结构如下:

![](img/dd18e210-40f0-4f5e-b898-37d023af1add.png)

数据集分为训练,测试和验证数据集。 让我们来提取图像。

执行以下步骤以加载数据集:

1.  首先创建包含外观标签的`.h5`文件列表和包含外观图像的`.h5`另一个列表,如下所示:

```py
data_dir_path = os.path.join(data_dir, data_type)

# Get all .h5 files containing training images facade_photos_h5 = [f for f in os.listdir(os.path.join(data_dir_path, 'images')) if '.h5' in f]
facade_labels_h5 = [f for f in os.listdir(os.path.join(data_dir_path, 'facades')) if '.h5' in f]
```

2.  接下来,遍历列表以依次加载每个图像:

```py
final_facade_photos = None final_facade_labels = None   for index in range(len(facade_photos_h5)):
```

此步骤之后的所有代码都将位于前面的`for`循环中。

W
wizardforcel 已提交
338
3.  接下来,加载包含图像的`h5`文件,并检索实际图像的 Numpy N 维数组:
W
wizardforcel 已提交
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360

```py
facade_photos_path = data_dir_path + '/images/' +   
                     facade_photos_h5[index]
facade_labels_path = data_dir_path + '/facades/' +  
                     facade_labels_h5[index]

facade_photos = h5py.File(facade_photos_path, 'r')
facade_labels = h5py.File(facade_labels_path, 'r')
```

4.  接下来,将图像调整为所需的图像尺寸,如下所示:

```py
# Resize and normalize images num_photos = facade_photos['data'].shape[0]
num_labels = facade_labels['data'].shape[0]

all_facades_photos = np.array(facade_photos['data'], dtype=np.float32)
all_facades_photos = all_facades_photos.reshape((num_photos, img_width, img_height, 1)) / 255.0   all_facades_labels = np.array(facade_labels['data'], dtype=np.float32)
all_facades_labels = all_facades_labels.reshape((num_labels, img_width, img_height, 1)) / 255.0
```

W
wizardforcel 已提交
361
5.  接下来,将调整大小的图像添加到最终的 N 维数组中:
W
wizardforcel 已提交
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444

```py
if final_facade_photos is not None and final_facade_labels is not None:
final_facade_photos = np.concatenate([final_facade_photos, 
                                   all_facades_photos], axis=0)
final_facade_labels = np.concatenate([final_facade_labels, all_facades_labels], axis=0)
 else:
        final_facade_photos = all_facades_photos
        final_facade_labels = all_facades_labels
```

加载和调整图像大小的整个代码如下所示:

```py
def load_dataset(data_dir, data_type, img_width, img_height):
    data_dir_path = os.path.join(data_dir, data_type)

    # Get all .h5 files containing training images
  facade_photos_h5 = [f for f in os.listdir(os.path.join(data_dir_path, 'images')) if '.h5' in f]
    facade_labels_h5 = [f for f in os.listdir(os.path.join(data_dir_path, 'facades')) if '.h5' in f]

    final_facade_photos = None
  final_facade_labels = None   for index in range(len(facade_photos_h5)):
        facade_photos_path = data_dir_path + '/images/' + facade_photos_h5[index]
        facade_labels_path = data_dir_path + '/facades/' + facade_labels_h5[index]

        facade_photos = h5py.File(facade_photos_path, 'r')
        facade_labels = h5py.File(facade_labels_path, 'r')

        # Resize and normalize images
  num_photos = facade_photos['data'].shape[0]
        num_labels = facade_labels['data'].shape[0]

        all_facades_photos = np.array(facade_photos['data'], dtype=np.float32)
        all_facades_photos = all_facades_photos.reshape((num_photos, img_width, img_height, 1)) / 255.0    all_facades_labels = np.array(facade_labels['data'], dtype=np.float32)
        all_facades_labels = all_facades_labels.reshape((num_labels, img_width, img_height, 1)) / 255.0    if final_facade_photos is not None and final_facade_labels is not None:
            final_facade_photos = np.concatenate([final_facade_photos, all_facades_photos], axis=0)
            final_facade_labels = np.concatenate([final_facade_labels, all_facades_labels], axis=0)
        else:
            final_facade_photos = all_facades_photos
            final_facade_labels = all_facades_labels

    return final_facade_photos, final_facade_labels
```

先前的功能将从训练,测试和验证目录中的`.h5`文件加载图像。





# 可视化图像



可视化外观标签和外观图像的 Python 函数如下所示:

```py
def visualize_bw_image(img):
    """
 Visualize a black and white image """  fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.imshow(img, cmap='gray', interpolation='nearest')
    ax.axis("off")
    ax.set_title("Image")
    plt.show()
```

使用前面的功能可以使外观标签或外观照片可视化,如下所示:

```py
visualize_bw_image(image)
visualize_bw_image(image)
```

建筑物外墙的图像示例如下:

![](img/3af97dd6-cd57-48b8-92bb-cd83f75125d4.png)

下图表示前面的门面图像的建筑标签:

![](img/1afafd90-be55-4633-97c5-a1d1eb7fd6c3.png)

W
wizardforcel 已提交
445
我们将训练一个 pix2pix 网络,该网络能够从立面标签生成立面图像。 让我们开始使用生成器和判别器的 Keras 实现。
W
wizardforcel 已提交
446 447 448 449 450 451 452 453 454





# pix2pix 的 Keras 实现



W
wizardforcel 已提交
455
如前所述,pix2pix 具有两个网络:一个生成器和一个判别器。 该生成器受 U-Net 架构的启发。 同样,判别器网络也受到 PatchGAN 架构的启发。 我们将在以下部分中实现这两个网络。
W
wizardforcel 已提交
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476

在开始编写实现之前,创建一个 Python 文件 `main.py` ,然后按以下方式导入基本模块:

```py
import os
import time

import h5py
import keras.backend as K
import matplotlib.pyplot as plt
import numpy as np
from cv2 import imwrite
from keras import Input, Model
from keras.layers import Convolution2D, LeakyReLU, BatchNormalization, UpSampling2D, Dropout, Activation, Flatten, Dense, Lambda, Reshape, concatenate
from keras.optimizers import Adam
```





W
wizardforcel 已提交
477
# 生成器网络
W
wizardforcel 已提交
478 479 480



W
wizardforcel 已提交
481
生成器网络从源域 A 拍摄尺寸为`(256, 256, 1)`的图像,并将其转换为目标域 B 中尺寸为`(256, 256, 1)`的图像。 基本上,它将图像从源域 A 转换为目标域 B。让我们在 Keras 框架中实现生成器网络。
W
wizardforcel 已提交
482

W
wizardforcel 已提交
483
执行以下步骤来创建生成器网络:
W
wizardforcel 已提交
484 485 486 487 488 489 490 491 492 493 494 495 496

1.  首先定义生成器网络所需的超参数:

```py
kernel_size = 4 strides = 2 leakyrelu_alpha = 0.2 upsampling_size = 2 dropout = 0.5 output_channels = 1 input_shape = (256, 256, 1)
```

2.  现在创建一个输入层,将输入输入到网络,如下所示:

```py
input_layer = Input(shape=input_shape)
```

W
wizardforcel 已提交
497
输入层获取形状为`(256, 256, 1)`的输入图像,并将其传递到网络中的下一层。
W
wizardforcel 已提交
498 499 500

如上所述,生成器网络具有两个部分:编码器和解码器。 在接下来的几个步骤中,我们将编写编码器部分的代码。

W
wizardforcel 已提交
501
3.  使用先前在“pix2pix”部分中指示的参数,将第一个卷积块添加到生成器网络:
W
wizardforcel 已提交
502 503 504 505 506 507

```py
# 1st Convolutional block in the encoder network encoder1 = Convolution2D(filters=64, kernel_size=kernel_size, padding='same', strides=strides)(input_layer)
encoder1 = LeakyReLU(alpha=leakyrelu_alpha)(encoder1)
```

W
wizardforcel 已提交
508
第一卷积块包含具有激活函数的 2D 卷积层。 与其他七个卷积块不同,它没有批量规范化层。
W
wizardforcel 已提交
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 540 541 542 543 544 545 546 547 548

4.  将其他七个卷积块添加到生成器网络:

```py
# 2nd Convolutional block in the encoder network encoder2 = Convolution2D(filters=128, kernel_size=kernel_size, padding='same',
  strides=strides)(encoder1)
encoder2 = BatchNormalization()(encoder2)
encoder2 = LeakyReLU(alpha=leakyrelu_alpha)(encoder2)

# 3rd Convolutional block in the encoder network encoder3 = Convolution2D(filters=256, kernel_size=kernel_size, padding='same',
  strides=strides)(encoder2)
encoder3 = BatchNormalization()(encoder3)
encoder3 = LeakyReLU(alpha=leakyrelu_alpha)(encoder3)

# 4th Convolutional block in the encoder network encoder4 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same',
  strides=strides)(encoder3)
encoder4 = BatchNormalization()(encoder4)
encoder4 = LeakyReLU(alpha=leakyrelu_alpha)(encoder4)

# 5th Convolutional block in the encoder network encoder5 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same',
  strides=strides)(encoder4)
encoder5 = BatchNormalization()(encoder5)
encoder5 = LeakyReLU(alpha=leakyrelu_alpha)(encoder5)

# 6th Convolutional block in the encoder network encoder6 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same',
  strides=strides)(encoder5)
encoder6 = BatchNormalization()(encoder6)
encoder6 = LeakyReLU(alpha=leakyrelu_alpha)(encoder6)

# 7th Convolutional block in the encoder network encoder7 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same',
  strides=strides)(encoder6)
encoder7 = BatchNormalization()(encoder7)
encoder7 = LeakyReLU(alpha=leakyrelu_alpha)(encoder7)

# 8th Convolutional block in the encoder network encoder8 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same',
  strides=strides)(encoder7)
encoder8 = BatchNormalization()(encoder8)
encoder8 = LeakyReLU(alpha=leakyrelu_alpha)(encoder8)
```

W
wizardforcel 已提交
549
这是生成器网络中编码器部分的末端。 生成器网络中的第二部分是解码器。 在接下来的几个步骤中,让我们为解码器编写代码。
W
wizardforcel 已提交
550

W
wizardforcel 已提交
551
5.  将第一个上采样卷积块添加到先前在“pix2pix”部分的架构中指示的参数中:
W
wizardforcel 已提交
552 553 554 555 556 557 558 559 560 561

```py
# 1st Upsampling Convolutional Block in the decoder network decoder1 = UpSampling2D(size=upsampling_size)(encoder8)
decoder1 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same')(decoder1)
decoder1 = BatchNormalization()(decoder1)
decoder1 = Dropout(dropout)(decoder1)
decoder1 = concatenate([decoder1, encoder7], axis=3)
decoder1 = Activation('relu')(decoder1)
```

W
wizardforcel 已提交
562
第一个上采样模块从编码器部分的最后一层获取输入。 它具有一个 2D 上采样层,一个 2D 卷积层,一个批量归一化层,一个脱落层,一个连接操作和一个激活函数。 请参阅 Keras 文档以找到有关这些层的更多信息,该文档可从[这里](https://keras.io/)获得。
W
wizardforcel 已提交
563 564 565 566 567 568 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 599 600 601 602 603 604 605 606 607 608 609

6.  同样,添加以下七个卷积块,如下所示:

```py
# 2nd Upsampling Convolutional block in the decoder network decoder2 = UpSampling2D(size=upsampling_size)(decoder1)
decoder2 = Convolution2D(filters=1024, kernel_size=kernel_size, padding='same')(decoder2)
decoder2 = BatchNormalization()(decoder2)
decoder2 = Dropout(dropout)(decoder2)
decoder2 = concatenate([decoder2, encoder6])
decoder2 = Activation('relu')(decoder2)

# 3rd Upsampling Convolutional block in the decoder network decoder3 = UpSampling2D(size=upsampling_size)(decoder2)
decoder3 = Convolution2D(filters=1024, kernel_size=kernel_size, padding='same')(decoder3)
decoder3 = BatchNormalization()(decoder3)
decoder3 = Dropout(dropout)(decoder3)
decoder3 = concatenate([decoder3, encoder5])
decoder3 = Activation('relu')(decoder3)

# 4th Upsampling Convolutional block in the decoder network decoder4 = UpSampling2D(size=upsampling_size)(decoder3)
decoder4 = Convolution2D(filters=1024, kernel_size=kernel_size, padding='same')(decoder4)
decoder4 = BatchNormalization()(decoder4)
decoder4 = concatenate([decoder4, encoder4])
decoder4 = Activation('relu')(decoder4)

# 5th Upsampling Convolutional block in the decoder network decoder5 = UpSampling2D(size=upsampling_size)(decoder4)
decoder5 = Convolution2D(filters=1024, kernel_size=kernel_size, padding='same')(decoder5)
decoder5 = BatchNormalization()(decoder5)
decoder5 = concatenate([decoder5, encoder3])
decoder5 = Activation('relu')(decoder5)

# 6th Upsampling Convolutional block in the decoder network decoder6 = UpSampling2D(size=upsampling_size)(decoder5)
decoder6 = Convolution2D(filters=512, kernel_size=kernel_size, padding='same')(decoder6)
decoder6 = BatchNormalization()(decoder6)
decoder6 = concatenate([decoder6, encoder2])
decoder6 = Activation('relu')(decoder6)

# 7th Upsampling Convolutional block in the decoder network decoder7 = UpSampling2D(size=upsampling_size)(decoder6)
decoder7 = Convolution2D(filters=256, kernel_size=kernel_size, padding='same')(decoder7)
decoder7 = BatchNormalization()(decoder7)
decoder7 = concatenate([decoder7, encoder1])
decoder7 = Activation('relu')(decoder7)

# Last Convolutional layer decoder8 = UpSampling2D(size=upsampling_size)(decoder7)
decoder8 = Convolution2D(filters=output_channels, kernel_size=kernel_size, padding='same')(decoder8)
decoder8 = Activation('tanh')(decoder8)
```

W
wizardforcel 已提交
610
最后一层的激活函数为`'tanh'`,因为我们打算让生成器生成介于 -1 到 1 之间的值。`'concatenate'`层用于添加跳跃连接。 最后一层将生成张量为`(256, 256, 1)`的张量。
W
wizardforcel 已提交
611 612 613

The 'concatenate' layer concatenates tensors along the channel dimension. You can provide a value for the axis, along which you want your tensors to be concatenated.

W
wizardforcel 已提交
614
7.  最后,通过指定生成器网络的输入和输出来创建 Keras 模型:
W
wizardforcel 已提交
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 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 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 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758

```py
# Create a Keras model
model = Model(inputs=[input_layer], outputs=[decoder8])
```

Python 函数内部的生成器网络的整个代码如下所示:

```py
def build_unet_generator():
    """
 Create U-Net Generator using the hyperparameter values defined  
    below """    kernel_size = 4
  strides = 2
  leakyrelu_alpha = 0.2
  upsampling_size = 2
  dropout = 0.5
  output_channels = 1
  input_shape = (256, 256, 1)

    input_layer = Input(shape=input_shape)

    # Encoder Network   # 1st Convolutional block in the encoder network  encoder1 = Convolution2D(filters=64, kernel_size=kernel_size, 
                             padding='same',
  strides=strides)(input_layer)
    encoder1 = LeakyReLU(alpha=leakyrelu_alpha)(encoder1)

    # 2nd Convolutional block in the encoder network
  encoder2 = Convolution2D(filters=128, kernel_size=kernel_size, 
                             padding='same',
  strides=strides)(encoder1)
    encoder2 = BatchNormalization()(encoder2)
    encoder2 = LeakyReLU(alpha=leakyrelu_alpha)(encoder2)

    # 3rd Convolutional block in the encoder network
  encoder3 = Convolution2D(filters=256, kernel_size=kernel_size, 
                             padding='same',
  strides=strides)(encoder2)
    encoder3 = BatchNormalization()(encoder3)
    encoder3 = LeakyReLU(alpha=leakyrelu_alpha)(encoder3)

    # 4th Convolutional block in the encoder network
  encoder4 = Convolution2D(filters=512, kernel_size=kernel_size, 
                             padding='same',
  strides=strides)(encoder3)
    encoder4 = BatchNormalization()(encoder4)
    encoder4 = LeakyReLU(alpha=leakyrelu_alpha)(encoder4)

    # 5th Convolutional block in the encoder network
  encoder5 = Convolution2D(filters=512, kernel_size=kernel_size, 
                             padding='same',
  strides=strides)(encoder4)
    encoder5 = BatchNormalization()(encoder5)
    encoder5 = LeakyReLU(alpha=leakyrelu_alpha)(encoder5)

    # 6th Convolutional block in the encoder network
  encoder6 = Convolution2D(filters=512, kernel_size=kernel_size, 
                             padding='same',
  strides=strides)(encoder5)
    encoder6 = BatchNormalization()(encoder6)
    encoder6 = LeakyReLU(alpha=leakyrelu_alpha)(encoder6)

    # 7th Convolutional block in the encoder network
  encoder7 = Convolution2D(filters=512, kernel_size=kernel_size, 
                             padding='same',
  strides=strides)(encoder6)
    encoder7 = BatchNormalization()(encoder7)
    encoder7 = LeakyReLU(alpha=leakyrelu_alpha)(encoder7)

    # 8th Convolutional block in the encoder network
  encoder8 = Convolution2D(filters=512, kernel_size=kernel_size, 
                             padding='same',
  strides=strides)(encoder7)
    encoder8 = BatchNormalization()(encoder8)
    encoder8 = LeakyReLU(alpha=leakyrelu_alpha)(encoder8)

    # Decoder Network   # 1st Upsampling Convolutional Block in the decoder network  decoder1 = UpSampling2D(size=upsampling_size)(encoder8)
    decoder1 = Convolution2D(filters=512, kernel_size=kernel_size, 
                             padding='same')(decoder1)
    decoder1 = BatchNormalization()(decoder1)
    decoder1 = Dropout(dropout)(decoder1)
    decoder1 = concatenate([decoder1, encoder7], axis=3)
    decoder1 = Activation('relu')(decoder1)

    # 2nd Upsampling Convolutional block in the decoder network
  decoder2 = UpSampling2D(size=upsampling_size)(decoder1)
    decoder2 = Convolution2D(filters=1024, kernel_size=kernel_size, 
                             padding='same')(decoder2)
    decoder2 = BatchNormalization()(decoder2)
    decoder2 = Dropout(dropout)(decoder2)
    decoder2 = concatenate([decoder2, encoder6])
    decoder2 = Activation('relu')(decoder2)

    # 3rd Upsampling Convolutional block in the decoder network
  decoder3 = UpSampling2D(size=upsampling_size)(decoder2)
    decoder3 = Convolution2D(filters=1024, kernel_size=kernel_size, 
                             padding='same')(decoder3)
    decoder3 = BatchNormalization()(decoder3)
    decoder3 = Dropout(dropout)(decoder3)
    decoder3 = concatenate([decoder3, encoder5])
    decoder3 = Activation('relu')(decoder3)

    # 4th Upsampling Convolutional block in the decoder network
  decoder4 = UpSampling2D(size=upsampling_size)(decoder3)
    decoder4 = Convolution2D(filters=1024, kernel_size=kernel_size, 
                             padding='same')(decoder4)
    decoder4 = BatchNormalization()(decoder4)
    decoder4 = concatenate([decoder4, encoder4])
    decoder4 = Activation('relu')(decoder4)

    # 5th Upsampling Convolutional block in the decoder network
  decoder5 = UpSampling2D(size=upsampling_size)(decoder4)
    decoder5 = Convolution2D(filters=1024, kernel_size=kernel_size, 
                             padding='same')(decoder5)
    decoder5 = BatchNormalization()(decoder5)
    decoder5 = concatenate([decoder5, encoder3])
    decoder5 = Activation('relu')(decoder5)

    # 6th Upsampling Convolutional block in the decoder network
  decoder6 = UpSampling2D(size=upsampling_size)(decoder5)
    decoder6 = Convolution2D(filters=512, kernel_size=kernel_size, 
                             padding='same')(decoder6)
    decoder6 = BatchNormalization()(decoder6)
    decoder6 = concatenate([decoder6, encoder2])
    decoder6 = Activation('relu')(decoder6)

    # 7th Upsampling Convolutional block in the decoder network
  decoder7 = UpSampling2D(size=upsampling_size)(decoder6)
    decoder7 = Convolution2D(filters=256, kernel_size=kernel_size, 
                             padding='same')(decoder7)
    decoder7 = BatchNormalization()(decoder7)
    decoder7 = concatenate([decoder7, encoder1])
    decoder7 = Activation('relu')(decoder7)

    # Last Convolutional layer
  decoder8 = UpSampling2D(size=upsampling_size)(decoder7)
    decoder8 = Convolution2D(filters=output_channels, 
               kernel_size=kernel_size, padding='same')(decoder8)
    decoder8 = Activation('tanh')(decoder8)

    model = Model(inputs=[input_layer], outputs=[decoder8])
    return model
```

W
wizardforcel 已提交
759
我们现在已经成功地为生成器网络创建了 Keras 模型。 在下一节中,我们将为判别器网络创建 Keras 模型。
W
wizardforcel 已提交
760 761 762 763 764





W
wizardforcel 已提交
765
# 判别器网络
W
wizardforcel 已提交
766 767 768



W
wizardforcel 已提交
769
判别器网络受 PatchGAN 架构的启发。 它包含八个卷积块,一个密集层和一个展开层。 判别器网络获取从尺寸为`(256, 256, 1)`的图像中提取的一组补丁,并预测给定补丁的概率。 让我们在 Keras 中实现判别器。
W
wizardforcel 已提交
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786

1.  首先初始化生成器网络所需的超参数:

```py
kernel_size = 4 strides = 2 leakyrelu_alpha = 0.2 padding = 'same' num_filters_start = 64 # Number of filters to start with num_kernels = 100 kernel_dim = 5 patchgan_output_dim = (256, 256, 1)
patchgan_patch_dim = (256, 256, 1)

# Calculate number of patches
number_patches = int((patchgan_output_dim[0] / patchgan_patch_dim[0]) * (patchgan_output_dim[1] / patchgan_patch_dim[1]))
```

2.  让我们向网络添加一个输入层。 这需要一个张量为 `patchgan_patch_dim`的张量的补丁:

```py
input_layer = Input(shape=patchgan_patch_dim)
```

W
wizardforcel 已提交
787
3.  接下来,如下所示将卷积层添加到网络。 “pix2pix 的架构”部分中提供了该块的配置:
W
wizardforcel 已提交
788 789 790 791 792 793 794 795 796 797 798 799 800 801

```py
des = Convolution2D(filters=64, kernel_size=kernel_size, padding=padding, strides=strides)(input_layer)
des = LeakyReLU(alpha=leakyrelu_alpha)(des)
```

4.  接下来,使用以下代码计算卷积块的数量:

```py
# Calculate the number of convolutional layers total_conv_layers = int(np.floor(np.log(patchgan_output_dim[1]) / 
                    np.log(2)))
list_filters = [num_filters_start * min(total_conv_layers, (2 ** i)) for i in range(total_conv_layers)]
```

W
wizardforcel 已提交
802
5.  接下来,使用先前在“pix2pix”部分的架构中指示的超参数值添加另外七个卷积块:
W
wizardforcel 已提交
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817

```py
# Next 7 Convolutional blocks for filters in list_filters[1:]:
    des = Convolution2D(filters=filters, kernel_size=kernel_size, padding=padding, strides=strides)(des)
    des = BatchNormalization()(des)
    des = LeakyReLU(alpha=leakyrelu_alpha)(des)

```

6.  接下来,将平坦的层添加到网络,如下所示:

```py
flatten_layer = Flatten()(des)
```

W
wizardforcel 已提交
818
展开层将`n`维张量转换为一维张量。
W
wizardforcel 已提交
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833

7.  类似地,添加具有两个节点/神经元的密集层,并添加 `softmax` 作为激活函数。 这需要一个来自 `Flatten` 层的张量,并将其转换为尺寸为 `(batch_size, 2)`的张量:

```py
dense_layer = Dense(units=2, activation='softmax')(flatten_layer)
```

`softmax`函数将向量转换为概率分布。

8.  接下来,为 PatchGAN 网络创建 Keras 模型,如下所示:

```py
model_patch_gan = Model(inputs=[input_layer], outputs=[dense_layer, flatten_layer])
```

W
wizardforcel 已提交
834
PatchGAN 模型将输入张量作为输入,并输出两个张量,一个来自密集层,另一个来自展开层。 我们的 PatchGAN 网络现已准备就绪。 但是,它本身不能用作判别器。 而是将单个补丁分类为真实或伪造类别。 要创建完整的判别器,请按照下列步骤操作:
W
wizardforcel 已提交
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850

1.  我们将从输入图像中提取色块,并将它们逐一馈送到 PatchGAN。 创建一个等于补丁数量的输入层列表,如下所示:

```py
# Create a list of input layers equal to number of patches list_input_layers = [Input(shape=patchgan_patch_dim) for _ in range(number_patches)]
```

2.  接下来,将补丁传递到 PatchGAN 网络并获得概率分布:

```py
# Pass the patches to the PatchGAN and get probability distribution output1 = [model_patch_gan(patch)[0] for patch in list_input_layers]
output2 = [model_patch_gan(patch)[1] for patch in list_input_layers]
```

如果我们有多个面片,则`output1``output2`都是张量的列表。 我们现在应该有两个张量列表。

W
wizardforcel 已提交
851
3.  如果您有多个补丁,请沿着渠道维度将其连接起来以计算永久损失:
W
wizardforcel 已提交
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902

```py
# In case of multiple patches, concatenate them along the channel dimension to calculate perceptual loss if len(output1) > 1:
    output1 = concatenate(output1)
else:
    output1 = output1[0]

# In case of multiple patches, merge output2 as well if len(output2) > 1:
    output2 = concatenate(output2)
else:
    output2 = output2[0]
```

4.  接下来,创建一个密集层,如下所示:

```py
dense_layer2 = Dense(num_kernels * kernel_dim, use_bias=False, activation=None)
```

5.  接下来,添加一个自定义损失层。 该层计算馈入该层的张量的最小批判别:

```py
custom_loss_layer = Lambda(lambda x: K.sum(
    K.exp(-K.sum(K.abs(K.expand_dims(x, 3) - K.expand_dims(K.permute_dimensions(x, pattern=(1, 2, 0)), 0)), 2)), 2))
```

6.  接下来,使`output2`张量通过`dense_layer2`

```py
output2 = dense_layer2(output2)
```

7.  接下来,将`output2`重塑为`(num_kernels, kernel_dim)`的张量:

```py
output2 = Reshape((num_kernels, kernel_dim))(output2)
```

8.  接下来,将`output2`张量传递到`custom_loss_layer`

```py
output2 = custom_loss_layer(output2)
```

9.  接下来,连接`output1``output2`以创建一个张量,并将其通过密集层:

```py
output1 = concatenate([output1, output2])
final_output = Dense(2, activation="softmax")(output1)
```

W
wizardforcel 已提交
903
`"softmax"`用作最后一个密集层的激活函数。 这将返回概率分布。
W
wizardforcel 已提交
904

W
wizardforcel 已提交
905
10.  最后,通过如下指定网络的输入和输出来创建判别器模型:
W
wizardforcel 已提交
906 907 908 909 910

```py
discriminator = Model(inputs=list_input_layers, outputs=[final_output])
```

W
wizardforcel 已提交
911
判别器网络的完整代码如下:
W
wizardforcel 已提交
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995

```py
def build_patchgan_discriminator():
    """
 Create PatchGAN discriminator using the hyperparameter values defined below """  kernel_size = 4
  strides = 2
  leakyrelu_alpha = 0.2
  padding = 'same'
  num_filters_start = 64 # Number of filters to start with
  num_kernels = 100
  kernel_dim = 5
  patchgan_output_dim = (256, 256, 1)
    patchgan_patch_dim = (256, 256, 1)
    number_patches = int(
        (patchgan_output_dim[0] / patchgan_patch_dim[0]) * (patchgan_output_dim[1] / patchgan_patch_dim[1]))

    input_layer = Input(shape=patchgan_patch_dim)

    des = Convolution2D(filters=64, kernel_size=kernel_size, padding=padding, strides=strides)(input_layer)
    des = LeakyReLU(alpha=leakyrelu_alpha)(des)

    # Calculate the number of convolutional layers
  total_conv_layers = int(np.floor(np.log(patchgan_output_dim[1]) / np.log(2)))
    list_filters = [num_filters_start * min(total_conv_layers, (2 ** i)) for i in range(total_conv_layers)]

    # Next 7 Convolutional blocks
  for filters in list_filters[1:]:
        des = Convolution2D(filters=filters, kernel_size=kernel_size, padding=padding, strides=strides)(des)
        des = BatchNormalization()(des)
        des = LeakyReLU(alpha=leakyrelu_alpha)(des)

    # Add a flatten layer
  flatten_layer = Flatten()(des)

    # Add the final dense layer
  dense_layer = Dense(units=2, activation='softmax')(flatten_layer)

    # Create the PatchGAN model
  model_patch_gan = Model(inputs=[input_layer], outputs=[dense_layer, flatten_layer])

    # Create a list of input layers equal to the number of patches
  list_input_layers = [Input(shape=patchgan_patch_dim) for _ in range(number_patches)]

    # Pass the patches through the PatchGAN network
  output1 = [model_patch_gan(patch)[0] for patch in list_input_layers]
    output2 = [model_patch_gan(patch)[1] for patch in list_input_layers]

    # In case of multiple patches, concatenate outputs to calculate perceptual loss
  if len(output1) > 1:
        output1 = concatenate(output1)
    else:
        output1 = output1[0]

    # In case of multiple patches, merge output2 as well
  if len(output2) > 1:
        output2 = concatenate(output2)
    else:
        output2 = output2[0]

    # Add a dense layer
  dense_layer2 = Dense(num_kernels * kernel_dim, use_bias=False, activation=None)

    # Add a lambda layer
  custom_loss_layer = Lambda(lambda x: K.sum(
        K.exp(-K.sum(K.abs(K.expand_dims(x, 3) - K.expand_dims(K.permute_dimensions(x, pattern=(1, 2, 0)), 0)), 2)), 2))

    # Pass the output2 tensor through dense_layer2
  output2 = dense_layer2(output2)

    # Reshape the output2 tensor
  output2 = Reshape((num_kernels, kernel_dim))(output2)

    # Pass the output2 tensor through the custom_loss_layer
  output2 = custom_loss_layer(output2)

    # Finally concatenate output1 and output2
  output1 = concatenate([output1, output2])
    final_output = Dense(2, activation="softmax")(output1)

    # Create a discriminator model
  discriminator = Model(inputs=list_input_layers, outputs=[final_output])
    return discriminator
```

W
wizardforcel 已提交
996
我们现在已经成功创建了判别器网络。 接下来,让我们创建一个对抗网络。
W
wizardforcel 已提交
997 998 999 1000 1001 1002 1003 1004 1005





# 对抗网络



W
wizardforcel 已提交
1006
在本节中,我们将创建一个**对抗网络**,其中包含 U-Net 生成器网络和 PatchGAN 判别器网络。 执行以下步骤来创建对抗网络:
W
wizardforcel 已提交
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042

1.  首先初始化超参数:

```py
input_image_dim = (256, 256, 1)
patch_dim = (256, 256)
```

2.  接下来,创建一个输入层,将输入馈送到网络,如下所示:

```py
input_layer = Input(shape=input_image_dim)
```

3.  接下来,使用生成器网络生成伪造的图像:

```py
generated_images = generator(input_layer)
```

4.  接下来,从生成的图像中提取补丁:

```py
# Chop the generated images into patches img_height, img_width = input_img_dim[:2]
patch_height, patch_width = patch_dim

row_idx_list = [(i * patch_height, (i + 1) * patch_height) for i in range(int(img_height / patch_height))]
column_idx_list = [(i * patch_width, (i + 1) * patch_width) for i in range(int(img_width / patch_width))]

generated_patches_list = []
for row_idx in row_idx_list:
    for column_idx in column_idx_list:
        generated_patches_list.append(Lambda(lambda z: z[:, column_idx[0]:column_idx[1], row_idx[0]:row_idx[1], :],
  output_shape=input_img_dim)(generated_images))
```

W
wizardforcel 已提交
1043
5.  冻结了判别器网络的训练,因为我们不想训练判别器网络:
W
wizardforcel 已提交
1044 1045 1046 1047 1048

```py
discriminator.trainable = False
```

W
wizardforcel 已提交
1049
6.  现在,我们应该有一个补丁列表。 通过 PatchGAN 判别器网络传递它们:
W
wizardforcel 已提交
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061

```py
dis_output = discriminator(generated_patches_list)
```

7.  最后,通过如下指定网络的输入和输出来创建 Keras 模型:

```py
model = Model(inputs=[input_layer], outputs=[generated_images,  
              dis_output])
```

W
wizardforcel 已提交
1062
这些步骤使用两个网络(生成器网络和判别器网络)创建对抗模型。 对抗模型的整个代码如下:
W
wizardforcel 已提交
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096

```py
def build_adversarial_model(generator, discriminator):
    """
 Create an adversarial model """  input_image_dim = (256, 256, 1)
    patch_dim = (256, 256)

    # Create an input layer
  input_layer = Input(shape=input_image_dim)

    # Use the generator network to generate images
  generated_images = generator(input_layer)

    # Extract patches from the generated images
  img_height, img_width = input_img_dim[:2]
    patch_height, patch_width = patch_dim

    row_idx_list = [(i * patch_height, (i + 1) * patch_height) for i in range(int(img_height / patch_height))]
    column_idx_list = [(i * patch_width, (i + 1) * patch_width) for i in range(int(img_width / patch_width))]

    generated_patches_list = []
    for row_idx in row_idx_list:
        for column_idx in column_idx_list:
            generated_patches_list.append(Lambda(lambda z: z[:, column_idx[0]:column_idx[1], row_idx[0]:row_idx[1], :],
  output_shape=input_img_dim)(generated_images))

    discriminator.trainable = False    # Pass the generated patches through the discriminator network
  dis_output = discriminator(generated_patches_list)

    # Create a model
    model = Model(inputs=[input_layer], outputs=[generated_images, dis_output])
    return model
```

W
wizardforcel 已提交
1097
现在,我们已经成功地为生成器网络,判别器网络和对抗模型创建了模型。 我们准备训练 pix2pix。 在下一部分中,我们将在 Facades 数据集中训练 pix2pix 网络。
W
wizardforcel 已提交
1098 1099 1100 1101 1102 1103 1104 1105 1106





# 训练 pix2pix 网络



W
wizardforcel 已提交
1107
像任何其他 GAN 一样,训练 pix2pix 网络是一个两步过程。 第一步,我们训练判别器网络。 在第二步中,我们训练对抗网络,最终训练生成器网络。 让我们开始训练网络。
W
wizardforcel 已提交
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127

执行以下步骤来训练 SRGAN 网络:

1.  首先定义训练所需的超参数:

```py
epochs = 500 num_images_per_epoch = 400 batch_size = 1 img_width = 256 img_height = 256 num_channels = 1 input_img_dim = (256, 256, 1)
patch_dim = (256, 256)

# Specify dataset directory path
dataset_dir = "pix2pix-keras/pix2pix/data/facades_bw"
```

2.  接下来,定义通用优化器,如下所示:

```py
common_optimizer = Adam(lr=1E-4, beta_1=0.9, beta_2=0.999,  
                        epsilon=1e-08)
```

W
wizardforcel 已提交
1128
对于所有网络,我们将使用`Adam`优化器,其中`learning rate`等于`1e-4``beta_1` 等于 0.9, `beta_2`等于 0.999,并且`epsilon`等于`1e-08`
W
wizardforcel 已提交
1129

W
wizardforcel 已提交
1130
3.  接下来,构建并编译 PatchGAN 判别器网络,如下所示:
W
wizardforcel 已提交
1131 1132 1133 1134 1135 1136

```py
patchgan_discriminator = build_patchgan_discriminator()
patchgan_discriminator.compile(loss='binary_crossentropy', optimizer=common_optimizer)
```

W
wizardforcel 已提交
1137
要编译判别器模型,请使用`binary_crossentropy`作为损失函数,并使用`common_optimizer`作为训练优化器。
W
wizardforcel 已提交
1138 1139 1140 1141 1142 1143 1144 1145

4.  现在,构建并编译生成器网络,如下所示:

```py
unet_generator = build_unet_generator()
unet_generator.compile(loss='mae', optimizer=common_optimizer)
```

W
wizardforcel 已提交
1146
要编译判别器模型,请使用 `mse` 作为损失函数,并使用 `common_optimizer` 作为训练优化器。
W
wizardforcel 已提交
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166

5.  接下来,构建并编译对抗模型,如下所示:

```py
adversarial_model = build_adversarial_model(unet_generator, patchgan_discriminator)
adversarial_model.compile(loss=['mae', 'binary_crossentropy'], loss_weights=[1E2, 1], optimizer=common_optimizer)
```

要编译对抗模型,请使用损失列表 `['mse', 'binary_crossentropy']``common_optimizer` 作为训练优化器。

6.  现在,按如下所示加载训练,验证和测试数据集:

```py
training_facade_photos, training_facade_labels = load_dataset(data_dir=dataset_dir, data_type='training',img_width=img_width, img_height=img_height)

test_facade_photos, test_facade_labels = load_dataset(data_dir=dataset_dir, data_type='testing',img_width=img_width, img_height=img_height)

validation_facade_photos, validation_facade_labels = load_dataset(data_dir=dataset_dir, data_type='validation',img_width=img_width, img_height=img_height)
```

W
wizardforcel 已提交
1167
`load_dataset`函数是“数据准备”部分中定义的 。 每一组包含所有图像的一组`ndarray`。 每组的尺寸将为`(#total_images, 256, 256, 1)`
W
wizardforcel 已提交
1168 1169 1170 1171 1172 1173 1174 1175 1176

7.  添加  `tensorboard` 以可视化训练损失并可视化网络图:

```py
tensorboard = TensorBoard(log_dir="logs/".format(time.time()))
tensorboard.set_model(unet_generator)
tensorboard.set_model(patchgan_discriminator)
```

W
wizardforcel 已提交
1177
8.  接下来,创建一个`for`循环,该循环应运行的次数由时期数指定,如下所示:
W
wizardforcel 已提交
1178 1179 1180 1181 1182 1183

```py
for epoch in range(epochs):
    print("Epoch:{}".format(epoch))
```

W
wizardforcel 已提交
1184
9.  创建两个列表以存储所有小批量的损失:
W
wizardforcel 已提交
1185 1186 1187 1188 1189 1190 1191 1192 1193

```py
dis_losses = []
gen_losses = []

# Initialize a variable
batch_counter = 1
```

W
wizardforcel 已提交
1194
10.  接下来,在周期循环内创建另一个循环,并使它运行`num_batches`指定的次数,如下所示:
W
wizardforcel 已提交
1195 1196 1197 1198 1199 1200

```py
num_batches = int(training_facade_photos.shape[0] / batch_size) for index in range(int(training_facade_photos.shape[0] / batch_size)):
print("Batch:{}".format(index))
```

W
wizardforcel 已提交
1201
我们用于判别器网络和对抗网络训练的整个代码将在此循环内。
W
wizardforcel 已提交
1202

W
wizardforcel 已提交
1203
11.  接下来,对训练和验证数据进行小批量采样,如下所示:
W
wizardforcel 已提交
1204 1205 1206 1207 1208 1209 1210 1211 1212

```py
        train_facades_batch = training_facade_labels[index * batch_size:(index + 1) * batch_size]
        train_images_batch = training_facade_photos[index * batch_size:(index + 1) * batch_size]

        val_facades_batch = validation_facade_labels[index * batch_size:(index + 1) * batch_size]
        val_images_batch = validation_facade_photos[index * batch_size:(index + 1) * batch_size]
```

W
wizardforcel 已提交
1213
12.  接下来,生成一批假图像并从中提取补丁。 如下使用`generate_and_extract_patches`函数:
W
wizardforcel 已提交
1214 1215 1216 1217 1218

```py
patches, labels = generate_and_extract_patches(train_images_batch, train_facades_batch, unet_generator,batch_counter, patch_dim)
```

W
wizardforcel 已提交
1219
`generate_and_extract_patches`函数定义如下:
W
wizardforcel 已提交
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246

```py
def generate_and_extract_patches(images, facades, generator_model, batch_counter, patch_dim):
    # Alternatively, train the discriminator network on real and generated images
  if batch_counter % 2 == 0:
        # Generate fake images
  output_images = generator_model.predict(facades)

        # Create a batch of ground truth labels
  labels = np.zeros((output_images.shape[0], 2), dtype=np.uint8)
        labels[:, 0] = 1    else:
        # Take real images
  output_images = images

        # Create a batch of ground truth labels
  labels = np.zeros((output_images.shape[0], 2), dtype=np.uint8)
        labels[:, 1] = 1    patches = []
    for y in range(0, output_images.shape[0], patch_dim[0]):
        for x in range(0, output_images.shape[1], patch_dim[1]):
            image_patches = output_images[:, y: y + patch_dim[0], x: x + patch_dim[1], :]
            patches.append(np.asarray(image_patches, dtype=np.float32))

    return patches, labels
```

前面的功能使用生成器网络生成伪图像,然后从生成的图像中提取补丁。 现在,我们应该有一个补丁列表及其基本真理值。

W
wizardforcel 已提交
1247
13.  现在,在生成的补丁上训练判别器网络:
W
wizardforcel 已提交
1248 1249 1250 1251 1252

```py
d_loss = patchgan_discriminator.train_on_batch(patches, labels)
```

W
wizardforcel 已提交
1253
这将在提取的补丁和地面真相标签上训练判别器网络。
W
wizardforcel 已提交
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267

14.  接下来,训练对抗模型。 对抗性模型将训练生成器网络,但冻结区分器网络的训练。 使用以下代码:

```py
labels = np.zeros((train_images_batch.shape[0], 2), dtype=np.uint8)
        labels[:, 1] = 1   # Train the adversarial model        g_loss = adversarial_model.train_on_batch(train_facades_batch, [train_images_batch, labels])
```

15.  每个小批量完成后增加批计数器:

```py
batch_counter += 1
```

W
wizardforcel 已提交
1268
16.  在每个微型批量上完成一次迭代(循环)后,将损失存储在名为`dis_losses``gen_losses`的列表中:
W
wizardforcel 已提交
1269 1270 1271 1272 1273 1274

```py
dis_losses.append(d_loss)
gen_losses.append(g_loss)
```

W
wizardforcel 已提交
1275
17.  另外,将平均损失存储到  TensorBoard 以进行可视化。 既存储损失,也要存储生成器网络的平均损失和判别器网络的平均损失:
W
wizardforcel 已提交
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297

```py
write_log(tensorboard, 'discriminator_loss', np.mean(dis_losses), 
          epoch)
write_log(tensorboard, 'generator_loss', np.mean(gen_losses), epoch)
```

18.  每 10 个周期后,使用生成器网络生成一组图像:

```py
 # After every 10th epoch, generate and save images for visualization if epoch % 10 == 0:
        # Sample a batch of validation datasets
  val_facades_batch = validation_facade_labels[0:5]
        val_images_batch = validation_facade_photos[0:5]

        # Generate images
  validation_generated_images = unet_generator.predict(val_facades_batch)

        # Save images
  save_images(val_images_batch, val_facades_batch, validation_generated_images, epoch, 'validation', limit=5)
```

W
wizardforcel 已提交
1298
将前面的代码块放入周期循环中。 每隔 10 个时间段,它将生成一批伪图像并将其保存到结果目录。 这里,`save_images()`是如下定义的效用函数:
W
wizardforcel 已提交
1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315

```py
def save_images(real_images, real_sketches, generated_images, num_epoch, dataset_name, limit):
    real_sketches = real_sketches * 255.0
  real_images = real_images * 255.0
  generated_images = generated_images * 255.0    # Save some images only
  real_sketches = real_sketches[:limit]
    generated_images = generated_images[:limit]
    real_images = real_images[:limit]

    # Create a stack of images
  X = np.hstack((real_sketches, generated_images, real_images))

    # Save stack of images
  imwrite('results/X_full_{}_{}.png'.format(dataset_name, num_epoch), X[0])
```

W
wizardforcel 已提交
1316
现在,我们已经成功地在立面数据集上训练了 pix2pix 网络。 对网络进行 1000 个时代的训练,以获取高质量的生成器网络。
W
wizardforcel 已提交
1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332





# 保存模型



在 Keras 中保存模型只需要一行代码。 要保存生成器模型,请添加以下行:

```py
# Specify the path for the generator model
unet_generator.save_weights("generator.h5")
```

W
wizardforcel 已提交
1333
同样,通过添加以下行来保存判别器模型:
W
wizardforcel 已提交
1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351

```py
# Specify the path for the discriminator model
patchgan_discriminator.save_weights("discriminator.h5")
```





# 可视化生成的图像



在将网络训练了 20 个时间段后,网络将开始生成体面的图像:让我们看一下由生成器网络生成的图像。

在 20、50、150 和 200 个时代(从左到右) 之后,图像如下所示:

W
wizardforcel 已提交
1352 1353 1354 1355 1356 1357 1358
![](img/81830986-deb9-4db9-9dba-518aa6313466.png) 

![](img/515bff70-925a-4f3b-a995-470637fd676e.png) 

![](img/550e5193-b6a1-49ba-81eb-cd1fe71c7634.png) 

![](img/277d3877-16f5-4ce5-b5f0-2703bc2d3075.png)
W
wizardforcel 已提交
1359

W
wizardforcel 已提交
1360
每个块均包含垂直堆叠的外观标签,生成的照片和实际图像。 我建议您将网络训练 1000 个周期。 如果一切顺利,则在 1000 个时期之后,生成器网络将开始生成逼真的图像。
W
wizardforcel 已提交
1361 1362 1363 1364 1365 1366 1367 1368 1369





# 可视化损失



W
wizardforcel 已提交
1370
要可视化训练损失,请启动 TensorBoard 服务器,如下所示:
W
wizardforcel 已提交
1371 1372 1373 1374 1375

```py
tensorboard --logdir=logs
```

W
wizardforcel 已提交
1376
现在,在浏览器中打开`localhost:6006`。 TensorBoard 的**标量**部分包含两种损失的图表,如以下屏幕截图所示:
W
wizardforcel 已提交
1377 1378 1379

![](img/ff705572-5fb8-4216-883c-63bbfb809286.png)

W
wizardforcel 已提交
1380
TensorBoard 的标量部分
W
wizardforcel 已提交
1381

W
wizardforcel 已提交
1382
这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止训练,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 尝试使用超参数,然后选择一组您认为可以提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。
W
wizardforcel 已提交
1383 1384 1385 1386 1387





W
wizardforcel 已提交
1388
# 可视化图
W
wizardforcel 已提交
1389 1390 1391



W
wizardforcel 已提交
1392
TensorBoard 的`GRAPHS`部分包含两个网络的图形。 如果网络性能不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的操作:
W
wizardforcel 已提交
1393 1394 1395

![](img/7c0476a9-d31c-4ef3-b3b3-2193680e7c0c.png)

W
wizardforcel 已提交
1396
张量流和每个图形内部的不同操作
W
wizardforcel 已提交
1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415



# pix2pix 网络的实际应用



pix2pix 网络有很多应用。 其中包括:

*   将像素级分割转换为真实图像
*   要将白天图像转换为夜间图像,反之亦然
*   将卫星平面图像转换为地图图像
*   将草图转换为照片
*   将黑白图像转换为彩色图像,反之亦然

以下是从指定的官方文件中拍摄的图像。 它显示了 pix2pix 网络的不同用例:

![](img/beb77a0a-2316-4a3e-9bf9-dead211714b4.png)

W
wizardforcel 已提交
1416
使用条件对抗网络的图像到图像翻译,来源:`arXiv:1611.07004 [cs.CV]`
W
wizardforcel 已提交
1417 1418 1419



W
wizardforcel 已提交
1420
# 总结
W
wizardforcel 已提交
1421 1422 1423



W
wizardforcel 已提交
1424
在本章中,我们了解了 pix2pix 网络是什么,并探讨了其架构。 我们从数据加载开始,准备要训练的数据集,然后准备了项目,并研究了 pix2pix 网络的 Keras 实现。 之后,我们研究了训练 pix2pix 网络的目标函数。 然后,我们在立面数据集上训练了 pix2pix 网络,并探索了 pix2pix 网络的一些实际应用。
W
wizardforcel 已提交
1425 1426 1427 1428

在下一章中,我们将预测 GAN 的未来。 我们将研究 GAN 领域在不久的将来会发生什么,以及它将如何改变我们的行业和我们的日常生活。