├── .gitignore ├── 01_gan-intro_Introduction to GAN.ipynb ├── 02_deep-convolutional-gan_generate human face.ipynb ├── 03_conditional-gan_generate digits with different labels.ipynb ├── 04_pixel2pixel-gan_Unet to transform image.ipynb ├── LICENSE ├── README.md └── img ├── Pixel2pixel-Unet.png ├── cgan.png ├── complicated_netD.png ├── complicated_netG.png ├── dcgan.png ├── fake_bedrooms.png ├── readme.md └── simple_netD.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /02_deep-convolutional-gan_generate human face.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 深度卷积对抗式生成网络\n", 8 | "\n", 9 | "在[introduction to generative adversarial networks (GANs)](./gan-intro.ipynb)这一节中,我们介绍了GANs工作机制背后的一些基本想法。我们展示了如何从一些简单,易于抽样的分布(例如均匀分布,正态分布)当中抽取一些样本,并将他们转换成和某些数据集的数据分布相匹配的样本。虽然我们匹配得到了一些2D高斯分布的数据样本,但那一点也不让人感到兴奋。\n", 10 | "\n", 11 | "在这一节中,我们会演示如何使用GANs来生成照片般逼真的图像。本节所用的模型基于[这篇文章](https://arxiv.org/abs/1511.06434)。由于卷积神经网络在计算机视觉判别问题上已经取得的成功,我们将借用其结构来展示如何将其用在GANs中。\n", 12 | "\n", 13 | "在本教程中,我们将精力集中包含了大约13000张图像的[LWF人脸数据集](http://vis-www.cs.umass.edu/lfw/)上。当教程结束时,你会明白你可以利用任何图片数据集来生成你感兴趣的,照片般逼真的图像。首先,让我们看一下该怎么做。" 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "## 导入需要的库" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": { 27 | "collapsed": true 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "from __future__ import print_function\n", 32 | "import os\n", 33 | "import matplotlib as mpl\n", 34 | "import tarfile\n", 35 | "import matplotlib.image as mpimg\n", 36 | "from matplotlib import pyplot as plt\n", 37 | "\n", 38 | "import mxnet as mx\n", 39 | "from mxnet import gluon\n", 40 | "from mxnet import ndarray as nd\n", 41 | "from mxnet.gluon import nn, utils\n", 42 | "from mxnet import autograd\n", 43 | "import numpy as np" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "## 设置训练参数" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "metadata": { 57 | "collapsed": true 58 | }, 59 | "outputs": [], 60 | "source": [ 61 | "epochs = 2 # Set low by default for tests, set higher when you actually run this code.\n", 62 | "batch_size = 64\n", 63 | "latent_z_size = 100\n", 64 | "\n", 65 | "use_gpu = True\n", 66 | "ctx = mx.gpu() if use_gpu else mx.cpu()\n", 67 | "\n", 68 | "lr = 0.0002\n", 69 | "beta1 = 0.5" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "## 下载LWF人脸数据集" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "metadata": { 83 | "collapsed": true 84 | }, 85 | "outputs": [], 86 | "source": [ 87 | "lfw_url = 'http://vis-www.cs.umass.edu/lfw/lfw-deepfunneled.tgz'\n", 88 | "data_path = 'lfw_dataset'\n", 89 | "if not os.path.exists(data_path):\n", 90 | " os.makedirs(data_path)\n", 91 | " data_file = utils.download(lfw_url)\n", 92 | " with tarfile.open(data_file) as tar:\n", 93 | " tar.extractall(path=data_path)" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "首先,我们将图像缩放到$64\\times64$。然后,将像素值归一化到$[-1, 1]$之间。" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": { 107 | "collapsed": true 108 | }, 109 | "outputs": [], 110 | "source": [ 111 | "target_wd = 64\n", 112 | "target_ht = 64\n", 113 | "img_list = []\n", 114 | "\n", 115 | "def transform(data, target_wd, target_ht):\n", 116 | " # resize to target_wd * target_ht\n", 117 | " data = mx.image.imresize(data, target_wd, target_ht)\n", 118 | " # transpose from (target_wd, target_ht, 3) \n", 119 | " # to (3, target_wd, target_ht)\n", 120 | " data = nd.transpose(data, (2,0,1))\n", 121 | " # normalize to [-1, 1]\n", 122 | " data = data.astype(np.float32)/127.5 - 1\n", 123 | " # if image is greyscale, repeat 3 times to get RGB image.\n", 124 | " if data.shape[0] == 1:\n", 125 | " data = nd.tile(data, (3, 1, 1))\n", 126 | " return data.reshape((1,) + data.shape)\n", 127 | "\n", 128 | "for path, _, fnames in os.walk(data_path):\n", 129 | " for fname in fnames:\n", 130 | " if not fname.endswith('.jpg'):\n", 131 | " continue\n", 132 | " img = os.path.join(path, fname)\n", 133 | " img_arr = mx.image.imread(img)\n", 134 | " img_arr = transform(img_arr, target_wd, target_ht)\n", 135 | " img_list.append(img_arr)\n", 136 | "train_data = mx.io.NDArrayIter(data=nd.concatenate(img_list), batch_size=batch_size)" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "metadata": {}, 142 | "source": [ 143 | "可视化其中的四张图像:" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "metadata": { 150 | "collapsed": true 151 | }, 152 | "outputs": [], 153 | "source": [ 154 | "def visualize(img_arr):\n", 155 | " plt.imshow(((img_arr.asnumpy().transpose(1, 2, 0) + 1.0) * 127.5).astype(np.uint8))\n", 156 | " plt.axis('off')\n", 157 | "\n", 158 | "for i in range(4):\n", 159 | " plt.subplot(1,4,i+1)\n", 160 | " visualize(img_list[i + 10][0])\n", 161 | "plt.show()" 162 | ] 163 | }, 164 | { 165 | "cell_type": "markdown", 166 | "metadata": {}, 167 | "source": [ 168 | "## 设计网络\n", 169 | "\n", 170 | "DCGAN的核心结构是:在判别模型中使用标准的CNN结构,而在生成模型中使用上卷积替代卷积。因为在生成模型中要将低维的向量转变成高维的图像,所以每一层的表示都会随着层数逐渐变大。\n", 171 | "\n", 172 | "- 取消原有的池化层,而在判别模型中改用跨步卷积,在生成模型中使用微步卷积;\n", 173 | "- 在判别模型和生成模型中均使用批量标准化;\n", 174 | "- 在判别模型取消全连接层以获得更深的网络结构;\n", 175 | "- 在生成模型中,除了输出层使用Tanh激活外,其余层均使用ReLu激活;\n", 176 | "- 在判别模型的所有层中均使用LeakyReLu激活。\n", 177 | "\n", 178 | "![](img/dcgan.png \"DCGAN Architecture\")" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": null, 184 | "metadata": { 185 | "collapsed": true 186 | }, 187 | "outputs": [], 188 | "source": [ 189 | "# build the generator\n", 190 | "nc = 3\n", 191 | "ngf = 64\n", 192 | "netG = nn.Sequential()\n", 193 | "with netG.name_scope():\n", 194 | " # input is Z, going into a convolution\n", 195 | " netG.add(nn.Conv2DTranspose(ngf * 8, 4, 1, 0, use_bias=False))\n", 196 | " netG.add(nn.BatchNorm())\n", 197 | " netG.add(nn.Activation('relu'))\n", 198 | " # state size. (ngf*8) x 4 x 4\n", 199 | " netG.add(nn.Conv2DTranspose(ngf * 4, 4, 2, 1, use_bias=False))\n", 200 | " netG.add(nn.BatchNorm())\n", 201 | " netG.add(nn.Activation('relu'))\n", 202 | " # state size. (ngf*8) x 8 x 8\n", 203 | " netG.add(nn.Conv2DTranspose(ngf * 2, 4, 2, 1, use_bias=False))\n", 204 | " netG.add(nn.BatchNorm())\n", 205 | " netG.add(nn.Activation('relu'))\n", 206 | " # state size. (ngf*8) x 16 x 16\n", 207 | " netG.add(nn.Conv2DTranspose(ngf, 4, 2, 1, use_bias=False))\n", 208 | " netG.add(nn.BatchNorm())\n", 209 | " netG.add(nn.Activation('relu'))\n", 210 | " # state size. (ngf*8) x 32 x 32\n", 211 | " netG.add(nn.Conv2DTranspose(nc, 4, 2, 1, use_bias=False))\n", 212 | " netG.add(nn.Activation('tanh'))\n", 213 | " # state size. (nc) x 64 x 64\n", 214 | "\n", 215 | "# build the discriminator\n", 216 | "ndf = 64\n", 217 | "netD = nn.Sequential()\n", 218 | "with netD.name_scope():\n", 219 | " # input is (nc) x 64 x 64\n", 220 | " netD.add(nn.Conv2D(ndf, 4, 2, 1, use_bias=False))\n", 221 | " netD.add(nn.LeakyReLU(0.2))\n", 222 | " # state size. (ndf) x 32 x 32\n", 223 | " netD.add(nn.Conv2D(ndf * 2, 4, 2, 1, use_bias=False))\n", 224 | " netD.add(nn.BatchNorm())\n", 225 | " netD.add(nn.LeakyReLU(0.2))\n", 226 | " # state size. (ndf) x 16 x 16\n", 227 | " netD.add(nn.Conv2D(ndf * 4, 4, 2, 1, use_bias=False))\n", 228 | " netD.add(nn.BatchNorm())\n", 229 | " netD.add(nn.LeakyReLU(0.2))\n", 230 | " # state size. (ndf) x 8 x 8\n", 231 | " netD.add(nn.Conv2D(ndf * 8, 4, 2, 1, use_bias=False))\n", 232 | " netD.add(nn.BatchNorm())\n", 233 | " netD.add(nn.LeakyReLU(0.2))\n", 234 | " # state size. (ndf) x 4 x 4\n", 235 | " netD.add(nn.Conv2D(1, 4, 1, 0, use_bias=False))" 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": {}, 241 | "source": [ 242 | "## 设置损失函数和优化器\n", 243 | "\n", 244 | "使用二分类的交叉熵损失函数作为损失函数,并且使用Adam进行优化;网络参数的初始化则使用正态分布采样。" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": null, 250 | "metadata": { 251 | "collapsed": true 252 | }, 253 | "outputs": [], 254 | "source": [ 255 | "# loss\n", 256 | "loss = gluon.loss.SigmoidBinaryCrossEntropyLoss()\n", 257 | "\n", 258 | "# initialize the generator and the discriminator\n", 259 | "netG.initialize(mx.init.Normal(0.02), ctx=ctx)\n", 260 | "netD.initialize(mx.init.Normal(0.02), ctx=ctx)\n", 261 | "\n", 262 | "# trainer for the generator and the discriminator\n", 263 | "trainerG = gluon.Trainer(netG.collect_params(), 'adam', {'learning_rate': lr, 'beta1': beta1})\n", 264 | "trainerD = gluon.Trainer(netD.collect_params(), 'adam', {'learning_rate': lr, 'beta1': beta1})" 265 | ] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": {}, 270 | "source": [ 271 | "## 训练\n", 272 | "\n", 273 | "我们推荐你使用GPU来进行训练,这样在几轮训练之后你就可以看到类似人脸的图像被生成出来。" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": null, 279 | "metadata": { 280 | "collapsed": true 281 | }, 282 | "outputs": [], 283 | "source": [ 284 | "from datetime import datetime\n", 285 | "import time\n", 286 | "import logging\n", 287 | "\n", 288 | "real_label = nd.ones((batch_size,), ctx=ctx)\n", 289 | "fake_label = nd.zeros((batch_size,),ctx=ctx)\n", 290 | "\n", 291 | "def facc(label, pred):\n", 292 | " pred = pred.ravel()\n", 293 | " label = label.ravel()\n", 294 | " return ((pred > 0.5) == label).mean()\n", 295 | "metric = mx.metric.CustomMetric(facc)\n", 296 | "\n", 297 | "stamp = datetime.now().strftime('%Y_%m_%d-%H_%M')\n", 298 | "logging.basicConfig(level=logging.DEBUG)\n", 299 | "\n", 300 | "for epoch in range(epochs):\n", 301 | " tic = time.time()\n", 302 | " btic = time.time()\n", 303 | " train_data.reset()\n", 304 | " iter = 0\n", 305 | " for batch in train_data:\n", 306 | " ############################\n", 307 | " # (1) Update D network: maximize log(D(x)) + log(1 - D(G(z)))\n", 308 | " ###########################\n", 309 | " data = batch.data[0].as_in_context(ctx)\n", 310 | " latent_z = mx.nd.random_normal(0, 1, shape=(batch_size, latent_z_size, 1, 1), ctx=ctx)\n", 311 | "\n", 312 | " with autograd.record():\n", 313 | " # train with real image\n", 314 | " output = netD(data).reshape((-1, 1))\n", 315 | " errD_real = loss(output, real_label)\n", 316 | " metric.update([real_label,], [output,])\n", 317 | "\n", 318 | " # train with fake image\n", 319 | " fake = netG(latent_z)\n", 320 | " output = netD(fake.detach()).reshape((-1, 1))\n", 321 | " errD_fake = loss(output, fake_label)\n", 322 | " errD = errD_real + errD_fake\n", 323 | " errD.backward()\n", 324 | " metric.update([fake_label,], [output,])\n", 325 | "\n", 326 | " trainerD.step(batch.data[0].shape[0])\n", 327 | "\n", 328 | " ############################\n", 329 | " # (2) Update G network: maximize log(D(G(z)))\n", 330 | " ###########################\n", 331 | " with autograd.record():\n", 332 | " fake = netG(latent_z)\n", 333 | " output = netD(fake).reshape((-1, 1))\n", 334 | " errG = loss(output, real_label)\n", 335 | " errG.backward()\n", 336 | "\n", 337 | " trainerG.step(batch.data[0].shape[0])\n", 338 | "\n", 339 | " # Print log infomation every ten batches\n", 340 | " if iter % 10 == 0:\n", 341 | " name, acc = metric.get()\n", 342 | " logging.info('speed: {} samples/s'.format(batch_size / (time.time() - btic)))\n", 343 | " logging.info('discriminator loss = %f, generator loss = %f, binary training acc = %f at iter %d epoch %d' \n", 344 | " %(nd.mean(errD).asscalar(), \n", 345 | " nd.mean(errG).asscalar(), acc, iter, epoch))\n", 346 | " iter = iter + 1\n", 347 | " btic = time.time()\n", 348 | "\n", 349 | " name, acc = metric.get()\n", 350 | " metric.reset()\n", 351 | " # logging.info('\\nbinary training acc at epoch %d: %s=%f' % (epoch, name, acc))\n", 352 | " # logging.info('time: %f' % (time.time() - tic))\n", 353 | "\n", 354 | " # Visualize one generated image for each epoch\n", 355 | " # fake_img = fake[0]\n", 356 | " # visualize(fake_img)\n", 357 | " # plt.show()" 358 | ] 359 | }, 360 | { 361 | "cell_type": "markdown", 362 | "metadata": {}, 363 | "source": [ 364 | "## 结果\n", 365 | "\n", 366 | "使用已经训练好的生成模型,我们可以生成一些人脸图像了。" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": null, 372 | "metadata": { 373 | "collapsed": true 374 | }, 375 | "outputs": [], 376 | "source": [ 377 | "num_image = 8\n", 378 | "for i in range(num_image):\n", 379 | " latent_z = mx.nd.random_normal(0, 1, shape=(1, latent_z_size, 1, 1), ctx=ctx)\n", 380 | " img = netG(latent_z)\n", 381 | " plt.subplot(2,4,i+1)\n", 382 | " visualize(img[0])\n", 383 | "plt.show()" 384 | ] 385 | }, 386 | { 387 | "cell_type": "markdown", 388 | "metadata": {}, 389 | "source": [ 390 | "我们可以通过对输入向量的线性插值,并观察相应图像的变化。可以看到,输入向量上的一些细微变化会导致生成图像上发生一些渐变。" 391 | ] 392 | }, 393 | { 394 | "cell_type": "code", 395 | "execution_count": null, 396 | "metadata": { 397 | "collapsed": true 398 | }, 399 | "outputs": [], 400 | "source": [ 401 | "num_image = 12\n", 402 | "latent_z = mx.nd.random_normal(0, 1, shape=(1, latent_z_size, 1, 1), ctx=ctx)\n", 403 | "step = 0.05\n", 404 | "for i in range(num_image):\n", 405 | " img = netG(latent_z)\n", 406 | " plt.subplot(3,4,i+1)\n", 407 | " visualize(img[0])\n", 408 | " latent_z += 0.05\n", 409 | "plt.show()" 410 | ] 411 | }, 412 | { 413 | "cell_type": "markdown", 414 | "metadata": {}, 415 | "source": [ 416 | "在这里你可以看到更多的信息, [打开Github以获取更多信息](https://github.com/mli/gluon-tutorials-zh)。" 417 | ] 418 | }, 419 | { 420 | "cell_type": "code", 421 | "execution_count": null, 422 | "metadata": { 423 | "collapsed": true 424 | }, 425 | "outputs": [], 426 | "source": [] 427 | } 428 | ], 429 | "metadata": { 430 | "anaconda-cloud": {}, 431 | "kernelspec": { 432 | "display_name": "Python 3", 433 | "language": "python", 434 | "name": "python3" 435 | }, 436 | "language_info": { 437 | "codemirror_mode": { 438 | "name": "ipython", 439 | "version": 3 440 | }, 441 | "file_extension": ".py", 442 | "mimetype": "text/x-python", 443 | "name": "python", 444 | "nbconvert_exporter": "python", 445 | "pygments_lexer": "ipython3", 446 | "version": "3.6.2" 447 | } 448 | }, 449 | "nbformat": 4, 450 | "nbformat_minor": 2 451 | } 452 | -------------------------------------------------------------------------------- /03_conditional-gan_generate digits with different labels.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 条件生成式对抗网络\n", 8 | "\n", 9 | "在[深度卷积生成式对抗网络](./dcgan.ipynb)这一节中,我们介绍了如何使用噪声数据生成人脸图像。使用DCGAN,我们可以用随机的一些向量生成一些图像。但是我们得到的是什么样的图像呢?我们能不能指定生成一张男人的脸或者女人的脸?\n", 10 | "\n", 11 | "在本节中,我们将介绍[条件生成式对抗网络](https://arxiv.org/abs/1411.1784),它可以接受标签作为生成模型和判别模型的输入。这样,你就可以通过选择相应的标签来生成对应的数据。我们将以MNIST数据集为例进行训练。" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 1, 17 | "metadata": { 18 | "collapsed": true 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "from __future__ import print_function\n", 23 | "import numpy as np\n", 24 | "from matplotlib import pyplot as plt\n", 25 | "\n", 26 | "import mxnet as mx\n", 27 | "from mxnet import gluon, test_utils, autograd\n", 28 | "from mxnet import ndarray as nd\n", 29 | "from mxnet.gluon import nn, utils" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "## 设置训练参数" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 2, 42 | "metadata": { 43 | "collapsed": true 44 | }, 45 | "outputs": [], 46 | "source": [ 47 | "epochs = 1000\n", 48 | "batch_size = 64\n", 49 | "label_size = 10\n", 50 | "latent_z_size = 100\n", 51 | "hidden_units = 128\n", 52 | "img_wd = 28\n", 53 | "img_ht = 28\n", 54 | "\n", 55 | "use_gpu = True\n", 56 | "ctx = mx.gpu() if use_gpu else mx.cpu()\n", 57 | "\n", 58 | "lr = 0.001" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "## 下载和处理MNIST数据集" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": 3, 71 | "metadata": { 72 | "collapsed": true 73 | }, 74 | "outputs": [], 75 | "source": [ 76 | "# Pixel values of mnist image are normalized to be from 0 to 1.\n", 77 | "mnist_data = test_utils.get_mnist()\n", 78 | "train_data = mnist_data['train_data']\n", 79 | "train_label = nd.one_hot(nd.array(mnist_data['train_label']), 10)\n", 80 | "train_iter = mx.io.NDArrayIter(data=train_data, label=train_label, batch_size=batch_size)" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "可视化其中的四张图像:" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 4, 93 | "metadata": {}, 94 | "outputs": [ 95 | { 96 | "data": { 97 | "image/png": "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\n", 98 | "text/plain": [ 99 | "" 100 | ] 101 | }, 102 | "metadata": {}, 103 | "output_type": "display_data" 104 | } 105 | ], 106 | "source": [ 107 | "def visualize(img_arr):\n", 108 | " plt.imshow((img_arr.asnumpy().reshape(img_wd, img_ht) * 255).astype(np.uint8), cmap='gray')\n", 109 | " plt.axis('off')\n", 110 | "\n", 111 | "for i in range(4):\n", 112 | " plt.subplot(1,4,i+1)\n", 113 | " visualize(nd.array(train_data[i + 10]))\n", 114 | "plt.show()" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "## 设计网络\n", 122 | "\n", 123 | "在生成模型中,将随机的噪音向量和数字标签的独热编码向量拼接在一起作为其输入。随后是一个relu激活的全连接层。输出层则由另外一个sigmoid激活的全连接层构成。\n", 124 | "\n", 125 | "和生成模型类似,将图像展平后的向量和数字标签的独热编码向量拼接在一起后作为判别模型的输入。随后是一个relu激活的全连接层。输出层则由另外一个全连接层构成。**(此处的英文教程有误)**在本教程中,我们在输出层中不使用sigmod激活函数,这样训练过程的数值稳定性会更好。\n", 126 | "\n", 127 | "![](img/cgan.png \"Conditional GAN Architecture\")" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 5, 133 | "metadata": { 134 | "collapsed": true 135 | }, 136 | "outputs": [], 137 | "source": [ 138 | "w_init = mx.init.Xavier()\n", 139 | "\n", 140 | "# Build the generator\n", 141 | "netG = nn.HybridSequential()\n", 142 | "with netG.name_scope():\n", 143 | " netG.add(nn.Dense(units=hidden_units, activation='relu', weight_initializer=w_init))\n", 144 | " netG.add(nn.Dense(units=img_wd * img_ht, activation='sigmoid', weight_initializer=w_init))\n", 145 | "\n", 146 | "# Build the discriminator\n", 147 | "netD = nn.HybridSequential()\n", 148 | "with netD.name_scope():\n", 149 | " netD.add(nn.Dense(units=hidden_units, activation='relu', weight_initializer=w_init))\n", 150 | " netD.add(nn.Dense(units=1, weight_initializer=w_init))" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "## 设计损失函数和优化器\n", 158 | "\n", 159 | "我们使用二分类的交叉熵损失函数作为损失函数,使用Adam进行优化。网络的初始化使用正态分布完成。" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 6, 165 | "metadata": { 166 | "collapsed": true 167 | }, 168 | "outputs": [], 169 | "source": [ 170 | "# Loss\n", 171 | "loss = gluon.loss.SigmoidBinaryCrossEntropyLoss()\n", 172 | "\n", 173 | "# Initialize the generator and the discriminator\n", 174 | "netG.initialize(ctx=ctx)\n", 175 | "netD.initialize(ctx=ctx)\n", 176 | "\n", 177 | "# Trainer for the generator and the discriminator\n", 178 | "trainerG = gluon.Trainer(netG.collect_params(), 'adam', {'learning_rate': lr})\n", 179 | "trainerD = gluon.Trainer(netD.collect_params(), 'adam', {'learning_rate': lr})" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "## 训练\n", 187 | "\n", 188 | "我们推荐你使用GPU进行训练,这样在几轮训练之后你就能够看到生成的数字图像。**(此处的英文教程有误)**" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 7, 194 | "metadata": { 195 | "collapsed": true 196 | }, 197 | "outputs": [], 198 | "source": [ 199 | "from datetime import datetime\n", 200 | "import time\n", 201 | "import logging\n", 202 | "\n", 203 | "real_label = nd.ones((batch_size,), ctx=ctx)\n", 204 | "fake_label = nd.zeros((batch_size,),ctx=ctx)\n", 205 | "\n", 206 | "def facc(label, pred):\n", 207 | " pred = pred.ravel()\n", 208 | " label = label.ravel()\n", 209 | " return ((pred > 0.5) == label).mean()\n", 210 | "metric = mx.metric.CustomMetric(facc)\n", 211 | "\n", 212 | "stamp = datetime.now().strftime('%Y_%m_%d-%H_%M')\n", 213 | "logging.basicConfig(level=logging.INFO)\n", 214 | "\n", 215 | "for epoch in range(epochs):\n", 216 | " tic = time.time()\n", 217 | " btic = time.time()\n", 218 | " train_iter.reset()\n", 219 | " iter = 0\n", 220 | " for batch in train_iter:\n", 221 | " ############################\n", 222 | " # (1) Update D network: maximize log(D(x)) + log(1 - D(G(z)))\n", 223 | " ###########################\n", 224 | " data = batch.data[0].as_in_context(ctx)\n", 225 | " label = batch.label[0].as_in_context(ctx)\n", 226 | " latent_z = mx.nd.random_normal(0, 1, shape=(batch_size, latent_z_size), ctx=ctx)\n", 227 | " D_input = nd.concat(data.reshape((data.shape[0], -1)), label)\n", 228 | " G_input = nd.concat(latent_z, label)\n", 229 | "\n", 230 | " with autograd.record():\n", 231 | " # train with real image\n", 232 | " output = netD(D_input)\n", 233 | " errD_real = loss(output, real_label)\n", 234 | " metric.update([real_label,], [output,])\n", 235 | "\n", 236 | " # train with fake image\n", 237 | " fake = netG(G_input)\n", 238 | " D_fake_input = nd.concat(fake.reshape((fake.shape[0], -1)), label)\n", 239 | " output = netD(D_fake_input.detach())\n", 240 | " errD_fake = loss(output, fake_label)\n", 241 | " errD = errD_real + errD_fake\n", 242 | " errD.backward()\n", 243 | " metric.update([fake_label,], [output,])\n", 244 | "\n", 245 | " trainerD.step(batch.data[0].shape[0])\n", 246 | "\n", 247 | " ############################\n", 248 | " # (2) Update G network: maximize log(D(G(z)))\n", 249 | " ###########################\n", 250 | " with autograd.record():\n", 251 | " fake = netG(G_input)\n", 252 | " D_fake_input = nd.concat(fake.reshape((fake.shape[0], -1)), label)\n", 253 | " output = netD(D_fake_input)\n", 254 | " errG = loss(output, real_label)\n", 255 | " errG.backward()\n", 256 | "\n", 257 | " trainerG.step(batch.data[0].shape[0])\n", 258 | "\n", 259 | " # Print log infomation every ten batches\n", 260 | " if iter % 10 == 0:\n", 261 | " name, acc = metric.get()\n", 262 | " logging.info('speed: {} samples/s'.format(batch_size / (time.time() - btic)))\n", 263 | " logging.info('discriminator loss = %f, generator loss = %f, binary training acc = %f at iter %d epoch %d' \n", 264 | " %(nd.mean(errD).asscalar(), \n", 265 | " nd.mean(errG).asscalar(), acc, iter, epoch))\n", 266 | " iter = iter + 1\n", 267 | " btic = time.time()\n", 268 | "\n", 269 | " name, acc = metric.get()\n", 270 | " metric.reset()\n", 271 | " logging.info('\\nbinary training acc at epoch %d: %s=%f' % (epoch, name, acc))\n", 272 | " logging.info('time: %f' % (time.time() - tic))\n", 273 | "\n", 274 | " # Visualize one generated image for each epoch\n", 275 | " fake_img = fake[0]\n", 276 | " visualize(fake_img)\n", 277 | " plt.show()" 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "metadata": {}, 283 | "source": [ 284 | "## 结果\n", 285 | "\n", 286 | "使用训练好的生成模型,我们生成几张数字图片。" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": 8, 292 | "metadata": {}, 293 | "outputs": [ 294 | { 295 | "data": { 296 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAT8AAAD8CAYAAAABraMFAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4wLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvpW3flQAAIABJREFUeJztnXl0VOX5x7937qzJTHZCgAARU0kh\nQpDIJmVRAa0VQS1tKipowVKFSq2itaKVoiIiCrYo4A+Fg0oBQSkCQmVTZCdsgbAEkpA9k0wyzHpn\n7vP7gybHmARJZsKd5fmc855jZsY7z/3wznPfu7zvIxARGIZhwg2V0gEwDMMoASc/hmHCEk5+DMOE\nJZz8GIYJSzj5MQwTlnDyYxgmLOHkxzBMWMLJj2GYsISTH8MwYYn6en6ZIAh+m05CRIK/thUKsNu2\nhf22HUq55ZEfwzBhCSc/hmHCkoBLfgkJCfj000+xfPlyLFu2DLIsY9OmTUqHFdQkJCRgxYoVkGUZ\nsizD6/UqHVJIMXr0aDz88MM4fPgw3G43iAgmkwlRUVFKhxYyjB07Fnl5eTh79ixmzZqFqqoq6PV6\n3zZKRNetAaCrNbVaTS6Xi4iI1q5dS3379iVJksjlclGHDh0afPZ6xh0M7WperVYr/ZiCggIyGAxN\nfl7pfQnE1pzbZ599lmRZbuSXiMjhcJBarWa/rXRb1/r160cWi4W8Xi/t2rWLlixZQh6PhzZs2OCT\nW8V38n8XO+n3v/89SZJERESyLNOoUaMoJiaGXnnlFSovL6eXXnqJO1AL3er1evr000/JYrGQ1Wql\nmpoa8nq9VEdNTQ117tyZf5yt9Pvss8/Snj17qLCwkEpLSyk/P5/mzZtH77//PsmyTLW1tbR7925q\n164d+22h27q2evVqkiSJzGYzrVixgkwmE6lUKpoyZQrl5+f75DYgTntVKhVefvlleL1eHDlyBCtX\nroRarYYkSViwYAFsNhsGDhyodJhBh1arRfv27WEwGGA2m3HPPfegqqoKNTU1AICoqCj87W9/UzjK\n4KZjx47wer1YsmQJfv7zn2PRokXIyckBAOj1enTv3h21tbUKRxm89O3bF263G2vWrME777yDiIgI\nEBHWrFmD3NxcvPjii63fuNIZ/pNPPqHy8nIiIvrqq69IFMVGp8IXL16kc+fO8dGzhW4PHz5MhYWF\ntHLlygYjPFEUaenSpSTLMsmyTMuWLWO3rfD79ttvU0lJCc2YMaP+EoJKpaLevXuTRqOh3NxcKigo\noNjYWPbbQrcAaO7cueT1emnixImN3ktISKDjx4/TZ5991mq31/U5vx8jCAJ69+6NwsJCfPTRRzh8\n+HCji/F6vR6JiYkoLi5WKMrgJT09HbIsY9asWSgqKqp/XZZlvPzyyxgzZgzi4uLQuXNnBaMMXkaP\nHo2oqCgsWLAAbrcbwBW3R48eBQBYrVZs3LgRoigqGWZQkpaWhvT0dDz44IPYvn17o/cjIyMRGRmJ\ns2fPtv5LlMrwgiDQnDlzyOVyUa9evZo951epVERE9N133/HR8xrdGo1G+u9//0tERGfOnGnSq1ar\npcLCQvJ4PDR48GB22wK/Op2O7r33XiIiKisra9KvWq2myspKOnz4ME2aNIn9XqNbAGQwGKioqIjW\nrVvXbF7o1asXud1uGjt2bPBd84uLi8Ojjz4KlUqFixcvNvu59PR0SJKE3Nzc6xdckHPvvffC6XTC\nYrHg9ttvb/IzkiTh2LFjkGUZBw4cuM4RBjd6vR49e/YEEWHbtm1Nfkar1eLEiRNIT0/H5cuXr3OE\nwU1ERATi4+NhtVqbfF+j0eC9995DTk6OT4/BKZb84uPjERcXh5qammZ3UqVSYdCgQZBlGbfddtt1\njjB4OXLkCEwmE8xmM6qrq5v8jFqtRnJyMpxOJzwez3WOMLiRJAk9e/aEJElISEho8jN2ux0PPfQQ\nXC4XJk+efJ0jDG5+//vfg4hw+PDhJt8fNGgQUlJSsGnTJp+eWVXsml9aWhq+/fZbPP/883VD30bk\n5+fDYrFg9uzZmDdv3nWOMHjp0KEDampqYDabccMNN+DEiRONPlN3jernP/85P/TcQrxeL9577z30\n7dsXERERiIyMhM1ma/CZ6OhoPPbYY9BqtQpFGZzo9Xp4PB5s3LgRCxYsaPIzO3bsgN1ux/z58yFJ\nUqu/S7GR37Rp0+ByuRAREdHk+6mpqTh37hzOnTuH+fPnw+FwXOcIg5f9+/dj2LBhuO2223Drrbei\nY8eO0Ol0AK7cZDIajdizZw+8Xi8KCgoUjjb46Nq1K7p164bY2FicPXsWLpcLgtBwPv3ly5cxZswY\nOBwO5OXlKRRp8CFJErxeL2677TakpaXVv67VajF06FDk5eXB7XZj7ty5qKio8Om7hOZGXW3BD1dv\nKCwsRFFREY4fP44//OEPkGUZAwcOxPbt26HVakFEMBqNsNvtTW6LeGWMBvx4ZYx+/frhtddew+DB\ng+sTX92FXqfTidraWtxyyy0oKSlptC1225gf+42Pj8fzzz+Pvn374rvvvoNOpwMRYcqUKdBoNPV9\n2GAwNBqdsN+G/NhtREQEqqqqIMsybr/9dqxevRrJyckAgJqaGsTExDS7rZa4Vey0NyEhAXFxcejX\nrx/uv/9+REREQK/Xg4ggyzJ27drVbOJjfprDhw/j1KlT6Nu3b33yczgcyMnJQadOndCpU6dmLzcw\nP01VVRVUKhV+9rOfYeDAgbh8+TJkWYZarYZOp4PT6cSiRYt8Oi0LV+x2OxwOB44dO4bPPvsMnTp1\ngizLKCgoaDAa9BXFRn6+wkfPhrDbtqUpv+3bt8f777+PwYMHQ6vVwu1246uvvsL8+fORnZ3d7LbY\nb0OU6ruc/EIEdtu2sN+2gxczZRiGuY5c15EfwzBMoMAjP4ZhwhJOfgzDhCVcvS1EYLdtC/ttO/iG\nB8MwzHWEkx/DMGFJwCU/k8mEnJwc35anZprk1KlTOHbsGEwmk9KhhCQajQYvvPACXC4XNBqN0uGE\nHCkpKbBarWjfvr1fthdwya9bt24gIq570Abk5uZi0aJFPG2wjYiJicFzzz0HtVqNVatWKR1OyPH4\n448jMjIS//rXv/yyvYBLfhMnTkS3bt1w6NAhpUMJOW6++WbEx8crHUbIkpOTg+joaAiCUL9QL+Mf\nYmJiMGnSJHg8HowbN84v2wy4fx1RFKFWq1FaWqp0KCGHXq/HwYMHlQ4jZKkrCk9EcDgckGVZ6ZBC\nhrFjxyImJgYul8tv608GXPKLj4+HWq2G2WxWOpSQo6amBk8++SQvXtoGCIKA6OhoqFQqSJLEic/P\nzJkzBzqdzq8Hb0WrtzXF8OHDAVwZ5tbVl2X8Q0JCQv0Kzox/UalUUKuv/Jx41Od/oqOjIcsyPv74\nY79tM+CSX0JCAsrLyxuUWmT8Q1xcHNfraCP0en19icqXXnpJ4WhCi969e0Or1cJqteLf//6337Yb\ncKe9arUa0dHR/CNtA0RRvOoquEzrEEURAwYMAAC4XC785z//UTii0MFoNGLRokVwOp2YPHmyX59U\nCLiR34kTJ/iUoY24dOkSvv/+e6XDCDkiIyPx6KOPwuv14sSJE8jPz1c6pJDB4/Hg5MmTSEtLa7ZM\naGsJuJHfz3/+c+48bURiYiK++OILpcMIObp06YI777wTwJUCPLxMnP9ITExEv379MHjwYL/fBA24\n5OdwODBkyBClwwhJNBoNnnrqKaXDCDl+/etfIzIyEiqVCkeOHFE6nJBiyZIl6NWrF0pKSvx+UAm4\n5FdbW4tly5YpHUZIUlVVhcLCQn741s988MEHMJvNcDqdsNlsPLXNTwiCgJEjRwIAqqur/b79gPsV\nXL58Ge+//77SYYQkgiAgNze3UY1Zxjf0ej0qKipgs9kQGRmpdDghQ91Buq1qdnMBoxCB3bYtV/Or\n0Wjw/PPPY9y4cWjXrh06dOhw1VM09tsQrt7WQrgDNYTdti3st+3gxUwZhmGuI1y9jWGYsIRHfgzD\nhCWc/BiGCUs4+TEME5Zw6coQgd22Ley37eC7vf8jJSUF9957L1JSUlBcXIy4uDilQ2KYqyIIAqZN\nm4ZbbrkF/fr1w5gxY5CYmMgPkwc4AZX8BEFAVFRU/Yqtly9fxqZNm5QOK+i5ePEiPB4PZFlGXl4e\niAhEhB07digdWtAjiiJ27tyJPn36YM6cOdi+fTvWrl2LtWvX8gIHfkKj0eCtt96C1WoFEcHj8SA9\nPd3n7QZU8hs4cCDefvtt9O/fHxs3bkSnTp3Qo0cPaLVapUMLWrRaLSIiInDy5En06tULjz32GGpq\nalBRUcGjah8RBAF33nknkpKScMstt+Czzz7D+fPnUVxcjIqKChgMBqVDDAkWLVqEP//5zzAajQCu\nTHv729/+5vuG60YB16MBoObawoULKTs7m15//XUaOXIkaTQakiSJjh8/TiaTqdHnr2fcwdCa85qS\nkkJvvfUWdezYscHrNpuN3G53k/+P0vsSiK0pTwkJCbR+/Xp64IEHKCIigv537YpiY2PJbrfT5MmT\n2W8r3QIgQRDo5MmTlJeXR+3btydBEEgQBCIislqtPrsNiJ0EQBqNhubNm0fR0dEEgLRaLdlsNior\nK6vvVNyBWuf2xhtvJKPR2KBTbd26lUpKSvjH6YPf0aNHU3x8PImi2KCP6vV6stlstGLFCvbbSrcA\nqH///vTpp5/S4sWL6e677yaVSkUqlYq8Xi+dOnUqdJLfj1tiYiI5HA56/fXXuQP52a0oiuTxeOjC\nhQvs1ge/KpWKRFFs9NrUqVPJ4/HQtm3b2G8r3TY3EvzNb35DTqeThgwZ4rPbgFvGHriyRNDkyZMh\nCAL279+vdDghh1arxeXLlzFx4kSlQwlqmiq3oNVqcfjwYTgcDr8V12au3Fjq3r07tFotLl26hIsX\nL/q8zYBJfoIgoGvXrkhISMB///tfREREYMyYMfjqq6+UDi3o0Wg0MBgMiIyMxOzZs3H77bcjKSkJ\nLpdL6dBCgtWrV+PWW2/F9OnT0b9/f/Ts2ROyLKN3797Yvn270uGFBJGRkTh58iQA+O0RIsWTX/v2\n7XH33XfD6XTihhtuwPjx4xEREQGLxYKNGzcqHV5QExkZCYPBgOrqaoiiiG+//RbJycmwWq3weDx1\npxxMKxk3bhymTp2K2267DQDw0UcfoaKiAsnJybDZbOjatSsiIiIwatQorFu3TuFog5u6g4hfqzoq\nfW6fkZFBf//738npdFJtbS3Jskxer5emTZt21fN/pa9TBFprytGQIUNIkiRat24dlZSUEBGR1+sl\nj8dDGo2G3bbQryRJDTytWrWKPB4PSZJElZWVtGPHDrLb7STLMlmtVnr00Uepf//+dPjwYfb7E26v\n1jp27EhERLIs06RJk/yWFxTdSYPBQMXFxXTmzBnKy8sjm81GkiSR2+2mDz74gEaNGtXgLiV3oJZ1\nIKfTSQUFBdSuXTvq3r07paen09tvv01ERMuXL6fu3btTampqo7vpSu9LILam/Nrtdjp37hxptVoS\nBIFqampIlmUiIqqqqqJDhw7Rm2++SRkZGey3hW5FUaQXX3yR9u7dS1arlR5++OH69zQaDS1dujR4\nk58gCJSZmUlOp5N27dpFTqez/oiZm5tLX3/9NZWVldHKlSs5+bWyAxUWFtKKFStIpVLRxIkT6b//\n/S9lZ2eTLMskSRJ99NFHlJWVRQaDgd22wm/d4yx6vZ4+//xzkmWZXC4XWSwWOnnyJM2dO5fat29P\nnTt3Zr8tdLt7924iIvrkk0+orKyMzGYzJSQk0NChQ+nixYtkt9t9fgROsZ0UBIHuu+8+OnnyJHm9\nXkpLSyO9Xt9gVHj8+HHq2rUrJ79WdqALFy5QcXEx5ebmkiRJNG3aNBo7dixlZWXRSy+9RFOmTCGt\nVstuW+m3vLycvF4veb1ekiSpydGIKIoUExPDflvgNikpqd5rWVkZuVwu6t69O7322mvk8Xjo6aef\npsTExOAd+QFXnuXLysqijz/+uNHzUpGRkfTSSy81ObuDO9BPuwVAGzZsoJKSEpIkiWRZJpPJ1OTR\nkt22zm9mZiZZLBaqra0ll8vVrE+1Ws1+W+A2KyuLXC4XuVyu+rOUf/7zn1RcXOzX69VcwChEYLdt\nC/ttO3hJK4ZhmOsIJz+GYcISrt7GMExYwiM/hmHCEk5+DMOEJZz8GIYJS7h6W4jAbtsW9tt28KMu\nDMMw1xHFk59Go0F6ejqWL1+O2tpaeDwefPnll0qHFRIMHjwYixYtwuHDh2E2m+HxeJCXl4dly5Yp\nHVpIoFKp0K5dO8yfPx+5ubnwer1YsmQJ3n//faVDC3p0Oh1mz54Nh8MBSZLgcrmwd+9euN1uiKLo\nny9ReopQXl4eeb1e+jFVVVU8BcsHt6Io0o4dO0iSpAZ+PR4PWa1WioiIYLc++FWpVDRmzBiy2Wz1\nK7nUIcsyrVy5ssl50+z3p90CV+b2L1iwgKxWK9lsNurWrRtlZWWRxWKhVatWNTtNs0Xfq+ROmkym\n+g4zadIkeuCBBxp0orS0tEZzfrkDXZtblUpF48aNo4yMDLrxxhspNTWVRFEkrVZLS5cuJSKilJQU\ndttKv7fccgsRXVkfcfv27XTTTTdRRkYG/eEPf6CTJ0+SLMvk8XjYbyvc/rClp6dTly5d6uullJaW\n0vLlyyktLS24k59Wq6U6NBoNqdVqcrvdRHTl6MkjP/90oOZ+uP7oQOHSfuyoa9euVFhYSEVFRfUl\nFQVBIJPJRC+88ALVwX5b7vaHTa1Wk16vJ1EU6Z577iG3293siLqlbhW95idJEi5cuIB33nkHkiTB\n4/FAFEXY7XbU1NTg4YcfVjK8kEOtVmPWrFnYt28fKisrcfr0aaVDClouXbqE1atX45577qn/ManV\najz11FOYNm0avF4vLl++rHSYQU98fDxmzpyJgwcPon///vjPf/4DSZL8sm1Fa3gQEfLy8tCxY0fo\ndDpMmjQJbrcbOp0Obrcbffr0wb59+3DmzBklwwxaBEEAESE2NhZdu3bFpk2bkJiYCJVKhZkzZyod\nXlDj9Xqxdu1aAFcOKunp6Rg6dChmzJiB6OhonDhxAlqtVuEogxe1Wg1ZlrFp0yYYjUZ07doVqamp\nqK6urhst+v4dftmKD7zxxhv4+uuvMWjQIGi1WsiyjJycHLhcLixduhTbtm1D165d/bbD4cTIkSOx\nadMmAFcS4T/+8Q9s2bIF6enpsFgsEEURXq9X4SiDl169euGf//wngCsH8pqaGqhUKlRUVECtVkOr\n1SIqKgqxsbHIz89XONrgQhRF/OIXv0BqaioEQYDb7YZWq4XdbodGo/HL6E/xR1127NiB7OxszJ07\nF2+//TaAK1n/q6++wrFjxxAbG4v+/fvXf16lUjzkoOGOO+6AzWaD1WrF0aNH8eabb6KyshKCIODv\nf/87UlNTlQ4xqOnevTsuX76MwsJC7NmzB9u3b8fOnTsxbNgw3HDDDYiLi4PRaITZbFY61KBDEARU\nVFTgmWeewdixY5Gbmwu3243Y2Fjcf//9iIiI8P1LAuHCplqtJlEUSaPRkFarpaioKBoxYgSZzWaq\nqqqqLwDzw9vbSl+kDbTWlFe9Xk8Gg4Gio6MpMjKy/i7w4MGDyeFw0KBBg/iCvA9+6+5ARkREUHx8\nfP1ND1EU6ezZs2S1WulPf/oTtWvXjv220G1dXkhMTCSj0Uivv/46ff/992S1WunDDz+kP/3pT8F9\nt/eHLSkpqdGzO7W1tWS32+ndd99tdGdS6X+wQGtXc/vj1qlTJ6qurqbJkydz8vPRr1arbfLuY13y\nW7lyZaM6NErvS6C1a+23AwYMoLNnz5LZbKZTp04F993eOiIjI5Gbm4tdu3Y1eN1sNtcX3eabHv5B\nFEW8+eabEATBb3fNwhWdTodu3bohMjKywetGoxHt27eHwWDAJ598AkHgqbwtRaVSNfCWnp6O1NRU\nOJ1OuN1uHDhwwPcvCYQMHxERQXWUlpZSUVER1dTUkMfjoYKCAoqLi+PRSSvcRkREUK9eveqfoYyI\niKDXXnuNamtraffu3XT33XfzyM8HvzNnzqTCwkIaNWoUpaSk0MaNG6miooIKCwvJYrHQq6++Wn8q\nzH6vzW3d5a+qqirKz8+nrKwsWrFiBZ08eZK++eYbOnbsGP3zn/+k5OTk0DntraOuZB0RUW1tLS1Y\nsIB/oK10O3PmTDp16hQdPHiQ3nnnHbLb7STLMjkcDpo3bx499NBD7NYHv2fOnKmvjGc2m0mWZZJl\nmUpLS2nWrFmNSlay32tzKwgCWa3W+jrIdVM0a2trad26dc1WdGyp24Cp3qZWq9GvXz9ER0ejoKAA\nTz/9NLZt24YtW7bAYrE0+jzxskANaMrtjTfeiDlz5mDgwIEwm81ISkrCoUOH8Pe//x179+5tdlvs\ntjHN9d0xY8bgz3/+M4xGI/bt24evv/4a33zzDWpqaprdFvttiFJLWgVM8msp3IEawm7bFvbbdvB6\nfgzDMNcRrt7GMExYwiM/hmHCEk5+DMOEJZz8GIYJS7h6W4jAbtsW9tt28N1ehmGY6wgnvzBFp9Nh\n3759SElJ4bmnbYBOp8O2bduQlJSkdCghhyAIuOmmm7Bt2zafKrkFTPKLiYlB+/btG7x24cIFXmyz\njXj33Xdx4403olOnTrxGog8IggCj0djoAPLRRx+hT58+KC8vVyiy0EEURcTFxdX/bTAYsH//fiQm\nJkKn07V6uwHR6zUaDY4dOwaXy9Xg9aSkJGzYsEGhqEIDlUqF5557DgsXLkS7du3qX3/00UcBAHv2\n7OEDTCuJiorCM888gxEjRuDHz8uWlpbi0KFDkGVZoehChxMnTuCRRx6p/7uwsBB79+5Fr169YLfb\nW71dxZOfSqVCXFwczGYzHA5H/es6nQ5Hjx7F1KlTFYwu+NHr9XjhhRdw7733oqKiosF7+/bt4zoT\nPhAREYG0tDRs3ry50XuZmZn4xz/+oUBUocXs2bORnJyMTz/9tP41jUbjn+JQSq+MkZSURH/5y19I\no9HUv6bRaCg3N5fi4+P9snpDOLSmHHXv3p1effVVKi8vpzlz5hBwZfXhKVOmUF5eHiUlJbFbH/x+\n9913ZLPZGr0eFxdHW7ZsadCn2W/L3AJXViJ3u930/PPP17+WmJhIkiQ1ucxdS90qPvJLTEzE7bff\nDpPJBODKtb/x48ejY8eOfDrmI0899RTGjRuHuXPnYsWKFdBqtcjMzMQrr7yCf//73ygrK1M6xKDm\npptuwuXLl6HRaBAXF4fIyEikpqZi06ZNUKlUfMrrA4IgYMSIEdi1axfeeOON+tdvv/12OBwOVFVV\n+fwdildvO3HiBJKTk3H+/HlERUXh2LFjSEhIgNFoxIwZM1BaWop3331X6TCDjt27d+PWW2+FJElQ\nq9V48cUXMWLECBQVFSEmJgYbNmyAVqttdJ2VuXaio6Oh0WhQW1sLURRRUFCA9957D6WlpSgoKED/\n/v2xZ88epcMMSo4ePYobbrgB0dHR9a+9/PLLGDVqFJYuXYrp06dj/vz5vn1JIAxvRVEkm81GREQZ\nGRmUm5tLlZWV9MADD/hleBsO7YduNBoNHT9+nIiIZFmmOoqLi8lqtZLH46FZs2Y1KqzDbq/Nb10r\nLS0lSZLI5XJRcXExjRgxgqZMmUKPPPIIbd26lTIzM9lvK9wKgkCSJJHdbiedTkd6vZ6efvrp+oWO\n7XY7SZJEarXaJ7eKj/yAKwWg605764pqr1y5sr4oNNMyvF4vFixYgA8++ACyLKO6uhqXLl1CVlYW\nVq9ejR49eiAnJwfV1dVKhxrUJCUlQa1W11+eqSu3+MUXX2DTpk3Izc1VOMLghIjg9XohiiIcDkf9\nf9eh1+sBwPfLCkofPX/cVq9eTTU1NT/5OaWPVoHWrsXtyJEjyWq1UlFRUaO6EuzWd7+CIJDFYqHa\n2lrS6/Xs1we38fHxFBkZSaIo0sqVK+tLBKxZs4Y6dOjQbP9tyfcqfsPjx4wZMwbTpk1TOoyQZNCg\nQTAYDCgtLa3rdIwfMRqNiIqKwscffwyn06l0OEGN2WyGzWaDWq3GgQMH4Ha7cfPNN+M3v/kNSkpK\n/NN/lc7wP2wxMTFUWFh41VEJ+OjZKrcAaNeuXVRcXEx33nknu20Dv4WFheR0Oqlv377s109uhw4d\nSpIk0dixY0kURb/mhYAa+RER1q9fz3NN24i0tDTU1NTg1KlTSocScqhUKnTo0AFlZWXs14/88pe/\nhEqlwjfffOP3R98C4oZHHXPmzEFaWho/H9UGREREwGAwQBAEFBcXKx1OyNGhQweYzWZMmDDBpylX\nTEMmTJiA5cuXX7UaXmsJqOptJpMJBw8eRI8ePX4yyxOvidYAXm+ubWG/bQeXrmwh3IEawm7bFvbb\ndoRF8mMYhgkUAuqGB8MwzPWCkx/DMGEJJz+GYcISrt4WIrDbtoX9th1cvY1hGOY6EjDJTxAEaLVa\n3HPPPRg1ahQ+/PBDEBGqq6sxZMgQvPjii4iPj1c6zKDkpptuwm9/+1vs378f48ePx/Dhw+F2u1Fd\nXY2VK1fiySefRIcOHZQOM2jp1KkT+vfvj4ULF2LJkiX16/sxvrNz506sX78eZrMZdrsdVqvVfxsP\nlDl8Go2Ghg4dSuXl5bRixYoG69Bt2rSJXn31VZ4f2Qq3Wq2WzGYzud1uslgs5PF4yOv1ksvlIqvV\nSkePHqVvvvmmwUoZSu9LILbm/Op0Otq9ezc5HI769eZkWaa5c+eSVqvlub0+uJ0xYwZJkkSyLNPn\nn39Od911F8XExPhtbm9A7GTdQqYul4tKSkrI4/HQgQMHaMKECSSKIomi2GixA6X/wQKtNeU1Li6O\nNmzYQF6vl/Lz82nMmDEUGRnZwKVWq220KKTS+xKIrSm/giBQTU0NeTweeumll5qt2cHJr+Vu9Xo9\nHT58mLZv337NXlvqVvG5vSaTCXq9Hjk5OSgrK8Nvf/tbrnXqJx544AEAwJYtW7Bw4UJs2rSp0Wfc\nbvf1DitkiIqKQmRkJKqqqvD+++9DkiSlQwoZdDodTCYTjhw5gk6dOuHixYv+/xKlM/w777xDlZWV\n1Llz52vO7uCj51XdiqJIsbElqkeWAAAckElEQVSxZLFYyGq1Nns6zG5b57euRURE0Pbt22nx4sX1\no2lBEMhoNF51+SWl9yXQWlOO1Go1bdq0iYiIBg0aRLt27SKbzUZer9dvfVfxGx7PPfccDh8+jHHj\nxjWqIZuamqpQVMGN0WiEKIoQRRHffvstNBoNYmNjAQCiKEKr1eJ3v/udwlEGP3a7HY8//jh69eqF\ntWvXIikpCTt27MBtt92mdGhBjyzL9UuDffDBB8jMzERERARUKhXOnj2LnTt3+v4lSmd4AGS32yk7\nO5ssFgsVFhYSEdHu3btp5syZXMColW61Wi1NnTqVnnvuOdq/fz8dP36cTp06RV6vl7Zt20YpKSnU\ntWtXGjduHKlUKnbbQr91rXv37lRaWkpVVVVks9nql1tfsWIFZWVlcd9tpVtBEKi4uJi8Xi9Nnz69\nwUja4XAQXfkffXKr+E4CoDVr1tTf9JBlmTweD9ntdrLb7dyBfHD7wgsv0OOPP05nzpyhnJwcMpvN\n5PV6ac+ePbRt2zZavHgxxcTEUHJyMrtthV8A9PTTT9PYsWOpR48epNFoKCYmpr5yXlVVFffdVro1\nmUzkcrloz549ZDQaG7zXtWtXoiv/Y/Anv8jISJIkiTweD1mtVnrggQfo7Nmz5PV6ye12cwdqpVtR\nFCkxMZF+9atfkcFgIK1WS8nJyZSZmUknT56kyspK6t27N82bN4/dtsIvABo7dizpdLpGoxaiK08v\ncN9tvdsNGzZQREREo9d79OhBsiz77Dag1/PzeDwQRbHJZe2Jpwg14Mduo6KiYLfbIctykytjp6am\n4siRI1i3bh2eeuop1NbW1r/HbhvT0r5bVVUFlUqFmJiYRu+x34a01K3X64UgCFCpGt+yaIlbxW94\nNIcgCBBFES6XS+lQgg6NRoNhw4bhww8/bHbmxsCBA6HRaLB169YGiY/xD99//z2OHz+udBghSVNJ\nr1UoPbwVBKHBQ7c6nY5GjRpF3333HVmt1kYX48GnDtfk1mQyUWVlJRUVFdH+/fspISGB9Ho9RUdH\n069//WsiIlqxYkWTlfKU3pdAbE31wbr+GxsbS4IgkEajoU6dOtEdd9xBLpeLZs+ezX3XB7cAyO12\nU3l5OeXn55PZbCaHw0E2m63RdcDWuFV8Jz/88EOKi4ujjIwMiouLo1OnTlFhYSG5XK4mz/e5A127\n23vvvZc2bNhAREQej4feffddOnfuHEmSRLW1tRQZGcluffCL/13zc7lc9Mknn9D8+fPp6NGjtHfv\nXsrNzaWUlBT264NbAPXTXIuLi0mWZbJardStW7dGs5KCMvmdP3+ePv30U6quriZJkshqtVKvXr0o\nKSmpWSHcgVrWgbp160arVq2iM2fO0IgRI+iLL75gt37yKwgC9ezZk2688UayWq20e/duevDBB9mv\nH9wCIJVKRcnJyXTfffeRRqNp9oDdGrcBfcPjahBfNG4Au21b2G/bwev5MQzDXEe4ehvDMGEJj/wY\nhglLOPkxDBOWcPJjGCYsCbjqbYcOHUJiYiK6du3a5LSsOviOWUP4bmTbcq1+VSoV3nzzTYiiiOnT\npzf5GfbbEL7b+z9uvvlmlJaWwmg0Kh1KSKLT6WC1WhEXF6d0KCGHIAiYOXMmnnnmGUycOFHpcEKK\nDRs2oLi4GCtWrPDbNgMq+SUlJcHlcqF///4837QNSE9Ph9VqhdFoRE1NjdLhhBx//vOf8cQTT+Cl\nl15qckEDpnWIooj+/ftDFEV8/PHHfttuwDzkrFKpkJWVhaqqqiZrTfwYPnVoyLWcOpw6dQppaWmo\nra1FdHR0s59jt435Kb+CIODSpUsYOHAgCgoKrrot9tuQq7kVBAE33XQTTpw4gaqqKqSkpMDpdKK5\nvNUSt4oXMKpj9erVGDlyJB8x24jhw4cjOTkZ/fv3x/79+5UOJ+T49ttvkZCQgIyMDBQWFjb742Ra\nRr9+/bB+/Xo4nU4MHToULpfLf24DZQ5fQUEBlZeXX3XeHlo5hy8c2tVcpaenU05ODqWlpV21sA67\nbZ3fW265hbxeL7377rs/OfeU/bbM7dKlS6myspKGDRvW7ApPrXUbEDuZlZVF+fn59OGHHzZ4XRTF\nZmt2Kv0PFmjtah3CZrNRaWlpg9dMJlOTy1mx25b5FQSB7HZ7/crCarWa1Go1GY1GGjZsGPv1wW1m\nZiY5HA4qKCiod1u3hJher/fZbUDc8Fi/fj0qKyuh1WqhVqthMpmgVqsxZMiQOjmMD5SVleHEiRMQ\nBAEajQZdunTBmjVrIIqi0qEFPTqdDqIo4r333kNUVBQee+wxvPHGGxgwYAAOHDigdHhBS1RUFJ54\n4gkQEU6ePImuXbti6NChePLJJ7Fx40ZMnTrV9y8JhAxvMplIlmWaNWsW1dbWUk1NDXk8HqqurqbM\nzEw+evrgNi0tjaxWK+Xl5VFZWRlJkkRut5v2799PO3fuZLc++BUEgR5++GGSJImio6NpzJgxdPHi\nRXrrrbfo9ddfJ5vNxn5b6Xbt2rUkSRJVVVXR73//e8rOziabzUaSJNW35OTkRpdxWvK9ATHyMxgM\nICJUVlbi9OnTEAQB58+fh8FgwH333ad0eEFNdHQ0vF4vTp06BYfDAbPZjPPnz6N9+/b1tXyZ1qHT\n6fDHP/4RkiRhypQpcLlcmDFjBmbPno2//vWvsNvt/DxlK+nTpw9kWYbX60VhYSFUKhVcLhfsdjuA\nK/V9Hnzwwbrk2SoCIvmlpqaiuroaBoMBBw8eREZGBgYNGgSv14sxY8YoHV5Qc//992Pnzp2YPHky\nfvvb3+KTTz7BgAED4HK5YLValQ4vqImPj0e/fv2g0+nwj3/8A1u3bsXnn3+O6upqEBEcDgdGjhyp\ndJhBSXZ2NjweD3bt2oXY2Fio1WoUFBTgjTfeQE5ODsrLyzF69Ogmi5tdKwHxqMvp06eh0+nwu9/9\nDrfffjsyMjIwYsQI2Gw25OTkKB1eUJOcnIxnnnkGZWVlKC0txeXLlzFlyhSYTCYsWbJE6fCCmqKi\nIlRUVCA+Ph5HjhyBx+NBQkIC7rrrLnzwwQcwGAzYuHGj0mEGJc8//zxEUYTRaMTcuXPx2muvQa1W\nQ61Wo0uXLjAYDHj55Zd9Gvkpfm6P/93V3bNnD/3hD3+gyspKqqqqop07d1719rbS1ykCrTXn9vz5\n85STk0NRUVE0Y8YMevvttyknJ4c2b95MWq2W3frod9KkSZSXl0dms5lKSkqI6Eq93pqaGnrggQfY\nbyvdqtVqWrVqFdntdrJarWS1WslisVB+fj5VVFTQokWLKCkpqdETCy36XqV38oc7O2HCBNq/fz+d\nP3+eJkyY0OxnuQNdu9u0tLT6IjCSJNGRI0eavYnEblvuVxAEGjduHLnd7voaHu3atWO/ProVBIG0\nWi1t376diouLyWazkdVqpfz8fOrdu7dfHoELmOltLYV4ilADmnMrCAI2b96MO++8E0uXLsUf//hH\neL3eq26L3TaG+27b0ZxbrVYLQRDgdrthNBphs9l+mDCbpCVuOfmFCOy2bWG/bQcvacUwDHMd4eTH\nMExYwtXbGIYJS3jkxzBMWMLJj2GYsISTH8MwYUnAVW+7VvhxgYaw27aF/bYd/KgLwzDMdYSTH8Mw\nYQknP4ZhwhJOfiGKRqNBSkoKvv/+e3zzzTcQBKHB2mcvvPACIiIicNttt8FgMCgYafBS51Sj0SA6\nOhr/93//h1mzZuGJJ55AQkKC0uExP4WSqzdMnz6diIi8Xi/VIcsy5eTk0KpVq2jEiBG8MkYr3b76\n6qskSRK5XC76IbIsk8lkIpvNRkRXVnrZvHkzu22h35iYGJJlmTweD8myTLIsU3V1NU2cOJFmzJhB\nJ0+epFdeeYX7bivciqJI2dnZVFhYSB6PhyRJojo8Hg95vV46dOiQz24VXczUaDQiPz8f69evx/vv\nv4/Nmzdj/vz52LhxI06fPg29Xo+tW7cqGWLQ8tVXX2HixIlwu93o1q1b/euCIKC2trbB3wMHDlQi\nxKAmOTkZZWVlWLRoET788EP06dMHmzZtgizL6NixI86cOYPBgwcrHWbQIQgCEhMTYbFYYLFYsHnz\nZowcORIWiwUFBQVYvHgxpk2bhu+//973L1MywxsMhiazd0ZGBrlcrqvW6VT6aBVorSlHer2eevfu\nTXl5eXT27Flyu91ks9nq1/cjInK73fTII4+w21b4ba6ZTCbKz8+nL7/8kvtuK9wKgkB6vb7J378g\nCFRaWkpr16712a2i1/wcDkezr69duxYRERHXOaLQwul04ujRo+jWrRt+9rOfwWg0oqKiov7an8fj\nwb59+7B69WqFIw0dVCoVMjMzERMTg8jISKXDCUqICE6nE7IsN3pPp9PB6XSiurraP18UaEdPnU5H\nVVVVlJiYyCM/P7qtqqqiOgoLC2n69Ons1o9+AdCxY8eIiMjhcNDkyZPZrx/d9uzZkxwOBy1btox0\nOl1wj/yaY8+ePYiNjYXT6VQ6lJAiJiamrrPBbDZjwYIFCkcUetSdrdTW1mLx4sUKRxM6CIKAN954\nA3q9Hp999hlcLpfP2wyI5CeKYoO/L126BADo0qWLEuGEJO3atWvwuMvHH3/8k8vZMy2nXbt28Hg8\nWLVqldKhhBSCIECSJHi9Xhw4cMA/Gw3E4W1NTQ3Rlf+BT3v94PZXv/oVWSwWIrryWFFFRQW79aPf\nulZWVkYul4v+9Kc/Naoqxn59c7thwwYqKSmhOXPm+K3vBsTI74eIogitVqt0GCGFRqNBVFQUAODf\n//43hg8frnBEoUm7du1QWVkJtVpd96Nm/MRdd92FsrIyVFZW+m+jgZbhExISyOl00sWLF3l04ge3\nnTp1ojq8Xm+zJf/Ybev8Alceyn3mmWfI6XTSyJEjrzrqY78tc6vVamnXrl1ksVjopZdeavbxuNa4\nDbiRX92FzPj4eIUjCX46derU4GHQ8vJyREZGQqUKuH/2oCYyMhKDBg3C6tWrsXPnzrofNOMHUlJS\noNPpsGbNGqxYscK/N0EDJcPXtfT0dJIkiU6dOsVHTx/dlpaWksPhIKIr09iu9ugQu22537p25MgR\ncjqdPzniY78td7tu3TpyuVw0atQov7sNuLq9Go0GNpsNsiwjOjq62VvaxAtCNqApt1u2bMGAAQPw\n1ltvYcuWLTh//jzMZvNPbovdNuZqfdfr9cJut8NkMl3TtthvQ5pzK4oizGYzNBoNTCZTkw89/5iW\nuA245HetcAdqCLttW9hv26GUWy5dyTBMWMJXvhmGCUs4+TEME5Zw9bYQgd22Ley37eDqbQzDMNcR\nTn4Mw4QlAZP81Go1CgoK4HA4eBHTNkIQBBgMBkyePBmyLOPChQvQ6/VKhxUSCIKA8vJy2O12FBcX\nw+FwIDs7G1qttkHhKKblaLVaeDwefP/997j77rv9Nzc9EJ7kHj58eIMiJQ6Hg7p27cpPyfvpKfm6\nlpWVVe9ZlmWy2Wz01VdfsVs/+F28eDFJkkRer5ccDgdVV1dTYWEhdenShZKTk9lvK90ajUaaPHky\neTweOnfuHNXW1lJVVRU9++yzPucFxXeypKSErFYrOZ1OGjp0KE2fPp1KSkrI4/GQ3W6n3r17c/Lz\nsQMNHTq0vhKW0+mkQ4cO0bJly8jr9dKqVasa1UpQel8CsV3N70cffURms5l2795NHTt2JFEUKTU1\nlfbs2UOrVq2iOXPm0IQJE9hvC9xGR0eT2Wwmr9dL5eXllJKSQjt27CC73V5fg8bXvKD4aW9CQgKM\nRiOeffZZ7Nq1C++88w4yMzORk5MDg8GAnTt3Kh1i0JOVlYWYmBgcO3YMS5YsARGhU6dOEAQBTzzx\nxDVNG2Kap67uscvlQm1tLQwGA0RRhEqlwpAhQ1BSUoLs7GyFowwuvv32W8TFxQEApk+fjoKCAhQW\nFkKv19clTN9ROsNbrVay2WykVqsJAPXo0YOcTicREc2bN49Pe31wC1yphJednU1er5cefvhhmjlz\nJu3evZtkWaZ9+/axWx/9AqCIiAjKzMykXbt20bBhw2jAgAFUWFhILpeLUlNTeWTdCrdDhw4lWZbp\nwIED1KFDB1Kr1fTBBx+Qy+Uil8tFJpMp+E973W43FRYW0t/+9je64447yOFwkMfjIZvNdtVrLEr/\ngwVaa85TRUUFud1ustvtdOzYMbJYLOR0Oslut1NKSgq79dEvAOrVq1d9ScUvv/yS8vLyyGw2N9uH\nld6XQGtNORJFkWRZpuLiYsrKyqK4uDiqrKwkWZbpqaeeIlEUgz/5AVdueIwfP56IrlQVKy4upsLC\nQk5+Prq98cYb6Yd4PB6SJImsVit99913dPfdd7NbH/zWtdjYWNq5c2e9Z0mSaOfOnX4ZnYRDa86r\nWq2mF154gdxuN3m9XiIiMpvNzR60W+pW8Wt+ALB9+3ZcvnwZq1atQl5eHoYMGQKNRoOysjJ07txZ\n6fCClvPnz+Py5csAAFmW8eWXX+LVV19FdnY2Dhw4gG3btikcYWiwceNGZGRk1P9tt9vx8ssvw2az\nKRhV8OPxeLB161YUFRVBpVLB4/EgMjISQ4YM8cv2r+v0tquxefNmXLx4EdnZ2ejYsSPy8vLQp08f\nxMfH49KlS3VHCKaFREVFQa1WQ5IkiKKIPn364LHHHsPEiRMhSZLS4QU1oiji1ltvRWZmJlQqFdxu\nN0RRhM1mw/79+/lGkh/o06cPoqOjUVtbi5qaGnTq1Kn+BpPPBMLwFgDNmjWLiKjBxWFZlunixYs0\nYcIEmj17Np86tNBtZGQkDR8+vP5vlUpFvXv3piNHjlDPnj39Uvg5XNqPHSUkJJAsy+T1eql9+/aU\nkJBA+/fvJ7fbTVarlWJjY/mSTSvd/rBVVVVRfn4+JScn08KFC+nrr78mq9UaOtf86hIdXflQfSsv\nL6e77rqLvvjiC+5ArXBbU1ND06dPJ41GQ4IgUHx8PFmtVsrNzaXVq1dz8vPB75o1a4iIqLy8nARB\nII1GQ5IkkSzLVFlZyaUrfXBb1/r27Us7d+6kpKQkAq48T3ns2DE6duwYpaamhsY1PwCw2Ww4ffo0\nXnvtNUyYMAE5OTnwer0YOHAg7rvvPqXDC0qioqIwb948SJIEvV6PM2fOwOl0IiUlBd9++y1SU1OV\nDjEo0el0sFgssFqtcLlcGD9+PKqqqqBSqZCfn4/777+/7kfNtJIOHTpg//79GD9+PIxGI5YvX44R\nI0YgNTUVO3fuxLlz53z/kkDI8ABo/fr1VIfFYiGr1UqSJNGDDz7IR89WuvV4PGS1WumRRx6hTz75\nhDweD3k8Htq6dSvFxcU1OzpRel8Csf3Qj16vp2HDhpHL5aJTp07Vj/i+/vprysjIaPRcH/u9drc/\nbA6Hg9xud/3jWXXTMf01qg6InaxrRqORvvvuO7rrrrto+PDhfOrgo9sBAwaQ1WolWZaptraWtm3b\nRvfddx//OP3kt7VN6X0JtNacp9GjR5MkSVRTU0Pz588nk8nk15rIXMAoRGC3bQv7bTt4MVOGYZjr\nCFdvYxgmLOGRH8MwYQknP4ZhwhKu3hYisNu2hf22HXzDg2EY5jrCyY9hmLCEkx/DMGFJwCW/zMxM\neDweyLIMWZZRWVmpdEghQ0JCArp164bMzEwup+hH3nnnHZSUlGDLli2w2+04ffo0Zs6cidGjRysd\nWsgwevRoHDhwAPn5+SguLsaoUaN832ggTGMBQGPHjqWqqiqyWq30yiuv0NSpU6mOpj6v9JScQGtX\ncztp0qT6+dJ1q+eo1Wqe2+sHvwMGDCC32021tbW0fft2On36NDmdTvJ4POR2u9mvD24B0J49e6im\npobsdjvl5ubW92GXyxUac3sFQaDq6mpyu90N5p1WVFT4ZSfDoTXlSKVSUefOnWnv3r3kcrnIZrNR\nHTxv2ne/JpOJjh8/Tna7naZOnUrx8fHUoUMH+te//kUnT56kCxcusN9WugVAOp2OLBZLfR1vrVZL\ncXFxVFpa2myZi5Z8r+KnvSkpKaisrITJZMLNN99cv/qtIAiQZdlvS1aHI48++ijeeustSJKELVu2\nAAAqKipw6dIlqNUBs4h30JKTk4Pu3btj5MiRWLhwIcxmM0pKSjBjxgy8/vrr2LZtW4Pl7ZlrZ/To\n0di7dy+Ki4vRsWNH5OfnQ5ZljB07Fi6XC8uWLfP9S5TO8AMGDCCXy0XV1dU0aNAgUqlU1KVLF3r1\n1VdJkiRasGABGQwGPnq2wu3p06epsLCQFi5cSHfccQdZrVayWq20YcOGJp2y22v3m5ycTA6HgyRJ\norS0NNJqtSSKIhkMBho+fDjl5OSQ0+nkvtsKtwAoOzubysvL6ZFHHiGtVkvdunWjdevWkdvtpk2b\nNlFGRobPfVfxVV0EQUDnzp2Rl5cHSZKwfft2eL1e3H333fjLX/6CmpqaJrM88YOiDWjK7eLFi/GL\nX/wCKSkpqK6uhtFohFqthsFgQHJyMoqKiprcFrttzI/9jhs3DnPnzsXXX3+N8ePHQ5IklJeXIyEh\nAUajEYIgoKqqComJifjxb4z9NqSpvltSUgJJklBVVQWLxYLBgwcDuFI3pX///jh27BicTmejbbXE\nreLnPkSEgoIC3HTTTSgrK8Py5cvRr18/iKKIFStWwG63Kx1i0JKSkgKPx4M1a9ZAFEXs378fr732\nGgDAYDBAEIRGP0zm2li3bh3Gjh2LmJgY6PV66PV6uN1uREVFQRAESJKEAwcOsN9WYjQaodPp0Llz\nZxARSkpKkJiYCADo0aMHcnJyfP4OxZNfHXl5eQCAbdu24Ze//CWsViscDgccDofCkQUvf/nLXzB8\n+HDMmDEDWVlZcLlcqK6uhsFggE6n4x+mD0iShCeffBI2mw3nzp1DUVERRowYgXvvvRderxcff/wx\nunXrpnSYQUtFRQUAoF27djh48CA+//xzvPXWWwCAs2fP+qcsqNLn9j9uNpuNioqKfvJzSl+nCLT2\nU760Wi2lpKTQmTNnyGq1NltQm9223K8gCPTggw+Sx+Mhr9dLDoeD4uPj2a8f3A4ZMoQeeughKiws\nJK/XSwUFBX7LCwEz8gOA4cOHw2AwoGfPnkqHEnK43W58+eWXuOGGGyAIAhfU9iMPPfQQZsyYAVEU\n4fF4UFZWBovFonRYIcGePXvQrl07dOjQAYIgQKfT+W/jgZLhP//8cyIievHFF39y1Ac+erbIbV3r\n3LkzXbhwgc6fP09arZbd+sGvIAhktVrJ4/HQN998Q+3ateMaKX5yW9dSUlKosrKSzp8/T2PHjvWb\nW8Wf86vjnnvuARFhz5490Gq1SocTkthsNrhcLnTs2BGSJCkdTkhgMBhgMBggiiI+/fRT2O12njro\nZ+655x5ERUWhtLQUubm5/ttwIGT4jIwM8nq9ZLFYrmnUBz56tvjoCVyZQmi326mqquqqoxOl9yUQ\nW3OunnnmGfJ6vc3ORGK/rXcLXLlWbbfbaffu3WQ0GkkUxdC65te/f3988cUX/j2fZxpRVFSEgwcP\nQqvV1nU6xkcOHDiAsrIy/PWvf1U6lJBErVbDYrHg8ccfh91ur58B5g8Uf8i5tRA/KNoAdtu2sN+2\nQym3XL2NYZiwJGBueDAMw1xPOPkxDBOWcPJjGCYs4eTHMExYwsmPYZiwhJMfwzBhCSc/hmHCEk5+\nDMOEJZz8GIYJSzj5MQwTlnDyYxgmLOHkxzBMWMLJj2GYsISTH8MwYQknP4ZhwhJOfgzDhCWc/BiG\nCUs4+TEME5Zw8mMYJizh5McwTFjCyY9hmLCEkx/DMGEJJz+GYcKS/wdjOpx6pXFKxAAAAABJRU5E\nrkJggg==\n", 297 | "text/plain": [ 298 | "" 299 | ] 300 | }, 301 | "metadata": {}, 302 | "output_type": "display_data" 303 | } 304 | ], 305 | "source": [ 306 | "num_image = 4\n", 307 | "for digit in range(10):\n", 308 | " for i in range(num_image):\n", 309 | " latent_z = mx.nd.random_normal(0, 1, shape=(1, latent_z_size), ctx=ctx)\n", 310 | " label = nd.one_hot(nd.array([[digit]]), 10).as_in_context(ctx)\n", 311 | " img = netG(nd.concat(latent_z, label.reshape((1, 10))))\n", 312 | " plt.subplot(10, 4, digit * 4 + i + 1)\n", 313 | " visualize(img[0])\n", 314 | "plt.show()" 315 | ] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": {}, 320 | "source": [ 321 | "For whinges or inquiries, [open an issue on GitHub.](https://github.com/zackchase/mxnet-the-straight-dope)" 322 | ] 323 | } 324 | ], 325 | "metadata": { 326 | "anaconda-cloud": {}, 327 | "kernelspec": { 328 | "display_name": "Python 3", 329 | "language": "python", 330 | "name": "python3" 331 | }, 332 | "language_info": { 333 | "codemirror_mode": { 334 | "name": "ipython", 335 | "version": 3 336 | }, 337 | "file_extension": ".py", 338 | "mimetype": "text/x-python", 339 | "name": "python", 340 | "nbconvert_exporter": "python", 341 | "pygments_lexer": "ipython3", 342 | "version": "3.6.2" 343 | } 344 | }, 345 | "nbformat": 4, 346 | "nbformat_minor": 2 347 | } 348 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 wangx404 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GAN_gluon_tutorials 2 | This tutorial of GAN is translation of its English version which is created by MXNet group. 3 | 4 | 此教程为GAN英文教程的中文翻译,以便帮助中国的深度学习爱好者进行学习。 5 | 6 | 原教程英文版链接地址:http://gluon.mxnet.io, Github链接:https://github.com/zackchase/mxnet-the-straight-dope 7 | 8 | 原教程部分代码可能略有瑕疵,但并不影响代码的正常运行,可自行对train部分的内容进行修改调整。 9 | -------------------------------------------------------------------------------- /img/Pixel2pixel-Unet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangx404/GAN_gluon_tutorials/2ca4fa24243cb35993ada8082a6b0944c6712ce4/img/Pixel2pixel-Unet.png -------------------------------------------------------------------------------- /img/cgan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangx404/GAN_gluon_tutorials/2ca4fa24243cb35993ada8082a6b0944c6712ce4/img/cgan.png -------------------------------------------------------------------------------- /img/complicated_netD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangx404/GAN_gluon_tutorials/2ca4fa24243cb35993ada8082a6b0944c6712ce4/img/complicated_netD.png -------------------------------------------------------------------------------- /img/complicated_netG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangx404/GAN_gluon_tutorials/2ca4fa24243cb35993ada8082a6b0944c6712ce4/img/complicated_netG.png -------------------------------------------------------------------------------- /img/dcgan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangx404/GAN_gluon_tutorials/2ca4fa24243cb35993ada8082a6b0944c6712ce4/img/dcgan.png -------------------------------------------------------------------------------- /img/fake_bedrooms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangx404/GAN_gluon_tutorials/2ca4fa24243cb35993ada8082a6b0944c6712ce4/img/fake_bedrooms.png -------------------------------------------------------------------------------- /img/readme.md: -------------------------------------------------------------------------------- 1 | This folder contains images used in jupyter notebooks. 2 | -------------------------------------------------------------------------------- /img/simple_netD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangx404/GAN_gluon_tutorials/2ca4fa24243cb35993ada8082a6b0944c6712ce4/img/simple_netD.png --------------------------------------------------------------------------------