├── .gitignore ├── ACGAN_intro_for_MNIST.ipynb ├── ACGAN_intro_for_cifar10.ipynb ├── CGAN_intro_for_MNIST.ipynb ├── DCGAN_intro_for_Fashion_MNIST.ipynb ├── DCGAN_intro_for_MNIST.ipynb ├── DCGAN_intro_for_cifar10.ipynb ├── LICENSE ├── README.md ├── gan ├── CycleGAN-keras.ipynb ├── dragan-keras.ipynb ├── pix2pix-keras-font.ipynb └── wgan-keras.ipynb ├── pix2pix.ipynb ├── pix2pix_eager.ipynb ├── stylegan_train.ipynb ├── stylegan_use.ipynb └── stylegan_use2.ipynb /.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 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 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 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /DCGAN_intro_for_MNIST.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "accelerator": "GPU", 6 | "colab": { 7 | "name": "DCGAN-intro for MNIST", 8 | "provenance": [], 9 | "collapsed_sections": [], 10 | "include_colab_link": true 11 | }, 12 | "kernelspec": { 13 | "display_name": "Python 3", 14 | "language": "python", 15 | "name": "python3" 16 | }, 17 | "language_info": { 18 | "codemirror_mode": { 19 | "name": "ipython", 20 | "version": 3 21 | }, 22 | "file_extension": ".py", 23 | "mimetype": "text/x-python", 24 | "name": "python", 25 | "nbconvert_exporter": "python", 26 | "pygments_lexer": "ipython3", 27 | "version": "3.6.8" 28 | } 29 | }, 30 | "cells": [ 31 | { 32 | "cell_type": "markdown", 33 | "metadata": { 34 | "id": "view-in-github", 35 | "colab_type": "text" 36 | }, 37 | "source": [ 38 | "\"Open" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "metadata": { 44 | "colab": { 45 | "base_uri": "https://localhost:8080/", 46 | "height": 35 47 | }, 48 | "id": "ckOHCuiArv5c", 49 | "outputId": "b796dc2e-b9c0-48ef-c0cb-b6ad6eafbbd9" 50 | }, 51 | "source": [ 52 | "import tensorflow as tf\n", 53 | "\n", 54 | "import matplotlib.pyplot as plt\n", 55 | "import numpy as np\n", 56 | "import os\n", 57 | "import PIL\n", 58 | "import time\n", 59 | "from skimage.io import imshow\n", 60 | "\n", 61 | "from IPython.display import display\n", 62 | "tf.__version__" 63 | ], 64 | "execution_count": 1, 65 | "outputs": [ 66 | { 67 | "output_type": "execute_result", 68 | "data": { 69 | "application/vnd.google.colaboratory.intrinsic+json": { 70 | "type": "string" 71 | }, 72 | "text/plain": [ 73 | "'2.4.1'" 74 | ] 75 | }, 76 | "metadata": { 77 | "tags": [] 78 | }, 79 | "execution_count": 1 80 | } 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "metadata": { 86 | "colab": { 87 | "base_uri": "https://localhost:8080/" 88 | }, 89 | "id": "1V2PJHrDrxf2", 90 | "outputId": "e45bcfed-45b1-46fc-a953-71e706b3676b" 91 | }, 92 | "source": [ 93 | "(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()\n" 94 | ], 95 | "execution_count": 2, 96 | "outputs": [ 97 | { 98 | "output_type": "stream", 99 | "text": [ 100 | "Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz\n", 101 | "11493376/11490434 [==============================] - 0s 0us/step\n" 102 | ], 103 | "name": "stdout" 104 | } 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "metadata": { 110 | "colab": { 111 | "base_uri": "https://localhost:8080/" 112 | }, 113 | "id": "L0iNo21Arykj", 114 | "outputId": "1d172878-0d12-4da4-8080-1f4538f166ae" 115 | }, 116 | "source": [ 117 | "train_images.dtype, train_images.shape" 118 | ], 119 | "execution_count": 3, 120 | "outputs": [ 121 | { 122 | "output_type": "execute_result", 123 | "data": { 124 | "text/plain": [ 125 | "(dtype('uint8'), (60000, 28, 28))" 126 | ] 127 | }, 128 | "metadata": { 129 | "tags": [] 130 | }, 131 | "execution_count": 3 132 | } 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "metadata": { 138 | "colab": { 139 | "base_uri": "https://localhost:8080/", 140 | "height": 314 141 | }, 142 | "id": "uAcCsSinus1d", 143 | "outputId": "f87865f8-ca2f-42b4-e1a1-dd3251e91acf" 144 | }, 145 | "source": [ 146 | "imshow(train_images[0])" 147 | ], 148 | "execution_count": 4, 149 | "outputs": [ 150 | { 151 | "output_type": "execute_result", 152 | "data": { 153 | "text/plain": [ 154 | "" 155 | ] 156 | }, 157 | "metadata": { 158 | "tags": [] 159 | }, 160 | "execution_count": 4 161 | }, 162 | { 163 | "output_type": "display_data", 164 | "data": { 165 | "image/png": "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\n", 166 | "text/plain": [ 167 | "
" 168 | ] 169 | }, 170 | "metadata": { 171 | "tags": [], 172 | "needs_background": "light" 173 | } 174 | } 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "metadata": { 180 | "id": "uSDHJmuwtxT4" 181 | }, 182 | "source": [ 183 | "def img_to_float(img):\n", 184 | " return (np.float32(img)[..., None]-127.5)/127.5\n", 185 | "def img_to_uint8(img):\n", 186 | " return np.uint8(img*127.5+128).clip(0, 255)[...,0]" 187 | ], 188 | "execution_count": 5, 189 | "outputs": [] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "metadata": { 194 | "colab": { 195 | "base_uri": "https://localhost:8080/", 196 | "height": 314 197 | }, 198 | "id": "9yGVvMP9vUn0", 199 | "outputId": "450d4a4b-603f-464c-8f5b-c271b0b67863" 200 | }, 201 | "source": [ 202 | "train_img_f32 = img_to_float(train_images)\n", 203 | "imshow(img_to_uint8(train_img_f32[0]))" 204 | ], 205 | "execution_count": 6, 206 | "outputs": [ 207 | { 208 | "output_type": "execute_result", 209 | "data": { 210 | "text/plain": [ 211 | "" 212 | ] 213 | }, 214 | "metadata": { 215 | "tags": [] 216 | }, 217 | "execution_count": 6 218 | }, 219 | { 220 | "output_type": "display_data", 221 | "data": { 222 | "image/png": "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\n", 223 | "text/plain": [ 224 | "
" 225 | ] 226 | }, 227 | "metadata": { 228 | "tags": [], 229 | "needs_background": "light" 230 | } 231 | } 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "metadata": { 237 | "id": "oSrY5b6ev0kO" 238 | }, 239 | "source": [ 240 | "BUFFER_SIZE = train_img_f32.shape[0]\n", 241 | "BATCH_SIZE = 32\n", 242 | "train_dataset = tf.data.Dataset.from_tensor_slices(train_img_f32).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)" 243 | ], 244 | "execution_count": 7, 245 | "outputs": [] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "metadata": { 250 | "id": "ZaJFsMs2v1R1" 251 | }, 252 | "source": [ 253 | "from tensorflow.keras.layers import Dense, BatchNormalization, LeakyReLU, Reshape, Conv2DTranspose\n", 254 | "latent_dim = 100\n", 255 | "generator = tf.keras.Sequential([\n", 256 | " Dense(7*7*256, use_bias=False, input_shape=(latent_dim,)),\n", 257 | " BatchNormalization(),\n", 258 | " LeakyReLU(),\n", 259 | " Reshape((7, 7, 256)),\n", 260 | " Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False),\n", 261 | " BatchNormalization(),\n", 262 | " LeakyReLU(),\n", 263 | " Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False),\n", 264 | " BatchNormalization(),\n", 265 | " LeakyReLU(),\n", 266 | " Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh')\n", 267 | "]\n", 268 | ")" 269 | ], 270 | "execution_count": 10, 271 | "outputs": [] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "metadata": { 276 | "id": "kqdl08rxvWiS" 277 | }, 278 | "source": [ 279 | "from tensorflow.keras.layers import Conv2D, Dropout, Flatten\n", 280 | "discriminator = tf.keras.Sequential([\n", 281 | " Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=(28,28, 1)),\n", 282 | " BatchNormalization(),\n", 283 | " LeakyReLU(),\n", 284 | " Dropout(0.3),\n", 285 | " Conv2D(128, (5, 5), strides=(2, 2), padding='same'),\n", 286 | " BatchNormalization(),\n", 287 | " LeakyReLU(),\n", 288 | " Dropout(0.3),\n", 289 | " Flatten(),\n", 290 | " Dense(1)]\n", 291 | ")" 292 | ], 293 | "execution_count": 11, 294 | "outputs": [] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "metadata": { 299 | "id": "nd8zcnhjzKWq" 300 | }, 301 | "source": [ 302 | "loss_fn = tf.keras.losses.BinaryCrossentropy(from_logits=True)\n", 303 | "def generator_loss(generated_output):\n", 304 | " return loss_fn(tf.ones_like(generated_output), generated_output)" 305 | ], 306 | "execution_count": 12, 307 | "outputs": [] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "metadata": { 312 | "id": "fgWHoC6FzbUA" 313 | }, 314 | "source": [ 315 | "def discriminator_loss(real_output, generated_output):\n", 316 | " # [1,1,...,1] with real output since it is true and we want our generated examples to look like it\n", 317 | " real_loss = loss_fn(tf.ones_like(real_output), real_output)\n", 318 | "\n", 319 | " # [0,0,...,0] with generated images since they are fake\n", 320 | " generated_loss = loss_fn(tf.zeros_like(generated_output), generated_output)\n", 321 | "\n", 322 | " total_loss = real_loss + generated_loss\n", 323 | "\n", 324 | " return total_loss" 325 | ], 326 | "execution_count": 13, 327 | "outputs": [] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "metadata": { 332 | "id": "KCYwQqxgz8o_" 333 | }, 334 | "source": [ 335 | "generator_optimizer = tf.keras.optimizers.Adam(1e-4)\n", 336 | "discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)" 337 | ], 338 | "execution_count": 14, 339 | "outputs": [] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "metadata": { 344 | "id": "Rb2-KU6K1J0I" 345 | }, 346 | "source": [ 347 | "EPOCHS = 50\n", 348 | "num_examples_to_generate = 16\n", 349 | "\n", 350 | "# We'll re-use this random vector used to seed the generator so\n", 351 | "# it will be easier to see the improvement over time.\n", 352 | "random_vector_for_generation = tf.random.normal([num_examples_to_generate,\n", 353 | " latent_dim])" 354 | ], 355 | "execution_count": 15, 356 | "outputs": [] 357 | }, 358 | { 359 | "cell_type": "code", 360 | "metadata": { 361 | "id": "Xnu88uGR1etc" 362 | }, 363 | "source": [ 364 | "@tf.function\n", 365 | "def train_step(images):\n", 366 | " # generating noise from a normal distribution\n", 367 | " noise = tf.random.normal([BATCH_SIZE, latent_dim])\n", 368 | " \n", 369 | " with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n", 370 | " generated_images = generator(noise, training=True)\n", 371 | " \n", 372 | " real_logits = discriminator(images, training=True)\n", 373 | " generated_logits = discriminator(generated_images, training=True)\n", 374 | " \n", 375 | " gen_loss = generator_loss(generated_logits)\n", 376 | " disc_loss = discriminator_loss(real_logtis, generated_logits)\n", 377 | " \n", 378 | " gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)\n", 379 | " gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)\n", 380 | " \n", 381 | " generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))\n", 382 | " discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))" 383 | ], 384 | "execution_count": null, 385 | "outputs": [] 386 | }, 387 | { 388 | "cell_type": "code", 389 | "metadata": { 390 | "colab": { 391 | "base_uri": "https://localhost:8080/", 392 | "height": 706 393 | }, 394 | "id": "1DPQkOVx2yon", 395 | "outputId": "0f39dec5-31b6-4ffb-99d9-c0020ba94748" 396 | }, 397 | "source": [ 398 | "for epoch in range(15):\n", 399 | " start_time = time.time()\n", 400 | " for images in train_dataset:\n", 401 | " train_step(images)\n", 402 | " fake = generator(random_vector_for_generation, training=False)\n", 403 | " fake_concat = np.transpose(img_to_uint8(fake), [1,0,2]).reshape((28,-1))\n", 404 | " print(epoch, time.time()-start_time)\n", 405 | " display(PIL.Image.fromarray(fake_concat))\n", 406 | " \n", 407 | " " 408 | ], 409 | "execution_count": null, 410 | "outputs": [ 411 | { 412 | "output_type": "stream", 413 | "text": [ 414 | "0 11.480594396591187\n" 415 | ], 416 | "name": "stdout" 417 | }, 418 | { 419 | "output_type": "display_data", 420 | "data": { 421 | "image/png": "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\n", 422 | "text/plain": [ 423 | "" 424 | ] 425 | }, 426 | "metadata": { 427 | "tags": [] 428 | } 429 | }, 430 | { 431 | "output_type": "stream", 432 | "text": [ 433 | "1 7.339160680770874\n" 434 | ], 435 | "name": "stdout" 436 | }, 437 | { 438 | "output_type": "display_data", 439 | "data": { 440 | "image/png": "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\n", 441 | "text/plain": [ 442 | "" 443 | ] 444 | }, 445 | "metadata": { 446 | "tags": [] 447 | } 448 | }, 449 | { 450 | "output_type": "stream", 451 | "text": [ 452 | "2 7.42051887512207\n" 453 | ], 454 | "name": "stdout" 455 | }, 456 | { 457 | "output_type": "display_data", 458 | "data": { 459 | "image/png": "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\n", 460 | "text/plain": [ 461 | "" 462 | ] 463 | }, 464 | "metadata": { 465 | "tags": [] 466 | } 467 | }, 468 | { 469 | "output_type": "stream", 470 | "text": [ 471 | "3 7.219139337539673\n" 472 | ], 473 | "name": "stdout" 474 | }, 475 | { 476 | "output_type": "display_data", 477 | "data": { 478 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAcAAAAAcCAAAAADTxTBPAAARGUlEQVR4nO06aXhURbbnVN2+t7eku7OQkBBCCBAiBgRkBxVUkFU2R0BcGRzFQfET1EFnxgWfIoqCLII4jiCigwqCiuOT8QkCAgLKsBoTCGQhZE+nk+6+91bNj17v0ry8ed/3vve+N+dPd9epc+psdU6dqkZB5QAAgCT0QQUqcEUNMg4Cj+AQEQEAOefAATgAhTAOAIFQRijIjEdYsQgd8MgsQA4AQFCFGCDX4kJ0CBghQ2KhAaZdL7osAHLgCATCdJSoDIDHz9HSIQChKg+pAJTK4ckISEVGMKBGFiZEQ8cRkCEAB0CwsAgOARB4SHCuo0MeQgNHDEkocCVmDaQcKZd5WGOCUT9wYhW5zGU5MpkQFUJLRwwbshEA5RKidghBsDpblIAab19ApJxwZJRzJWwhjNcPgQgWv8pAB6gxZ7sB46iIoDKuWy8RGRK9lzWsDCJpeAoC9avQHiBcyxOB8OiQiZwEkDODLHFhqudG3byt1WBM07koRHlwQQqowNFyk/T3hkYfQLymtoxWyan4Uj1HWiLT4/hwjpjetf7noMF81kD7bGKQLMbJIrTq10sExIKKbp6BjCdE9qypap9wxjjlGB0zymlBxkxQPJFKBCSr+1R7/AccQIjRpV9/slIG6/3Dyo6pQY18JH3OoA5upyLypmXbvGasMWtFr/qN7zdr3YXuoUerrhCaCeWiEGOUYSlulzIAQK/K/ak80O5ltJD1h1XtdKBOE2rncjDBVADosdD/VEv7daeuZMzvcbq9KiMgIKEWySIO/+lyedm2Zz85tT0dtXNsgw42B1TGVMbkC5smWtHIpsuegBKoWD/Yohn2HLu8pTA/M0MiSOzJRjIzgQhBAJocZWQ/enKkKSWhhiHpxbLSdZ4rcbdaSQIxLC1K1VvDXKQ9QmpAnLeop5RQN7JJVXYKpihzmuS5f3hsx8G11v98YSIQBCJIKkepcyrr9mQaIBV693P+uiY6hQOgIKaPctXTVIGDAOAYVHFAUUBXCCwfFRGl8QL2KqlXIVS/Ebnt6e6NvxTdnt6xZsM+8RphcxBAF75IRQUYcBoq2JRzkSoyADIpfJwgQ7rzV6aVq7HyHQJhxIbFWyNhSsNIVbVZ7xyx4IAvFr+huhOuPnTkuIptDc3MbD9sdDDPfXOCE79uR63VqLBifHPJpaARSbmQWuCdfgeyRYoZl8wBJ5pajDvXPcKqDEjOch7+7idNOkMklPE4Thl51WUAHGyiKKTc8NGGL7//YtGUQYPWtQa7RucQIlApOa9g8qQ+2VcPyJ54sDXQ/N04D0UAiI9k/JHJn/S+5pY+qWJ4FGl60T0vFR+8w+kumLaj5PzxPWff62/cMp61J86VlzcH21YTAADJ6vGkCwgAiABgf+fC5sFLz7Y1vGhDTbgS6nxU5b4oPyEsDKbO3HmpyaeoymaHLSXbRsKMqDUtPTs3t3DTpfqz7y4cYdxlSIaprHGvwtkSo6VJzxZVCfjbNghosmumBwL7RkgWwzhmPnLK5zu0V+U+0622j3PO1eWGcduWE2WywhhntZKGn9h5aYPcdqK48tiLI3Ily9BTdS9YAAAcAqHOqUuuc0iIgJbZbSwtSiRQpESwiIIFERDF0d7ghR6WkDFpnFhzOK+xEyEtKZwrCKLt2j2XLzyWAgBAPDNqA/6SFTl2vSZipaIosso5b+0uIEB6SlqmzRKZha+rXP1Lv5uf+6BQjE84KDpSh51kPBh1oCXiEpo09hsf45yzmsOnXxvhtCBBAKRpRQveLt5/9KKvav+3pYd7GYydtUsJLEGc1qYeNlj0RpWHgH2ZH6oe8dEkHWby9/kuqyE4cVaZrLTcO+KiahITAA+FeKqpBrr3G2VFYZxzdjIp3NoBAGDK2mbGI6CU7K0uvwsBAAhGurzQ5Bo+LMZML1Nj8+TIDxKiIZSQ7jJnn0k0pV+kOiISW6839o2O0Nu9LLjATg2ReIlzpgZUzrn6chYBkCwx9wFAP8bZGisKuh2D9kxb+mrG60zqFc0/FnKg7/z2eVluW0QzItj73f77P6/q7xnXrNTa9GQpXn8aAACZ9B8pOlSqyjlnisI5CzYut+vsQno2Mvn7grQ8h0EU65OfjRLRsqfxSaOYcL3CuVrhUwL79PUR1wUCP48f8sSBykP7PxwVw1LHU/VR/3H1zFvr38wzhBMAwE4+PcZMZ53dgQ+jRiMIiIQkXTVhyTnG6p4Zdc9rL3rCBBSR2HIzo4SSwraY5JFrGedKy7EalXPvYAKhYIo3wSXW0MVE++RCu6VPG1ttwlJa3eqvP9/QVrv1VidBxMj9BAAACh0kBHczk4fpKdPq1oW1Kq3X7c+NnM1GAPDMrlBYZVKIU4xnXr1Svyg1OSl66IivLABgP9VYYBQz40x9XREAuqeefitJiyI/+3eEN968so9jWxCJbXQbY7J8cky3DxpLCghGrl50bjqkZhsXDKtZVRw7HCEgEin52m/rWn3ev82atrXRVzUpbC2CqKmRw1iTibEnNDPWcnb78uMBpu5L1iFJkkOgr5TOMjsV2goczsleXx8jBle3yLVLek1Z/nz0PBy/MAKAK8gqDdtlZFlWeNlGdgzj52O5ek3oh1SlKqv1mZJO9avnXYix7KJV1LG/5U2j6q4zTT1D3/Iutb0SIyUIMPL8y5Gf7zYtjvFEFDzL9hTZEABmyHWxoqtPkycTNkN0xtq46EQEQh1pd+26ePnI/C7WXkcCim992L96q7+jOg3ccHVr4KvBrsLbl5c01Z0Ypzs2k7lHPyvKHDXErP6jo1PhQ2dq3xWIoSWY4Fe9K7M63/TKEFcEpZdllnLZcEQXly0PeyZfkTdp9pCr7OPw97GK+pEh3+UdDCrLNaOa9bKDrMqupwHh9eBX4e8rFWVXDGN3Z0woLo0EyY1ylSNCAgBEdKQlIQCA5OcVehWi0P/yiEQoqXCqGC8HIArunO55eXlZ7pSB37fK3jeMfgIAIHuOGwcHelvnIKCj6IFP3x1S0EXUYi3ZDy27//ktpj0dIRmvNgXr5nbo3Eu3HpapzUfnrzpddXiKZEYJAOJfjxtQmH13RujL8KbWjS7Nnk2aFDairaTtNUM6IHl/aQn+u7H+hcEa5OpfRf0ous7UdAMAAOGuNqb8MbaexTP6h9YjIQcKnynKvREKAACkgpScnEKAvM254RwWgTEnnzGeiMNAk4piHQZEDgcIKOTcNvWJDV9/vLKDeRssPDfLMIYr5CobAHrGrnzZAcZEgMQ6qqR6tAk3QujwWlU597vR6z+5RUtma2n989IdflWpWugy18H29NZMwyD2nm5DtLtnn1HkVZL2UICI5DffLraJz7R8Hs2fcYcY+8rK4zdbzBIFADzH2e6h3ey69Eo67FztQCCu6YtP+/2lQ+OIaY9dgcsj0wc8temCytlhoqEDANqn+MAdM3fL35ivB1DK6xP5D2nOPQ2VBu0RAMjCqvpjMzPdNmOjBwBAOr1lDBhSx/x9COm45bJ3jdZiED4Uk/WBwA5D+AKdMP2OWpUFNg+eU3pxpzYbTlWVijqZcdV/roepTTvW1JolmKFbJiYN+dKncq5Op4Zez7HLVzG08w/e+WYsyS2/1L5k6AXCUM4afrW0pKQwphsgkk4fXHyzt+fec1UH9uwo3ruqa3zUZ5QyzhjnnHHOJhg5ChsPfvV0eqL0Aqs4758ARTrceqCasdf044iAyYdk5ZJJyBNioVKyJ/f2F++OIwh9uIOclW18/HCLwsboOVqtAgKmflNf9cMEwy0U3VfjUxXf027XzEMVv3TRGHUbU1v9QTXoUwILTayNPf3qKbMkMSXQWuFXGOdcXSTqJ6B10vrr3QMqvPlmBxUsPN/wict8B4qlTfNnljF2NtK2IAIK2cXB4MV/e88v+06uWjPp+m4d44lTj4R6PeaTVeU2E56YfP9NxqiOINv4+UQ4KNh0qprxGoOo1h7dO+9tbn3ZRAeh16rPVzw8rN+jVR/H9mZ4Xr7Muer7Zl+FquboxRAyeufnPLBj56+31Pje1/MlCw7+fPT1G5IFahu727urc3xZHn/xxw0LR+YN+E7xjzUol5R3Z5Nao2/zAABgHeecM1Vmau1vsnXhLXjSnTZif6Ryt3luIjM3F5ljhFePTs6aXROsfsBjp1HdhQlexrniZ0xuOve6i1o0V8voORsMepu8pzc0yPI88wUnmuoAAADL6nTX2HHf6QN39S9VDhh2740Xv8mdtPmLTmZ0lqv6F3YQ3fOqL7ljaoUqsm1pXXPj58lit/MXDHsCs96rC6iBl93XNatNkSQZLU2IiETKlBDohNozD5pF/53KEWM2dxTdd3a7+R3xWM6VpvnD+t5d4Cq4ra+WYbeHxkmEFB39qdCUFEDMyTAvHbO/64SYOX+cJNgf7R1XNm+t4Zy1NFf6ZOXHBHfW9sK5Z2vK15oGBn5f0ynR3fkLj+vmxn8vFMB2228NDtzG2iaNn7s7y5ROkGxWodvvzm5Lj+IEKogum4TUUXCfG4nzbaNhcEwT40z5su9Xsno0kkMJxnUN2Gl8MoLlhZaauWb14L4qk1G05k1MMg4DAOT/8FJ4H6Bzle7t4+q/vXN99sg9Py8wf1IAEB7KMz27Wff2AgBARLqk5WR82svxqs2LexR8KKvVhjuhEDjHFHTtPXaU6QOOtbFWU+cwZm9xj96BSKMHeewLAO6xHfX81jB575/ee0zT7cS1tVRK/dXbm5fGzqeSKF61eEESABCnVRr192lGIYU/yqoSaDx9Jsi80W5doMRqSwrHZMbqZR2pOLPU/3U3M+P99lmTQQCLI8EpyxN1LI45MV+LFD879vXeCzVbUxM9NPW7tEY3ggQB3W8/GP5p2xBUP9W0GuSqziKiY2f1UfPjCD7RHwFI50FmbdQDL+kvPdAWfs4aXd9bL4lrzuxw5DnuF4AWrsvV8xMv1p36apw2tON1JRlzFrzx0rhoBBJCkwZ2IQBAu41aUXvBWJHR9syZ33fOue5PtW1Vsb4FAZB2HZEKADA8ECz74NmtZXW195m9xDleTRDY+mtDAACw5D0SSwLi7rP6g7aQ0mHKx6uvdrjc5nvw+da+uhEiSNbelf6bQmuuVRhrGGLq/tw39fU/BJ5+CACYPaOrUWDMJgZFhLQUBACcMDASoZE7TUKH1gZnAADAPv/2WSs/eaaLkWNyjv6JV/Mz/eGZTz/cRYjHhfHiyC8qHjbyI/bhISMmZerVtvR49vWrM2ernPmaa89tmuY284i4ca35TtO+gUXA/uX5JdHh604tMDu+OmwW8dq+xg4SAKCmweBYFDK2MPYpAmBmI+es2PyCAywn3zC7BMCOEgAAGTzGpD+haQA45TFNyQLRIyJgXISFDYcAwjmm7HdS8cGAGqwseee6RKZJDN3XjB8omhkaAKjhhQkALZ1uSvgubllY13S6WFWbP906o4OBFqUkZ68nqxufS1TkzYA8Xr4rorlj/3LTpQkCEQRTrhisMxl17GCcq4xzxlhbV5MJAAAwtfG4/qINAMA2nAIA7TOjp8nL5bwDY3O/bbqocy21dc3NM28vpG0VjRWnH1/0UXnZoVnt+1uEFm7YPjlR/TcHaXzfxMtg2m3XXD1yqPFpCgAArJkPf+f1lyXIoAmApNkjhhq4w3yXXeGPFhZ1gZmYmedCDZ3/9gRNIgD2PFJ9swlnMrx/ElLn+Bl9TJCdvJwFGg4a/U6cif+FgaI9dB+ecMaVgJjmuSsB/a85XEecl/PPk1Nz11/pjzKOpvxEzFyTJ15BdXRlFpieYdBmRUTBYprsLMX+E3n/Hfv8f4QrRq7NeM/7L/i/BP9cWvofgv/Vwv0LAADgH1MVdOcorsbgAAAAAElFTkSuQmCC\n", 479 | "text/plain": [ 480 | "" 481 | ] 482 | }, 483 | "metadata": { 484 | "tags": [] 485 | } 486 | }, 487 | { 488 | "output_type": "stream", 489 | "text": [ 490 | "4 7.330965757369995\n" 491 | ], 492 | "name": "stdout" 493 | }, 494 | { 495 | "output_type": "display_data", 496 | "data": { 497 | "image/png": "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\n", 498 | "text/plain": [ 499 | "" 500 | ] 501 | }, 502 | "metadata": { 503 | "tags": [] 504 | } 505 | }, 506 | { 507 | "output_type": "stream", 508 | "text": [ 509 | "5 7.140302658081055\n" 510 | ], 511 | "name": "stdout" 512 | }, 513 | { 514 | "output_type": "display_data", 515 | "data": { 516 | "image/png": "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\n", 517 | "text/plain": [ 518 | "" 519 | ] 520 | }, 521 | "metadata": { 522 | "tags": [] 523 | } 524 | }, 525 | { 526 | "output_type": "stream", 527 | "text": [ 528 | "6 7.489628314971924\n" 529 | ], 530 | "name": "stdout" 531 | }, 532 | { 533 | "output_type": "display_data", 534 | "data": { 535 | "image/png": "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\n", 536 | "text/plain": [ 537 | "" 538 | ] 539 | }, 540 | "metadata": { 541 | "tags": [] 542 | } 543 | }, 544 | { 545 | "output_type": "stream", 546 | "text": [ 547 | "7 7.206034898757935\n" 548 | ], 549 | "name": "stdout" 550 | }, 551 | { 552 | "output_type": "display_data", 553 | "data": { 554 | "image/png": "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\n", 555 | "text/plain": [ 556 | "" 557 | ] 558 | }, 559 | "metadata": { 560 | "tags": [] 561 | } 562 | }, 563 | { 564 | "output_type": "stream", 565 | "text": [ 566 | "8 7.269426584243774\n" 567 | ], 568 | "name": "stdout" 569 | }, 570 | { 571 | "output_type": "display_data", 572 | "data": { 573 | "image/png": "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\n", 574 | "text/plain": [ 575 | "" 576 | ] 577 | }, 578 | "metadata": { 579 | "tags": [] 580 | } 581 | }, 582 | { 583 | "output_type": "stream", 584 | "text": [ 585 | "9 7.2453320026397705\n" 586 | ], 587 | "name": "stdout" 588 | }, 589 | { 590 | "output_type": "display_data", 591 | "data": { 592 | "image/png": "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\n", 593 | "text/plain": [ 594 | "" 595 | ] 596 | }, 597 | "metadata": { 598 | "tags": [] 599 | } 600 | }, 601 | { 602 | "output_type": "stream", 603 | "text": [ 604 | "10 7.195350646972656\n" 605 | ], 606 | "name": "stdout" 607 | }, 608 | { 609 | "output_type": "display_data", 610 | "data": { 611 | "image/png": "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\n", 612 | "text/plain": [ 613 | "" 614 | ] 615 | }, 616 | "metadata": { 617 | "tags": [] 618 | } 619 | }, 620 | { 621 | "output_type": "stream", 622 | "text": [ 623 | "11 7.311627149581909\n" 624 | ], 625 | "name": "stdout" 626 | }, 627 | { 628 | "output_type": "display_data", 629 | "data": { 630 | "image/png": "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\n", 631 | "text/plain": [ 632 | "" 633 | ] 634 | }, 635 | "metadata": { 636 | "tags": [] 637 | } 638 | }, 639 | { 640 | "output_type": "stream", 641 | "text": [ 642 | "12 7.385121583938599\n" 643 | ], 644 | "name": "stdout" 645 | }, 646 | { 647 | "output_type": "display_data", 648 | "data": { 649 | "image/png": "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\n", 650 | "text/plain": [ 651 | "" 652 | ] 653 | }, 654 | "metadata": { 655 | "tags": [] 656 | } 657 | }, 658 | { 659 | "output_type": "stream", 660 | "text": [ 661 | "13 7.402552843093872\n" 662 | ], 663 | "name": "stdout" 664 | }, 665 | { 666 | "output_type": "display_data", 667 | "data": { 668 | "image/png": "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\n", 669 | "text/plain": [ 670 | "" 671 | ] 672 | }, 673 | "metadata": { 674 | "tags": [] 675 | } 676 | }, 677 | { 678 | "output_type": "stream", 679 | "text": [ 680 | "14 7.439730405807495\n" 681 | ], 682 | "name": "stdout" 683 | }, 684 | { 685 | "output_type": "display_data", 686 | "data": { 687 | "image/png": "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\n", 688 | "text/plain": [ 689 | "" 690 | ] 691 | }, 692 | "metadata": { 693 | "tags": [] 694 | } 695 | } 696 | ] 697 | } 698 | ] 699 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GAN_Tutorial -------------------------------------------------------------------------------- /gan/dragan-keras.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "kernelspec": { 6 | "display_name": "Python 3", 7 | "language": "python", 8 | "name": "python3" 9 | }, 10 | "language_info": { 11 | "codemirror_mode": { 12 | "name": "ipython", 13 | "version": 3 14 | }, 15 | "file_extension": ".py", 16 | "mimetype": "text/x-python", 17 | "name": "python", 18 | "nbconvert_exporter": "python", 19 | "pygments_lexer": "ipython3", 20 | "version": "3.5.3" 21 | }, 22 | "colab": { 23 | "name": "dragan-keras.ipynb", 24 | "version": "0.3.2", 25 | "provenance": [] 26 | } 27 | }, 28 | "cells": [ 29 | { 30 | "cell_type": "code", 31 | "metadata": { 32 | "id": "HwZckpzYY5x1", 33 | "colab_type": "code", 34 | "colab": {} 35 | }, 36 | "source": [ 37 | "import os\n", 38 | "os.environ['KERAS_BACKEND']='tensorflow' # 也可以使用 tensorflow\n", 39 | "#os.environ['THEANO_FLAGS']='floatX=float32,device=cuda,exception_verbosity=high'\n", 40 | "os.environ['THEANO_FLAGS']='floatX=float32,device=cuda,optimizer=fast_compile'" 41 | ], 42 | "execution_count": 0, 43 | "outputs": [] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": { 48 | "id": "FjC5lbRqY5x_", 49 | "colab_type": "text" 50 | }, 51 | "source": [ 52 | "modifed from https://github.com/martinarjovsky/WassersteinGAN " 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "metadata": { 58 | "id": "8JBgz3JzY5yC", 59 | "colab_type": "code", 60 | "colab": {} 61 | }, 62 | "source": [ 63 | "import keras.backend as K\n", 64 | "K.set_image_data_format('channels_first')\n", 65 | "from keras.models import Sequential, Model\n", 66 | "from keras.layers import Conv2D, ZeroPadding2D, BatchNormalization, Input\n", 67 | "from keras.layers import Conv2DTranspose, Reshape, Activation, Cropping2D, Flatten\n", 68 | "from keras.layers.advanced_activations import LeakyReLU\n", 69 | "from keras.activations import relu\n", 70 | "from keras.initializers import RandomNormal\n", 71 | "conv_init = RandomNormal(0, 0.02)\n", 72 | "gamma_init = RandomNormal(1., 0.02)\n" 73 | ], 74 | "execution_count": 0, 75 | "outputs": [] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "metadata": { 80 | "id": "s6-9m626Y5yL", 81 | "colab_type": "code", 82 | "colab": {} 83 | }, 84 | "source": [ 85 | "def DCGAN_D(isize, nz, nc, ndf, n_extra_layers=0):\n", 86 | " assert isize%2==0\n", 87 | " _ = inputs = Input(shape=(nc, isize, isize))\n", 88 | " _ = Conv2D(filters=ndf, kernel_size=4, strides=2, use_bias=False,\n", 89 | " padding = \"same\",\n", 90 | " kernel_initializer = conv_init, \n", 91 | " name = 'initial.conv.{0}-{1}'.format(nc, ndf) \n", 92 | " ) (_)\n", 93 | " _ = LeakyReLU(alpha=0.2, name = 'initial.relu.{0}'.format(ndf))(_)\n", 94 | " csize, cndf = isize// 2, ndf\n", 95 | " while csize > 5:\n", 96 | " assert csize%2==0\n", 97 | " in_feat = cndf\n", 98 | " out_feat = cndf*2\n", 99 | " _ = Conv2D(filters=out_feat, kernel_size=4, strides=2, use_bias=False,\n", 100 | " padding = \"same\",\n", 101 | " kernel_initializer = conv_init,\n", 102 | " name = 'pyramid.{0}-{1}.conv'.format(in_feat, out_feat) \n", 103 | " ) (_)\n", 104 | " if 0: # toggle batchnormalization\n", 105 | " _ = BatchNormalization(name = 'pyramid.{0}.batchnorm'.format(out_feat), \n", 106 | " momentum=0.9, axis=1, epsilon=1.01e-5,\n", 107 | " gamma_initializer = gamma_init, \n", 108 | " )(_, training=1) \n", 109 | " _ = LeakyReLU(alpha=0.2, name = 'pyramid.{0}.relu'.format(out_feat))(_)\n", 110 | " csize, cndf = (csize+1)//2, cndf*2\n", 111 | " _ = Conv2D(filters=1, kernel_size=csize, strides=1, use_bias=False,\n", 112 | " kernel_initializer = conv_init,\n", 113 | " name = 'final.{0}-{1}.conv'.format(cndf, 1) \n", 114 | " ) (_)\n", 115 | " outputs = Flatten()(_)\n", 116 | " return Model(inputs=inputs, outputs=outputs)\n" 117 | ], 118 | "execution_count": 0, 119 | "outputs": [] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "metadata": { 124 | "id": "M0862KAxY5yT", 125 | "colab_type": "code", 126 | "colab": {} 127 | }, 128 | "source": [ 129 | "def DCGAN_G(isize, nz, nc, ngf, n_extra_layers=0):\n", 130 | " cngf= ngf//2\n", 131 | " tisize = isize\n", 132 | " while tisize > 5:\n", 133 | " cngf = cngf * 2\n", 134 | " assert tisize%2==0\n", 135 | " tisize = tisize // 2\n", 136 | " _ = inputs = Input(shape=(nz,))\n", 137 | " _ = Reshape((nz, 1,1))(_)\n", 138 | " _ = Conv2DTranspose(filters=cngf, kernel_size=tisize, strides=1, use_bias=False,\n", 139 | " kernel_initializer = conv_init, \n", 140 | " name = 'initial.{0}-{1}.convt'.format(nz, cngf))(_)\n", 141 | " _ = BatchNormalization(gamma_initializer = gamma_init, momentum=0.9, axis=1, epsilon=1.01e-5,\n", 142 | " name = 'initial.{0}.batchnorm'.format(cngf))(_, training=1)\n", 143 | " _ = Activation(\"relu\", name = 'initial.{0}.relu'.format(cngf))(_)\n", 144 | " csize, cndf = tisize, cngf\n", 145 | " \n", 146 | "\n", 147 | " while csize < isize//2:\n", 148 | " in_feat = cngf\n", 149 | " out_feat = cngf//2\n", 150 | " _ = Conv2DTranspose(filters=out_feat, kernel_size=4, strides=2, use_bias=False,\n", 151 | " kernel_initializer = conv_init, padding=\"same\",\n", 152 | " name = 'pyramid.{0}-{1}.convt'.format(in_feat, out_feat) \n", 153 | " ) (_)\n", 154 | " _ = BatchNormalization(gamma_initializer = gamma_init, \n", 155 | " momentum=0.9, axis=1, epsilon=1.01e-5,\n", 156 | " name = 'pyramid.{0}.batchnorm'.format(out_feat))(_, training=1)\n", 157 | " \n", 158 | " _ = Activation(\"relu\", name = 'pyramid.{0}.relu'.format(out_feat))(_)\n", 159 | " csize, cngf = csize*2, cngf//2\n", 160 | " _ = Conv2DTranspose(filters=nc, kernel_size=4, strides=2, use_bias=False,\n", 161 | " kernel_initializer = conv_init, padding=\"same\",\n", 162 | " name = 'final.{0}-{1}.convt'.format(cngf, nc)\n", 163 | " )(_)\n", 164 | " outputs = Activation(\"tanh\", name = 'final.{0}.tanh'.format(nc))(_)\n", 165 | " return Model(inputs=inputs, outputs=outputs)\n" 166 | ], 167 | "execution_count": 0, 168 | "outputs": [] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": { 173 | "id": "eQBeIh2bY5yb", 174 | "colab_type": "text" 175 | }, 176 | "source": [ 177 | "Parameters" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "metadata": { 183 | "id": "CK2upJ5xY5ye", 184 | "colab_type": "code", 185 | "colab": {} 186 | }, 187 | "source": [ 188 | "nc = 3\n", 189 | "nz = 100\n", 190 | "ngf = 64\n", 191 | "ndf = 64\n", 192 | "n_extra_layers = 0\n", 193 | "Diters = 5\n", 194 | "λ = 10\n", 195 | "\n", 196 | "imageSize = 32\n", 197 | "batchSize = 64\n", 198 | "lrD = 1e-4\n", 199 | "lrG = 1e-4\n" 200 | ], 201 | "execution_count": 0, 202 | "outputs": [] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": { 207 | "id": "HYbTJfm-Y5yn", 208 | "colab_type": "text" 209 | }, 210 | "source": [ 211 | "print models" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "metadata": { 217 | "id": "VSfTYSaGY5yq", 218 | "colab_type": "code", 219 | "colab": {} 220 | }, 221 | "source": [ 222 | "netD = DCGAN_D(imageSize, nz, nc, ndf, n_extra_layers)\n", 223 | "netD.summary()" 224 | ], 225 | "execution_count": 0, 226 | "outputs": [] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "metadata": { 231 | "id": "opAyrS2yY5yx", 232 | "colab_type": "code", 233 | "colab": {} 234 | }, 235 | "source": [ 236 | "netG = DCGAN_G(imageSize, nz, nc, ngf, n_extra_layers)\n", 237 | "netG.summary()" 238 | ], 239 | "execution_count": 0, 240 | "outputs": [] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "metadata": { 245 | "id": "ENxECYB8Y5y4", 246 | "colab_type": "code", 247 | "colab": {} 248 | }, 249 | "source": [ 250 | "from keras.optimizers import RMSprop, SGD, Adam" 251 | ], 252 | "execution_count": 0, 253 | "outputs": [] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "metadata": { 258 | "id": "iYBF6jt3Y5zB", 259 | "colab_type": "text" 260 | }, 261 | "source": [ 262 | "compute Wasserstein loss and gradient penalty" 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "metadata": { 268 | "id": "j341F66AY5zF", 269 | "colab_type": "code", 270 | "colab": {} 271 | }, 272 | "source": [ 273 | "netD_real_input = Input(shape=(nc, imageSize, imageSize))\n", 274 | "noisev = Input(shape=(nz,))\n", 275 | "netD_fake_input = netG(noisev)\n", 276 | "\n", 277 | "ϵ_input = K.placeholder(shape=(None, nc,imageSize,imageSize))\n", 278 | "netD_mixed_input = Input(shape=(nc, imageSize, imageSize), tensor=netD_real_input + ϵ_input)\n", 279 | "\n", 280 | "\n", 281 | "loss_real = K.mean(netD(netD_real_input))\n", 282 | "loss_fake = K.mean(netD(netD_fake_input))\n", 283 | "\n", 284 | "grad_mixed = K.gradients(netD(netD_mixed_input), [netD_mixed_input])[0]\n", 285 | "norm_grad_mixed = K.sqrt(K.sum(K.square(grad_mixed), axis=[1,2,3]))\n", 286 | "grad_penalty = K.mean(K.square(norm_grad_mixed -1))\n", 287 | "\n", 288 | "loss = loss_fake - loss_real + λ * grad_penalty\n", 289 | "\n", 290 | "\n", 291 | "training_updates = Adam(lr=lrD).get_updates(netD.trainable_weights,[],loss)\n", 292 | "netD_train = K.function([netD_real_input, noisev, ϵ_input],\n", 293 | " [loss_real, loss_fake], \n", 294 | " training_updates)" 295 | ], 296 | "execution_count": 0, 297 | "outputs": [] 298 | }, 299 | { 300 | "cell_type": "markdown", 301 | "metadata": { 302 | "id": "x1eefes6Y5zS", 303 | "colab_type": "text" 304 | }, 305 | "source": [ 306 | "loss for netG" 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "metadata": { 312 | "id": "H7ITg8GpY5zU", 313 | "colab_type": "code", 314 | "colab": {} 315 | }, 316 | "source": [ 317 | "loss = -loss_fake \n", 318 | "training_updates = Adam(lr=lrG).get_updates(netG.trainable_weights,[], loss)\n", 319 | "netG_train = K.function([noisev], [loss], training_updates)\n" 320 | ], 321 | "execution_count": 0, 322 | "outputs": [] 323 | }, 324 | { 325 | "cell_type": "markdown", 326 | "metadata": { 327 | "collapsed": false, 328 | "id": "NzqYN2o9Y5zf", 329 | "colab_type": "text" 330 | }, 331 | "source": [ 332 | "Download CIFAR10 if needed" 333 | ] 334 | }, 335 | { 336 | "cell_type": "code", 337 | "metadata": { 338 | "id": "8KI6vzTrY5zi", 339 | "colab_type": "code", 340 | "colab": {} 341 | }, 342 | "source": [ 343 | "from PIL import Image\n", 344 | "import numpy as np\n", 345 | "import tarfile\n", 346 | "\n", 347 | "# Download dataset\n", 348 | "url = \"https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz\"\n", 349 | "import os\n", 350 | "import urllib\n", 351 | "from urllib.request import urlretrieve\n", 352 | "def reporthook(a,b,c):\n", 353 | " print(\"\\rdownloading: %5.1f%%\"%(a*b*100.0/c), end=\"\")\n", 354 | "tar_gz = \"cifar-10-python.tar.gz\"\n", 355 | "if not os.path.isfile(tar_gz):\n", 356 | " print('Downloading data from %s' % url)\n", 357 | " urlretrieve(url, tar_gz, reporthook=reporthook)\n", 358 | "\n", 359 | "import pickle\n", 360 | "train_X=[]\n", 361 | "train_y=[]\n", 362 | "tar_gz = \"cifar-10-python.tar.gz\"\n", 363 | "with tarfile.open(tar_gz) as tarf:\n", 364 | " for i in range(1, 6):\n", 365 | " dataset = \"cifar-10-batches-py/data_batch_%d\"%i\n", 366 | " print(\"load\",dataset)\n", 367 | " with tarf.extractfile(dataset) as f:\n", 368 | " result = pickle.load(f, encoding='latin1')\n", 369 | " train_X.extend( result['data'].reshape(-1,3,32,32)/255*2-1)\n", 370 | " train_y.extend(result['labels'])\n", 371 | " train_X=np.float32(train_X)\n", 372 | " train_y=np.int32(train_y)\n", 373 | " dataset = \"cifar-10-batches-py/test_batch\"\n", 374 | " print(\"load\",dataset)\n", 375 | " with tarf.extractfile(dataset) as f:\n", 376 | " result = pickle.load(f, encoding='latin1')\n", 377 | " test_X=np.float32(result['data'].reshape(-1,3,32,32)/255*2-1)\n", 378 | " test_y=np.int32(result['labels'])\n", 379 | " " 380 | ], 381 | "execution_count": 0, 382 | "outputs": [] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": { 387 | "id": "yE7ODz5bY5zp", 388 | "colab_type": "text" 389 | }, 390 | "source": [ 391 | "also using test_X" 392 | ] 393 | }, 394 | { 395 | "cell_type": "code", 396 | "metadata": { 397 | "id": "vVV6oIdaY5zr", 398 | "colab_type": "code", 399 | "colab": {} 400 | }, 401 | "source": [ 402 | "train_X = np.concatenate([train_X, test_X])\n", 403 | "train_X = np.concatenate([train_X[:,:,:,::-1], train_X])" 404 | ], 405 | "execution_count": 0, 406 | "outputs": [] 407 | }, 408 | { 409 | "cell_type": "markdown", 410 | "metadata": { 411 | "id": "j5zFXXoRY5zz", 412 | "colab_type": "text" 413 | }, 414 | "source": [ 415 | "utility to show images" 416 | ] 417 | }, 418 | { 419 | "cell_type": "code", 420 | "metadata": { 421 | "id": "aH7Z0q41Y5z1", 422 | "colab_type": "code", 423 | "colab": {} 424 | }, 425 | "source": [ 426 | "from IPython.display import display\n", 427 | "def showX(X, rows=1):\n", 428 | " assert X.shape[0]%rows == 0\n", 429 | " int_X = ( (X+1)/2*255).clip(0,255).astype('uint8')\n", 430 | " # N*3072 -> N*3*32*32 -> 32 * 32N * 3\n", 431 | " int_X = np.moveaxis(int_X.reshape(-1,3,32,32), 1, 3)\n", 432 | " int_X = int_X.reshape(rows, -1, 32, 32,3).swapaxes(1,2).reshape(rows*32,-1, 3)\n", 433 | " display(Image.fromarray(int_X))\n", 434 | "# 訓練資料, X 的前 20 筆\n", 435 | "showX(train_X[:20])\n", 436 | "print(train_y[:20])\n", 437 | "name_array = np.array(\"airplane car bird cat deer dog frog horse boat truck\".split(' '))\n", 438 | "print(name_array[train_y[:20]])" 439 | ], 440 | "execution_count": 0, 441 | "outputs": [] 442 | }, 443 | { 444 | "cell_type": "code", 445 | "metadata": { 446 | "id": "FYuV48BHY5z8", 447 | "colab_type": "code", 448 | "colab": {} 449 | }, 450 | "source": [ 451 | "fixed_noise = np.random.normal(size=(batchSize, nz)).astype('float32')" 452 | ], 453 | "execution_count": 0, 454 | "outputs": [] 455 | }, 456 | { 457 | "cell_type": "code", 458 | "metadata": { 459 | "scrolled": false, 460 | "id": "TKmySl5HY50E", 461 | "colab_type": "code", 462 | "colab": {} 463 | }, 464 | "source": [ 465 | "import time\n", 466 | "t0 = time.time()\n", 467 | "niter = 100\n", 468 | "gen_iterations = 0\n", 469 | "errG = 0\n", 470 | "targetD = np.float32([2]*batchSize+[-2]*batchSize)[:, None]\n", 471 | "targetG = np.ones(batchSize, dtype=np.float32)[:, None]\n", 472 | "for epoch in range(niter):\n", 473 | " i = 0\n", 474 | " # 每個 epoch 洗牌一下\n", 475 | " np.random.shuffle(train_X)\n", 476 | " batches = train_X.shape[0]//batchSize\n", 477 | " while i < batches:\n", 478 | " if gen_iterations < 25 or gen_iterations % 500 == 0:\n", 479 | " _Diters = 100\n", 480 | " else:\n", 481 | " _Diters = Diters\n", 482 | " j = 0\n", 483 | " while j < _Diters and i < batches:\n", 484 | " j+=1\n", 485 | " real_data = train_X[i*batchSize:(i+1)*batchSize]\n", 486 | " i+=1\n", 487 | " noise = np.random.normal(size=(batchSize, nz)) \n", 488 | " ϵ = real_data.std() * np.random.uniform(-0.5,0.5, size=real_data.shape) \n", 489 | " ϵ *= np.random.uniform(size=(batchSize, 1,1,1))\n", 490 | " errD_real, errD_fake = netD_train([real_data, noise, ϵ]) \n", 491 | " errD = errD_real - errD_fake\n", 492 | " \n", 493 | " if gen_iterations%500==0:\n", 494 | " print('[%d/%d][%d/%d][%d] Loss_D: %f Loss_G: %f Loss_D_real: %f Loss_D_fake %f'\n", 495 | " % (epoch, niter, i, batches, gen_iterations,errD, errG, errD_real, errD_fake), time.time()-t0)\n", 496 | " fake = netG.predict(fixed_noise)\n", 497 | " showX(fake, 4)\n", 498 | " \n", 499 | " noise = np.random.normal(size=(batchSize, nz)) \n", 500 | " errG, = netG_train([noise])\n", 501 | " gen_iterations+=1 \n", 502 | " " 503 | ], 504 | "execution_count": 0, 505 | "outputs": [] 506 | }, 507 | { 508 | "cell_type": "code", 509 | "metadata": { 510 | "id": "IAVN_iV6Y50K", 511 | "colab_type": "code", 512 | "colab": {} 513 | }, 514 | "source": [ 515 | "" 516 | ], 517 | "execution_count": 0, 518 | "outputs": [] 519 | }, 520 | { 521 | "cell_type": "code", 522 | "metadata": { 523 | "id": "Fo-8KzKiY50Q", 524 | "colab_type": "code", 525 | "colab": {} 526 | }, 527 | "source": [ 528 | "" 529 | ], 530 | "execution_count": 0, 531 | "outputs": [] 532 | } 533 | ] 534 | } -------------------------------------------------------------------------------- /pix2pix.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "view-in-github" 8 | }, 9 | "source": [ 10 | "\"Open" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": { 16 | "colab_type": "text", 17 | "id": "0TD5ZrvEMbhZ" 18 | }, 19 | "source": [ 20 | "##### Copyright 2018 The TensorFlow Authors.\n", 21 | "\n", 22 | "Licensed under the Apache License, Version 2.0 (the \"License\").\n", 23 | "\n", 24 | "# Pix2Pix: An example with tf.keras and eager\n", 25 | "\n" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": { 31 | "colab_type": "text", 32 | "id": "e1_Y75QXJS6h" 33 | }, 34 | "source": [ 35 | "## Import TensorFlow and enable eager execution" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": null, 41 | "metadata": { 42 | "colab": {}, 43 | "colab_type": "code", 44 | "id": "YfIk2es3hJEd" 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "import tensorflow as tf\n", 49 | "import os\n", 50 | "import time\n", 51 | "import numpy as np\n", 52 | "import matplotlib.pyplot as plt\n", 53 | "import PIL\n", 54 | "from IPython.display import clear_output, display" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": { 60 | "colab_type": "text", 61 | "id": "iYn4MdZnKCey" 62 | }, 63 | "source": [ 64 | "## Load the dataset\n", 65 | "\n", 66 | "You can download this dataset and similar datasets from [here](https://people.eecs.berkeley.edu/~tinghuiz/projects/pix2pix/datasets). As mentioned in the [paper](https://arxiv.org/abs/1611.07004) we apply random jittering and mirroring to the training dataset.\n", 67 | "* In random jittering, the image is resized to `286 x 286` and then randomly cropped to `256 x 256`\n", 68 | "* In random mirroring, the image is randomly flipped horizontally i.e left to right." 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "metadata": { 75 | "colab": {}, 76 | "colab_type": "code", 77 | "id": "Kn-k8kTXuAlv" 78 | }, 79 | "outputs": [], 80 | "source": [ 81 | "path_to_zip = tf.keras.utils.get_file('facades.tar.gz',\n", 82 | " cache_subdir=os.path.abspath('.'),\n", 83 | " origin='https://people.eecs.berkeley.edu/~tinghuiz/projects/pix2pix/datasets/facades.tar.gz', \n", 84 | " extract=True)\n", 85 | "\n", 86 | "PATH = os.path.join(os.path.dirname(path_to_zip), 'facades/')" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": { 93 | "colab": {}, 94 | "colab_type": "code", 95 | "id": "2CbTEt448b4R" 96 | }, 97 | "outputs": [], 98 | "source": [ 99 | "BUFFER_SIZE = 400\n", 100 | "BATCH_SIZE = 1\n", 101 | "IMG_WIDTH = 256\n", 102 | "IMG_HEIGHT = 256" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "metadata": { 109 | "colab": {}, 110 | "colab_type": "code", 111 | "id": "tyaP4hLJ8b4W" 112 | }, 113 | "outputs": [], 114 | "source": [ 115 | "def load_image(image_file, is_train):\n", 116 | " image = tf.io.read_file(image_file)\n", 117 | " image = tf.image.decode_jpeg(image)\n", 118 | "\n", 119 | " w = tf.shape(image)[1]\n", 120 | "\n", 121 | " w = w // 2\n", 122 | " real_image = image[:, :w, :]\n", 123 | " input_image = image[:, w:, :]\n", 124 | "\n", 125 | " input_image = tf.cast(input_image, tf.float32)\n", 126 | " real_image = tf.cast(real_image, tf.float32)\n", 127 | "\n", 128 | " if is_train:\n", 129 | " # random jittering\n", 130 | " \n", 131 | " # resizing to 286 x 286 x 3 \n", 132 | " input_image = tf.image.resize(input_image, [286, 286])\n", 133 | " real_image = tf.image.resize(real_image, [286, 286])\n", 134 | " \n", 135 | " # randomly cropping to 256 x 256 x 3\n", 136 | " stacked_image = tf.stack([input_image, real_image], axis=0)\n", 137 | " cropped_image = tf.image.random_crop(stacked_image, size=[2, IMG_HEIGHT, IMG_WIDTH, 3])\n", 138 | " input_image, real_image = cropped_image[0], cropped_image[1]\n", 139 | "\n", 140 | " if np.random.random() > 0.5:\n", 141 | " # random mirroring\n", 142 | " input_image = tf.image.flip_left_right(input_image)\n", 143 | " real_image = tf.image.flip_left_right(real_image)\n", 144 | " else:\n", 145 | " input_image = tf.image.resize(input_image, size=[IMG_HEIGHT, IMG_WIDTH])\n", 146 | " real_image = tf.image.resize(real_image, size=[IMG_HEIGHT, IMG_WIDTH])\n", 147 | " \n", 148 | " # normalizing the images to [-1, 1]\n", 149 | " input_image = (input_image / 127.5) - 1\n", 150 | " real_image = (real_image / 127.5) - 1\n", 151 | "\n", 152 | " return input_image, real_image" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": { 158 | "colab_type": "text", 159 | "id": "PIGN6ouoQxt3" 160 | }, 161 | "source": [ 162 | "## Use tf.data to create batches, map(do preprocessing) and shuffle the dataset" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": null, 168 | "metadata": { 169 | "colab": {}, 170 | "colab_type": "code", 171 | "id": "SQHmYSmk8b4b" 172 | }, 173 | "outputs": [], 174 | "source": [ 175 | "train_dataset = tf.data.Dataset.list_files(PATH+'train/*.jpg')\n", 176 | "train_dataset = train_dataset.shuffle(BUFFER_SIZE)\n", 177 | "train_dataset = train_dataset.map(lambda x: load_image(x, True))\n", 178 | "train_dataset = train_dataset.batch(1)" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": null, 184 | "metadata": { 185 | "colab": {}, 186 | "colab_type": "code", 187 | "id": "MS9J0yA58b4g", 188 | "scrolled": false 189 | }, 190 | "outputs": [], 191 | "source": [ 192 | "test_dataset = tf.data.Dataset.list_files(PATH+'test/*.jpg')\n", 193 | "test_dataset = test_dataset.map(lambda x: load_image(x, False))\n", 194 | "test_dataset = test_dataset.batch(1)" 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": { 200 | "colab_type": "text", 201 | "id": "THY-sZMiQ4UV" 202 | }, 203 | "source": [ 204 | "## Write the generator and discriminator models\n", 205 | "\n", 206 | "* **Generator** \n", 207 | " * The architecture of generator is a modified U-Net.\n", 208 | " * Each block in the encoder is (Conv -> Batchnorm -> Leaky ReLU)\n", 209 | " * Each block in the decoder is (Transposed Conv -> Batchnorm -> Dropout(applied to the first 3 blocks) -> ReLU)\n", 210 | " * There are skip connections between the encoder and decoder (as in U-Net).\n", 211 | " \n", 212 | "* **Discriminator**\n", 213 | " * The Discriminator is a PatchGAN.\n", 214 | " * Each block in the discriminator is (Conv -> BatchNorm -> Leaky ReLU)\n", 215 | " * The shape of the output after the last layer is (batch_size, 30, 30, 1)\n", 216 | " * Each 30x30 patch of the output classifies a 70x70 portion of the input image (such an architecture is called a PatchGAN).\n", 217 | " * Discriminator receives 2 inputs.\n", 218 | " * Input image and the target image, which it should classify as real.\n", 219 | " * Input image and the generated image (output of generator), which it should classify as fake. \n", 220 | " * We concatenate these 2 inputs together in the code (`tf.concat([inp, tar], axis=-1)`)\n", 221 | "\n", 222 | "* Shape of the input travelling through the generator and the discriminator is in the comments in the code.\n", 223 | "\n", 224 | "To learn more about the architecture and the hyperparameters you can refer the [paper](https://arxiv.org/abs/1611.07004).\n", 225 | " " 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": null, 231 | "metadata": { 232 | "colab": {}, 233 | "colab_type": "code", 234 | "id": "tqqvWxlw8b4l" 235 | }, 236 | "outputs": [], 237 | "source": [ 238 | "OUTPUT_CHANNELS = 3" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "metadata": { 245 | "colab": {}, 246 | "colab_type": "code", 247 | "id": "lFPI4Nu-8b4q" 248 | }, 249 | "outputs": [], 250 | "source": [ 251 | "class Downsample(tf.keras.Model):\n", 252 | " \n", 253 | " def __init__(self, filters, size, apply_batchnorm=True):\n", 254 | " super(Downsample, self).__init__()\n", 255 | " self.apply_batchnorm = apply_batchnorm\n", 256 | " initializer = tf.random_normal_initializer(0., 0.02)\n", 257 | "\n", 258 | " self.conv1 = tf.keras.layers.Conv2D(filters, \n", 259 | " (size, size), \n", 260 | " strides=2, \n", 261 | " padding='same',\n", 262 | " kernel_initializer=initializer,\n", 263 | " use_bias=False)\n", 264 | " if self.apply_batchnorm:\n", 265 | " self.batchnorm = tf.keras.layers.BatchNormalization()\n", 266 | " \n", 267 | " def call(self, x, training):\n", 268 | " x = self.conv1(x)\n", 269 | " if self.apply_batchnorm:\n", 270 | " x = self.batchnorm(x, training=training)\n", 271 | " x = tf.nn.leaky_relu(x)\n", 272 | " return x \n", 273 | "\n", 274 | "\n", 275 | "class Upsample(tf.keras.Model):\n", 276 | " \n", 277 | " def __init__(self, filters, size, apply_dropout=False):\n", 278 | " super(Upsample, self).__init__()\n", 279 | " self.apply_dropout = apply_dropout\n", 280 | " initializer = tf.random_normal_initializer(0., 0.02)\n", 281 | "\n", 282 | " self.up_conv = tf.keras.layers.Conv2DTranspose(filters, \n", 283 | " (size, size), \n", 284 | " strides=2, \n", 285 | " padding='same',\n", 286 | " kernel_initializer=initializer,\n", 287 | " use_bias=False)\n", 288 | " self.batchnorm = tf.keras.layers.BatchNormalization()\n", 289 | " if self.apply_dropout:\n", 290 | " self.dropout = tf.keras.layers.Dropout(0.5)\n", 291 | "\n", 292 | " def call(self, x1, x2, training):\n", 293 | " x = self.up_conv(x1)\n", 294 | " x = self.batchnorm(x, training=training)\n", 295 | " if self.apply_dropout:\n", 296 | " x = self.dropout(x, training=training)\n", 297 | " x = tf.nn.relu(x)\n", 298 | " x = tf.concat([x, x2], axis=-1)\n", 299 | " return x\n", 300 | "\n", 301 | "\n", 302 | "class Generator(tf.keras.Model):\n", 303 | " \n", 304 | " def __init__(self):\n", 305 | " super(Generator, self).__init__()\n", 306 | " initializer = tf.random_normal_initializer(0., 0.02)\n", 307 | " \n", 308 | " self.down1 = Downsample(64, 4, apply_batchnorm=False)\n", 309 | " self.down2 = Downsample(128, 4)\n", 310 | " self.down3 = Downsample(256, 4)\n", 311 | " self.down4 = Downsample(512, 4)\n", 312 | " self.down5 = Downsample(512, 4)\n", 313 | " self.down6 = Downsample(512, 4)\n", 314 | " self.down7 = Downsample(512, 4)\n", 315 | " self.down8 = Downsample(512, 4)\n", 316 | "\n", 317 | " self.up1 = Upsample(512, 4, apply_dropout=True)\n", 318 | " self.up2 = Upsample(512, 4, apply_dropout=True)\n", 319 | " self.up3 = Upsample(512, 4, apply_dropout=True)\n", 320 | " self.up4 = Upsample(512, 4)\n", 321 | " self.up5 = Upsample(256, 4)\n", 322 | " self.up6 = Upsample(128, 4)\n", 323 | " self.up7 = Upsample(64, 4)\n", 324 | "\n", 325 | " self.last = tf.keras.layers.Conv2DTranspose(OUTPUT_CHANNELS, \n", 326 | " (4, 4), \n", 327 | " strides=2, \n", 328 | " padding='same',\n", 329 | " kernel_initializer=initializer)\n", 330 | " \n", 331 | " @tf.function\n", 332 | " def call(self, x, training):\n", 333 | " # x shape == (bs, 256, 256, 3) \n", 334 | " x1 = self.down1(x, training=training) # (bs, 128, 128, 64)\n", 335 | " x2 = self.down2(x1, training=training) # (bs, 64, 64, 128)\n", 336 | " x3 = self.down3(x2, training=training) # (bs, 32, 32, 256)\n", 337 | " x4 = self.down4(x3, training=training) # (bs, 16, 16, 512)\n", 338 | " x5 = self.down5(x4, training=training) # (bs, 8, 8, 512)\n", 339 | " x6 = self.down6(x5, training=training) # (bs, 4, 4, 512)\n", 340 | " x7 = self.down7(x6, training=training) # (bs, 2, 2, 512)\n", 341 | " x8 = self.down8(x7, training=training) # (bs, 1, 1, 512)\n", 342 | "\n", 343 | " x9 = self.up1(x8, x7, training=training) # (bs, 2, 2, 1024)\n", 344 | " x10 = self.up2(x9, x6, training=training) # (bs, 4, 4, 1024)\n", 345 | " x11 = self.up3(x10, x5, training=training) # (bs, 8, 8, 1024)\n", 346 | " x12 = self.up4(x11, x4, training=training) # (bs, 16, 16, 1024)\n", 347 | " x13 = self.up5(x12, x3, training=training) # (bs, 32, 32, 512)\n", 348 | " x14 = self.up6(x13, x2, training=training) # (bs, 64, 64, 256)\n", 349 | " x15 = self.up7(x14, x1, training=training) # (bs, 128, 128, 128)\n", 350 | "\n", 351 | " x16 = self.last(x15) # (bs, 256, 256, 3)\n", 352 | " x16 = tf.nn.tanh(x16)\n", 353 | "\n", 354 | " return x16" 355 | ] 356 | }, 357 | { 358 | "cell_type": "code", 359 | "execution_count": null, 360 | "metadata": { 361 | "colab": {}, 362 | "colab_type": "code", 363 | "id": "ll6aNeQx8b4v" 364 | }, 365 | "outputs": [], 366 | "source": [ 367 | "class DiscDownsample(tf.keras.Model):\n", 368 | " \n", 369 | " def __init__(self, filters, size, apply_batchnorm=True):\n", 370 | " super(DiscDownsample, self).__init__()\n", 371 | " self.apply_batchnorm = apply_batchnorm\n", 372 | " initializer = tf.random_normal_initializer(0., 0.02)\n", 373 | "\n", 374 | " self.conv1 = tf.keras.layers.Conv2D(filters, \n", 375 | " (size, size), \n", 376 | " strides=2, \n", 377 | " padding='same',\n", 378 | " kernel_initializer=initializer,\n", 379 | " use_bias=False)\n", 380 | " if self.apply_batchnorm:\n", 381 | " self.batchnorm = tf.keras.layers.BatchNormalization()\n", 382 | " \n", 383 | " def call(self, x, training):\n", 384 | " x = self.conv1(x)\n", 385 | " if self.apply_batchnorm:\n", 386 | " x = self.batchnorm(x, training=training)\n", 387 | " x = tf.nn.leaky_relu(x)\n", 388 | " return x \n", 389 | "\n", 390 | "class Discriminator(tf.keras.Model):\n", 391 | " \n", 392 | " def __init__(self):\n", 393 | " super(Discriminator, self).__init__()\n", 394 | " initializer = tf.random_normal_initializer(0., 0.02)\n", 395 | " \n", 396 | " self.down1 = DiscDownsample(64, 4, False)\n", 397 | " self.down2 = DiscDownsample(128, 4)\n", 398 | " self.down3 = DiscDownsample(256, 4)\n", 399 | " \n", 400 | " # we are zero padding here with 1 because we need our shape to \n", 401 | " # go from (batch_size, 32, 32, 256) to (batch_size, 31, 31, 512)\n", 402 | " self.zero_pad1 = tf.keras.layers.ZeroPadding2D()\n", 403 | " self.conv = tf.keras.layers.Conv2D(512, \n", 404 | " (4, 4), \n", 405 | " strides=1, \n", 406 | " kernel_initializer=initializer, \n", 407 | " use_bias=False)\n", 408 | " self.batchnorm1 = tf.keras.layers.BatchNormalization()\n", 409 | " \n", 410 | " # shape change from (batch_size, 31, 31, 512) to (batch_size, 30, 30, 1)\n", 411 | " self.zero_pad2 = tf.keras.layers.ZeroPadding2D()\n", 412 | " self.last = tf.keras.layers.Conv2D(1, \n", 413 | " (4, 4), \n", 414 | " strides=1,\n", 415 | " kernel_initializer=initializer)\n", 416 | " \n", 417 | " @tf.function\n", 418 | " def call(self, inp, tar, training):\n", 419 | " # concatenating the input and the target\n", 420 | " x = tf.concat([inp, tar], axis=-1) # (bs, 256, 256, channels*2)\n", 421 | " x = self.down1(x, training=training) # (bs, 128, 128, 64)\n", 422 | " x = self.down2(x, training=training) # (bs, 64, 64, 128)\n", 423 | " x = self.down3(x, training=training) # (bs, 32, 32, 256)\n", 424 | "\n", 425 | " x = self.zero_pad1(x) # (bs, 34, 34, 256)\n", 426 | " x = self.conv(x) # (bs, 31, 31, 512)\n", 427 | " x = self.batchnorm1(x, training=training)\n", 428 | " x = tf.nn.leaky_relu(x)\n", 429 | " \n", 430 | " x = self.zero_pad2(x) # (bs, 33, 33, 512)\n", 431 | " # don't add a sigmoid activation here since\n", 432 | " # the loss function expects raw logits.\n", 433 | " x = self.last(x) # (bs, 30, 30, 1)\n", 434 | "\n", 435 | " return x" 436 | ] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "execution_count": null, 441 | "metadata": { 442 | "colab": {}, 443 | "colab_type": "code", 444 | "id": "gDkA05NE6QMs" 445 | }, 446 | "outputs": [], 447 | "source": [ 448 | "# The call function of Generator and Discriminator have been decorated\n", 449 | "# with tf.contrib.eager.defun()\n", 450 | "# We get a performance speedup if defun is used (~25 seconds per epoch)\n", 451 | "generator = Generator()\n", 452 | "discriminator = Discriminator()" 453 | ] 454 | }, 455 | { 456 | "cell_type": "markdown", 457 | "metadata": { 458 | "colab_type": "text", 459 | "id": "0FMYgY_mPfTi" 460 | }, 461 | "source": [ 462 | "## Define the loss functions and the optimizer\n", 463 | "\n", 464 | "* **Discriminator loss**\n", 465 | " * The discriminator loss function takes 2 inputs; **real images, generated images**\n", 466 | " * real_loss is a sigmoid cross entropy loss of the **real images** and an **array of ones(since these are the real images)**\n", 467 | " * generated_loss is a sigmoid cross entropy loss of the **generated images** and an **array of zeros(since these are the fake images)**\n", 468 | " * Then the total_loss is the sum of real_loss and the generated_loss\n", 469 | " \n", 470 | "* **Generator loss**\n", 471 | " * It is a sigmoid cross entropy loss of the generated images and an **array of ones**.\n", 472 | " * The [paper](https://arxiv.org/abs/1611.07004) also includes L1 loss which is MAE (mean absolute error) between the generated image and the target image.\n", 473 | " * This allows the generated image to become structurally similar to the target image.\n", 474 | " * The formula to calculate the total generator loss = gan_loss + LAMBDA * l1_loss, where LAMBDA = 100. This value was decided by the authors of the [paper](https://arxiv.org/abs/1611.07004)." 475 | ] 476 | }, 477 | { 478 | "cell_type": "code", 479 | "execution_count": null, 480 | "metadata": { 481 | "colab": {}, 482 | "colab_type": "code", 483 | "id": "cyhxTuvJyIHV" 484 | }, 485 | "outputs": [], 486 | "source": [ 487 | "LAMBDA = 100" 488 | ] 489 | }, 490 | { 491 | "cell_type": "code", 492 | "execution_count": null, 493 | "metadata": { 494 | "colab": {}, 495 | "colab_type": "code", 496 | "id": "wkMNfBWlT-PV" 497 | }, 498 | "outputs": [], 499 | "source": [ 500 | "BCE = tf.keras.losses.BinaryCrossentropy(from_logits=True)\n", 501 | "CCE = tf.keras.losses.CategoricalCrossentropy(from_logits=True)\n", 502 | "def discriminator_loss(disc_real_output, disc_generated_output):\n", 503 | " real_loss = BCE(tf.ones_like(disc_real_output), disc_real_output)\n", 504 | " generated_loss = BCE(tf.zeros_like(disc_generated_output), disc_generated_output)\n", 505 | " total_disc_loss = real_loss + generated_loss\n", 506 | " return total_disc_loss" 507 | ] 508 | }, 509 | { 510 | "cell_type": "code", 511 | "execution_count": null, 512 | "metadata": { 513 | "colab": {}, 514 | "colab_type": "code", 515 | "id": "90BIcCKcDMxz" 516 | }, 517 | "outputs": [], 518 | "source": [ 519 | "def generator_loss(disc_generated_output, gen_output, target):\n", 520 | " gan_loss = BCE(tf.ones_like(disc_generated_output), disc_generated_output) \n", 521 | " # mean absolute error\n", 522 | " l1_loss = tf.reduce_mean(tf.abs(target - gen_output))\n", 523 | "\n", 524 | " total_gen_loss = gan_loss + (LAMBDA * l1_loss)\n", 525 | "\n", 526 | " return total_gen_loss" 527 | ] 528 | }, 529 | { 530 | "cell_type": "code", 531 | "execution_count": null, 532 | "metadata": { 533 | "colab": {}, 534 | "colab_type": "code", 535 | "id": "iWCn_PVdEJZ7" 536 | }, 537 | "outputs": [], 538 | "source": [ 539 | "generator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\n", 540 | "discriminator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)" 541 | ] 542 | }, 543 | { 544 | "cell_type": "markdown", 545 | "metadata": { 546 | "colab_type": "text", 547 | "id": "Rw1fkAczTQYh" 548 | }, 549 | "source": [ 550 | "## Training\n", 551 | "\n", 552 | "* We start by iterating over the dataset\n", 553 | "* The generator gets the input image and we get a generated output.\n", 554 | "* The discriminator receives the input_image and the generated image as the first input. The second input is the input_image and the target_image.\n", 555 | "* Next, we calculate the generator and the discriminator loss.\n", 556 | "* Then, we calculate the gradients of loss with respect to both the generator and the discriminator variables(inputs) and apply those to the optimizer.\n", 557 | "\n", 558 | "## Generate Images\n", 559 | "\n", 560 | "* After training, its time to generate some images!\n", 561 | "* We pass images from the test dataset to the generator.\n", 562 | "* The generator will then translate the input image into the output we expect.\n", 563 | "* Last step is to plot the predictions and **voila!**" 564 | ] 565 | }, 566 | { 567 | "cell_type": "code", 568 | "execution_count": null, 569 | "metadata": { 570 | "colab": {}, 571 | "colab_type": "code", 572 | "id": "NS2GWywBbAWo" 573 | }, 574 | "outputs": [], 575 | "source": [ 576 | "EPOCHS = 200" 577 | ] 578 | }, 579 | { 580 | "cell_type": "code", 581 | "execution_count": null, 582 | "metadata": { 583 | "colab": {}, 584 | "colab_type": "code", 585 | "id": "RmdVsmvhPxyy" 586 | }, 587 | "outputs": [], 588 | "source": [ 589 | "def generate_images(model, test_input, tar):\n", 590 | " # the training=True is intentional here since\n", 591 | " # we want the batch statistics while running the model\n", 592 | " # on the test dataset. If we use training=False, we will get \n", 593 | " # the accumulated statistics learned from the training dataset\n", 594 | " # (which we don't want)\n", 595 | " prediction = model(test_input, training=True)\n", 596 | " plt.figure(figsize=(15,15))\n", 597 | "\n", 598 | " display_list = [test_input[0], tar[0], prediction[0]]\n", 599 | " title = ['Input Image', 'Ground Truth', 'Predicted Image']\n", 600 | "\n", 601 | " for i in range(3):\n", 602 | " plt.subplot(1, 3, i+1)\n", 603 | " plt.title(title[i])\n", 604 | " # getting the pixel values between [0, 1] to plot it.\n", 605 | " plt.imshow(display_list[i] * 0.5 + 0.5)\n", 606 | " plt.axis('off')\n", 607 | " plt.show()" 608 | ] 609 | }, 610 | { 611 | "cell_type": "code", 612 | "execution_count": null, 613 | "metadata": { 614 | "colab": {}, 615 | "colab_type": "code", 616 | "id": "2M7LmLtGEMQJ" 617 | }, 618 | "outputs": [], 619 | "source": [ 620 | "def train(dataset, epochs): \n", 621 | " for epoch in range(epochs):\n", 622 | " start = time.time()\n", 623 | "\n", 624 | " for input_image, target in dataset:\n", 625 | "\n", 626 | " with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n", 627 | " gen_output = generator(input_image, training=True)\n", 628 | "\n", 629 | " disc_real_output = discriminator(input_image, target, training=True)\n", 630 | " disc_generated_output = discriminator(input_image, gen_output, training=True)\n", 631 | "\n", 632 | " gen_loss = generator_loss(disc_generated_output, gen_output, target)\n", 633 | " disc_loss = discriminator_loss(disc_real_output, disc_generated_output)\n", 634 | "\n", 635 | " generator_gradients = gen_tape.gradient(gen_loss, \n", 636 | " generator.trainable_variables)\n", 637 | " discriminator_gradients = disc_tape.gradient(disc_loss, \n", 638 | " discriminator.trainable_variables)\n", 639 | "\n", 640 | " generator_optimizer.apply_gradients(zip(generator_gradients, \n", 641 | " generator.trainable_variables))\n", 642 | " discriminator_optimizer.apply_gradients(zip(discriminator_gradients, \n", 643 | " discriminator.trainable_variables))\n", 644 | "\n", 645 | " if epoch % 1 == 0:\n", 646 | " for inp, tar in test_dataset.take(3):\n", 647 | " generate_images(generator, inp, tar)\n", 648 | " \n", 649 | " print ('Time taken for epoch {} is {} sec\\n'.format(epoch + 1,\n", 650 | " time.time()-start))" 651 | ] 652 | }, 653 | { 654 | "cell_type": "code", 655 | "execution_count": null, 656 | "metadata": { 657 | "colab": {}, 658 | "colab_type": "code", 659 | "id": "a1zZmKmvOH85" 660 | }, 661 | "outputs": [], 662 | "source": [ 663 | "train(train_dataset, EPOCHS)" 664 | ] 665 | }, 666 | { 667 | "cell_type": "markdown", 668 | "metadata": { 669 | "colab_type": "text", 670 | "id": "1RGysMU_BZhx" 671 | }, 672 | "source": [ 673 | "## Testing on the entire test dataset" 674 | ] 675 | }, 676 | { 677 | "cell_type": "code", 678 | "execution_count": null, 679 | "metadata": { 680 | "colab": {}, 681 | "colab_type": "code", 682 | "id": "KUgSnmy2nqSP" 683 | }, 684 | "outputs": [], 685 | "source": [ 686 | "# Run the trained model on the entire test dataset\n", 687 | "for inp, tar in test_dataset:\n", 688 | " generate_images(generator, inp, tar)" 689 | ] 690 | }, 691 | { 692 | "cell_type": "code", 693 | "execution_count": null, 694 | "metadata": { 695 | "colab": {}, 696 | "colab_type": "code", 697 | "id": "3AJXOByaZVOf" 698 | }, 699 | "outputs": [], 700 | "source": [] 701 | } 702 | ], 703 | "metadata": { 704 | "accelerator": "GPU", 705 | "colab": { 706 | "collapsed_sections": [], 707 | "include_colab_link": true, 708 | "name": "pix2pix_eager.ipynb", 709 | "private_outputs": true, 710 | "provenance": [], 711 | "version": "0.3.2" 712 | }, 713 | "kernelspec": { 714 | "display_name": "Python 3", 715 | "language": "python", 716 | "name": "python3" 717 | }, 718 | "language_info": { 719 | "codemirror_mode": { 720 | "name": "ipython", 721 | "version": 3 722 | }, 723 | "file_extension": ".py", 724 | "mimetype": "text/x-python", 725 | "name": "python", 726 | "nbconvert_exporter": "python", 727 | "pygments_lexer": "ipython3", 728 | "version": "3.6.8" 729 | } 730 | }, 731 | "nbformat": 4, 732 | "nbformat_minor": 1 733 | } 734 | -------------------------------------------------------------------------------- /pix2pix_eager.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "accelerator": "GPU", 6 | "colab": { 7 | "name": "pix2pix_eager.ipynb", 8 | "version": "0.3.2", 9 | "provenance": [], 10 | "private_outputs": true, 11 | "collapsed_sections": [], 12 | "include_colab_link": true 13 | }, 14 | "kernelspec": { 15 | "display_name": "Python 3", 16 | "language": "python", 17 | "name": "python3" 18 | } 19 | }, 20 | "cells": [ 21 | { 22 | "cell_type": "markdown", 23 | "metadata": { 24 | "id": "view-in-github", 25 | "colab_type": "text" 26 | }, 27 | "source": [ 28 | "\"Open" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": { 34 | "colab_type": "text", 35 | "id": "0TD5ZrvEMbhZ" 36 | }, 37 | "source": [ 38 | "##### Copyright 2018 The TensorFlow Authors.\n", 39 | "\n", 40 | "Licensed under the Apache License, Version 2.0 (the \"License\").\n", 41 | "\n", 42 | "# Pix2Pix: An example with tf.keras and eager\n", 43 | "\n" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": { 49 | "colab_type": "text", 50 | "id": "e1_Y75QXJS6h" 51 | }, 52 | "source": [ 53 | "## Import TensorFlow and enable eager execution" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "metadata": { 59 | "colab_type": "code", 60 | "id": "YfIk2es3hJEd", 61 | "colab": {} 62 | }, 63 | "source": [ 64 | "# Import TensorFlow >= 1.10 and enable eager execution\n", 65 | "import tensorflow as tf\n", 66 | "tf.enable_eager_execution()\n", 67 | "\n", 68 | "import os\n", 69 | "import time\n", 70 | "import numpy as np\n", 71 | "import matplotlib.pyplot as plt\n", 72 | "import PIL\n", 73 | "from IPython.display import clear_output, display" 74 | ], 75 | "execution_count": 0, 76 | "outputs": [] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": { 81 | "colab_type": "text", 82 | "id": "iYn4MdZnKCey" 83 | }, 84 | "source": [ 85 | "## Load the dataset\n", 86 | "\n", 87 | "You can download this dataset and similar datasets from [here](https://people.eecs.berkeley.edu/~tinghuiz/projects/pix2pix/datasets). As mentioned in the [paper](https://arxiv.org/abs/1611.07004) we apply random jittering and mirroring to the training dataset.\n", 88 | "* In random jittering, the image is resized to `286 x 286` and then randomly cropped to `256 x 256`\n", 89 | "* In random mirroring, the image is randomly flipped horizontally i.e left to right." 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "metadata": { 95 | "colab_type": "code", 96 | "id": "Kn-k8kTXuAlv", 97 | "colab": {} 98 | }, 99 | "source": [ 100 | "path_to_zip = tf.keras.utils.get_file('facades.tar.gz',\n", 101 | " cache_subdir=os.path.abspath('.'),\n", 102 | " origin='https://people.eecs.berkeley.edu/~tinghuiz/projects/pix2pix/datasets/facades.tar.gz', \n", 103 | " extract=True)\n", 104 | "\n", 105 | "PATH = os.path.join(os.path.dirname(path_to_zip), 'facades/')" 106 | ], 107 | "execution_count": 0, 108 | "outputs": [] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "metadata": { 113 | "colab_type": "code", 114 | "id": "2CbTEt448b4R", 115 | "colab": {} 116 | }, 117 | "source": [ 118 | "BUFFER_SIZE = 400\n", 119 | "BATCH_SIZE = 1\n", 120 | "IMG_WIDTH = 256\n", 121 | "IMG_HEIGHT = 256" 122 | ], 123 | "execution_count": 0, 124 | "outputs": [] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "metadata": { 129 | "colab_type": "code", 130 | "id": "tyaP4hLJ8b4W", 131 | "colab": {} 132 | }, 133 | "source": [ 134 | "def load_image(image_file, is_train):\n", 135 | " image = tf.read_file(image_file)\n", 136 | " image = tf.image.decode_jpeg(image)\n", 137 | "\n", 138 | " w = tf.shape(image)[1]\n", 139 | "\n", 140 | " w = w // 2\n", 141 | " real_image = image[:, :w, :]\n", 142 | " input_image = image[:, w:, :]\n", 143 | "\n", 144 | " input_image = tf.cast(input_image, tf.float32)\n", 145 | " real_image = tf.cast(real_image, tf.float32)\n", 146 | "\n", 147 | " if is_train:\n", 148 | " # random jittering\n", 149 | " \n", 150 | " # resizing to 286 x 286 x 3\n", 151 | " input_image = tf.image.resize_images(input_image, [286, 286], \n", 152 | " align_corners=True, \n", 153 | " method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)\n", 154 | " real_image = tf.image.resize_images(real_image, [286, 286], \n", 155 | " align_corners=True, \n", 156 | " method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)\n", 157 | " \n", 158 | " # randomly cropping to 256 x 256 x 3\n", 159 | " stacked_image = tf.stack([input_image, real_image], axis=0)\n", 160 | " cropped_image = tf.random_crop(stacked_image, size=[2, IMG_HEIGHT, IMG_WIDTH, 3])\n", 161 | " input_image, real_image = cropped_image[0], cropped_image[1]\n", 162 | "\n", 163 | " if np.random.random() > 0.5:\n", 164 | " # random mirroring\n", 165 | " input_image = tf.image.flip_left_right(input_image)\n", 166 | " real_image = tf.image.flip_left_right(real_image)\n", 167 | " else:\n", 168 | " input_image = tf.image.resize_images(input_image, size=[IMG_HEIGHT, IMG_WIDTH], \n", 169 | " align_corners=True, method=2)\n", 170 | " real_image = tf.image.resize_images(real_image, size=[IMG_HEIGHT, IMG_WIDTH], \n", 171 | " align_corners=True, method=2)\n", 172 | " \n", 173 | " # normalizing the images to [-1, 1]\n", 174 | " input_image = (input_image / 127.5) - 1\n", 175 | " real_image = (real_image / 127.5) - 1\n", 176 | "\n", 177 | " return input_image, real_image" 178 | ], 179 | "execution_count": 0, 180 | "outputs": [] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": { 185 | "colab_type": "text", 186 | "id": "PIGN6ouoQxt3" 187 | }, 188 | "source": [ 189 | "## Use tf.data to create batches, map(do preprocessing) and shuffle the dataset" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "metadata": { 195 | "colab_type": "code", 196 | "id": "SQHmYSmk8b4b", 197 | "colab": {} 198 | }, 199 | "source": [ 200 | "train_dataset = tf.data.Dataset.list_files(PATH+'train/*.jpg')\n", 201 | "train_dataset = train_dataset.shuffle(BUFFER_SIZE)\n", 202 | "train_dataset = train_dataset.map(lambda x: load_image(x, True))\n", 203 | "train_dataset = train_dataset.batch(1)" 204 | ], 205 | "execution_count": 0, 206 | "outputs": [] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "metadata": { 211 | "colab_type": "code", 212 | "id": "MS9J0yA58b4g", 213 | "colab": {} 214 | }, 215 | "source": [ 216 | "test_dataset = tf.data.Dataset.list_files(PATH+'test/*.jpg')\n", 217 | "test_dataset = test_dataset.map(lambda x: load_image(x, False))\n", 218 | "test_dataset = test_dataset.batch(1)" 219 | ], 220 | "execution_count": 0, 221 | "outputs": [] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "metadata": { 226 | "colab_type": "text", 227 | "id": "THY-sZMiQ4UV" 228 | }, 229 | "source": [ 230 | "## Write the generator and discriminator models\n", 231 | "\n", 232 | "* **Generator** \n", 233 | " * The architecture of generator is a modified U-Net.\n", 234 | " * Each block in the encoder is (Conv -> Batchnorm -> Leaky ReLU)\n", 235 | " * Each block in the decoder is (Transposed Conv -> Batchnorm -> Dropout(applied to the first 3 blocks) -> ReLU)\n", 236 | " * There are skip connections between the encoder and decoder (as in U-Net).\n", 237 | " \n", 238 | "* **Discriminator**\n", 239 | " * The Discriminator is a PatchGAN.\n", 240 | " * Each block in the discriminator is (Conv -> BatchNorm -> Leaky ReLU)\n", 241 | " * The shape of the output after the last layer is (batch_size, 30, 30, 1)\n", 242 | " * Each 30x30 patch of the output classifies a 70x70 portion of the input image (such an architecture is called a PatchGAN).\n", 243 | " * Discriminator receives 2 inputs.\n", 244 | " * Input image and the target image, which it should classify as real.\n", 245 | " * Input image and the generated image (output of generator), which it should classify as fake. \n", 246 | " * We concatenate these 2 inputs together in the code (`tf.concat([inp, tar], axis=-1)`)\n", 247 | "\n", 248 | "* Shape of the input travelling through the generator and the discriminator is in the comments in the code.\n", 249 | "\n", 250 | "To learn more about the architecture and the hyperparameters you can refer the [paper](https://arxiv.org/abs/1611.07004).\n", 251 | " " 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "metadata": { 257 | "colab_type": "code", 258 | "id": "tqqvWxlw8b4l", 259 | "colab": {} 260 | }, 261 | "source": [ 262 | "OUTPUT_CHANNELS = 3" 263 | ], 264 | "execution_count": 0, 265 | "outputs": [] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "metadata": { 270 | "colab_type": "code", 271 | "id": "lFPI4Nu-8b4q", 272 | "colab": {} 273 | }, 274 | "source": [ 275 | "class Downsample(tf.keras.Model):\n", 276 | " \n", 277 | " def __init__(self, filters, size, apply_batchnorm=True):\n", 278 | " super(Downsample, self).__init__()\n", 279 | " self.apply_batchnorm = apply_batchnorm\n", 280 | " initializer = tf.random_normal_initializer(0., 0.02)\n", 281 | "\n", 282 | " self.conv1 = tf.keras.layers.Conv2D(filters, \n", 283 | " (size, size), \n", 284 | " strides=2, \n", 285 | " padding='same',\n", 286 | " kernel_initializer=initializer,\n", 287 | " use_bias=False)\n", 288 | " if self.apply_batchnorm:\n", 289 | " self.batchnorm = tf.keras.layers.BatchNormalization()\n", 290 | " \n", 291 | " def call(self, x, training):\n", 292 | " x = self.conv1(x)\n", 293 | " if self.apply_batchnorm:\n", 294 | " x = self.batchnorm(x, training=training)\n", 295 | " x = tf.nn.leaky_relu(x)\n", 296 | " return x \n", 297 | "\n", 298 | "\n", 299 | "class Upsample(tf.keras.Model):\n", 300 | " \n", 301 | " def __init__(self, filters, size, apply_dropout=False):\n", 302 | " super(Upsample, self).__init__()\n", 303 | " self.apply_dropout = apply_dropout\n", 304 | " initializer = tf.random_normal_initializer(0., 0.02)\n", 305 | "\n", 306 | " self.up_conv = tf.keras.layers.Conv2DTranspose(filters, \n", 307 | " (size, size), \n", 308 | " strides=2, \n", 309 | " padding='same',\n", 310 | " kernel_initializer=initializer,\n", 311 | " use_bias=False)\n", 312 | " self.batchnorm = tf.keras.layers.BatchNormalization()\n", 313 | " if self.apply_dropout:\n", 314 | " self.dropout = tf.keras.layers.Dropout(0.5)\n", 315 | "\n", 316 | " def call(self, x1, x2, training):\n", 317 | " x = self.up_conv(x1)\n", 318 | " x = self.batchnorm(x, training=training)\n", 319 | " if self.apply_dropout:\n", 320 | " x = self.dropout(x, training=training)\n", 321 | " x = tf.nn.relu(x)\n", 322 | " x = tf.concat([x, x2], axis=-1)\n", 323 | " return x\n", 324 | "\n", 325 | "\n", 326 | "class Generator(tf.keras.Model):\n", 327 | " \n", 328 | " def __init__(self):\n", 329 | " super(Generator, self).__init__()\n", 330 | " initializer = tf.random_normal_initializer(0., 0.02)\n", 331 | " \n", 332 | " self.down1 = Downsample(64, 4, apply_batchnorm=False)\n", 333 | " self.down2 = Downsample(128, 4)\n", 334 | " self.down3 = Downsample(256, 4)\n", 335 | " self.down4 = Downsample(512, 4)\n", 336 | " self.down5 = Downsample(512, 4)\n", 337 | " self.down6 = Downsample(512, 4)\n", 338 | " self.down7 = Downsample(512, 4)\n", 339 | " self.down8 = Downsample(512, 4)\n", 340 | "\n", 341 | " self.up1 = Upsample(512, 4, apply_dropout=True)\n", 342 | " self.up2 = Upsample(512, 4, apply_dropout=True)\n", 343 | " self.up3 = Upsample(512, 4, apply_dropout=True)\n", 344 | " self.up4 = Upsample(512, 4)\n", 345 | " self.up5 = Upsample(256, 4)\n", 346 | " self.up6 = Upsample(128, 4)\n", 347 | " self.up7 = Upsample(64, 4)\n", 348 | "\n", 349 | " self.last = tf.keras.layers.Conv2DTranspose(OUTPUT_CHANNELS, \n", 350 | " (4, 4), \n", 351 | " strides=2, \n", 352 | " padding='same',\n", 353 | " kernel_initializer=initializer)\n", 354 | " \n", 355 | " @tf.contrib.eager.defun\n", 356 | " def call(self, x, training):\n", 357 | " # x shape == (bs, 256, 256, 3) \n", 358 | " x1 = self.down1(x, training=training) # (bs, 128, 128, 64)\n", 359 | " x2 = self.down2(x1, training=training) # (bs, 64, 64, 128)\n", 360 | " x3 = self.down3(x2, training=training) # (bs, 32, 32, 256)\n", 361 | " x4 = self.down4(x3, training=training) # (bs, 16, 16, 512)\n", 362 | " x5 = self.down5(x4, training=training) # (bs, 8, 8, 512)\n", 363 | " x6 = self.down6(x5, training=training) # (bs, 4, 4, 512)\n", 364 | " x7 = self.down7(x6, training=training) # (bs, 2, 2, 512)\n", 365 | " x8 = self.down8(x7, training=training) # (bs, 1, 1, 512)\n", 366 | "\n", 367 | " x9 = self.up1(x8, x7, training=training) # (bs, 2, 2, 1024)\n", 368 | " x10 = self.up2(x9, x6, training=training) # (bs, 4, 4, 1024)\n", 369 | " x11 = self.up3(x10, x5, training=training) # (bs, 8, 8, 1024)\n", 370 | " x12 = self.up4(x11, x4, training=training) # (bs, 16, 16, 1024)\n", 371 | " x13 = self.up5(x12, x3, training=training) # (bs, 32, 32, 512)\n", 372 | " x14 = self.up6(x13, x2, training=training) # (bs, 64, 64, 256)\n", 373 | " x15 = self.up7(x14, x1, training=training) # (bs, 128, 128, 128)\n", 374 | "\n", 375 | " x16 = self.last(x15) # (bs, 256, 256, 3)\n", 376 | " x16 = tf.nn.tanh(x16)\n", 377 | "\n", 378 | " return x16" 379 | ], 380 | "execution_count": 0, 381 | "outputs": [] 382 | }, 383 | { 384 | "cell_type": "code", 385 | "metadata": { 386 | "colab_type": "code", 387 | "id": "ll6aNeQx8b4v", 388 | "colab": {} 389 | }, 390 | "source": [ 391 | "class DiscDownsample(tf.keras.Model):\n", 392 | " \n", 393 | " def __init__(self, filters, size, apply_batchnorm=True):\n", 394 | " super(DiscDownsample, self).__init__()\n", 395 | " self.apply_batchnorm = apply_batchnorm\n", 396 | " initializer = tf.random_normal_initializer(0., 0.02)\n", 397 | "\n", 398 | " self.conv1 = tf.keras.layers.Conv2D(filters, \n", 399 | " (size, size), \n", 400 | " strides=2, \n", 401 | " padding='same',\n", 402 | " kernel_initializer=initializer,\n", 403 | " use_bias=False)\n", 404 | " if self.apply_batchnorm:\n", 405 | " self.batchnorm = tf.keras.layers.BatchNormalization()\n", 406 | " \n", 407 | " def call(self, x, training):\n", 408 | " x = self.conv1(x)\n", 409 | " if self.apply_batchnorm:\n", 410 | " x = self.batchnorm(x, training=training)\n", 411 | " x = tf.nn.leaky_relu(x)\n", 412 | " return x \n", 413 | "\n", 414 | "class Discriminator(tf.keras.Model):\n", 415 | " \n", 416 | " def __init__(self):\n", 417 | " super(Discriminator, self).__init__()\n", 418 | " initializer = tf.random_normal_initializer(0., 0.02)\n", 419 | " \n", 420 | " self.down1 = DiscDownsample(64, 4, False)\n", 421 | " self.down2 = DiscDownsample(128, 4)\n", 422 | " self.down3 = DiscDownsample(256, 4)\n", 423 | " \n", 424 | " # we are zero padding here with 1 because we need our shape to \n", 425 | " # go from (batch_size, 32, 32, 256) to (batch_size, 31, 31, 512)\n", 426 | " self.zero_pad1 = tf.keras.layers.ZeroPadding2D()\n", 427 | " self.conv = tf.keras.layers.Conv2D(512, \n", 428 | " (4, 4), \n", 429 | " strides=1, \n", 430 | " kernel_initializer=initializer, \n", 431 | " use_bias=False)\n", 432 | " self.batchnorm1 = tf.keras.layers.BatchNormalization()\n", 433 | " \n", 434 | " # shape change from (batch_size, 31, 31, 512) to (batch_size, 30, 30, 1)\n", 435 | " self.zero_pad2 = tf.keras.layers.ZeroPadding2D()\n", 436 | " self.last = tf.keras.layers.Conv2D(1, \n", 437 | " (4, 4), \n", 438 | " strides=1,\n", 439 | " kernel_initializer=initializer)\n", 440 | " \n", 441 | " @tf.contrib.eager.defun\n", 442 | " def call(self, inp, tar, training):\n", 443 | " # concatenating the input and the target\n", 444 | " x = tf.concat([inp, tar], axis=-1) # (bs, 256, 256, channels*2)\n", 445 | " x = self.down1(x, training=training) # (bs, 128, 128, 64)\n", 446 | " x = self.down2(x, training=training) # (bs, 64, 64, 128)\n", 447 | " x = self.down3(x, training=training) # (bs, 32, 32, 256)\n", 448 | "\n", 449 | " x = self.zero_pad1(x) # (bs, 34, 34, 256)\n", 450 | " x = self.conv(x) # (bs, 31, 31, 512)\n", 451 | " x = self.batchnorm1(x, training=training)\n", 452 | " x = tf.nn.leaky_relu(x)\n", 453 | " \n", 454 | " x = self.zero_pad2(x) # (bs, 33, 33, 512)\n", 455 | " # don't add a sigmoid activation here since\n", 456 | " # the loss function expects raw logits.\n", 457 | " x = self.last(x) # (bs, 30, 30, 1)\n", 458 | "\n", 459 | " return x" 460 | ], 461 | "execution_count": 0, 462 | "outputs": [] 463 | }, 464 | { 465 | "cell_type": "code", 466 | "metadata": { 467 | "colab_type": "code", 468 | "id": "gDkA05NE6QMs", 469 | "colab": {} 470 | }, 471 | "source": [ 472 | "# The call function of Generator and Discriminator have been decorated\n", 473 | "# with tf.contrib.eager.defun()\n", 474 | "# We get a performance speedup if defun is used (~25 seconds per epoch)\n", 475 | "generator = Generator()\n", 476 | "discriminator = Discriminator()" 477 | ], 478 | "execution_count": 0, 479 | "outputs": [] 480 | }, 481 | { 482 | "cell_type": "markdown", 483 | "metadata": { 484 | "colab_type": "text", 485 | "id": "0FMYgY_mPfTi" 486 | }, 487 | "source": [ 488 | "## Define the loss functions and the optimizer\n", 489 | "\n", 490 | "* **Discriminator loss**\n", 491 | " * The discriminator loss function takes 2 inputs; **real images, generated images**\n", 492 | " * real_loss is a sigmoid cross entropy loss of the **real images** and an **array of ones(since these are the real images)**\n", 493 | " * generated_loss is a sigmoid cross entropy loss of the **generated images** and an **array of zeros(since these are the fake images)**\n", 494 | " * Then the total_loss is the sum of real_loss and the generated_loss\n", 495 | " \n", 496 | "* **Generator loss**\n", 497 | " * It is a sigmoid cross entropy loss of the generated images and an **array of ones**.\n", 498 | " * The [paper](https://arxiv.org/abs/1611.07004) also includes L1 loss which is MAE (mean absolute error) between the generated image and the target image.\n", 499 | " * This allows the generated image to become structurally similar to the target image.\n", 500 | " * The formula to calculate the total generator loss = gan_loss + LAMBDA * l1_loss, where LAMBDA = 100. This value was decided by the authors of the [paper](https://arxiv.org/abs/1611.07004)." 501 | ] 502 | }, 503 | { 504 | "cell_type": "code", 505 | "metadata": { 506 | "colab_type": "code", 507 | "id": "cyhxTuvJyIHV", 508 | "colab": {} 509 | }, 510 | "source": [ 511 | "LAMBDA = 100" 512 | ], 513 | "execution_count": 0, 514 | "outputs": [] 515 | }, 516 | { 517 | "cell_type": "code", 518 | "metadata": { 519 | "colab_type": "code", 520 | "id": "wkMNfBWlT-PV", 521 | "colab": {} 522 | }, 523 | "source": [ 524 | "def discriminator_loss(disc_real_output, disc_generated_output):\n", 525 | " real_loss = tf.losses.sigmoid_cross_entropy(multi_class_labels = tf.ones_like(disc_real_output), \n", 526 | " logits = disc_real_output)\n", 527 | " generated_loss = tf.losses.sigmoid_cross_entropy(multi_class_labels = tf.zeros_like(disc_generated_output), \n", 528 | " logits = disc_generated_output)\n", 529 | "\n", 530 | " total_disc_loss = real_loss + generated_loss\n", 531 | "\n", 532 | " return total_disc_loss" 533 | ], 534 | "execution_count": 0, 535 | "outputs": [] 536 | }, 537 | { 538 | "cell_type": "code", 539 | "metadata": { 540 | "colab_type": "code", 541 | "id": "90BIcCKcDMxz", 542 | "colab": {} 543 | }, 544 | "source": [ 545 | "def generator_loss(disc_generated_output, gen_output, target):\n", 546 | " gan_loss = tf.losses.sigmoid_cross_entropy(multi_class_labels = tf.ones_like(disc_generated_output),\n", 547 | " logits = disc_generated_output) \n", 548 | " # mean absolute error\n", 549 | " l1_loss = tf.reduce_mean(tf.abs(target - gen_output))\n", 550 | "\n", 551 | " total_gen_loss = gan_loss + (LAMBDA * l1_loss)\n", 552 | "\n", 553 | " return total_gen_loss" 554 | ], 555 | "execution_count": 0, 556 | "outputs": [] 557 | }, 558 | { 559 | "cell_type": "code", 560 | "metadata": { 561 | "colab_type": "code", 562 | "id": "iWCn_PVdEJZ7", 563 | "colab": {} 564 | }, 565 | "source": [ 566 | "generator_optimizer = tf.train.AdamOptimizer(2e-4, beta1=0.5)\n", 567 | "discriminator_optimizer = tf.train.AdamOptimizer(2e-4, beta1=0.5)" 568 | ], 569 | "execution_count": 0, 570 | "outputs": [] 571 | }, 572 | { 573 | "cell_type": "markdown", 574 | "metadata": { 575 | "colab_type": "text", 576 | "id": "aKUZnDiqQrAh" 577 | }, 578 | "source": [ 579 | "## Checkpoints (Object-based saving)" 580 | ] 581 | }, 582 | { 583 | "cell_type": "code", 584 | "metadata": { 585 | "colab_type": "code", 586 | "id": "WJnftd5sQsv6", 587 | "colab": {} 588 | }, 589 | "source": [ 590 | "checkpoint_dir = './training_checkpoints'\n", 591 | "checkpoint_prefix = os.path.join(checkpoint_dir, \"ckpt\")\n", 592 | "checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,\n", 593 | " discriminator_optimizer=discriminator_optimizer,\n", 594 | " generator=generator,\n", 595 | " discriminator=discriminator)" 596 | ], 597 | "execution_count": 0, 598 | "outputs": [] 599 | }, 600 | { 601 | "cell_type": "markdown", 602 | "metadata": { 603 | "colab_type": "text", 604 | "id": "Rw1fkAczTQYh" 605 | }, 606 | "source": [ 607 | "## Training\n", 608 | "\n", 609 | "* We start by iterating over the dataset\n", 610 | "* The generator gets the input image and we get a generated output.\n", 611 | "* The discriminator receives the input_image and the generated image as the first input. The second input is the input_image and the target_image.\n", 612 | "* Next, we calculate the generator and the discriminator loss.\n", 613 | "* Then, we calculate the gradients of loss with respect to both the generator and the discriminator variables(inputs) and apply those to the optimizer.\n", 614 | "\n", 615 | "## Generate Images\n", 616 | "\n", 617 | "* After training, its time to generate some images!\n", 618 | "* We pass images from the test dataset to the generator.\n", 619 | "* The generator will then translate the input image into the output we expect.\n", 620 | "* Last step is to plot the predictions and **voila!**" 621 | ] 622 | }, 623 | { 624 | "cell_type": "code", 625 | "metadata": { 626 | "colab_type": "code", 627 | "id": "NS2GWywBbAWo", 628 | "colab": {} 629 | }, 630 | "source": [ 631 | "EPOCHS = 200" 632 | ], 633 | "execution_count": 0, 634 | "outputs": [] 635 | }, 636 | { 637 | "cell_type": "code", 638 | "metadata": { 639 | "colab_type": "code", 640 | "id": "RmdVsmvhPxyy", 641 | "colab": {} 642 | }, 643 | "source": [ 644 | "def generate_images(model, test_input, tar):\n", 645 | " # the training=True is intentional here since\n", 646 | " # we want the batch statistics while running the model\n", 647 | " # on the test dataset. If we use training=False, we will get \n", 648 | " # the accumulated statistics learned from the training dataset\n", 649 | " # (which we don't want)\n", 650 | " prediction = model(test_input, training=True)\n", 651 | " plt.figure(figsize=(15,15))\n", 652 | "\n", 653 | " display_list = [test_input[0], tar[0], prediction[0]]\n", 654 | " title = ['Input Image', 'Ground Truth', 'Predicted Image']\n", 655 | "\n", 656 | " for i in range(3):\n", 657 | " plt.subplot(1, 3, i+1)\n", 658 | " plt.title(title[i])\n", 659 | " # getting the pixel values between [0, 1] to plot it.\n", 660 | " plt.imshow(display_list[i] * 0.5 + 0.5)\n", 661 | " plt.axis('off')\n", 662 | " plt.show()" 663 | ], 664 | "execution_count": 0, 665 | "outputs": [] 666 | }, 667 | { 668 | "cell_type": "code", 669 | "metadata": { 670 | "colab_type": "code", 671 | "id": "2M7LmLtGEMQJ", 672 | "colab": {} 673 | }, 674 | "source": [ 675 | "def train(dataset, epochs): \n", 676 | " for epoch in range(epochs):\n", 677 | " start = time.time()\n", 678 | "\n", 679 | " for input_image, target in dataset:\n", 680 | "\n", 681 | " with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n", 682 | " gen_output = generator(input_image, training=True)\n", 683 | "\n", 684 | " disc_real_output = discriminator(input_image, target, training=True)\n", 685 | " disc_generated_output = discriminator(input_image, gen_output, training=True)\n", 686 | "\n", 687 | " gen_loss = generator_loss(disc_generated_output, gen_output, target)\n", 688 | " disc_loss = discriminator_loss(disc_real_output, disc_generated_output)\n", 689 | "\n", 690 | " generator_gradients = gen_tape.gradient(gen_loss, \n", 691 | " generator.variables)\n", 692 | " discriminator_gradients = disc_tape.gradient(disc_loss, \n", 693 | " discriminator.variables)\n", 694 | "\n", 695 | " generator_optimizer.apply_gradients(zip(generator_gradients, \n", 696 | " generator.variables))\n", 697 | " discriminator_optimizer.apply_gradients(zip(discriminator_gradients, \n", 698 | " discriminator.variables))\n", 699 | "\n", 700 | " if epoch % 1 == 0:\n", 701 | " for inp, tar in test_dataset.take(3):\n", 702 | " generate_images(generator, inp, tar)\n", 703 | " \n", 704 | " # saving (checkpoint) the model every 20 epochs\n", 705 | " if (epoch + 1) % 20 == 0:\n", 706 | " checkpoint.save(file_prefix = checkpoint_prefix)\n", 707 | "\n", 708 | " print ('Time taken for epoch {} is {} sec\\n'.format(epoch + 1,\n", 709 | " time.time()-start))" 710 | ], 711 | "execution_count": 0, 712 | "outputs": [] 713 | }, 714 | { 715 | "cell_type": "code", 716 | "metadata": { 717 | "colab_type": "code", 718 | "id": "a1zZmKmvOH85", 719 | "colab": {} 720 | }, 721 | "source": [ 722 | "train(train_dataset, EPOCHS)" 723 | ], 724 | "execution_count": 0, 725 | "outputs": [] 726 | }, 727 | { 728 | "cell_type": "markdown", 729 | "metadata": { 730 | "colab_type": "text", 731 | "id": "kz80bY3aQ1VZ" 732 | }, 733 | "source": [ 734 | "## Restore the latest checkpoint and test" 735 | ] 736 | }, 737 | { 738 | "cell_type": "code", 739 | "metadata": { 740 | "colab_type": "code", 741 | "id": "4t4x69adQ5xb", 742 | "colab": {} 743 | }, 744 | "source": [ 745 | "# restoring the latest checkpoint in checkpoint_dir\n", 746 | "checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))" 747 | ], 748 | "execution_count": 0, 749 | "outputs": [] 750 | }, 751 | { 752 | "cell_type": "markdown", 753 | "metadata": { 754 | "colab_type": "text", 755 | "id": "1RGysMU_BZhx" 756 | }, 757 | "source": [ 758 | "## Testing on the entire test dataset" 759 | ] 760 | }, 761 | { 762 | "cell_type": "code", 763 | "metadata": { 764 | "colab_type": "code", 765 | "id": "KUgSnmy2nqSP", 766 | "colab": {} 767 | }, 768 | "source": [ 769 | "# Run the trained model on the entire test dataset\n", 770 | "for inp, tar in test_dataset:\n", 771 | " generate_images(generator, inp, tar)" 772 | ], 773 | "execution_count": 0, 774 | "outputs": [] 775 | }, 776 | { 777 | "cell_type": "code", 778 | "metadata": { 779 | "colab_type": "code", 780 | "id": "3AJXOByaZVOf", 781 | "colab": {} 782 | }, 783 | "source": [ 784 | "" 785 | ], 786 | "execution_count": 0, 787 | "outputs": [] 788 | } 789 | ] 790 | } -------------------------------------------------------------------------------- /stylegan_use.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "kernelspec": { 6 | "name": "python3", 7 | "display_name": "Python 3" 8 | }, 9 | "language_info": { 10 | "codemirror_mode": { 11 | "name": "ipython", 12 | "version": 3 13 | }, 14 | "file_extension": ".py", 15 | "mimetype": "text/x-python", 16 | "name": "python", 17 | "nbconvert_exporter": "python", 18 | "pygments_lexer": "ipython3", 19 | "version": "3.6.5" 20 | }, 21 | "colab": { 22 | "name": "convert-frozen-sep.ipynb", 23 | "version": "0.3.2", 24 | "provenance": [] 25 | }, 26 | "accelerator": "GPU" 27 | }, 28 | "cells": [ 29 | { 30 | "cell_type": "code", 31 | "metadata": { 32 | "id": "huNsqyrQjb_R", 33 | "colab_type": "code", 34 | "colab": { 35 | "base_uri": "https://localhost:8080/", 36 | "height": 86 37 | }, 38 | "outputId": "d780ebc3-e805-45d0-9437-8a9b64d4964c" 39 | }, 40 | "source": [ 41 | "!git clone https://github.com/NVlabs/stylegan" 42 | ], 43 | "execution_count": 1, 44 | "outputs": [ 45 | { 46 | "output_type": "stream", 47 | "text": [ 48 | "Cloning into 'stylegan'...\n", 49 | "remote: Enumerating objects: 77, done.\u001b[K\n", 50 | "remote: Total 77 (delta 0), reused 0 (delta 0), pack-reused 77\u001b[K\n", 51 | "Unpacking objects: 100% (77/77), done.\n" 52 | ], 53 | "name": "stdout" 54 | } 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "metadata": { 60 | "id": "1BWYmZqDjxjt", 61 | "colab_type": "code", 62 | "colab": {} 63 | }, 64 | "source": [ 65 | "import os\n", 66 | "os.chdir('stylegan')" 67 | ], 68 | "execution_count": 0, 69 | "outputs": [] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "metadata": { 74 | "id": "MzaH2gk5jWwm", 75 | "colab_type": "code", 76 | "colab": { 77 | "base_uri": "https://localhost:8080/", 78 | "height": 157 79 | }, 80 | "outputId": "7eeee15d-e247-4893-eb8a-72ede28076b0" 81 | }, 82 | "source": [ 83 | "import time\n", 84 | "import re\n", 85 | "import bisect\n", 86 | "from collections import OrderedDict\n", 87 | "import numpy as np\n", 88 | "import tensorflow as tf\n", 89 | "import scipy.ndimage\n", 90 | "import scipy.misc\n", 91 | "import pickle\n", 92 | "import dnnlib\n", 93 | "import dnnlib.tflib as tflib\n", 94 | "from PIL import Image\n", 95 | "from IPython.display import display" 96 | ], 97 | "execution_count": 3, 98 | "outputs": [ 99 | { 100 | "output_type": "stream", 101 | "text": [ 102 | "WARNING: Logging before flag parsing goes to stderr.\n", 103 | "W0828 23:46:56.914453 139852722513792 deprecation_wrapper.py:119] From /content/stylegan/dnnlib/tflib/tfutil.py:34: The name tf.Dimension is deprecated. Please use tf.compat.v1.Dimension instead.\n", 104 | "\n", 105 | "W0828 23:46:56.915941 139852722513792 deprecation_wrapper.py:119] From /content/stylegan/dnnlib/tflib/tfutil.py:74: The name tf.variable_scope is deprecated. Please use tf.compat.v1.variable_scope instead.\n", 106 | "\n", 107 | "W0828 23:46:56.916904 139852722513792 deprecation_wrapper.py:119] From /content/stylegan/dnnlib/tflib/tfutil.py:128: The name tf.Session is deprecated. Please use tf.compat.v1.Session instead.\n", 108 | "\n" 109 | ], 110 | "name": "stderr" 111 | } 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "metadata": { 117 | "id": "uY4h81mIjWw6", 118 | "colab_type": "code", 119 | "colab": {} 120 | }, 121 | "source": [ 122 | "sess = tf.InteractiveSession()" 123 | ], 124 | "execution_count": 0, 125 | "outputs": [] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "metadata": { 130 | "id": "Fk699HyBj9S5", 131 | "colab_type": "code", 132 | "colab": { 133 | "base_uri": "https://localhost:8080/", 134 | "height": 175 135 | }, 136 | "outputId": "ae473fb2-ac22-4094-f5f7-5663fd477283" 137 | }, 138 | "source": [ 139 | "url = 'https://drive.google.com/uc?id=1MEGjdvVpUsu1jB4zrXZN7Y4kBBOzizDQ' # karras2019stylegan-ffhq-1024x1024.pkl\n", 140 | "with dnnlib.util.open_url(url, '.') as f:\n", 141 | " _G, _D, Gs = pickle.load(f)" 142 | ], 143 | "execution_count": 5, 144 | "outputs": [ 145 | { 146 | "output_type": "stream", 147 | "text": [ 148 | "Downloading https://drive.google.com/uc?id=1MEGjdvVpUsu1jB4zrXZN7Y4kBBOzizDQ .... done\n" 149 | ], 150 | "name": "stdout" 151 | }, 152 | { 153 | "output_type": "stream", 154 | "text": [ 155 | "W0828 23:47:01.073774 139852722513792 deprecation_wrapper.py:119] From /content/stylegan/dnnlib/tflib/tfutil.py:124: The name tf.get_default_session is deprecated. Please use tf.compat.v1.get_default_session instead.\n", 156 | "\n", 157 | "W0828 23:47:01.084098 139852722513792 deprecation_wrapper.py:119] From /content/stylegan/dnnlib/tflib/network.py:142: The name tf.get_default_graph is deprecated. Please use tf.compat.v1.get_default_graph instead.\n", 158 | "\n", 159 | "W0828 23:47:05.489010 139852722513792 deprecation.py:323] From :364: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.\n", 160 | "Instructions for updating:\n", 161 | "Use tf.where in 2.0, which has the same broadcast rule as np.where\n" 162 | ], 163 | "name": "stderr" 164 | } 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "metadata": { 170 | "id": "7iGxUNeQjWw-", 171 | "colab_type": "code", 172 | "colab": {} 173 | }, 174 | "source": [ 175 | "fmt = dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=True)\n" 176 | ], 177 | "execution_count": 0, 178 | "outputs": [] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "metadata": { 183 | "id": "O7FaWfSMjWxB", 184 | "colab_type": "code", 185 | "colab": {} 186 | }, 187 | "source": [ 188 | "L = 5" 189 | ], 190 | "execution_count": 0, 191 | "outputs": [] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "metadata": { 196 | "id": "FVwHBbSOjWxD", 197 | "colab_type": "code", 198 | "colab": {} 199 | }, 200 | "source": [ 201 | "src_latents = np.random.randn(L, *Gs.input_shape[1:])\n", 202 | "dst_latents = np.random.randn(L, *Gs.input_shape[1:])" 203 | ], 204 | "execution_count": 0, 205 | "outputs": [] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "metadata": { 210 | "id": "Qm7NVRJnjWxF", 211 | "colab_type": "code", 212 | "colab": {} 213 | }, 214 | "source": [ 215 | "src_dlatents = Gs.components.mapping.run(src_latents, None) # [seed, layer, component]\n", 216 | "dst_dlatents = Gs.components.mapping.run(dst_latents, None) # [seed, layer, component]" 217 | ], 218 | "execution_count": 0, 219 | "outputs": [] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "metadata": { 224 | "id": "0uVVisryjWxH", 225 | "colab_type": "code", 226 | "colab": { 227 | "base_uri": "https://localhost:8080/", 228 | "height": 67 229 | }, 230 | "outputId": "0e4d5314-112a-47a8-82ad-17f33c9fcca8" 231 | }, 232 | "source": [ 233 | "for k,v in Gs.components.mapping._run_cache.items():\n", 234 | " print(k)\n", 235 | " print(v[0])\n", 236 | " print(v[1])" 237 | ], 238 | "execution_count": 10, 239 | "outputs": [ 240 | { 241 | "output_type": "stream", 242 | "text": [ 243 | "[('assume_frozen', False), ('dynamic_kwargs', []), ('input_transform', None), ('num_gpus', 1), ('output_transform', None)]\n", 244 | "[ dtype=float32>, dtype=float32>]\n", 245 | "[]\n" 246 | ], 247 | "name": "stdout" 248 | } 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "metadata": { 254 | "id": "DMZZ9VB3jWxL", 255 | "colab_type": "code", 256 | "colab": { 257 | "base_uri": "https://localhost:8080/", 258 | "height": 34 259 | }, 260 | "outputId": "2c15c151-9ec2-4ff8-a8ac-12306f2eefe8" 261 | }, 262 | "source": [ 263 | "v[1][0].op.inputs[0]" 264 | ], 265 | "execution_count": 11, 266 | "outputs": [ 267 | { 268 | "output_type": "execute_result", 269 | "data": { 270 | "text/plain": [ 271 | "" 272 | ] 273 | }, 274 | "metadata": { 275 | "tags": [] 276 | }, 277 | "execution_count": 11 278 | } 279 | ] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "metadata": { 284 | "id": "4xAFfKRajWxP", 285 | "colab_type": "code", 286 | "colab": {} 287 | }, 288 | "source": [ 289 | "synthesis_kwargs = dict(output_transform=fmt)\n", 290 | "src_images = Gs.components.synthesis.run(src_dlatents, randomize_noise=False, **synthesis_kwargs)\n", 291 | "dst_images = Gs.components.synthesis.run(dst_dlatents, randomize_noise=False, **synthesis_kwargs)" 292 | ], 293 | "execution_count": 0, 294 | "outputs": [] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "metadata": { 299 | "id": "76WZLXlVozVY", 300 | "colab_type": "code", 301 | "colab": {} 302 | }, 303 | "source": [ 304 | "Image.fromarray(src_images[0])" 305 | ], 306 | "execution_count": 0, 307 | "outputs": [] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "metadata": { 312 | "id": "CGeqNpi4jWxR", 313 | "colab_type": "code", 314 | "colab": { 315 | "base_uri": "https://localhost:8080/", 316 | "height": 87 317 | }, 318 | "outputId": "994bee60-1224-4085-c0b4-c62309468c05" 319 | }, 320 | "source": [ 321 | "for k,v in Gs.components.synthesis._run_cache.items():\n", 322 | " print(k)\n", 323 | " print(v[0])\n", 324 | " print(v[1])" 325 | ], 326 | "execution_count": 13, 327 | "outputs": [ 328 | { 329 | "output_type": "stream", 330 | "text": [ 331 | "[('assume_frozen', False), ('dynamic_kwargs', [('randomize_noise', False)]), ('input_transform', None), ('num_gpus', 1), ('output_transform', [('func', 'dnnlib.tflib.tfutil.convert_images_to_uint8'), ('nchw_to_nhwc', True)])]\n", 332 | "[ dtype=float32>]\n", 333 | "[]\n" 334 | ], 335 | "name": "stdout" 336 | } 337 | ] 338 | }, 339 | { 340 | "cell_type": "code", 341 | "metadata": { 342 | "id": "XlcUFsApjWxT", 343 | "colab_type": "code", 344 | "colab": { 345 | "base_uri": "https://localhost:8080/", 346 | "height": 34 347 | }, 348 | "outputId": "6c4413ab-3078-4dd8-e16c-38df15969b6e" 349 | }, 350 | "source": [ 351 | "v[1][0].op.inputs[0]" 352 | ], 353 | "execution_count": 14, 354 | "outputs": [ 355 | { 356 | "output_type": "execute_result", 357 | "data": { 358 | "text/plain": [ 359 | "" 360 | ] 361 | }, 362 | "metadata": { 363 | "tags": [] 364 | }, 365 | "execution_count": 14 366 | } 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "metadata": { 372 | "id": "3hoZ9P40jWxV", 373 | "colab_type": "code", 374 | "colab": {} 375 | }, 376 | "source": [ 377 | "" 378 | ], 379 | "execution_count": 0, 380 | "outputs": [] 381 | }, 382 | { 383 | "cell_type": "code", 384 | "metadata": { 385 | "id": "oOc7__DejWxX", 386 | "colab_type": "code", 387 | "colab": { 388 | "base_uri": "https://localhost:8080/", 389 | "height": 34 390 | }, 391 | "outputId": "57a1371a-0399-4691-fb89-9da8ccf8d814" 392 | }, 393 | "source": [ 394 | "src_dlatents.shape" 395 | ], 396 | "execution_count": 15, 397 | "outputs": [ 398 | { 399 | "output_type": "execute_result", 400 | "data": { 401 | "text/plain": [ 402 | "(5, 18, 512)" 403 | ] 404 | }, 405 | "metadata": { 406 | "tags": [] 407 | }, 408 | "execution_count": 15 409 | } 410 | ] 411 | }, 412 | { 413 | "cell_type": "code", 414 | "metadata": { 415 | "id": "7olCYpB4jWxZ", 416 | "colab_type": "code", 417 | "colab": {} 418 | }, 419 | "source": [ 420 | "w,h=256,256\n", 421 | "style_ranges=[range(0,1), range(0,3), range(0,5), range(0,8), range(0,11)]\n", 422 | "canvas = Image.new('RGB', (w * (L + 1), h * (L + 1)) , 'white')\n", 423 | "for col, src_image in enumerate(list(src_images)):\n", 424 | " canvas.paste(Image.fromarray(src_image, 'RGB').resize((w,h)), ((col + 1) * w, 0))\n", 425 | "for row, dst_image in enumerate(list(dst_images)):\n", 426 | " canvas.paste(Image.fromarray(dst_image, 'RGB').resize((w,h)), (0, (row + 1) * h))\n", 427 | " row_dlatents = np.stack([dst_dlatents[row]] * L)\n", 428 | " row_dlatents[:, style_ranges[row]] = src_dlatents[:, style_ranges[row]]\n", 429 | " row_images = Gs.components.synthesis.run(row_dlatents, randomize_noise=False, **synthesis_kwargs)\n", 430 | " for col, image in enumerate(list(row_images)):\n", 431 | " canvas.paste(Image.fromarray(image, 'RGB').resize((w,h)), ((col + 1) * w, (row + 1) * h))\n", 432 | "display(canvas)" 433 | ], 434 | "execution_count": 0, 435 | "outputs": [] 436 | }, 437 | { 438 | "cell_type": "code", 439 | "metadata": { 440 | "id": "PrERxHQhjWxk", 441 | "colab_type": "code", 442 | "colab": {} 443 | }, 444 | "source": [ 445 | "" 446 | ], 447 | "execution_count": 0, 448 | "outputs": [] 449 | }, 450 | { 451 | "cell_type": "code", 452 | "metadata": { 453 | "id": "Uj247q3OjWxm", 454 | "colab_type": "code", 455 | "colab": {} 456 | }, 457 | "source": [ 458 | "" 459 | ], 460 | "execution_count": 0, 461 | "outputs": [] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "metadata": { 466 | "id": "lxI5ohkujWxq", 467 | "colab_type": "code", 468 | "colab": {} 469 | }, 470 | "source": [ 471 | "" 472 | ], 473 | "execution_count": 0, 474 | "outputs": [] 475 | } 476 | ] 477 | } 478 | --------------------------------------------------------------------------------