7.md 39.1 KB
Newer Older
W
wizardforcel 已提交
1 2 3 4 5 6


# CycleGAN-将绘画变成照片



W
wizardforcel 已提交
7
CycleGAN 是一种**生成对抗网络****GAN**),用于跨域传输任务,例如更改图像的样式,将绘画转变为照片, 反之亦然,例如照片增强功能,更改照片的季节等等。 CycleGAN 由朱俊彦,Taesung Park,Phillip Isola 和 Alexei A. Efros 在题为《使用循环生成对抗网络的非配对图像到图像转换》中引入。 该产品于 2018 年 2 月在加州大学伯克利分校的 **Berkeley AI Research****BAIR**)实验室生产,[可通过以下链接获得](https://arxiv.org/pdf/1703.10593.pdf)。 由于其广泛的使用案例,CycleGAN 在 GAN 社区引起了轰动。 在本章中,我们将与 CycleGAN 一起使用,尤其是使用它们将绘画转换为照片。
W
wizardforcel 已提交
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

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

*   CycleGAN 简介
*   CycleGAN 的架构
*   数据收集与准备
*   Keras 的 CycleGAN 实现
*   目标功能
*   训练 CycleGAN
*   CycleGAN 的实际应用





# CycleGAN 简介



W
wizardforcel 已提交
27
为了将照片变成一幅画或一幅画,再将它们变成照片,普通 GAN 需要一对图像。 CycleGAN 是一种 GAN 网络,可以将图像从一个域 X 转换为另一个域 Y,而无需配对图像。 CycleGAN 尝试学习生成器网络,而生成器网络又学习了两个映射。 CycleGAN 无需训练大多数 GAN 中使用的单个生成器网络,而是训练两个生成器和两个判别器网络。
W
wizardforcel 已提交
28

W
wizardforcel 已提交
29
CycleGAN 中有两个生成器网络,如下所示:
W
wizardforcel 已提交
30

W
wizardforcel 已提交
31 32
1.  **生成器`A`**:学习映射![](img/5ee63015-1266-4f90-b3cc-dca5d99f293e.png),其中`X`是源域,`Y`是目标域。 它从源域`A`拍摄图像,并将其转换为与目标域`B`相似的图像。 基本上,网络的目的是学习映射,以使`G(X)``Y`相似。
2.  **生成器`B`**:学习映射![](img/4afb529e-38ff-40f5-b294-dd6545cd5251.png),然后从目标域`B`中获取图像,并将其转换为与源域`A`中的图像相似的图像。类似地, 网络要学习另一个映射,以便`F(G(X))`类似于`X`)。
W
wizardforcel 已提交
33

W
wizardforcel 已提交
34
这两个网络的架构相同,但我们分别对其进行训练。
W
wizardforcel 已提交
35

W
wizardforcel 已提交
36
CycleGAN 中有两个判别器网络,如下所示:
W
wizardforcel 已提交
37

W
wizardforcel 已提交
38 39
1.  **判别器`A`**:判别器`A`的工作是区分由生成器网络`A`生成的图像,这些图像表示为`G(X)`和来自源域`A`的真实图像,它们表示为`X`
2.  **判别器`B`**:判别器`B`的工作是区分由生成器网络`B`生成的图像,这些图像表示为`F(Y)`以及来自源域`B`的真实图像,它们表示为`Y`
W
wizardforcel 已提交
40 41 42 43 44

两个网络的架构是相同的。 与生成器网络类似,我们分别训练区分器网络。 如下图所示:

![](img/969f8518-7d6b-42ce-9030-fdc7ca31818b.png)

W
wizardforcel 已提交
45
具有两个生成器和两个对抗性判别器网络的 CycleGAN 的插图。来源:[`arxiv:1703.10593`](https://arxiv.org/pdf/1703.10593.pdf)
W
wizardforcel 已提交
46

W
wizardforcel 已提交
47
在下一节中,让我们详细了解 CycleGAN 的架构。
W
wizardforcel 已提交
48 49 50 51 52 53 54 55 56





# CycleGAN 的架构



W
wizardforcel 已提交
57
CycleGAN 总体上由两种架构组成:生成器和判别器。 生成器架构用于创建两个模型,生成器 A 和生成器 B。判别器架构用于创建另外两个模型,判别器 A 和判别器 B。我们现在将在接下来的两节中介绍两个网络的架构。
W
wizardforcel 已提交
58 59 60 61 62





W
wizardforcel 已提交
63
# 生成器的架构
W
wizardforcel 已提交
64 65 66 67 68



生成器网络是自动编码器类型的网络。 它以图像作为输入并输出另一个图像。 它由两部分组成:编码器和解码器。 编码器包含具有下采样功能的卷积层,并将`128x128x3`形状的输入转换为内部表示。 解码器包含两个上采样块和最后一个卷积层,该层将内部表示形式转换为`128x128x3`形状的输出。

W
wizardforcel 已提交
69
生成器网络包含以下块:
W
wizardforcel 已提交
70 71

*   卷积块
W
wizardforcel 已提交
72
*   残差块
W
wizardforcel 已提交
73 74 75 76 77
*   上采样块
*   最后的卷积层

让我们逐一介绍每个组件:

W
wizardforcel 已提交
78
*   **卷积块**:卷积块包含 2D 卷积层,然后是实例规范化层和 relu 作为激活函数。 请参阅第 1 章,“生成对抗网络简介”,以了解有关“实例规范化”的更多信息。
W
wizardforcel 已提交
79 80 81

生成器网络包含三个卷积块,其配置如下:

W
wizardforcel 已提交
82
| **层名称** | **超参数** | **输入形状** | **输出形状** |
W
wizardforcel 已提交
83 84 85 86 87 88 89 90 91 92
| --- | --- | --- | --- |
| 2D 卷积层 | `filters=32, kernel_size=7, strides=1, padding='same'` | `(128, 128, 3)` | `(128, 128, 32)` |
| 实例规范化层 | `axis=1` | `(128, 128, 32)` | `(128, 128, 32)` |
| 激活层 | `activation='relu'` | `(128, 128, 32)` | `(128, 128, 32)` |
| 2D 卷积层 | `filters=64, kernel_size=3, strides=2, padding='same'` | `(128, 128, 32)` | `(64, 64, 64)` |
| 实例规范化层 | `axis=1` | `(64, 64, 64)` | `(64, 64, 64)` |
| 激活层 | `activation='relu'` | `(64, 64, 64)` | `(64, 64, 64)` |
| 2D 卷积层 | `filters=128, kernel_size=3, strides=2, padding='same'` | `(64, 64, 64)` | `(32, 32, 128)` |
| 实例规范化层 | `axis=1` | `(32, 32, 128)` | `(32, 32, 128)` |
| 激活层 | `activation='relu'` | `(32, 32, 128)` | `(32, 32, 128)` |
W
wizardforcel 已提交
93

W
wizardforcel 已提交
94
*   **残差块**:残差块包含两个 2D 卷积层。 两层之后是动量值等于 0.8 的批归一化层。 生成器网络包含六个残差块,其配置如下:
W
wizardforcel 已提交
95

W
wizardforcel 已提交
96
| **层名称** | **超参数** | **输入形状** | **输出形状** |
W
wizardforcel 已提交
97 98 99 100 101
| 2D 卷积层 | `filters=128, kernel_size=3, strides=1, padding='same'` | `(32, 32, 128)` | `(32, 32, 128)` |
| 批量归一化层 | `axis=3, momentum=0.9, epsilon=1e-5` | `(32, 32, 128)` | `(32, 32, 128)` |
| 2D 卷积层 | `filters=138, kernel_size=3, strides=1, padding='same'` | `(32, 32, 128)` | (`(32, 32, 128)` |
| 批量归一化层 | `axis=3, momentum=0.9, epsilon=1e-5` | `(32, 32, 128)` | `(32, 32, 128)` |
| 加法层 | `None` | `(32, 32, 128)` | `(32, 32, 128)` |
W
wizardforcel 已提交
102 103 104

加法层计算输入到块的  张量  与最后一批归一化层的输出之和。

W
wizardforcel 已提交
105
*   **上采样块**:上采样块包含 2D 转置卷积层,并使用`relu`作为激活函数。 生成器网络中有两个上采样模块。 第一个升采样模块的配置如下:
W
wizardforcel 已提交
106

W
wizardforcel 已提交
107
| **层名称** | **超参数** | **输入形状** | **输出形状** |
W
wizardforcel 已提交
108 109 110
| 2D 转置卷积层 | `filters=64, kernel_size=3, strides=2, padding='same', use_bias=False` | `(32, 32, 128)` | `(64, 64, 64)` |
| 实例规范化层 | `axis=1` | `(64, 64, 64)` | `(64, 64, 64)` |
| 激活层 | `activation='relu'` | `(64, 64, 64)` | `(64, 64, 64)` |
W
wizardforcel 已提交
111 112 113

第二个上采样模块的配置如下:

W
wizardforcel 已提交
114
| **层名称** | **超参数** | **输入形状** | **输出形状** |
W
wizardforcel 已提交
115 116 117
| 2D 转置卷积层 | `filters=32, kernel_size=3, strides=2, padding='same', use_bias=False` | `(64, 64, 64)` | `(128, 128, 32)` |
| 实例规范化层 | `axis=1` | `(128, 128, 32)` | `(128, 128, 32)` |
| 激活层 | `activation='relu'` | `(128, 128, 32)` | `(128, 128, 32)` |
W
wizardforcel 已提交
118

W
wizardforcel 已提交
119
*   **最后的卷积层**:最后一层是使用`tanh`作为激活函数的 2D 卷积层。 它生成形状为`(256, 256, 3)`的图像。 最后一层的配置如下:
W
wizardforcel 已提交
120

W
wizardforcel 已提交
121
| **层名称** | **超参数** | **输入形状** | **输出形状** |
W
wizardforcel 已提交
122
| 2D 卷积层 | `filters=3, kernel_size=7, strides=1, padding='same', activation='tanh'` | `(128, 128, 32)` | `(128, 128, 3)` |
W
wizardforcel 已提交
123 124 125 126 127

These hyperparameters are best suited for the Keras framework. If you are using any other framework, modify accordingly.



W
wizardforcel 已提交
128
# 判别器的架构
W
wizardforcel 已提交
129 130 131



W
wizardforcel 已提交
132
判别器网络的架构类似于 PatchGAN 网络中的判别器架构。 它是一个深度卷积神经网络,包含多个卷积块。 基本上,它会拍摄形状为`(128, 128, 3)`的图像,并预测该图像是真实的还是假的。 它包含几个 ZeroPadding2D 层,[可以在以下链接中找到其文档](https://keras.io/layers/convolutional/#zeropadding2d)。 下表详细显示了判别器网络的架构:
W
wizardforcel 已提交
133

W
wizardforcel 已提交
134
| **层名称** | **超参数** | **输入形状** | **输出形状** |
W
wizardforcel 已提交
135 136 137
| 输入层 | `none` | `(128, 128, 3)` | `(128, 128, 3)` |
| ZeroPadding2D 层 | `padding(1, 1)` | `(128, 128, 3)` | `(130, 130, 3)` |
| 2D 卷积层 | `filters=64, kernel_size=4, strides=2, padding='valid'` | `(130, 130, 3)` | `(64, 64, 64)` |
W
wizardforcel 已提交
138
| 激活层 | `activation='leakyrelu', alpha=0.2` | `(64, 64, 64)` | `(64, 64, 64)` |
W
wizardforcel 已提交
139 140 141 142 143 144
| ZeroPadding2D 层 | `padding(1, 1)` | `(64, 64, 64)` | `(66, 66, 64)` |
| 2D 卷积层 | `filters=128, kernel_size=4, strides=2, padding='valid'` | `(66, 66, 64)` | `(32, 32, 128)` |
| 实例规范化层 | `axis=1` | `(32, 32, 128)` | `(32, 32, 128)` |
| 激活层 | `activation='leakyrelu', alpha=0.2` | `(32, 32, 128)` | `(32, 32, 128)` |
| ZeroPadding2D 层 | `padding(1, 1)` | `(32, 32, 128)` | `(34, 34, 128)` |
| 2D 卷积层 | `filters=256, kernel_size=4, strides=2, padding='valid'` | `(34, 34, 128)` | `(16, 16, 256)` |
W
wizardforcel 已提交
145 146
| 实例规范化层 | `axis=1` | `(16, 16, 256)` | `(16, 16, 256)` |
| 激活层 | `activation='leakyrelu', alpha=0.2` | `(16, 16, 256)` | `(16, 16, 256)` |
W
wizardforcel 已提交
147 148
| ZeroPadding2D 层 | `padding(1, 1)` | `(16, 16, 256)` | `(18, 18, 256)` |
| 2D 卷积层 | `filters=512, kernel_size=4, strides=2, padding='valid'` | `(18, 18, 256)` | `(8, 8, 512)` |
W
wizardforcel 已提交
149 150
| 实例规范化层 | `axis=1` | `(8, 8, 512)` | `(8, 8, 512)` |
| 激活层 | `activation='leakyrelu', alpha=0.2` | `(8, 8, 512)` | `(8, 8, 512)` |
W
wizardforcel 已提交
151 152
| ZeroPadding2D 层 | `padding(1, 1)` | `(8, 8, 512)` | `(10, 10, 512)` |
| 2D 卷积层 | `filters=1, kernel_size=4, strides=1, padding='valid', activation='sigmoid'` | `(10, 10, 512)` | `(7, 7, 1)` |
W
wizardforcel 已提交
153

W
wizardforcel 已提交
154
判别器网络返回形状为`(7, 7, 1)`的张量。 现在,我们已经介绍了这两个网络的详细架构。 在下一节中,我们将介绍训练 CycleGAN 所需的目标函数。
W
wizardforcel 已提交
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178

The ZeroPadding2D layer adds rows and columns of zeros at the top, bottom, left, and right of an image tensor.



# 训练目标功能



与其他 GAN 相似,CycleGAN 具有训练目标函数,我们需要将其最小化以训练模型。 损失函数是以下损失的加权总和:

1.  对抗损失
2.  循环一致性损失

在以下各节中,让我们详细研究对抗性损失和周期一致性损失。





# 对抗损失



W
wizardforcel 已提交
179
对抗性损失是实际分布`A``B`的图像与生成器网络生成的图像之间的损失。 我们有两个映射功能,我们将对两个映射应用对抗性损失。
W
wizardforcel 已提交
180 181 182 183 184

映射![](img/4777940b-3156-48bb-8ab8-a0b8d3d97978.png)的对抗损失如下所示:

![](img/f278fd84-eb49-4d88-a98e-31396fe4ea3a.png)

W
wizardforcel 已提交
185
在此,`x`是来自分布`A`的一个域的图像,`y`是来自分布`B`的另一个域的图像。判别器![](img/c420501d-952a-4e5b-84b0-0aa891624a10.png)试图区分`G`映射(![](img/e57cd8de-afc5-4e00-a0d4-b87786d7318f.png))生成的图像和来自不同的分布`B`的真实图像`y`。判别器![](img/e0cef545-08a1-4089-a238-9b6945c1d821.png)试图区分`F`映射生成的图像(![](img/968121df-410e-4bff-9e76-57d447e34db5.png))和来自分布`A`的真实图像`x``G`的目的是使对抗损失函数最小 对抗对手`D`,后者不断尝试使其最大化。
W
wizardforcel 已提交
186 187 188 189 190 191 192 193 194





# 循环一致性损失



W
wizardforcel 已提交
195
仅使用对抗损失的问题在于,网络可以将同一组输入图像映射到目标域中图像的任何随机排列。 因此,任何学习的映射都可以学习类似于目标分布的输出分布。 ![](img/95c36bf0-5253-4227-9d80-7ab5e826094a.png)和![](img/de2f63f2-3d6f-4cb6-90d0-9a9789508d31.png)之间可能有许多可能的映射功能。 循环一致性损失通过减少可能的映射数来克服了这个问题。 周期一致映射功能是可以将图像`x`从域`A`转换为域`B`中的另一个图像`y`并生成原始图像的功能。
W
wizardforcel 已提交
196 197 198 199 200 201 202 203 204 205 206 207 208

前向循环一致性映射功能如下所示:

![](img/b6e02c98-294c-42d4-b687-3c2c7e84644a.png)

向后循环一致映射函数如下所示:

![](img/bf736fcf-a789-4a17-b1a9-a452b23e07f9.png)

循环一致性损失的公式如下:

![](img/bae8d146-d70d-479e-bc91-801a8140d502.png)

W
wizardforcel 已提交
209
由于循环一致性损失,由`F(G(x))``G(F(y))`重构的图像分别类似于`x``y`
W
wizardforcel 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222





# 完整目标功能



完整目标函数是对抗损失和周期一致性损失两者的加权总和,表示如下:

![](img/b72c39e8-2d9b-487b-83df-3b73ab2c550f.png)

W
wizardforcel 已提交
223
在此,![](img/a04311c3-8eb2-45e2-98c0-e691deb818e0.png)是第一个对抗性损失,![](img/c9de7008-1df0-4381-92f5-fcfc5aa276a3.png)是第二个对抗性损失。 在生成器`A`和判别器`B`上计算第一个对抗损失。在生成器`B`和判别器`A`上计算第二个对抗损失。
W
wizardforcel 已提交
224 225 226 227 228

要训​​练 CycleGAN,我们需要优化以下功能:

![](img/b0e30420-c79d-434a-b6d9-6fcaaa2a61ad.png)

W
wizardforcel 已提交
229
前面的等式表明,训练一个 CycleGAN,需要最小化生成器网络的损失,并使判别器网络的损失最大化。 优化之后,我们将获得一组训练有素的网络,能够从绘画中生成照片。
W
wizardforcel 已提交
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





# 设置项目



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

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

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

2.  现在,将目录从当前目录更改为  `Chapter07`,如以下示例所示:

```py
cd Chapter07
```

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 已提交
291
在本章中,我们将使用`monet2photo`数据集。 该数据集是开放源代码,可以由 UC Berkeley 的 **Berkeley AI Research****BAIR**)实验室使用。 [您可以从以下链接选择手动下载数据集](https://people.eecs.berkeley.edu/~taesung_park/CycleGAN/datasets/monet2photo.zip)
W
wizardforcel 已提交
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307

下载后,将其解压缩到根目录中。

或者,要自动下载数据集,请执行以下命令:

```py
wget https://people.eecs.berkeley.edu/~taesung_park/CycleGAN/datasets/monet2photo.zip
upzip monet2photo.zip
```

这些命令将下载数据集并将其解压缩到项目的根目录中。

The `monet2photo` dataset is available for educational purposes only. To use it in commercial projects, you have to get permission from the BAIR laboratory, UC Berkeley. We don't hold the copyright for the images available in the dataset.



W
wizardforcel 已提交
308
# 硬实现 CycleGAN
W
wizardforcel 已提交
309 310 311



W
wizardforcel 已提交
312
如本章前面“CycleGAN 简介”部分中所述,CycleGAN 具有两种网络架构,即生成器网络和判别器网络。 在本节中,我们将编写所有网络的实现。
W
wizardforcel 已提交
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332

但是,在开始编写实现之前,请创建一个 Python 文件`main.py`并导入基本模块,如下所示:

```py
from glob import glob
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from keras import Input, Model
from keras.layers import Conv2D, BatchNormalization, Activation, Add, Conv2DTranspose, \
    ZeroPadding2D, LeakyReLU
from keras.optimizers import Adam
from keras_contrib.layers import InstanceNormalization
from scipy.misc import imread, imresize
```





W
wizardforcel 已提交
333
# 生成器网络
W
wizardforcel 已提交
334 335 336



W
wizardforcel 已提交
337
在本章前面的“生成器网络的架构”部分中,我们已经探讨了生成器网络的架构。 让我们首先在 Keras 框架中编写生成器网络的层,然后使用 Keras 框架的函数式 API 创建 Keras 模型。
W
wizardforcel 已提交
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353

执行以下步骤以在 Keras 中实现生成器网络:

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

```py
input_shape = (128, 128, 3)
residual_blocks = 6
```

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

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

W
wizardforcel 已提交
354
3.  将第一个卷积块与先前在*生成器网络*部分的架构中指定的超参数相加,如下所示:
W
wizardforcel 已提交
355 356 357 358 359 360 361 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

```py
x = Conv2D(filters=32, kernel_size=7, strides=1, padding="same")(input_layer)
x = InstanceNormalization(axis=1)(x)
x = Activation("relu")(x)
```

4.  添加第二个卷积块,如下所示:

```py
x = Conv2D(filters=64, kernel_size=3, strides=2, padding="same")(x)
x = InstanceNormalization(axis=1)(x)
x = Activation("relu")(x)
```

5.  添加第三个卷积块,如下所示:

```py
x = Conv2D(filters=128, kernel_size=3, strides=2, padding="same")(x)
x = InstanceNormalization(axis=1)(x)
x = Activation("relu")(x)
```

6.  定义残差块,如下所示:

```py
def residual_block(x):
    """
 Residual block """  res = Conv2D(filters=128, kernel_size=3, strides=1, padding="same")(x)
    res = BatchNormalization(axis=3, momentum=0.9, epsilon=1e-5)(res)
    res = Activation('relu')(res)

    res = Conv2D(filters=128, kernel_size=3, strides=1, padding="same")(res)
    res = BatchNormalization(axis=3, momentum=0.9, epsilon=1e-5)(res)

    return Add()([res, x])
```

现在,使用`residual_block()`函数向模型添加六个残差块,如以下示例所示:

```py
for _ in range(residual_blocks):
    x = residual_block(x)
```

7.  接下来,添加一个升采样块,如下所示:

```py
x = Conv2DTranspose(filters=64, kernel_size=3, strides=2, padding='same', use_bias=False)(x)
x = InstanceNormalization(axis=1)(x)
x = Activation("relu")(x)
```

8.  添加另一个升采样模块,如下所示:

```py
x = Conv2DTranspose(filters=32, kernel_size=3, strides=2, padding='same', use_bias=False)(x)
x = InstanceNormalization(axis=1)(x)
x = Activation("relu")(x)
```

9.  最后,添加输出卷积层,如下所示:

```py
x = Conv2D(filters=3, kernel_size=7, strides=1, padding="same")(x)
output = Activation('tanh')(x)
```

W
wizardforcel 已提交
423
这是生成器网络的最后一层。 它生成形状为`(128, 128, 3)`的图像。
W
wizardforcel 已提交
424 425 426 427 428 429 430

10.  现在,通过为网络指定`inputs``outputs`来创建 Keras 模型,如下所示:

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

W
wizardforcel 已提交
431
生成器网络的整个代码如下所示:
W
wizardforcel 已提交
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477

```py
def build_generator():
    """
 Create a generator network using the hyperparameter values defined below """  input_shape = (128, 128, 3)
    residual_blocks = 6
  input_layer = Input(shape=input_shape)

    # First Convolution block
  x = Conv2D(filters=32, kernel_size=7, strides=1, padding="same")(input_layer)
    x = InstanceNormalization(axis=1)(x)
    x = Activation("relu")(x)

    # 2nd Convolution block
  x = Conv2D(filters=64, kernel_size=3, strides=2, padding="same")(x)
    x = InstanceNormalization(axis=1)(x)
    x = Activation("relu")(x)

    # 3rd Convolution block
  x = Conv2D(filters=128, kernel_size=3, strides=2, padding="same")(x)
    x = InstanceNormalization(axis=1)(x)
    x = Activation("relu")(x)

    # Residual blocks
  for _ in range(residual_blocks):
        x = residual_block(x)

    # Upsampling blocks
     # 1st Upsampling block
  x = Conv2DTranspose(filters=64, kernel_size=3, strides=2, padding='same', use_bias=False)(x)
    x = InstanceNormalization(axis=1)(x)
    x = Activation("relu")(x)

    # 2nd Upsampling block
  x = Conv2DTranspose(filters=32, kernel_size=3, strides=2, padding='same', use_bias=False)(x)
    x = InstanceNormalization(axis=1)(x)
    x = Activation("relu")(x)

    # Last Convolution layer
  x = Conv2D(filters=3, kernel_size=7, strides=1, padding="same")(x)
    output = Activation('tanh')(x)

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

W
wizardforcel 已提交
478
我们已经成功地为生成器网络创建了 Keras 模型。 在下一节中,我们将为判别器网络创建 Keras 模型。
W
wizardforcel 已提交
479 480 481 482 483





W
wizardforcel 已提交
484
# 判别器网络
W
wizardforcel 已提交
485 486 487



W
wizardforcel 已提交
488
我们已经在“判别器网络的架构”部分中探索了判别器网络的架构。让我们首先在 Keras 框架中编写判别器网络的层,然后使用 Keras 框架的函数式 API 创建 Keras 模型。
W
wizardforcel 已提交
489

W
wizardforcel 已提交
490
执行以下步骤以在 Keras 中实现判别器网络:
W
wizardforcel 已提交
491

W
wizardforcel 已提交
492
1.  首先定义判别器网络所需的超参数,如下所示:
W
wizardforcel 已提交
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510

```py
input_shape = (128, 128, 3)
hidden_layers = 3
```

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

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

3.  接下来,添加一个二维零填充层,如下所示:

```py
x = ZeroPadding2D(padding=(1, 1))(input_layer)
```

W
wizardforcel 已提交
511
该层将在`x``y`轴上为输入张量添加填充。
W
wizardforcel 已提交
512

W
wizardforcel 已提交
513
4.  接下来,使用先前在*判别器网络*部分的架构中指定的超参数添加卷积块,如下所示:
W
wizardforcel 已提交
514 515 516 517 518 519 520 521 522 523 524 525

```py
x = Conv2D(filters=64, kernel_size=4, strides=2, padding="valid")(x)
x = LeakyReLU(alpha=0.2)(x)
```

5.  接下来,添加另一个 2D 零填充层,如下所示:

```py
x = ZeroPadding2D(padding=(1, 1))(x)
```

W
wizardforcel 已提交
526
6.  接下来,使用先前在*判别器网络* 部分中指定的超参数添加三个卷积块,如下所示:
W
wizardforcel 已提交
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549

```py
for i in range(1, hidden_layers + 1):
    x = Conv2D(filters=2 ** i * 64, kernel_size=4, strides=2, padding="valid")(x)
    x = InstanceNormalization(axis=1)(x)
    x = LeakyReLU(alpha=0.2)(x)
    x = ZeroPadding2D(padding=(1, 1))(x)
```

每个卷积块都有两个卷积层,实例规范化层,激活层和 2D 零填充层。

7.  现在,将最终(`output`)卷积层添加到网络,如下所示:

```py
output = Conv2D(filters=1, kernel_size=4, strides=1, activation="sigmoid")(x)
```

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

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

W
wizardforcel 已提交
550
判别器网络的整个代码如下所示:
W
wizardforcel 已提交
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580

```py
def build_discriminator():
    """
 Create a discriminator network using the hyperparameter values defined below """  input_shape = (128, 128, 3)
    hidden_layers = 3    input_layer = Input(shape=input_shape)

    x = ZeroPadding2D(padding=(1, 1))(input_layer)

    # 1st Convolutional block
  x = Conv2D(filters=64, kernel_size=4, strides=2, padding="valid")(x)
    x = LeakyReLU(alpha=0.2)(x)

    x = ZeroPadding2D(padding=(1, 1))(x)

    # 3 Hidden Convolution blocks
  for i in range(1, hidden_layers + 1):
        x = Conv2D(filters=2 ** i * 64, kernel_size=4, strides=2, padding="valid")(x)
        x = InstanceNormalization(axis=1)(x)
        x = LeakyReLU(alpha=0.2)(x)

        x = ZeroPadding2D(padding=(1, 1))(x)

    # Last Convolution layer
  output = Conv2D(filters=1, kernel_size=4, strides=1, activation="sigmoid")(x)

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

W
wizardforcel 已提交
581
我们也已经成功地为判别器网络创建了 Keras 模型。 在下一部分中,我们将训练网络。
W
wizardforcel 已提交
582 583 584 585 586 587 588 589 590





# 训练 CycleGAN



W
wizardforcel 已提交
591
我们已经在“CycleGANs 简介”部分中介绍了训练目标功能。 我们还为两个网络分别创建了 Keras 模型。 训练 CycleGAN 是一个多步骤的过程。 我们将执行以下步骤来训练网络:
W
wizardforcel 已提交
592 593

1.  加载数据集
W
wizardforcel 已提交
594
2.  创建生成器和判别器网络
W
wizardforcel 已提交
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
3.  训练网络以达到指定次数
4.  绘制损失
5.  生成新图像

让我们在开始训练网络之前定义基本变量,如下所示:

```py
data_dir = "/Path/to/dataset/directory/*.*" batch_size = 1 epochs = 500
```





# 加载数据集



在执行其他任何操作之前,请执行以下步骤来加载数据集:

1.  首先使用`glob`模块创建图像路径列表,如下所示:

```py
imagesA = glob(data_dir + '/testA/*.*')
imagesB = glob(data_dir + '/testB/*.*')
```

W
wizardforcel 已提交
622
我们具有来自两个域`A``B`的数据,这就是为什么我们创建了两个列表的原因。
W
wizardforcel 已提交
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

2.  接下来,遍历列表。 在循环中加载,调整大小和水平翻转图像,如下所示:

```py
allImagesA = []
allImagesB = []
 # Iterate over the lists
for index, filename in enumerate(imagesA):
     # Load images
     imgA = imread(filename, mode='RGB')
     imgB = imread(imagesB[index], mode='RGB')

     # Resize images
     imgA = imresize(imgA, (128, 128))
     imgB = imresize(imgB, (128, 128))

     # Randomly horizontally flip images
     if np.random.random() > 0.5:
         imgA = np.fliplr(imgA)
         imgB = np.fliplr(imgB)

     allImagesA.append(imgA)
     allImagesB.append(imgB)
```

W
wizardforcel 已提交
648
3.  现在,对图像进行归一化以使像素值在 -1 和 1 之间的范围内,如下所示:
W
wizardforcel 已提交
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

```py
# Normalize images allImagesA = np.array(allImagesA) / 127.5 - 1. allImagesB = np.array(allImagesB) / 127.5 - 1.
```

加载数据集的整个代码如下所示:

```py
def load_images(data_dir):
    imagesA = glob(data_dir + '/testA/*.*')
    imagesB = glob(data_dir + '/testB/*.*')

    allImagesA = []
    allImagesB = []

    for index, filename in enumerate(imagesA):
        # Load images
        imgA = imread(filename, mode='RGB')
        imgB = imread(imagesB[index], mode='RGB')

        # Resize images
        imgA = imresize(imgA, (128, 128))
        imgB = imresize(imgB, (128, 128))

        # Randomly horizontally flip images
        if np.random.random() > 0.5:
            imgA = np.fliplr(imgA)
            imgB = np.fliplr(imgB)

        allImagesA.append(imgA)
        allImagesB.append(imgB)

    # Normalize images
  allImagesA = np.array(allImagesA) / 127.5 - 1.
  allImagesB = np.array(allImagesB) / 127.5 - 1.    return allImagesA, allImagesB
```

W
wizardforcel 已提交
686
前面的函数将返回两个 Numpy `ndarray`。 在开始训练之前,我们将使用它来加载和预处理图像。
W
wizardforcel 已提交
687 688 689 690 691 692 693 694 695





# 建立和编译网络



W
wizardforcel 已提交
696
在本节中,让我们构建必要的网络并准备进行训练。 执行以下步骤:
W
wizardforcel 已提交
697

W
wizardforcel 已提交
698
1.  首先定义训练所需的优化器,如以下代码所示:
W
wizardforcel 已提交
699 700 701 702 703 704 705

```py
# Define the common optimizer common_optimizer = Adam(0.0002, 0.5)
```

我们将使用`Adam`优化器,其中`learning_rate`等于 0.0002,并且`beta_1`值等于 0.5。

W
wizardforcel 已提交
706
2.  首先创建判别器网络,如以下代码所示:
W
wizardforcel 已提交
707 708 709 710 711 712

```py
discriminatorA = build_discriminator()
discriminatorB = build_discriminator()
```

W
wizardforcel 已提交
713
如“判别器网络的架构”部分所述,CycleGAN 具有两个判别器网络。
W
wizardforcel 已提交
714 715 716 717 718 719 720 721 722 723

3.  接下来,编译网络,如下所示:

```py
discriminatorA.compile(loss='mse', optimizer=common_optimizer, metrics=['accuracy'])
discriminatorB.compile(loss='mse', optimizer=common_optimizer, metrics=['accuracy'])
```

使用`mse`作为损失函数,并使用`accuracy`作为度量标准来编译网络。

W
wizardforcel 已提交
724
4.  接下来,创建生成器网络`A``generatorAToB`)和`B``generatorBToA`)。 生成器网络`A`的输入是数据集`A`的真实图像(`realA`),输出将是重构图像(`fakeB`)。 生成器网络`B`的输入是来自数据集`B`的真实图像(`realB`),输出将是重构图像(`fakeA`),如下所示:
W
wizardforcel 已提交
725 726 727 728 729 730

```py
generatorAToB = build_generator()
generatorBToA = build_generator()
```

W
wizardforcel 已提交
731
如“CycleGAN 的架构”部分所述,CycleGAN 具有两个生成器网络。 `generatorAToB`会将图像从域`A`转换为域`B``generatorBToA`会将图像从域`B`转换为域`A`
W
wizardforcel 已提交
732

W
wizardforcel 已提交
733
现在,我们已经创建了两个生成器网络和两个判别器网络。 在下一个小节中,我们将创建并编译一个对抗网络。
W
wizardforcel 已提交
734 735 736 737 738 739 740 741 742





# 创建和编译对抗网络



W
wizardforcel 已提交
743
对抗网络是一个组合网络。 它在单个 Keras 模型中使用所有四个网络。 创建对抗网络的主要目的是训练生成器网络。 当我们训练对抗网络时,它只训练生成器网络,但冻结了判别器网络的训练。 让我们创建一个具有所需功能的对抗模型。
W
wizardforcel 已提交
744 745 746 747 748 749 750 751

1.  首先为网络创建两个输入层,如下所示:

```py
inputA = Input(shape=(128, 128, 3))
inputB = Input(shape=(128, 128, 3))
```

W
wizardforcel 已提交
752
两个输入都将拍摄尺寸为`(128, 128, 3)`的图像。 这些是符号输入变量,不包含实际值。 它们用于创建 Keras 模型(TensorFlow 图)。
W
wizardforcel 已提交
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776

2.  接下来,使用生成器网络生成伪造图像,如下所示:

```py
generatedB = generatorAToB(inputA)
generatedA = generatorBToA(inputB)
```

使用符号输入层生成图像。

3.  现在,再次使用生成器网络重建原始图像,如下所示:

```py
reconstructedA = generatorBToA(generatedB)
reconstructedB = generatorAToB(generatedA)
```

4.  使用生成器网络生成伪造图像,如下所示:

```py
generatedAId = generatorBToA(inputA)
generatedBId = generatorAToB(inputB)
```

W
wizardforcel 已提交
777
生成器网络`A``generatorAToB`)将图像从域`A`转换为域`B`。类似地,生成器网络`B``generatorBToA`)将图像从域`B`转换为域`A`
W
wizardforcel 已提交
778

W
wizardforcel 已提交
779
5.  接下来,使两个判别器网络均不可训练,如下所示:
W
wizardforcel 已提交
780 781 782 783 784

```py
discriminatorA.trainable = False discriminatorB.trainable = False
```

W
wizardforcel 已提交
785
我们不想在我们的对抗网络中训练判别器网络。
W
wizardforcel 已提交
786

W
wizardforcel 已提交
787
6.  使用判别器网络来预测每个生成的图像是真实的还是伪造的,如下所示:
W
wizardforcel 已提交
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813

```py
probsA = discriminatorA(generatedA)
probsB = discriminatorB(generatedB)
```

7.  创建 Keras 模型并指定网络的输入和输出,如下所示:

```py
adversarial_model = Model(inputs=[inputA, inputB],outputs=[probsA, probsB, reconstructedA, reconstructedB, generatedAId, generatedBId])
```

我们的对抗网络将采用两个输入值(即张量),并返回六个输出值(即张量)。

8.  接下来,按如下所示编译对抗网络:

```py
adversarial_model.compile(loss=['mse', 'mse', 'mae', 'mae', 'mae', 'mae'],
  loss_weights=[1, 1, 10.0, 10.0, 1.0, 1.0],
  optimizer=common_optimizer)
```

对抗网络返回六个值,我们需要为每个输出值指定损失函数。 对于前两个值,我们使用**均方误差**损失,因为这是**对抗性损失**的一部分。 对于接下来的四个值,我们使用**平均绝对误差**损失,这是周期一致性损失的一部分。 六个不同损失的权重值为 1,1,10.0,10.0,1.0,1.0。 我们正在使用`common_optimizer`训练网络。

现在,我们已经成功为对抗网络创建了 Keras 模型。 如果您在理解 Keras 模型的工作方式时遇到困难,请查看 TensorFlow 图及其功能的文档。

W
wizardforcel 已提交
814
在开始训练之前,请执行以下两个基本步骤。 TensorBoard 将在后面的部分中使用:
W
wizardforcel 已提交
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833

添加 TensorBoard 来存储损失和图形以用于可视化目的,如下所示:

```py
tensorboard = TensorBoard(log_dir="logs/{}".format(time.time()), write_images=True, write_grads=True,
  write_graph=True)
tensorboard.set_model(generatorAToB)
tensorboard.set_model(generatorBToA)
tensorboard.set_model(discriminatorA)
tensorboard.set_model(discriminatorB)
```

创建一个包含所有等于 1 的值的 4 维数组,该数组表示真实标签。 同样,创建另一个所有值均等于零的三维数组,其中 代表伪标签,如下所示:

```py
real_labels = np.ones((batch_size, 7, 7, 1))
fake_labels = np.zeros((batch_size, 7, 7, 1))
```

W
wizardforcel 已提交
834
使用 numpy 的`ones()``zeros()`函数创建所需的`ndarray`。 现在我们已经准备好基本组件,让我们开始训练。
W
wizardforcel 已提交
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860





# 开始训练



要针对指定的时期数训练网络,请执行以下步骤:

1.  首先加载两个域的数据集,如下所示:

```py
imagesA, imagesB = load_images(data_dir=data_dir)
```

我们已经在*中定义了`load_images`功能。*

2.  接下来,创建一个`for`循环,该循环应运行由时期数指定的次数,如下所示:

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

W
wizardforcel 已提交
861
3.  创建两个列表以存储所有迷你批量的损失,如下所示:
W
wizardforcel 已提交
862 863 864 865 866 867 868 869 870 871 872 873 874 875

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

4.  计算`epochs`循环内的迷你批处理数量,如下所示:

```py
num_batches = int(min(imagesA.shape[0], imagesB.shape[0]) /  
                  batch_size)
print("Number of batches:{}".format(num_batches))
```

W
wizardforcel 已提交
876
5.  接下来,在周期循环内创建另一个循环,并使其运行`num_batches`指定的次数,如下所示:
W
wizardforcel 已提交
877 878 879 880 881 882

```py
 for index in range(num_batches):
        print("Batch:{}".format(index))
```

W
wizardforcel 已提交
883
我们用于判别器网络和对抗网络训练的整个代码将在此循环内。
W
wizardforcel 已提交
884 885 886 887 888





W
wizardforcel 已提交
889
# 训练判别器网络
W
wizardforcel 已提交
890 891 892



W
wizardforcel 已提交
893
本小节中的代码是上一节中代码的延续。 在这里,您将看到如何训练判别器网络:
W
wizardforcel 已提交
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908

1.  首先对两个域的图像进行小批量采样,如以下代码所示:

```py
        batchA = imagesA[index * batch_size:(index + 1) * batch_size]
        batchB = imagesB[index * batch_size:(index + 1) * batch_size]
```

2.  接下来,使用生成器网络生成伪造图像,如下所示:

```py
        generatedB = generatorAToB.predict(batchA)
        generatedA = generatorBToA.predict(batchB)
```

W
wizardforcel 已提交
909
3.  然后,对判别器网络`A`进行真实图像和伪图像(由生成器网络生成)的训练,如下所示:
W
wizardforcel 已提交
910 911 912 913 914 915

```py
        dALoss1 = discriminatorA.train_on_batch(batchA, real_labels)
        dALoss2 = discriminatorB.train_on_batch(generatedA, fake_labels)
```

W
wizardforcel 已提交
916
此步骤将在真实图像和伪图像的微型批量上训练判别器`A`,并会稍微改善网络。
W
wizardforcel 已提交
917

W
wizardforcel 已提交
918
4.  接下来,对判别器`B`进行真实图像和伪图像的训练,如下所示:
W
wizardforcel 已提交
919 920 921 922 923 924

```py
dBLoss1 = discriminatorB.train_on_batch(batchB, real_labels)
dbLoss2 = discriminatorB.train_on_batch(generatedB, fake_labels)
```

W
wizardforcel 已提交
925
5.  现在,计算判别器网络的总损失值,如下所示:
W
wizardforcel 已提交
926 927 928 929 930 931

```py
d_loss = 0.5 * np.add(0.5 * np.add(dALoss1, dALoss2), 0.5 * 
         np.add(dBLoss1, dbLoss2))
```

W
wizardforcel 已提交
932
到目前为止,我们一直在添加代码来训练判别器网络。 在下一部分中,我们将训练对抗性网络以训练生成器网络。
W
wizardforcel 已提交
933 934 935 936 937 938 939 940 941





# 训练对抗网络



W
wizardforcel 已提交
942
为了训练对抗网络,我们需要输入值和真实值。 网络的输入值为`batchA``batchB`。 基本真值是`real_labels``real_labels``batchA``batchB``batchA``batchB`,如下所示:
W
wizardforcel 已提交
943 944 945 946 947 948

```py
        g_loss = adversarial_model.train_on_batch([batchA, batchB],
  [real_labels, real_labels, batchA, batchB, batchA, batchB])
```

W
wizardforcel 已提交
949
此步骤将训练生成器网络,而无需训练生成器网络。
W
wizardforcel 已提交
950

W
wizardforcel 已提交
951
在每个微型批处理上完成一次迭代(循环)之后,将损失存储在名为`dis_losses``gen_losses`的列表中,如下所示:
W
wizardforcel 已提交
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

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

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

```py
# Sample and save images after every 10 epochs if epoch % 10 == 0:
    # Get a batch of test data
  batchA, batchB = load_test_batch(data_dir=data_dir, batch_size=2)

    # Generate images
  generatedB = generatorAToB.predict(batchA)
    generatedA = generatorBToA.predict(batchB)

    # Get reconstructed images
  reconsA = generatorBToA.predict(generatedB)
    reconsB = generatorAToB.predict(generatedA)

    # Save original, generated and reconstructed images
  for i in range(len(generatedA)):
        save_images(originalA=batchA[i], generatedB=generatedB[i], recosntructedA=reconsA[i],
  originalB=batchB[i], generatedA=generatedA[i], reconstructedB=reconsB[i],
  path="results/gen_{}_{}".format(epoch, i))
```

将前面的代码块放入`epochs`循环中。 每隔 10 个时间段,它将生成一批伪图像并将其保存到结果目录。

W
wizardforcel 已提交
982
接下来,将平均损失存储到 TensorBoard 中以进行可视化。 既存储损失,也要存储生成器网络的平均损失和判别器网络的平均损失,如以下示例所示:
W
wizardforcel 已提交
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009

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

将前面的代码块放入`epochs`循环中。





# 保存模型



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

```py
# Specify the path for the generator A model
generatorAToB.save("directory/for/the/generatorAToB/model.h5") 

# Specify the path for the generator B model
generatorBToA.save("directory/for/the/generatorBToA/model.h5")
```

W
wizardforcel 已提交
1010
同样,通过添加以下行来保存判别器模型:
W
wizardforcel 已提交
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 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055

```py
# Specify the path for the discriminator A model
discriminatorA.save("directory/for/the/discriminatorA/model.h5") 

# Specify the path for the discriminator B model
discriminatorB.save("directory/for/the/discriminatorB/model.h5")
```





# 可视化生成的图像



在将网络训练了 100 个时期之后,网络将开始生成体面的图像。 让我们看一下生成器网络生成的图像。

10 个时期后,图像显示如下:

![](img/50a98c0c-f195-40b5-9025-436b7d52523f.png)

在 20 个时代之后,图像显示如下:

![](img/34d03920-182c-4630-bba6-25d7f728c1f6.png)

我建议您将网络训练 1000 个纪元。 如果一切顺利,则在 1000 个时期之后,生成器网络将开始生成逼真的图像。





# 可视化损失



为了可视化训练的损失,请按以下方式启动 TensorBoard 服务器:

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

现在,在浏览器中打开`localhost:6006`。 TensorBoard 的**标量**部分包含两个损失的图,如以下示例所示:

W
wizardforcel 已提交
1056
判别器网络的损失图如下所示:
W
wizardforcel 已提交
1057 1058 1059

![](img/69b11dd7-e38d-46ad-a293-69d3827daf0d.png)

W
wizardforcel 已提交
1060
生成器网络的损失图如下所示:
W
wizardforcel 已提交
1061 1062 1063

![](img/4b6517ec-d185-4ec3-a4f9-acfbba2ee45f.png)

W
wizardforcel 已提交
1064
这些图将帮助您决定是继续还是停止训练。 如果损失不再减少,您就可以停止训练,因为没有改善的机会。 如果损失持续增加,则必须停止训练。 使用超参数,然后选择一组您认为可以提供更好结果的超参数。 如果损失逐渐减少,请继续训练模型。
W
wizardforcel 已提交
1065 1066 1067 1068 1069 1070 1071 1072 1073





# 可视化图形



W
wizardforcel 已提交
1074
TensorBoard 的`GRAPHS`部分包含两个网络的图形。 如果网络性能不佳,这些图可以帮助您调试网络。 它们还显示了每个图中的张量流和不同的操作,如以下示例所示:
W
wizardforcel 已提交
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087

![](img/9bd3d80d-905f-4e80-8eb0-ee3827a69a8c.png)





# CycleGAN 的实际应用



CycleGAN 有许多应用。 在本章中,我们使用了 CycleGAN 将画作转换为照片。 它们还可以在以下情况下使用:

W
wizardforcel 已提交
1088
*   **样式转换**:例如,将照片转换为绘画,反之亦然,将马的图像转换为斑马,反之亦然,将橘子的图像转换为苹果,反之亦然
W
wizardforcel 已提交
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
*   **照片增强**:CycleGAN 可用于增强图片质量
*   **季节转换**:例如,将冬天的图片变成夏天的图片,反之亦然
*   **游戏风格转移**:CycleGAN 可用于将游戏 A 的风格转移到游戏 B





# 概要



W
wizardforcel 已提交
1101
在本章中,我们学习了如何使用 CycleGAN 将绘画转换为照片。 我们从介绍 CyleGAN 入手,并探讨了 CycleGAN 所涉及的网络架构。 我们还探讨了训练 CycleGAN 所需的不同损失函数。 接下来是在 Keras 框架中实现 CycleGAN。 我们在`monet2photo`数据集上训练了 CycleGAN,并可视化了生成的图像,损失和不同网络的图形。 在结束本章之前,我们探讨了 CycleGAN 的实际用例。
W
wizardforcel 已提交
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114

在下一章中,我们将在 pix2pix 网络上进行图像到图像的翻译。 在 pix2pix 中,我们将探索条件 GAN 进行图像翻译。





# 进一步阅读



CycleGAN 有许多已知的用例。 使用以下文章寻求帮助,探索新用途:

W
wizardforcel 已提交
1115 1116 1117 1118 1119
*   [通过深度学习(CycleGAN)将 Fortnite 变成 PUBG](https://towardsdatascience.com/turning-fortnite-into-pubg-with-deep-learning-cyclegan-2f9d339dcdb0)
*   [GAN-CycleGAN(用图片玩魔术)](https://towardsdatascience.com/turning-fortnite-into-pubg-with-deep-learning-cyclegan-2f9d339dcdb0) 
*   [GAN 和 CycleGAN](https://medium.com/@jonathan_hui/gan-cyclegan-6a50e7600d7)
*   [CycleGANs 简介](https://medium.com/coding-blocks/introduction-to-cyclegans-1dbdb8fbe781)
*   [在 TensorFlow 中理解和实现 CycleGAN](https://hardikbansal.github.io/CycleGANBlog/)
W
wizardforcel 已提交
1120 1121