<commit_before>{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "_style_transfer.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "metadata": {
        "colab_type": "text",
        "id": "g_nWetWWd_ns"
      },
      "cell_type": "markdown",
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "2pHVBk_seED1",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "jo5PziEC4hWs"
      },
      "cell_type": "markdown",
      "source": [
        "# tf.keras를 사용한 Neural Style Transfer\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ko/tutorials/eager/_style_transfer.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ko/tutorials/eager/_style_transfer.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "metadata": {
        "id": "yJ-t14sot8iX",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도\n",
        "불구하고 [공식 영문 문서](https://www.tensorflow.org/?hl=en)의 내용과 일치하지 않을 수 있습니다.\n",
        "이 번역에 개선할 부분이 있다면\n",
        "[tensorflow/docs](https://github.com/tensorflow/docs) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다.\n",
        "문서 번역이나 리뷰에 참여하려면 \n",
        "[<EMAIL>](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs)로\n",
        "메일을 보내주시기 바랍니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "aDyGj8DmXCJI"
      },
      "cell_type": "markdown",
      "source": [
        "## 개요\n",
        "\n",
        "이번 튜토리얼에서는 딥러닝을 사용하여 원하는 이미지를 다른 스타일의 이미지로 구성하는 법을 배워보겠습니다(피카소나 반 고흐처럼 그리기를 희망하시나요?). 이 기법은 **Neural Style Transfer**로 알려져있으며, <NAME>의 논문 [A Neural Algorithm of Artistic Style](https://arxiv.org/abs/1508.06576)에 잘 기술되어 있습니다. 꼭 읽어보셔야합니다.\n",
        "\n",
        "하지만 무엇이 Neural Style Transfer 일까요?\n",
        "\n",
        "Neural style transfer은 **콘텐츠(Content)** 이미지, **스타일 참조(Style Reference)** 이미지(유명한 작가의 삽화 같은), 그리고 여러분이 변경하기 원하는 **입력(Input)** 이미지 위 3가지의 이미지를 사용하여, 콘텐츠 이미지의 콘텐츠로 변형된 것처럼 보이고, 스타일 참조 이미지로 채색된 것 같은 입력 이미지를 생성하는 최적화 기술입니다.\n",
        "\n",
        "예를 들어, 아래의 거북이 이미지와 가츠시카 호쿠사이(Katsushika Hokusai) 작가의 *The Great Wave off Kanagawa* 이미지를 사용해봅시다.\n",
        "\n",
        "<img src=\"https://github.com/tensorflow/models/blob/master/research/nst_blogpost/Green_Sea_Turtle_grazing_seagrass.jpg?raw=1\" alt=\"Drawing\" style=\"width: 200px;\"/>\n",
        "<img src=\"https://github.com/tensorflow/models/blob/master/research/nst_blogpost/The_Great_Wave_off_Kanagawa.jpg?raw=1\" alt=\"Drawing\" style=\"width: 200px;\"/>\n",
        "\n",
        "[Image of Green Sea Turtle](https://commons.wikimedia.org/wiki/File:Green_Sea_Turtle_grazing_seagrass.jpg)\n",
        "-By P.Lindgren [CC BY-SA 3.0  (https://creativecommons.org/licenses/by-sa/3.0)], from Wikimedia Common\n",
        "\n",
        "\n",
        "만약 가츠시카 호쿠사이 작가가 오직 위 스타일로 거북이를 그린다면 어떻게 생겼을까요? 위와 비슷할까요?\n",
        "\n",
        "<img src=\"https://github.com/tensorflow/models/blob/master/research/nst_blogpost/wave_turtle.png?raw=1\" alt=\"Drawing\" style=\"width: 500px;\"/>\n",
        "\n",
        "이것은 마법일까요 아니면 그저 딥러닝일까요? 다행스럽게도, 이것은 어떠한 마법도 포함하고있지 않습니다. 스타일 변환은 신경망의 기능과 내부표현을 보여주는 재밌고 흥미로운 기술입니다.\n",
        "\n",
        "Neural style transfer의 원리는 두 이미지의 콘텐츠의 차이를 표현하는 함수인, $L_{content}$, 두 이미지의 스타일의 차이를 표현하는 함수인, $L_{style}$, 위 두 가지 거리함수를 정의하는 것입니다. 원하는 스타일 이미지, 콘텐츠 이미지 그리고 입력 이미지(콘텐츠 이미지로 초기화 된) 이 3가지의 이미지가 주어졌을 때, 콘텐츠 이미지의 콘텐츠와 스타일 이미지의 스타일 각각의 거리를 최소화하는 입력 이미지의 변형을 시도할 것입니다. \n",
        "\n",
        "요약하자면, 기본 입력 이미지, 일치시키려는 콘텐츠 및 스타일 이미지를 가져와, 역전파(backpropagation)를 이용하여 콘텐츠와 스타일 거리(손실)함수를 최소화시켜 콘텐츠 이미지의 콘텐츠와 스타일 이미지의 스타일이 매치된 이미지를 생성할 것입니다. \n",
        "\n",
        "### 적용되는 특별개념:\n",
        "이 과정에서, 우리는 실제 경험을 쌓고 아래의 개념을 중심으로 직관력을 개발할 것입니다.\n",
        "\n",
        "* **즉시 실행(Eager Execution)** - 연산을 즉각적으로 평가하는 Tensorflow의 명령형 프로그래밍 환경 사용\n",
        "  * [Learn more about eager execution](https://www.tensorflow.org/programmers_guide/eager)\n",
        "  * [See it in action](https://www.tensorflow.org/get_started/eager)\n",
        "* ** 모델 정의를 위한 [함수형 API(Functional API)](https://keras.io/getting-started/functional-api-guide/) 사용** - 함수형 API(Functional API)를 사용하여 필요한 중간 활성화에 대한 접근을 제공할 모델의 부분집합을 구성할 것입니다.\n",
        "* **사전학습 모델의 특성맵(Feature map) 활용** - 사전학습된 모델과 그 모델의 특성맵에 대해 배우겠습니다.\n",
        "* **사용자 지정 훈련 루프 만들기** - 입력변수와 관련하여 주어진 손실함수를 최소화하기 위해 옵티마이저(optimizer)를 설정하는법을 살펴 보겠습니다.\n",
        "\n",
        "### 우리는 스타일 변형을 수행하기 위해 아래와 같은 일반적인 단계들을 진행할 것입니다.\n",
        "\n",
        "1. 데이터 시각화(Visualize data)\n",
        "2. 기본 전처리 및 데이터 준비(Basic Preprocessing/preparing our data)\n",
        "3. 손실함수 설정(Set up loss functions)\n",
        "4. 모델 생성(Create model)\n",
        "5. 손실함수 최적화(Optimize for loss function)\n",
        "\n",
        "**참고:** -\t이 게시글은 기본 머신러닝 개념에 익숙한 중간사용자를 대상으로합니다. 이 게시글을 최대한 활용하려면, 아래 사이트를 참조하세요.\n",
        "* [Gatys' paper](https://arxiv.org/abs/1508.06576) - 이번 튜토리얼에서 전체적인 과정에 대해 설명할 것이지만, 위 논문은 이 과제에 대한 보다 나은 이해를 제공할 것입니다.\n",
        "* [Understand reducing loss with gradient descent](https://developers.google.com/machine-learning/crash-course/reducing-loss/gradient-descent)\n",
        "\n",
        "**예상시간**: 30분\n"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "U8ajP_u73s6m"
      },
      "cell_type": "markdown",
      "source": [
        "## 설정\n",
        "\n",
        "### 다운로드 이미지"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "riWE_b8k3s6o",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "import os\n",
        "img_dir = '/tmp/nst'\n",
        "if not os.path.exists(img_dir):\n",
        "    os.makedirs(img_dir)\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/d/d7/Green_Sea_Turtle_grazing_seagrass.jpg\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/0/0a/The_Great_Wave_off_Kanagawa.jpg\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/b/b4/Vassily_Kandinsky%2C_1913_-_Composition_7.jpg\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/0/00/Tuebingen_Neckarfront.jpg\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/6/68/Pillars_of_creation_2014_HST_WFC3-UVIS_full-res_denoised.jpg\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg/1024px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "eqxUicSPUOP6"
      },
      "cell_type": "markdown",
      "source": [
        "### 모듈 구성 및 임포트"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "sc1OLbOWhPCO",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import matplotlib as mpl\n",
        "mpl.rcParams['figure.figsize'] = (10,10)\n",
        "mpl.rcParams['axes.grid'] = False\n",
        "\n",
        "import numpy as np\n",
        "from PIL import Image\n",
        "import time\n",
        "import functools"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "RYEjlrYk3s6w",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "import tensorflow as tf\n",
        "import tensorflow.contrib.eager as tfe\n",
        "\n",
        "from tensorflow.python.keras.preprocessing import image as kp_image\n",
        "from tensorflow.python.keras import models \n",
        "from tensorflow.python.keras import losses\n",
        "from tensorflow.python.keras import layers\n",
        "from tensorflow.python.keras import backend as K"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "L7sjDODq67HQ"
      },
      "cell_type": "markdown",
      "source": [
        "우리는 [즉시 실행(eager execution)](https://www.tensorflow.org/guide/eager)을 활성화하여 시작할 것입니다.\n",
        "즉시 실행(eager execution)은 가장 명확하고, 읽기 쉬운 방식으로 작업을 할 수 있게 해줍니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "sfjsSAtNrqQx",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "tf.enable_eager_execution()\n",
        "print(\"Eager execution: {}\".format(tf.executing_eagerly()))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "IOiGrIV1iERH",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# 일부 전역변수를 여기에 설정하세요.\n",
        "content_path = '/tmp/nst/Green_Sea_Turtle_grazing_seagrass.jpg'\n",
        "style_path = '/tmp/nst/The_Great_Wave_off_Kanagawa.jpg'"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "xE4Yt8nArTeR"
      },
      "cell_type": "markdown",
      "source": [
        "## 입력 시각화"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "3TLljcwv5qZs",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def load_img(path_to_img):\n",
        "  max_dim = 512\n",
        "  img = Image.open(path_to_img)\n",
        "  long = max(img.size)\n",
        "  scale = max_dim/long\n",
        "  img = img.resize((round(img.size[0]*scale), round(img.size[1]*scale)), Image.ANTIALIAS)\n",
        "  \n",
        "  img = kp_image.img_to_array(img)\n",
        "  \n",
        "  # 배치 차원(batch dimension)을 갖기 위해 이미지 배열을 브로드캐스팅(broadcasting)할 필요가 있습니다.\n",
        "  img = np.expand_dims(img, axis=0)\n",
        "  return img"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "vupl0CI18aAG",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def imshow(img, title=None):\n",
        "  # 배치 차원(batch dimension) 제거\n",
        "  out = np.squeeze(img, axis=0)\n",
        "  # 디스플레이를 위한 정규화\n",
        "  out = out.astype('uint8')\n",
        "  plt.imshow(out)\n",
        "  if title is not None:\n",
        "    plt.title(title)\n",
        "  plt.imshow(out)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "2yAlRzJZrWM3"
      },
      "cell_type": "markdown",
      "source": [
        "여기 주여진 콘텐츠 이미지와 스타일 이미지가 있습니다. 우리는 콘텐츠 이미지의 콘텐츠와 스타일 이미지의 스타일로 구성된 이미지를 생성하기를 원합니다. "
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "_UWQmeEaiKkP",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "plt.figure(figsize=(10,10))\n",
        "\n",
        "content = load_img(content_path).astype('uint8')\n",
        "style = load_img(style_path).astype('uint8')\n",
        "\n",
        "plt.subplot(1, 2, 1)\n",
        "imshow(content, 'Content Image')\n",
        "\n",
        "plt.subplot(1, 2, 2)\n",
        "imshow(style, 'Style Image')\n",
        "plt.show()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "7qMVNvEsK-_D"
      },
      "cell_type": "markdown",
      "source": [
        "## 데이터 준비\n",
        "이미지를 쉽게 전처리하고 적재하기위한 메소드(method)를 만들어봅시다. 이 과정에서는 VGG 학습 프로세스에 따라 예상되는 것과 동일한 전처리 과정을 수행할것입니다. VGG 네트워크들은 각각의 채널이 `평균 = [103.939, 116.779, 123.68]` 및 BGR 채널들로 정규화(normalization)되어 있는 이미지로 훈련됩니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "hGwmTwJNmv2a",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def load_and_process_img(path_to_img):\n",
        "  img = load_img(path_to_img)\n",
        "  img = tf.keras.applications.vgg19.preprocess_input(img)\n",
        "  return img"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "xCgooqs6tAka"
      },
      "cell_type": "markdown",
      "source": [
        "최적화의 결과들을 보기 위해서는, 역전처리(inverse preprocessing) 과정을 수행 할 필요가 있습니다. 더욱이, 최적화 된 이미지의 값이 $- \\infty$ and $\\infty$사이의 어느값도 가질 수 있기 때문에 0-255사이의 값으로 고정해주여야 합니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "mjzlKRQRs_y2",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def deprocess_img(processed_img):\n",
        "  x = processed_img.copy()\n",
        "  if len(x.shape) == 4:\n",
        "    x = np.squeeze(x, 0)\n",
        "  assert len(x.shape) == 3, (\"Input to deprocess image must be an image of \"\n",
        "                             \"dimension [1, height, width, channel] or [height, width, channel]\")\n",
        "  if len(x.shape) != 3:\n",
        "    raise ValueError(\"Invalid input to deprocessing image\")\n",
        "  \n",
        "  # 역전처리 과정 수행\n",
        "  x[:, :, 0] += 103.939\n",
        "  x[:, :, 1] += 116.779\n",
        "  x[:, :, 2] += 123.68\n",
        "  x = x[:, :, ::-1]\n",
        "\n",
        "  x = np.clip(x, 0, 255).astype('uint8')\n",
        "  return x"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "GEwZ7FlwrjoZ"
      },
      "cell_type": "markdown",
      "source": [
        "### 콘텐츠와 스타일 표현(Representation) 정의\n",
        "\n",
        "이미지의 콘텐츠와 스타일 표현(representation)을 얻기 위해, 모델의 몇가지 중간층(layer)들을 살펴볼 것입니다. 모델을 더 깊게 만들수록, 중간층들은 더 고차원적 특성들을 나타냅니다. 이번 경우, 우리는 사전학습된 이미지 분류 네트워크인 VGG19 네트워크의 구조를 사용할 것입니다. 이 중간층들은 우리 이미지에서 콘텐츠와 스타일의 표현을 정의할 필요가 있습니다. 입력 이미지의 경우, 이 중간층들에서 콘텐츠와 스타일에 해당하는 타깃 표현들을 매치하기 위해 시도할 것입니다. \n",
        "\n",
        "#### 왜 중간층인가?\n",
        "\n",
        "사전학습된 이미지 분류 네트워크 속에서 중간 출력들이 스타일과 콘텐츠 표현을 어떻게 정의할 수 있는지 궁금할 수도 있습니다. 고수준에서, 이 현상은 네트워크가 이미지 분류(네트워크가 수행하도록 학습된)를 수행하기 위해서는 반드시 이미지를  이해해야 한다는 사실에 의해서 설명될 수 있습니다. 이것은 미가공 이미지를 입력 픽셀로 사용하고 이미지 내 존재하는 특성 표현(feature present)들의 복합 이해로 전환하는 변형을 통한 내부 표현(internal representation)을 만드는 작업이 포함됩니다. 또한 부분적으로 왜 합성곱(convolutional) 신경망의 일반화(generalize)가 쉽게 가능한지를 나타냅니다. 즉, 합성곱 신경망은 배경잡음(background noise)와 기타잡음(nuisances)에 대해 인지할 수없는 클래스 내에서 불변성(invariance)을 포착할 수 있고 특징을 정의할 수 있습니다.(예 : 고양이 vs 개와 같은). 따라서 입력된 미가공 이미지와 분류 레이블(label)이 출력되는 어딘가에서 모델은 복합 특성(complex feature) 추출기로 사용됩니다. 그러므로, 중간층에 액세스하여 입력 이미지의 콘텐츠와 스타일을 표현할 수 있습니다. \n",
        "\n",
        "우리는 네트워크에서 이 중간층들을 가져올 것입니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "N4-8eUp_Kc-j",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# 특성맵을 추출 할 Content layer\n",
        "content_layers = ['block5_conv2'] \n",
        "\n",
        "# 우리가 관심을 갖는 Style layer\n",
        "style_layers = ['block1_conv1',\n",
        "                'block2_conv1',\n",
        "                'block3_conv1', \n",
        "                'block4_conv1', \n",
        "                'block5_conv1'\n",
        "               ]\n",
        "\n",
        "num_content_layers = len(content_layers)\n",
        "num_style_layers = len(style_layers)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "Jt3i3RRrJiOX"
      },
      "cell_type": "markdown",
      "source": [
        "## 모델 만들기\n",
        "이번 경우, 우리는 [VGG19](https://keras.io/applications/#vgg19)모델을 적재하고, 입력 텐서를 모델에 제공할 것입니다. 이것은 콘텐츠, 스타일 그리고 생성된 이미지의 feature maps, 그리고 이어서 콘텐츠 및 스타일 표현을 추출할 수 있습니다. \n",
        "\n",
        "논문에서 제안한 것처럼 VGG19모델을 사용합니다. 또한 VGG19모델은 ResNet, Inception 등과 같은 모델에 비해 상대적으로 간단한 모델이므로 특징맵들은 이미지 변환에 더 효과적으로 작동할 것입니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "v9AnzEUU6hhx"
      },
      "cell_type": "markdown",
      "source": [
        "콘텐츠와 스타일 특징맵에 상호작용하는 중간층에 액세스하기 위해서 Keras의 [*함수형 API(Functional API)*](https://keras.io/getting-started/functional-api-guide/)를 사용하여 상호작용되는 출력을 얻고, 원하는 출력 활성화를 사용하여 모델을 정의합니다. 모델을 정의하는 함수형 API는 다음과 같이 간단히 입력 및 출력의 정의를 포함합니다.\n",
        "\n",
        "`model = Model(inputs, outputs)`"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "nfec6MuMAbPx",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def get_model():\n",
        "  \"\"\" Creates our model with access to intermediate layers. \n",
        "  \n",
        "  This function will load the VGG19 model and access the intermediate layers. \n",
        "  These layers will then be used to create a new model that will take input image\n",
        "  and return the outputs from these intermediate layers from the VGG model. \n",
        "  \n",
        "  Returns:\n",
        "    returns a keras model that takes image inputs and outputs the style and \n",
        "      content intermediate layers. \n",
        "  \"\"\"\n",
        "  # Load our model. We load pretrained VGG, trained on imagenet data\n",
        "  vgg = tf.keras.applications.vgg19.VGG19(include_top=False, weights='imagenet')\n",
        "  vgg.trainable = False\n",
        "  # Get output layers corresponding to style and content layers \n",
        "  style_outputs = [vgg.get_layer(name).output for name in style_layers]\n",
        "  content_outputs = [vgg.get_layer(name).output for name in content_layers]\n",
        "  model_outputs = style_outputs + content_outputs\n",
        "  # Build model \n",
        "  return models.Model(vgg.input, model_outputs)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "kl6eFGa7-OtV"
      },
      "cell_type": "markdown",
      "source": [
        "위 코드의 스니펫(snippet)에서, 우리는 사전 학습된 이미지 분류 네트워크를 적재할 것입니다. 그 후 우리가 이전에 정의한데로, 관심을 가지는 층들을 확보합니다. 그런 다음 모델의 입력을 이미지로, 출력을 콘텐츠와 스타일층들의 출력으로 설정하여 모델을 정의합니다. 다시 말하면, 우리는 입력으로 이미지를 사용하고 출력으로 콘텐츠와 스타일의 중간층들을 가지는 모델을 생성합니다. \n"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "vJdYvJTZ4bdS"
      },
      "cell_type": "markdown",
      "source": [
        "## 손실함수(loss function) 정의 및 생성(콘텐츠, 스타일 거리)"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "F2Hcepii7_qh"
      },
      "cell_type": "markdown",
      "source": [
        "### 콘텐츠 손실 함수"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "1FvH-gwXi4nq"
      },
      "cell_type": "markdown",
      "source": [
        "콘텐츠 손실 함수의 정의는 실제로 매우 간단합니다. 먼저 원하는 콘텐츠 이미지와 기본 입력 이미지를 네트워크에 전달합니다. 이렇게하면 모델에서 위에서 정의되었던 중간층들의 출력을 반환할 것입니다. 그 후 우리는 간단히 이 두 이미지의 중간 표현(representation) 사이의 유클리디안 거리(euclidean distance)를 얻을 수 있습니다.  \n",
        "\n",
        "좀 더 공식적으로, 콘텐츠 손실(content loss)은 출력이미지 $x$로 부터 콘텐츠 이미지 $p$까지의 콘텐츠의 거리를 표현한 함수입니다. $C_{nn}$을 사전 학습된 딥 합성곱 신경망 네트워크(deep convolutional neural network)라고 합시다. 다시, 이 경우 [VGG19](https://keras.io/applications/#vgg19)모델을 사용합니다. $X$를 어느 이미지라고 하면, $C_{nn}(X)$은 X로 주어진 네트워크입니다. layer $l$에서 입력값이 $x$와 $p$일때 $F^l_{ij}(x) \\in C_{nn}(x)$ 와 $P^l_{ij}(p) \\in C_{nn}(p)$가 중간 특성 표현(feature representation)을 나타낸다고 하면, 콘텐츠 거리(손실)는 다음과 같이 표현할 수 있습니다.\n",
        "$$L^l_{content}(p, x) = \\sum_{i, j} (F^l_{ij}(x) - P^l_{ij}(p))^2$$\n",
        "\n",
        "우리는 일반적인 방식으로 역전파(backpropagation) 과정을 수행하여 콘텐츠 손실(loss)를 최소화 시킬 것입니다. 따라서 특정층(콘텐츠 레이어로 정의 된)에서 원본 이미지와 비슷한 응답을 가질때까지 초기 이미지를 변화시킬것입니다. \n",
        "\n",
        "이것은 매우 간단하게 구현될 수 있습니다. 다시, 이것은 x, 입력이미지, p, 콘텐츠 이미지로 주어진 층 l에서의 특성맵을 입력으로 받아들이고 콘텐츠 거리를 반환할 것입니다.\n",
        "\n"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "6KsbqPA8J9DY"
      },
      "cell_type": "markdown",
      "source": [
        "### 콘텐츠 손실 계산\n",
        "실제로 우리는 원하는 각 층에 콘텐츠 손실을 추가할 것입니다. 이 방법은, 모델에 입력 이미지가 주어질때마다(단순히 (`model(input_image`)로 작동합니다.) 모델을 통한 모든 콘텐츠 손실이 적절히 계산될 것이며, 즉시 실행(eager excution)이기 때문에 모든 경사(gradients)는 계산될 것입니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "d2mf7JwRMkCd",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def get_content_loss(base_content, target):\n",
        "  return tf.reduce_mean(tf.square(base_content - target))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "lGUfttK9F8d5"
      },
      "cell_type": "markdown",
      "source": [
        "## 스타일 손실(loss)"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "I6XtkGK_YGD1"
      },
      "cell_type": "markdown",
      "source": [
        "스타일 손실을 계산하는것은 약간 더 복잡합니다. 하지만 같은 원리를 통하여, 이번에는 기본 입력 이미지와 스타일 이미지를 네트워크에 제공합니다. 기본 이미지와 스타일 이미지의 미가공 중간 출력을 비교하는 대신 두 출력의 그람 행렬(Gram matrix)를 비교합니다.\n",
        "\n",
        "수학적으로, 우리는 기본 입력 이미지 $x$와 스타일 이미지 $a$의 스타일 손실을 이들의 스타일 표현(the gram matrix)사이의 거리로 표현했습니다. 또한 layer $l$에서 벡터화된 특성맵 $i$ and $j$ 사이의 내부 산물을 $G^l_{ij}$로 정의할때, 이미지의 스타일 표현은 그람 행렬 $G^l$에 의해 주어진 필터 응답(filter response) 사이의 상관관계로 표현됩니다. 주어진 이미지의 특성맵 위에 생성된 $G^l_{ij}$은 특성맵 $i$ and $j$ 사이의 상관관계를 나타내는 것을 볼 수 있습니다.\n",
        "\n",
        "기본 입력 이미지의 스타일을 생성하기 위해서 콘텐츠 이미지에서 경사하강(gradient descent)을 수행하여 원본 이미지의 스타일 표현과 일치하는 이미지로 변환합니다. 스타일 이미지의 특성 상관 맵(feature correlation map)과 입력 이미지 간의 평균 제곱 거리(mean squared distance)를 최소화하여 그렇게합니다. 전체 스타일 손실(loss)에 대한 각 층(layer)의 분산은 다음과 같이 표현됩니다.\n",
        "$$E_l = \\frac{1}{4N_l^2M_l^2} \\sum_{i,j}(G^l_{ij} - A^l_{ij})^2$$\n",
        "\n",
        "여기서 $G^l_{ij}$ 와 $A^l_{ij}$은 layer $l$에서 $x$ 와 $a$의 스타일 표현을 나타냅니다. $N_l$은 각각의 크기가  $M_l = height * width$인 특성맵의 개수를 표현합니다. 그러므로 각 층의 총 스타일 손실은 다음과 같이 정의됩니다.\n",
        "$$L_{style}(a, x) = \\sum_{l \\in L} w_l E_l$$\n",
        "\n",
        "각 층의 손실의 분산에 가중치 $w_l$를 곱해줍니다. 이번 케이스의 경우 각 층마다 가중치($w_l =\\frac{1}{|L|}$)를 곱해 줄 것입니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "F21Hm61yLKk5"
      },
      "cell_type": "markdown",
      "source": [
        "### 스타일 손실 계산\n",
        "거리 행렬(distance matrix)로 스타일 손실을 구현합니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "N7MOqwKLLke8",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def gram_matrix(input_tensor):\n",
        "  # 먼저 이미지 채널을 만듭니다. \n",
        "  channels = int(input_tensor.shape[-1])\n",
        "  a = tf.reshape(input_tensor, [-1, channels])\n",
        "  n = tf.shape(a)[0]\n",
        "  gram = tf.matmul(a, a, transpose_a=True)\n",
        "  return gram / tf.cast(n, tf.float32)\n",
        "\n",
        "def get_style_loss(base_style, gram_target):\n",
        "  \"\"\"Expects two images of dimension h, w, c\"\"\"\n",
        "  # 높이, 넓이, 각 층의 필터 개수\n",
        "  # 손실은 주어진 특성맵의 크기와 필터의 개수에 따라 확장됩니다.\n",
        "  height, width, channels = base_style.get_shape().as_list()\n",
        "  gram_style = gram_matrix(base_style)\n",
        "  \n",
        "  return tf.reduce_mean(tf.square(gram_style - gram_target))# / (4. * (channels ** 2) * (width * height) ** 2)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "pXIUX6czZABh"
      },
      "cell_type": "markdown",
      "source": [
        "## 이미지에 스타일 변형 적용하기"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "y9r8Lyjb_m0u"
      },
      "cell_type": "markdown",
      "source": [
        "### 경사하강(Gradient Descent) 실행\n",
        "만약 경사하강이나 역전파에 대해 복습이 필요하거나 익숙하지 않을 경우 다음을 확인합니다.[awesome resource](https://developers.google.com/machine-learning/crash-course/reducing-loss/gradient-descent).\n",
        "\n",
        "이번 과정에서는 손실을 최소화하기 위해 [Adam](https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Adam)* 옵티마이저(optimizer)를 사용할 것입니다. 반복적으로 출력 이미지를 업데이트하여, 손실을 최소화 할 것입니다. 네트워크와 관련된 가중치(weight)를 업데이트하지는 않지만, 손실를 최소화하기 위해 입력 이미지를 학습시킬 것입니다. 이를 위해, 우리는 어떻게 손실과 그래디언트를 계산하는지 알아야합니다. \n",
        "\n",
        "\\* 참고 만약 위 알고리즘에 익숙하다면 L-BFGS를 사용하는것을 권장합니다. 이번 튜토리얼은 즉시 실행을 사용한 모범사례를 보여주는 것이기 때문에 L-BFGS는 사용하지 않습니다. 또한 Adam을 사용하여 사용자 지정 훈련 루프로 자동미분(autogard)/그래디언트 테이프(gradient tape) 기능을 시연할 수 있습니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "-kGzV6LTp4CU"
      },
      "cell_type": "markdown",
      "source": [
        "콘텐츠와 스타일 이미지를 적재하고, 네트워크에 피드를 제공하며, 모델에서 콘텐츠와 스타일 특성 표현을 출력하는 헬퍼 함수(helper function)을 정의합니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "O-lj5LxgtmnI",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def get_feature_representations(model, content_path, style_path):\n",
        "  \"\"\"Helper function to compute our content and style feature representations.\n",
        "\n",
        "  This function will simply load and preprocess both the content and style \n",
        "  images from their path. Then it will feed them through the network to obtain\n",
        "  the outputs of the intermediate layers. \n",
        "  \n",
        "  Arguments:\n",
        "    model: The model that we are using.\n",
        "    content_path: The path to the content image.\n",
        "    style_path: The path to the style image\n",
        "    \n",
        "  Returns:\n",
        "    returns the style features and the content features. \n",
        "  \"\"\"\n",
        "  # 이미지 적재 \n",
        "  content_image = load_and_process_img(content_path)\n",
        "  style_image = load_and_process_img(style_path)\n",
        "  \n",
        "  # 콘텐츠와 스타일 특성 배치 계산\n",
        "  style_outputs = model(style_image)\n",
        "  content_outputs = model(content_image)\n",
        "  \n",
        "  \n",
        "  # 콘텐츠와 스타일 표현 얻기\n",
        "  style_features = [style_layer[0] for style_layer in style_outputs[:num_style_layers]]\n",
        "  content_features = [content_layer[0] for content_layer in content_outputs[num_style_layers:]]\n",
        "  return style_features, content_features"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "3DopXw7-lFHa"
      },
      "cell_type": "markdown",
      "source": [
        "### 손실과 그래디언트 계산하기\n",
        "그래디언트를 계산하기위해서 [**tf.GradientTape**](https://www.tensorflow.org/programmers_guide/eager#computing_gradients)를 사용하였습니다. 이는 나중에 그래디언트 계산 과정을 추적하여 사용할 수 있는 automatic differentiation 기능을 활용할 수 있습니다. 이것은 정방향 전파동안의 명령들을 기록하여 역전파시 입력 이미지와 관련하여 손실 함수의 그래디언트를 계산할 수 있게 합니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "oVDhSo8iJunf",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def compute_loss(model, loss_weights, init_image, gram_style_features, content_features):\n",
        "  \"\"\"This function will compute the loss total loss.\n",
        "  \n",
        "  Arguments:\n",
        "    model: The model that will give us access to the intermediate layers\n",
        "    loss_weights: The weights of each contribution of each loss function. \n",
        "      (style weight, content weight, and total variation weight)\n",
        "    init_image: Our initial base image. This image is what we are updating with \n",
        "      our optimization process. We apply the gradients wrt the loss we are \n",
        "      calculating to this image.\n",
        "    gram_style_features: Precomputed gram matrices corresponding to the \n",
        "      defined style layers of interest.\n",
        "    content_features: Precomputed outputs from defined content layers of \n",
        "      interest.\n",
        "      \n",
        "  Returns:\n",
        "    returns the total loss, style loss, content loss, and total variational loss\n",
        "  \"\"\"\n",
        "  style_weight, content_weight = loss_weights\n",
        "  \n",
        "  # 초기 이미지를 모델에 제공합니다. 이것은 우리가 원하는 층에서 콘텐츠와 스타일 표현을 제공합니다.\n",
        "  # 즉시 실행을 사용하고 있기 때문에 모델은 다른 함수와 같이 호출 가능합니다.\n",
        "  model_outputs = model(init_image)\n",
        "  \n",
        "  style_output_features = model_outputs[:num_style_layers]\n",
        "  content_output_features = model_outputs[num_style_layers:]\n",
        "  \n",
        "  style_score = 0\n",
        "  content_score = 0\n",
        "\n",
        "  # 모든층들에서 스타일 손실을 합산합니다.\n",
        "  # 각 분산된 손실층에 가중치를 적용합니다.\n",
        "  weight_per_style_layer = 1.0 / float(num_style_layers)\n",
        "  for target_style, comb_style in zip(gram_style_features, style_output_features):\n",
        "    style_score += weight_per_style_layer * get_style_loss(comb_style[0], target_style)\n",
        "    \n",
        "  # 모든층들에서 콘텐츠 손실을 합산합니다.\n",
        "  weight_per_content_layer = 1.0 / float(num_content_layers)\n",
        "  for target_content, comb_content in zip(content_features, content_output_features):\n",
        "    content_score += weight_per_content_layer* get_content_loss(comb_content[0], target_content)\n",
        "  \n",
        "  style_score *= style_weight\n",
        "  content_score *= content_weight\n",
        "\n",
        "  # 총 손실을 구합니다.\n",
        "  loss = style_score + content_score \n",
        "  return loss, style_score, content_score"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "r5XTvbP6nJQa"
      },
      "cell_type": "markdown",
      "source": [
        "이렇게 하면 아래와 같이 그래디언트의 계산은 쉽습니다. "
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "fwzYeOqOUH9_",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def compute_grads(cfg):\n",
        "  with tf.GradientTape() as tape: \n",
        "    all_loss = compute_loss(**cfg)\n",
        "  # 입력 이미지에 관한 그래디언트 계산\n",
        "  total_loss = all_loss[0]\n",
        "  return tape.gradient(total_loss, cfg['init_image']), all_loss"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "T9yKu2PLlBIE"
      },
      "cell_type": "markdown",
      "source": [
        "### 루프 최적화"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "pj_enNo6tACQ",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "import IPython.display\n",
        "\n",
        "def run_style_transfer(content_path, \n",
        "                       style_path,\n",
        "                       num_iterations=1000,\n",
        "                       content_weight=1e3, \n",
        "                       style_weight=1e-2): \n",
        "  # 모델의 어떤층을 훈련시킬 필요가 없기 때문에 trainable에 False 값을 할당합니다.\n",
        "  model = get_model() \n",
        "  for layer in model.layers:\n",
        "    layer.trainable = False\n",
        "  \n",
        "  # 특정 중간층에서 스타일과 콘텐츠 표현을 얻습니다.\n",
        "  style_features, content_features = get_feature_representations(model, content_path, style_path)\n",
        "  gram_style_features = [gram_matrix(style_feature) for style_feature in style_features]\n",
        "  \n",
        "  # 초기 이미지 설정\n",
        "  init_image = load_and_process_img(content_path)\n",
        "  init_image = tfe.Variable(init_image, dtype=tf.float32)\n",
        "  # 옵티마이저 생성\n",
        "  opt = tf.train.AdamOptimizer(learning_rate=5, beta1=0.99, epsilon=1e-1)\n",
        "\n",
        "  # 중간 이미지 표시\n",
        "  iter_count = 1\n",
        "  \n",
        "  # 최적의 결과 저장\n",
        "  best_loss, best_img = float('inf'), None\n",
        "  \n",
        "  # 최적의 설정 만들기 \n",
        "  loss_weights = (style_weight, content_weight)\n",
        "  cfg = {\n",
        "      'model': model,\n",
        "      'loss_weights': loss_weights,\n",
        "      'init_image': init_image,\n",
        "      'gram_style_features': gram_style_features,\n",
        "      'content_features': content_features\n",
        "  }\n",
        "    \n",
        "  # 화면 표시\n",
        "  num_rows = 2\n",
        "  num_cols = 5\n",
        "  display_interval = num_iterations/(num_rows*num_cols)\n",
        "  start_time = time.time()\n",
        "  global_start = time.time()\n",
        "  \n",
        "  norm_means = np.array([103.939, 116.779, 123.68])\n",
        "  min_vals = -norm_means\n",
        "  max_vals = 255 - norm_means   \n",
        "  \n",
        "  imgs = []\n",
        "  for i in range(num_iterations):\n",
        "    grads, all_loss = compute_grads(cfg)\n",
        "    loss, style_score, content_score = all_loss\n",
        "    opt.apply_gradients([(grads, init_image)])\n",
        "    clipped = tf.clip_by_value(init_image, min_vals, max_vals)\n",
        "    init_image.assign(clipped)\n",
        "    end_time = time.time() \n",
        "    \n",
        "    if loss < best_loss:\n",
        "      # 총 손실에서 최적의 손실과 이미지 업데이트\n",
        "      best_loss = loss\n",
        "      best_img = deprocess_img(init_image.numpy())\n",
        "\n",
        "    if i % display_interval== 0:\n",
        "      start_time = time.time()\n",
        "      \n",
        "      # numpy array 구성을 위한 .numpy() 메소드 사용\n",
        "      plot_img = init_image.numpy()\n",
        "      plot_img = deprocess_img(plot_img)\n",
        "      imgs.append(plot_img)\n",
        "      IPython.display.clear_output(wait=True)\n",
        "      IPython.display.display_png(Image.fromarray(plot_img))\n",
        "      print('Iteration: {}'.format(i))        \n",
        "      print('Total loss: {:.4e}, ' \n",
        "            'style loss: {:.4e}, '\n",
        "            'content loss: {:.4e}, '\n",
        "            'time: {:.4f}s'.format(loss, style_score, content_score, time.time() - start_time))\n",
        "  print('Total time: {:.4f}s'.format(time.time() - global_start))\n",
        "  IPython.display.clear_output(wait=True)\n",
        "  plt.figure(figsize=(14,4))\n",
        "  for i,img in enumerate(imgs):\n",
        "      plt.subplot(num_rows,num_cols,i+1)\n",
        "      plt.imshow(img)\n",
        "      plt.xticks([])\n",
        "      plt.yticks([])\n",
        "      \n",
        "  return best_img, best_loss "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "vSVMx4burydi",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "best, best_loss = run_style_transfer(content_path, \n",
        "                                     style_path, num_iterations=1000)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "dzJTObpsO3TZ",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "Image.fromarray(best)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "dCXQ9vSnQbDy"
      },
      "cell_type": "markdown",
      "source": [
        "Colaboratory에서 이미지를 다운로드하려면 다음 코드의 주석처리를 제거하십시오."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "SSH6OpyyQn7w",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "#from google.colab import files\n",
        "#files.download('wave_turtle.png')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "LwiZfCW0AZwt"
      },
      "cell_type": "markdown",
      "source": [
        "## 출력 시각화\n",
        "적용되었던 처리 과정을 제거하기 위해 출력 이미지의 역처리 과정을 수행합니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "lqTQN1PjulV9",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def show_results(best_img, content_path, style_path, show_large_final=True):\n",
        "  plt.figure(figsize=(10, 5))\n",
        "  content = load_img(content_path) \n",
        "  style = load_img(style_path)\n",
        "\n",
        "  plt.subplot(1, 2, 1)\n",
        "  imshow(content, 'Content Image')\n",
        "\n",
        "  plt.subplot(1, 2, 2)\n",
        "  imshow(style, 'Style Image')\n",
        "\n",
        "  if show_large_final: \n",
        "    plt.figure(figsize=(10, 10))\n",
        "\n",
        "    plt.imshow(best_img)\n",
        "    plt.title('Output Image')\n",
        "    plt.show()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "i6d6O50Yvs6a",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "show_results(best, content_path, style_path)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "tyGMmWh2Pss8"
      },
      "cell_type": "markdown",
      "source": [
        "## 다른 이미지 시도\n",
        "\n",
        "튀빙겐(Tuebingen) 이미지 \n",
        "\n",
        "Photo By: <NAME> [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC BY 3.0  (https://creativecommons.org/licenses/by/3.0)], from Wikimedia Commons"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "x2TePU39k9lb"
      },
      "cell_type": "markdown",
      "source": [
        "### 별이 빛나는 밤(Starry night) + 튀빙겐(Tuebingen)"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "ES9dC6ZyJBD2",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "best_starry_night, best_loss = run_style_transfer('/tmp/nst/Tuebingen_Neckarfront.jpg',\n",
        "                                                  '/tmp/nst/1024px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "X8w8WLkKvzXu",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "show_results(best_starry_night, '/tmp/nst/Tuebingen_Neckarfront.jpg',\n",
        "             '/tmp/nst/1024px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "QcXwvViek4Br"
      },
      "cell_type": "markdown",
      "source": [
        "### 창조의 기둥(Pillars of Creation) + 튀빙겐(Tuebingen)"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "vJ3u2U-gGmgP",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "best_poc_tubingen, best_loss = run_style_transfer('/tmp/nst/Tuebingen_Neckarfront.jpg', \n",
        "                                                  '/tmp/nst/Pillars_of_creation_2014_HST_WFC3-UVIS_full-res_denoised.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "pQUq3KxpGv2O",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "show_results(best_poc_tubingen, \n",
        "             '/tmp/nst/Tuebingen_Neckarfront.jpg',\n",
        "             '/tmp/nst/Pillars_of_creation_2014_HST_WFC3-UVIS_full-res_denoised.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "bTZdTOdW3s8H"
      },
      "cell_type": "markdown",
      "source": [
        "### 칸딘스키(Kandinsky) Composition #7 + 튀빙겐(Tuebingen)"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "bt9mbQfl7exl",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "best_kandinsky_tubingen, best_loss = run_style_transfer('/tmp/nst/Tuebingen_Neckarfront.jpg', \n",
        "                                                  '/tmp/nst/Vassily_Kandinsky,_1913_-_Composition_7.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "Qnz8HeXSXg6P",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "show_results(best_kandinsky_tubingen, \n",
        "             '/tmp/nst/Tuebingen_Neckarfront.jpg',\n",
        "             '/tmp/nst/Vassily_Kandinsky,_1913_-_Composition_7.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "cg68lW2A3s8N"
      },
      "cell_type": "markdown",
      "source": [
        "### 창조의 기둥(Pillars of Creation) + 바다 거북(Sea Turtle)"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "dl0DUot_bFST",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "best_poc_turtle, best_loss = run_style_transfer('/tmp/nst/Green_Sea_Turtle_grazing_seagrass.jpg', \n",
        "                                                  '/tmp/nst/Pillars_of_creation_2014_HST_WFC3-UVIS_full-res_denoised.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "UzJfE0I1bQn8",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "show_results(best_poc_turtle, \n",
        "             '/tmp/nst/Green_Sea_Turtle_grazing_seagrass.jpg',\n",
        "             '/tmp/nst/Pillars_of_creation_2014_HST_WFC3-UVIS_full-res_denoised.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "sElaeNX-4Vnc"
      },
      "cell_type": "markdown",
      "source": [
        "## 주요 요점\n",
        "\n",
        "### 우리가 다룬 내용:\n",
        "* 몇가지의 다른 손실(loss)함수를 정의하였고, 손실을 최소화하기 위한 입력 이미지 변형을 위해 역전파(backpropagation)을 사용하였습니다.\n",
        "  * 이를 위해서 우리는 사전학습된 모델에 적재해야 했으며, 이 모델의 학습된 특성맵을 이미지의 콘텐츠와 스타일 표현을 표현하기위해 사용했습니다.\n",
        "    * 메인 손실(loss) 함수는 표현 차이(different representations)의 관점에서 주로 거리를 계산하는 것이었습니다. \n",
        "* 사용자 지정 모델과 **즉시 실행(eager execution)**을 사용하여 실행하였습니다.\n",
        "  * Functional API를 사용하여 사용자 지정 모델을 빌드했습니다.\n",
        "  * 즉시 실행은 파이썬 자연제어흐름을 사용하여 `Tensor`와 함께 동적인 수행을 제공했습니다. \n",
        "  * `Tensor`를 직접적으로 제어했습니다. 이는 디버깅과 작업 쉽게 만들어줍니다.  \n",
        "* **tf.gradient**를 사용한 옵티마이저(optimizer) 업데이트 법칙을 적용하여 반복적으로 이미지를 업데이트했습니다. 옵티마이저는 주어진 입력 이미지의 손실과 관련하여 최적화됩니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "U-y02GWonqnD"
      },
      "cell_type": "markdown",
      "source": [
        "\n",
        "**[Image of Tuebingen](https://commons.wikimedia.org/wiki/File:Tuebingen_Neckarfront.jpg)** \n",
        "Photo By: <NAME> [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC BY 3.0  (https://creativecommons.org/licenses/by/3.0)], from Wikimedia Commons\n",
        "\n",
        "**[Image of Green Sea Turtle](https://commons.wikimedia.org/wiki/File:Green_Sea_Turtle_grazing_seagrass.jpg)**\n",
        "By P.Lindgren [CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0)], from Wikimedia Commons\n",
        "\n"
      ]
    }
  ]
}<commit_msg>Update buttons<commit_after>{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "_style_transfer.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "metadata": {
        "colab_type": "text",
        "id": "g_nWetWWd_ns"
      },
      "cell_type": "markdown",
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "2pHVBk_seED1",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "jo5PziEC4hWs"
      },
      "cell_type": "markdown",
      "source": [
        "# tf.keras를 사용한 Neural Style Transfer\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/eager/\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />TensorFlow.org에서 보기</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ko/tutorials/eager/_style_transfer.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />구글 코랩(Colab)에서 실행하기</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ko/tutorials/eager/_style_transfer.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />깃허브(GitHub) 소스 보기</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "metadata": {
        "id": "yJ-t14sot8iX",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도\n",
        "불구하고 [공식 영문 문서](https://www.tensorflow.org/?hl=en)의 내용과 일치하지 않을 수 있습니다.\n",
        "이 번역에 개선할 부분이 있다면\n",
        "[tensorflow/docs](https://github.com/tensorflow/docs) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다.\n",
        "문서 번역이나 리뷰에 참여하려면 \n",
        "[<EMAIL>](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs)로\n",
        "메일을 보내주시기 바랍니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "aDyGj8DmXCJI"
      },
      "cell_type": "markdown",
      "source": [
        "## 개요\n",
        "\n",
        "이번 튜토리얼에서는 딥러닝을 사용하여 원하는 이미지를 다른 스타일의 이미지로 구성하는 법을 배워보겠습니다(피카소나 반 고흐처럼 그리기를 희망하시나요?). 이 기법은 **Neural Style Transfer**로 알려져있으며, <NAME>의 논문 [A Neural Algorithm of Artistic Style](https://arxiv.org/abs/1508.06576)에 잘 기술되어 있습니다. 꼭 읽어보셔야합니다.\n",
        "\n",
        "하지만 무엇이 Neural Style Transfer 일까요?\n",
        "\n",
        "Neural style transfer은 **콘텐츠(Content)** 이미지, **스타일 참조(Style Reference)** 이미지(유명한 작가의 삽화 같은), 그리고 여러분이 변경하기 원하는 **입력(Input)** 이미지 위 3가지의 이미지를 사용하여, 콘텐츠 이미지의 콘텐츠로 변형된 것처럼 보이고, 스타일 참조 이미지로 채색된 것 같은 입력 이미지를 생성하는 최적화 기술입니다.\n",
        "\n",
        "예를 들어, 아래의 거북이 이미지와 가츠시카 호쿠사이(Katsushika Hokusai) 작가의 *The Great Wave off Kanagawa* 이미지를 사용해봅시다.\n",
        "\n",
        "<img src=\"https://github.com/tensorflow/models/blob/master/research/nst_blogpost/Green_Sea_Turtle_grazing_seagrass.jpg?raw=1\" alt=\"Drawing\" style=\"width: 200px;\"/>\n",
        "<img src=\"https://github.com/tensorflow/models/blob/master/research/nst_blogpost/The_Great_Wave_off_Kanagawa.jpg?raw=1\" alt=\"Drawing\" style=\"width: 200px;\"/>\n",
        "\n",
        "[Image of Green Sea Turtle](https://commons.wikimedia.org/wiki/File:Green_Sea_Turtle_grazing_seagrass.jpg)\n",
        "-By P.Lindgren [CC BY-SA 3.0  (https://creativecommons.org/licenses/by-sa/3.0)], from Wikimedia Common\n",
        "\n",
        "\n",
        "만약 가츠시카 호쿠사이 작가가 오직 위 스타일로 거북이를 그린다면 어떻게 생겼을까요? 위와 비슷할까요?\n",
        "\n",
        "<img src=\"https://github.com/tensorflow/models/blob/master/research/nst_blogpost/wave_turtle.png?raw=1\" alt=\"Drawing\" style=\"width: 500px;\"/>\n",
        "\n",
        "이것은 마법일까요 아니면 그저 딥러닝일까요? 다행스럽게도, 이것은 어떠한 마법도 포함하고있지 않습니다. 스타일 변환은 신경망의 기능과 내부표현을 보여주는 재밌고 흥미로운 기술입니다.\n",
        "\n",
        "Neural style transfer의 원리는 두 이미지의 콘텐츠의 차이를 표현하는 함수인, $L_{content}$, 두 이미지의 스타일의 차이를 표현하는 함수인, $L_{style}$, 위 두 가지 거리함수를 정의하는 것입니다. 원하는 스타일 이미지, 콘텐츠 이미지 그리고 입력 이미지(콘텐츠 이미지로 초기화 된) 이 3가지의 이미지가 주어졌을 때, 콘텐츠 이미지의 콘텐츠와 스타일 이미지의 스타일 각각의 거리를 최소화하는 입력 이미지의 변형을 시도할 것입니다. \n",
        "\n",
        "요약하자면, 기본 입력 이미지, 일치시키려는 콘텐츠 및 스타일 이미지를 가져와, 역전파(backpropagation)를 이용하여 콘텐츠와 스타일 거리(손실)함수를 최소화시켜 콘텐츠 이미지의 콘텐츠와 스타일 이미지의 스타일이 매치된 이미지를 생성할 것입니다. \n",
        "\n",
        "### 적용되는 특별개념:\n",
        "이 과정에서, 우리는 실제 경험을 쌓고 아래의 개념을 중심으로 직관력을 개발할 것입니다.\n",
        "\n",
        "* **즉시 실행(Eager Execution)** - 연산을 즉각적으로 평가하는 Tensorflow의 명령형 프로그래밍 환경 사용\n",
        "  * [Learn more about eager execution](https://www.tensorflow.org/programmers_guide/eager)\n",
        "  * [See it in action](https://www.tensorflow.org/get_started/eager)\n",
        "* ** 모델 정의를 위한 [함수형 API(Functional API)](https://keras.io/getting-started/functional-api-guide/) 사용** - 함수형 API(Functional API)를 사용하여 필요한 중간 활성화에 대한 접근을 제공할 모델의 부분집합을 구성할 것입니다.\n",
        "* **사전학습 모델의 특성맵(Feature map) 활용** - 사전학습된 모델과 그 모델의 특성맵에 대해 배우겠습니다.\n",
        "* **사용자 지정 훈련 루프 만들기** - 입력변수와 관련하여 주어진 손실함수를 최소화하기 위해 옵티마이저(optimizer)를 설정하는법을 살펴 보겠습니다.\n",
        "\n",
        "### 우리는 스타일 변형을 수행하기 위해 아래와 같은 일반적인 단계들을 진행할 것입니다.\n",
        "\n",
        "1. 데이터 시각화(Visualize data)\n",
        "2. 기본 전처리 및 데이터 준비(Basic Preprocessing/preparing our data)\n",
        "3. 손실함수 설정(Set up loss functions)\n",
        "4. 모델 생성(Create model)\n",
        "5. 손실함수 최적화(Optimize for loss function)\n",
        "\n",
        "**참고:** -\t이 게시글은 기본 머신러닝 개념에 익숙한 중간사용자를 대상으로합니다. 이 게시글을 최대한 활용하려면, 아래 사이트를 참조하세요.\n",
        "* [Gatys' paper](https://arxiv.org/abs/1508.06576) - 이번 튜토리얼에서 전체적인 과정에 대해 설명할 것이지만, 위 논문은 이 과제에 대한 보다 나은 이해를 제공할 것입니다.\n",
        "* [Understand reducing loss with gradient descent](https://developers.google.com/machine-learning/crash-course/reducing-loss/gradient-descent)\n",
        "\n",
        "**예상시간**: 30분\n"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "U8ajP_u73s6m"
      },
      "cell_type": "markdown",
      "source": [
        "## 설정\n",
        "\n",
        "### 다운로드 이미지"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "riWE_b8k3s6o",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "import os\n",
        "img_dir = '/tmp/nst'\n",
        "if not os.path.exists(img_dir):\n",
        "    os.makedirs(img_dir)\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/d/d7/Green_Sea_Turtle_grazing_seagrass.jpg\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/0/0a/The_Great_Wave_off_Kanagawa.jpg\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/b/b4/Vassily_Kandinsky%2C_1913_-_Composition_7.jpg\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/0/00/Tuebingen_Neckarfront.jpg\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/6/68/Pillars_of_creation_2014_HST_WFC3-UVIS_full-res_denoised.jpg\n",
        "!wget --quiet -P /tmp/nst/ https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg/1024px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "eqxUicSPUOP6"
      },
      "cell_type": "markdown",
      "source": [
        "### 모듈 구성 및 임포트"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "sc1OLbOWhPCO",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import matplotlib as mpl\n",
        "mpl.rcParams['figure.figsize'] = (10,10)\n",
        "mpl.rcParams['axes.grid'] = False\n",
        "\n",
        "import numpy as np\n",
        "from PIL import Image\n",
        "import time\n",
        "import functools"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "RYEjlrYk3s6w",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "import tensorflow as tf\n",
        "import tensorflow.contrib.eager as tfe\n",
        "\n",
        "from tensorflow.python.keras.preprocessing import image as kp_image\n",
        "from tensorflow.python.keras import models \n",
        "from tensorflow.python.keras import losses\n",
        "from tensorflow.python.keras import layers\n",
        "from tensorflow.python.keras import backend as K"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "L7sjDODq67HQ"
      },
      "cell_type": "markdown",
      "source": [
        "우리는 [즉시 실행(eager execution)](https://www.tensorflow.org/guide/eager)을 활성화하여 시작할 것입니다.\n",
        "즉시 실행(eager execution)은 가장 명확하고, 읽기 쉬운 방식으로 작업을 할 수 있게 해줍니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "sfjsSAtNrqQx",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "tf.enable_eager_execution()\n",
        "print(\"Eager execution: {}\".format(tf.executing_eagerly()))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "IOiGrIV1iERH",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# 일부 전역변수를 여기에 설정하세요.\n",
        "content_path = '/tmp/nst/Green_Sea_Turtle_grazing_seagrass.jpg'\n",
        "style_path = '/tmp/nst/The_Great_Wave_off_Kanagawa.jpg'"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "xE4Yt8nArTeR"
      },
      "cell_type": "markdown",
      "source": [
        "## 입력 시각화"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "3TLljcwv5qZs",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def load_img(path_to_img):\n",
        "  max_dim = 512\n",
        "  img = Image.open(path_to_img)\n",
        "  long = max(img.size)\n",
        "  scale = max_dim/long\n",
        "  img = img.resize((round(img.size[0]*scale), round(img.size[1]*scale)), Image.ANTIALIAS)\n",
        "  \n",
        "  img = kp_image.img_to_array(img)\n",
        "  \n",
        "  # 배치 차원(batch dimension)을 갖기 위해 이미지 배열을 브로드캐스팅(broadcasting)할 필요가 있습니다.\n",
        "  img = np.expand_dims(img, axis=0)\n",
        "  return img"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "vupl0CI18aAG",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def imshow(img, title=None):\n",
        "  # 배치 차원(batch dimension) 제거\n",
        "  out = np.squeeze(img, axis=0)\n",
        "  # 디스플레이를 위한 정규화\n",
        "  out = out.astype('uint8')\n",
        "  plt.imshow(out)\n",
        "  if title is not None:\n",
        "    plt.title(title)\n",
        "  plt.imshow(out)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "2yAlRzJZrWM3"
      },
      "cell_type": "markdown",
      "source": [
        "여기 주여진 콘텐츠 이미지와 스타일 이미지가 있습니다. 우리는 콘텐츠 이미지의 콘텐츠와 스타일 이미지의 스타일로 구성된 이미지를 생성하기를 원합니다. "
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "_UWQmeEaiKkP",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "plt.figure(figsize=(10,10))\n",
        "\n",
        "content = load_img(content_path).astype('uint8')\n",
        "style = load_img(style_path).astype('uint8')\n",
        "\n",
        "plt.subplot(1, 2, 1)\n",
        "imshow(content, 'Content Image')\n",
        "\n",
        "plt.subplot(1, 2, 2)\n",
        "imshow(style, 'Style Image')\n",
        "plt.show()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "7qMVNvEsK-_D"
      },
      "cell_type": "markdown",
      "source": [
        "## 데이터 준비\n",
        "이미지를 쉽게 전처리하고 적재하기위한 메소드(method)를 만들어봅시다. 이 과정에서는 VGG 학습 프로세스에 따라 예상되는 것과 동일한 전처리 과정을 수행할것입니다. VGG 네트워크들은 각각의 채널이 `평균 = [103.939, 116.779, 123.68]` 및 BGR 채널들로 정규화(normalization)되어 있는 이미지로 훈련됩니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "hGwmTwJNmv2a",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def load_and_process_img(path_to_img):\n",
        "  img = load_img(path_to_img)\n",
        "  img = tf.keras.applications.vgg19.preprocess_input(img)\n",
        "  return img"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "xCgooqs6tAka"
      },
      "cell_type": "markdown",
      "source": [
        "최적화의 결과들을 보기 위해서는, 역전처리(inverse preprocessing) 과정을 수행 할 필요가 있습니다. 더욱이, 최적화 된 이미지의 값이 $- \\infty$ and $\\infty$사이의 어느값도 가질 수 있기 때문에 0-255사이의 값으로 고정해주여야 합니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "mjzlKRQRs_y2",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def deprocess_img(processed_img):\n",
        "  x = processed_img.copy()\n",
        "  if len(x.shape) == 4:\n",
        "    x = np.squeeze(x, 0)\n",
        "  assert len(x.shape) == 3, (\"Input to deprocess image must be an image of \"\n",
        "                             \"dimension [1, height, width, channel] or [height, width, channel]\")\n",
        "  if len(x.shape) != 3:\n",
        "    raise ValueError(\"Invalid input to deprocessing image\")\n",
        "  \n",
        "  # 역전처리 과정 수행\n",
        "  x[:, :, 0] += 103.939\n",
        "  x[:, :, 1] += 116.779\n",
        "  x[:, :, 2] += 123.68\n",
        "  x = x[:, :, ::-1]\n",
        "\n",
        "  x = np.clip(x, 0, 255).astype('uint8')\n",
        "  return x"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "GEwZ7FlwrjoZ"
      },
      "cell_type": "markdown",
      "source": [
        "### 콘텐츠와 스타일 표현(Representation) 정의\n",
        "\n",
        "이미지의 콘텐츠와 스타일 표현(representation)을 얻기 위해, 모델의 몇가지 중간층(layer)들을 살펴볼 것입니다. 모델을 더 깊게 만들수록, 중간층들은 더 고차원적 특성들을 나타냅니다. 이번 경우, 우리는 사전학습된 이미지 분류 네트워크인 VGG19 네트워크의 구조를 사용할 것입니다. 이 중간층들은 우리 이미지에서 콘텐츠와 스타일의 표현을 정의할 필요가 있습니다. 입력 이미지의 경우, 이 중간층들에서 콘텐츠와 스타일에 해당하는 타깃 표현들을 매치하기 위해 시도할 것입니다. \n",
        "\n",
        "#### 왜 중간층인가?\n",
        "\n",
        "사전학습된 이미지 분류 네트워크 속에서 중간 출력들이 스타일과 콘텐츠 표현을 어떻게 정의할 수 있는지 궁금할 수도 있습니다. 고수준에서, 이 현상은 네트워크가 이미지 분류(네트워크가 수행하도록 학습된)를 수행하기 위해서는 반드시 이미지를  이해해야 한다는 사실에 의해서 설명될 수 있습니다. 이것은 미가공 이미지를 입력 픽셀로 사용하고 이미지 내 존재하는 특성 표현(feature present)들의 복합 이해로 전환하는 변형을 통한 내부 표현(internal representation)을 만드는 작업이 포함됩니다. 또한 부분적으로 왜 합성곱(convolutional) 신경망의 일반화(generalize)가 쉽게 가능한지를 나타냅니다. 즉, 합성곱 신경망은 배경잡음(background noise)와 기타잡음(nuisances)에 대해 인지할 수없는 클래스 내에서 불변성(invariance)을 포착할 수 있고 특징을 정의할 수 있습니다.(예 : 고양이 vs 개와 같은). 따라서 입력된 미가공 이미지와 분류 레이블(label)이 출력되는 어딘가에서 모델은 복합 특성(complex feature) 추출기로 사용됩니다. 그러므로, 중간층에 액세스하여 입력 이미지의 콘텐츠와 스타일을 표현할 수 있습니다. \n",
        "\n",
        "우리는 네트워크에서 이 중간층들을 가져올 것입니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "N4-8eUp_Kc-j",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "# 특성맵을 추출 할 Content layer\n",
        "content_layers = ['block5_conv2'] \n",
        "\n",
        "# 우리가 관심을 갖는 Style layer\n",
        "style_layers = ['block1_conv1',\n",
        "                'block2_conv1',\n",
        "                'block3_conv1', \n",
        "                'block4_conv1', \n",
        "                'block5_conv1'\n",
        "               ]\n",
        "\n",
        "num_content_layers = len(content_layers)\n",
        "num_style_layers = len(style_layers)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "Jt3i3RRrJiOX"
      },
      "cell_type": "markdown",
      "source": [
        "## 모델 만들기\n",
        "이번 경우, 우리는 [VGG19](https://keras.io/applications/#vgg19)모델을 적재하고, 입력 텐서를 모델에 제공할 것입니다. 이것은 콘텐츠, 스타일 그리고 생성된 이미지의 feature maps, 그리고 이어서 콘텐츠 및 스타일 표현을 추출할 수 있습니다. \n",
        "\n",
        "논문에서 제안한 것처럼 VGG19모델을 사용합니다. 또한 VGG19모델은 ResNet, Inception 등과 같은 모델에 비해 상대적으로 간단한 모델이므로 특징맵들은 이미지 변환에 더 효과적으로 작동할 것입니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "v9AnzEUU6hhx"
      },
      "cell_type": "markdown",
      "source": [
        "콘텐츠와 스타일 특징맵에 상호작용하는 중간층에 액세스하기 위해서 Keras의 [*함수형 API(Functional API)*](https://keras.io/getting-started/functional-api-guide/)를 사용하여 상호작용되는 출력을 얻고, 원하는 출력 활성화를 사용하여 모델을 정의합니다. 모델을 정의하는 함수형 API는 다음과 같이 간단히 입력 및 출력의 정의를 포함합니다.\n",
        "\n",
        "`model = Model(inputs, outputs)`"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "nfec6MuMAbPx",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def get_model():\n",
        "  \"\"\" Creates our model with access to intermediate layers. \n",
        "  \n",
        "  This function will load the VGG19 model and access the intermediate layers. \n",
        "  These layers will then be used to create a new model that will take input image\n",
        "  and return the outputs from these intermediate layers from the VGG model. \n",
        "  \n",
        "  Returns:\n",
        "    returns a keras model that takes image inputs and outputs the style and \n",
        "      content intermediate layers. \n",
        "  \"\"\"\n",
        "  # Load our model. We load pretrained VGG, trained on imagenet data\n",
        "  vgg = tf.keras.applications.vgg19.VGG19(include_top=False, weights='imagenet')\n",
        "  vgg.trainable = False\n",
        "  # Get output layers corresponding to style and content layers \n",
        "  style_outputs = [vgg.get_layer(name).output for name in style_layers]\n",
        "  content_outputs = [vgg.get_layer(name).output for name in content_layers]\n",
        "  model_outputs = style_outputs + content_outputs\n",
        "  # Build model \n",
        "  return models.Model(vgg.input, model_outputs)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "kl6eFGa7-OtV"
      },
      "cell_type": "markdown",
      "source": [
        "위 코드의 스니펫(snippet)에서, 우리는 사전 학습된 이미지 분류 네트워크를 적재할 것입니다. 그 후 우리가 이전에 정의한데로, 관심을 가지는 층들을 확보합니다. 그런 다음 모델의 입력을 이미지로, 출력을 콘텐츠와 스타일층들의 출력으로 설정하여 모델을 정의합니다. 다시 말하면, 우리는 입력으로 이미지를 사용하고 출력으로 콘텐츠와 스타일의 중간층들을 가지는 모델을 생성합니다. \n"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "vJdYvJTZ4bdS"
      },
      "cell_type": "markdown",
      "source": [
        "## 손실함수(loss function) 정의 및 생성(콘텐츠, 스타일 거리)"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "F2Hcepii7_qh"
      },
      "cell_type": "markdown",
      "source": [
        "### 콘텐츠 손실 함수"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "1FvH-gwXi4nq"
      },
      "cell_type": "markdown",
      "source": [
        "콘텐츠 손실 함수의 정의는 실제로 매우 간단합니다. 먼저 원하는 콘텐츠 이미지와 기본 입력 이미지를 네트워크에 전달합니다. 이렇게하면 모델에서 위에서 정의되었던 중간층들의 출력을 반환할 것입니다. 그 후 우리는 간단히 이 두 이미지의 중간 표현(representation) 사이의 유클리디안 거리(euclidean distance)를 얻을 수 있습니다.  \n",
        "\n",
        "좀 더 공식적으로, 콘텐츠 손실(content loss)은 출력이미지 $x$로 부터 콘텐츠 이미지 $p$까지의 콘텐츠의 거리를 표현한 함수입니다. $C_{nn}$을 사전 학습된 딥 합성곱 신경망 네트워크(deep convolutional neural network)라고 합시다. 다시, 이 경우 [VGG19](https://keras.io/applications/#vgg19)모델을 사용합니다. $X$를 어느 이미지라고 하면, $C_{nn}(X)$은 X로 주어진 네트워크입니다. layer $l$에서 입력값이 $x$와 $p$일때 $F^l_{ij}(x) \\in C_{nn}(x)$ 와 $P^l_{ij}(p) \\in C_{nn}(p)$가 중간 특성 표현(feature representation)을 나타낸다고 하면, 콘텐츠 거리(손실)는 다음과 같이 표현할 수 있습니다.\n",
        "$$L^l_{content}(p, x) = \\sum_{i, j} (F^l_{ij}(x) - P^l_{ij}(p))^2$$\n",
        "\n",
        "우리는 일반적인 방식으로 역전파(backpropagation) 과정을 수행하여 콘텐츠 손실(loss)를 최소화 시킬 것입니다. 따라서 특정층(콘텐츠 레이어로 정의 된)에서 원본 이미지와 비슷한 응답을 가질때까지 초기 이미지를 변화시킬것입니다. \n",
        "\n",
        "이것은 매우 간단하게 구현될 수 있습니다. 다시, 이것은 x, 입력이미지, p, 콘텐츠 이미지로 주어진 층 l에서의 특성맵을 입력으로 받아들이고 콘텐츠 거리를 반환할 것입니다.\n",
        "\n"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "6KsbqPA8J9DY"
      },
      "cell_type": "markdown",
      "source": [
        "### 콘텐츠 손실 계산\n",
        "실제로 우리는 원하는 각 층에 콘텐츠 손실을 추가할 것입니다. 이 방법은, 모델에 입력 이미지가 주어질때마다(단순히 (`model(input_image`)로 작동합니다.) 모델을 통한 모든 콘텐츠 손실이 적절히 계산될 것이며, 즉시 실행(eager excution)이기 때문에 모든 경사(gradients)는 계산될 것입니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "d2mf7JwRMkCd",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def get_content_loss(base_content, target):\n",
        "  return tf.reduce_mean(tf.square(base_content - target))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "lGUfttK9F8d5"
      },
      "cell_type": "markdown",
      "source": [
        "## 스타일 손실(loss)"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "I6XtkGK_YGD1"
      },
      "cell_type": "markdown",
      "source": [
        "스타일 손실을 계산하는것은 약간 더 복잡합니다. 하지만 같은 원리를 통하여, 이번에는 기본 입력 이미지와 스타일 이미지를 네트워크에 제공합니다. 기본 이미지와 스타일 이미지의 미가공 중간 출력을 비교하는 대신 두 출력의 그람 행렬(Gram matrix)를 비교합니다.\n",
        "\n",
        "수학적으로, 우리는 기본 입력 이미지 $x$와 스타일 이미지 $a$의 스타일 손실을 이들의 스타일 표현(the gram matrix)사이의 거리로 표현했습니다. 또한 layer $l$에서 벡터화된 특성맵 $i$ and $j$ 사이의 내부 산물을 $G^l_{ij}$로 정의할때, 이미지의 스타일 표현은 그람 행렬 $G^l$에 의해 주어진 필터 응답(filter response) 사이의 상관관계로 표현됩니다. 주어진 이미지의 특성맵 위에 생성된 $G^l_{ij}$은 특성맵 $i$ and $j$ 사이의 상관관계를 나타내는 것을 볼 수 있습니다.\n",
        "\n",
        "기본 입력 이미지의 스타일을 생성하기 위해서 콘텐츠 이미지에서 경사하강(gradient descent)을 수행하여 원본 이미지의 스타일 표현과 일치하는 이미지로 변환합니다. 스타일 이미지의 특성 상관 맵(feature correlation map)과 입력 이미지 간의 평균 제곱 거리(mean squared distance)를 최소화하여 그렇게합니다. 전체 스타일 손실(loss)에 대한 각 층(layer)의 분산은 다음과 같이 표현됩니다.\n",
        "$$E_l = \\frac{1}{4N_l^2M_l^2} \\sum_{i,j}(G^l_{ij} - A^l_{ij})^2$$\n",
        "\n",
        "여기서 $G^l_{ij}$ 와 $A^l_{ij}$은 layer $l$에서 $x$ 와 $a$의 스타일 표현을 나타냅니다. $N_l$은 각각의 크기가  $M_l = height * width$인 특성맵의 개수를 표현합니다. 그러므로 각 층의 총 스타일 손실은 다음과 같이 정의됩니다.\n",
        "$$L_{style}(a, x) = \\sum_{l \\in L} w_l E_l$$\n",
        "\n",
        "각 층의 손실의 분산에 가중치 $w_l$를 곱해줍니다. 이번 케이스의 경우 각 층마다 가중치($w_l =\\frac{1}{|L|}$)를 곱해 줄 것입니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "F21Hm61yLKk5"
      },
      "cell_type": "markdown",
      "source": [
        "### 스타일 손실 계산\n",
        "거리 행렬(distance matrix)로 스타일 손실을 구현합니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "N7MOqwKLLke8",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def gram_matrix(input_tensor):\n",
        "  # 먼저 이미지 채널을 만듭니다. \n",
        "  channels = int(input_tensor.shape[-1])\n",
        "  a = tf.reshape(input_tensor, [-1, channels])\n",
        "  n = tf.shape(a)[0]\n",
        "  gram = tf.matmul(a, a, transpose_a=True)\n",
        "  return gram / tf.cast(n, tf.float32)\n",
        "\n",
        "def get_style_loss(base_style, gram_target):\n",
        "  \"\"\"Expects two images of dimension h, w, c\"\"\"\n",
        "  # 높이, 넓이, 각 층의 필터 개수\n",
        "  # 손실은 주어진 특성맵의 크기와 필터의 개수에 따라 확장됩니다.\n",
        "  height, width, channels = base_style.get_shape().as_list()\n",
        "  gram_style = gram_matrix(base_style)\n",
        "  \n",
        "  return tf.reduce_mean(tf.square(gram_style - gram_target))# / (4. * (channels ** 2) * (width * height) ** 2)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "pXIUX6czZABh"
      },
      "cell_type": "markdown",
      "source": [
        "## 이미지에 스타일 변형 적용하기"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "y9r8Lyjb_m0u"
      },
      "cell_type": "markdown",
      "source": [
        "### 경사하강(Gradient Descent) 실행\n",
        "만약 경사하강이나 역전파에 대해 복습이 필요하거나 익숙하지 않을 경우 다음을 확인합니다.[awesome resource](https://developers.google.com/machine-learning/crash-course/reducing-loss/gradient-descent).\n",
        "\n",
        "이번 과정에서는 손실을 최소화하기 위해 [Adam](https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Adam)* 옵티마이저(optimizer)를 사용할 것입니다. 반복적으로 출력 이미지를 업데이트하여, 손실을 최소화 할 것입니다. 네트워크와 관련된 가중치(weight)를 업데이트하지는 않지만, 손실를 최소화하기 위해 입력 이미지를 학습시킬 것입니다. 이를 위해, 우리는 어떻게 손실과 그래디언트를 계산하는지 알아야합니다. \n",
        "\n",
        "\\* 참고 만약 위 알고리즘에 익숙하다면 L-BFGS를 사용하는것을 권장합니다. 이번 튜토리얼은 즉시 실행을 사용한 모범사례를 보여주는 것이기 때문에 L-BFGS는 사용하지 않습니다. 또한 Adam을 사용하여 사용자 지정 훈련 루프로 자동미분(autogard)/그래디언트 테이프(gradient tape) 기능을 시연할 수 있습니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "-kGzV6LTp4CU"
      },
      "cell_type": "markdown",
      "source": [
        "콘텐츠와 스타일 이미지를 적재하고, 네트워크에 피드를 제공하며, 모델에서 콘텐츠와 스타일 특성 표현을 출력하는 헬퍼 함수(helper function)을 정의합니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "O-lj5LxgtmnI",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def get_feature_representations(model, content_path, style_path):\n",
        "  \"\"\"Helper function to compute our content and style feature representations.\n",
        "\n",
        "  This function will simply load and preprocess both the content and style \n",
        "  images from their path. Then it will feed them through the network to obtain\n",
        "  the outputs of the intermediate layers. \n",
        "  \n",
        "  Arguments:\n",
        "    model: The model that we are using.\n",
        "    content_path: The path to the content image.\n",
        "    style_path: The path to the style image\n",
        "    \n",
        "  Returns:\n",
        "    returns the style features and the content features. \n",
        "  \"\"\"\n",
        "  # 이미지 적재 \n",
        "  content_image = load_and_process_img(content_path)\n",
        "  style_image = load_and_process_img(style_path)\n",
        "  \n",
        "  # 콘텐츠와 스타일 특성 배치 계산\n",
        "  style_outputs = model(style_image)\n",
        "  content_outputs = model(content_image)\n",
        "  \n",
        "  \n",
        "  # 콘텐츠와 스타일 표현 얻기\n",
        "  style_features = [style_layer[0] for style_layer in style_outputs[:num_style_layers]]\n",
        "  content_features = [content_layer[0] for content_layer in content_outputs[num_style_layers:]]\n",
        "  return style_features, content_features"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "3DopXw7-lFHa"
      },
      "cell_type": "markdown",
      "source": [
        "### 손실과 그래디언트 계산하기\n",
        "그래디언트를 계산하기위해서 [**tf.GradientTape**](https://www.tensorflow.org/programmers_guide/eager#computing_gradients)를 사용하였습니다. 이는 나중에 그래디언트 계산 과정을 추적하여 사용할 수 있는 automatic differentiation 기능을 활용할 수 있습니다. 이것은 정방향 전파동안의 명령들을 기록하여 역전파시 입력 이미지와 관련하여 손실 함수의 그래디언트를 계산할 수 있게 합니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "oVDhSo8iJunf",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def compute_loss(model, loss_weights, init_image, gram_style_features, content_features):\n",
        "  \"\"\"This function will compute the loss total loss.\n",
        "  \n",
        "  Arguments:\n",
        "    model: The model that will give us access to the intermediate layers\n",
        "    loss_weights: The weights of each contribution of each loss function. \n",
        "      (style weight, content weight, and total variation weight)\n",
        "    init_image: Our initial base image. This image is what we are updating with \n",
        "      our optimization process. We apply the gradients wrt the loss we are \n",
        "      calculating to this image.\n",
        "    gram_style_features: Precomputed gram matrices corresponding to the \n",
        "      defined style layers of interest.\n",
        "    content_features: Precomputed outputs from defined content layers of \n",
        "      interest.\n",
        "      \n",
        "  Returns:\n",
        "    returns the total loss, style loss, content loss, and total variational loss\n",
        "  \"\"\"\n",
        "  style_weight, content_weight = loss_weights\n",
        "  \n",
        "  # 초기 이미지를 모델에 제공합니다. 이것은 우리가 원하는 층에서 콘텐츠와 스타일 표현을 제공합니다.\n",
        "  # 즉시 실행을 사용하고 있기 때문에 모델은 다른 함수와 같이 호출 가능합니다.\n",
        "  model_outputs = model(init_image)\n",
        "  \n",
        "  style_output_features = model_outputs[:num_style_layers]\n",
        "  content_output_features = model_outputs[num_style_layers:]\n",
        "  \n",
        "  style_score = 0\n",
        "  content_score = 0\n",
        "\n",
        "  # 모든층들에서 스타일 손실을 합산합니다.\n",
        "  # 각 분산된 손실층에 가중치를 적용합니다.\n",
        "  weight_per_style_layer = 1.0 / float(num_style_layers)\n",
        "  for target_style, comb_style in zip(gram_style_features, style_output_features):\n",
        "    style_score += weight_per_style_layer * get_style_loss(comb_style[0], target_style)\n",
        "    \n",
        "  # 모든층들에서 콘텐츠 손실을 합산합니다.\n",
        "  weight_per_content_layer = 1.0 / float(num_content_layers)\n",
        "  for target_content, comb_content in zip(content_features, content_output_features):\n",
        "    content_score += weight_per_content_layer* get_content_loss(comb_content[0], target_content)\n",
        "  \n",
        "  style_score *= style_weight\n",
        "  content_score *= content_weight\n",
        "\n",
        "  # 총 손실을 구합니다.\n",
        "  loss = style_score + content_score \n",
        "  return loss, style_score, content_score"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "r5XTvbP6nJQa"
      },
      "cell_type": "markdown",
      "source": [
        "이렇게 하면 아래와 같이 그래디언트의 계산은 쉽습니다. "
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "fwzYeOqOUH9_",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def compute_grads(cfg):\n",
        "  with tf.GradientTape() as tape: \n",
        "    all_loss = compute_loss(**cfg)\n",
        "  # 입력 이미지에 관한 그래디언트 계산\n",
        "  total_loss = all_loss[0]\n",
        "  return tape.gradient(total_loss, cfg['init_image']), all_loss"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "T9yKu2PLlBIE"
      },
      "cell_type": "markdown",
      "source": [
        "### 루프 최적화"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "pj_enNo6tACQ",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "import IPython.display\n",
        "\n",
        "def run_style_transfer(content_path, \n",
        "                       style_path,\n",
        "                       num_iterations=1000,\n",
        "                       content_weight=1e3, \n",
        "                       style_weight=1e-2): \n",
        "  # 모델의 어떤층을 훈련시킬 필요가 없기 때문에 trainable에 False 값을 할당합니다.\n",
        "  model = get_model() \n",
        "  for layer in model.layers:\n",
        "    layer.trainable = False\n",
        "  \n",
        "  # 특정 중간층에서 스타일과 콘텐츠 표현을 얻습니다.\n",
        "  style_features, content_features = get_feature_representations(model, content_path, style_path)\n",
        "  gram_style_features = [gram_matrix(style_feature) for style_feature in style_features]\n",
        "  \n",
        "  # 초기 이미지 설정\n",
        "  init_image = load_and_process_img(content_path)\n",
        "  init_image = tfe.Variable(init_image, dtype=tf.float32)\n",
        "  # 옵티마이저 생성\n",
        "  opt = tf.train.AdamOptimizer(learning_rate=5, beta1=0.99, epsilon=1e-1)\n",
        "\n",
        "  # 중간 이미지 표시\n",
        "  iter_count = 1\n",
        "  \n",
        "  # 최적의 결과 저장\n",
        "  best_loss, best_img = float('inf'), None\n",
        "  \n",
        "  # 최적의 설정 만들기 \n",
        "  loss_weights = (style_weight, content_weight)\n",
        "  cfg = {\n",
        "      'model': model,\n",
        "      'loss_weights': loss_weights,\n",
        "      'init_image': init_image,\n",
        "      'gram_style_features': gram_style_features,\n",
        "      'content_features': content_features\n",
        "  }\n",
        "    \n",
        "  # 화면 표시\n",
        "  num_rows = 2\n",
        "  num_cols = 5\n",
        "  display_interval = num_iterations/(num_rows*num_cols)\n",
        "  start_time = time.time()\n",
        "  global_start = time.time()\n",
        "  \n",
        "  norm_means = np.array([103.939, 116.779, 123.68])\n",
        "  min_vals = -norm_means\n",
        "  max_vals = 255 - norm_means   \n",
        "  \n",
        "  imgs = []\n",
        "  for i in range(num_iterations):\n",
        "    grads, all_loss = compute_grads(cfg)\n",
        "    loss, style_score, content_score = all_loss\n",
        "    opt.apply_gradients([(grads, init_image)])\n",
        "    clipped = tf.clip_by_value(init_image, min_vals, max_vals)\n",
        "    init_image.assign(clipped)\n",
        "    end_time = time.time() \n",
        "    \n",
        "    if loss < best_loss:\n",
        "      # 총 손실에서 최적의 손실과 이미지 업데이트\n",
        "      best_loss = loss\n",
        "      best_img = deprocess_img(init_image.numpy())\n",
        "\n",
        "    if i % display_interval== 0:\n",
        "      start_time = time.time()\n",
        "      \n",
        "      # numpy array 구성을 위한 .numpy() 메소드 사용\n",
        "      plot_img = init_image.numpy()\n",
        "      plot_img = deprocess_img(plot_img)\n",
        "      imgs.append(plot_img)\n",
        "      IPython.display.clear_output(wait=True)\n",
        "      IPython.display.display_png(Image.fromarray(plot_img))\n",
        "      print('Iteration: {}'.format(i))        \n",
        "      print('Total loss: {:.4e}, ' \n",
        "            'style loss: {:.4e}, '\n",
        "            'content loss: {:.4e}, '\n",
        "            'time: {:.4f}s'.format(loss, style_score, content_score, time.time() - start_time))\n",
        "  print('Total time: {:.4f}s'.format(time.time() - global_start))\n",
        "  IPython.display.clear_output(wait=True)\n",
        "  plt.figure(figsize=(14,4))\n",
        "  for i,img in enumerate(imgs):\n",
        "      plt.subplot(num_rows,num_cols,i+1)\n",
        "      plt.imshow(img)\n",
        "      plt.xticks([])\n",
        "      plt.yticks([])\n",
        "      \n",
        "  return best_img, best_loss "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "vSVMx4burydi",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "best, best_loss = run_style_transfer(content_path, \n",
        "                                     style_path, num_iterations=1000)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "dzJTObpsO3TZ",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "Image.fromarray(best)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "dCXQ9vSnQbDy"
      },
      "cell_type": "markdown",
      "source": [
        "Colaboratory에서 이미지를 다운로드하려면 다음 코드의 주석처리를 제거하십시오."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "SSH6OpyyQn7w",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "#from google.colab import files\n",
        "#files.download('wave_turtle.png')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "LwiZfCW0AZwt"
      },
      "cell_type": "markdown",
      "source": [
        "## 출력 시각화\n",
        "적용되었던 처리 과정을 제거하기 위해 출력 이미지의 역처리 과정을 수행합니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "lqTQN1PjulV9",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "def show_results(best_img, content_path, style_path, show_large_final=True):\n",
        "  plt.figure(figsize=(10, 5))\n",
        "  content = load_img(content_path) \n",
        "  style = load_img(style_path)\n",
        "\n",
        "  plt.subplot(1, 2, 1)\n",
        "  imshow(content, 'Content Image')\n",
        "\n",
        "  plt.subplot(1, 2, 2)\n",
        "  imshow(style, 'Style Image')\n",
        "\n",
        "  if show_large_final: \n",
        "    plt.figure(figsize=(10, 10))\n",
        "\n",
        "    plt.imshow(best_img)\n",
        "    plt.title('Output Image')\n",
        "    plt.show()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "i6d6O50Yvs6a",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "show_results(best, content_path, style_path)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "tyGMmWh2Pss8"
      },
      "cell_type": "markdown",
      "source": [
        "## 다른 이미지 시도\n",
        "\n",
        "튀빙겐(Tuebingen) 이미지 \n",
        "\n",
        "Photo By: <NAME> [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC BY 3.0  (https://creativecommons.org/licenses/by/3.0)], from Wikimedia Commons"
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "x2TePU39k9lb"
      },
      "cell_type": "markdown",
      "source": [
        "### 별이 빛나는 밤(Starry night) + 튀빙겐(Tuebingen)"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "ES9dC6ZyJBD2",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "best_starry_night, best_loss = run_style_transfer('/tmp/nst/Tuebingen_Neckarfront.jpg',\n",
        "                                                  '/tmp/nst/1024px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "X8w8WLkKvzXu",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "show_results(best_starry_night, '/tmp/nst/Tuebingen_Neckarfront.jpg',\n",
        "             '/tmp/nst/1024px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "QcXwvViek4Br"
      },
      "cell_type": "markdown",
      "source": [
        "### 창조의 기둥(Pillars of Creation) + 튀빙겐(Tuebingen)"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "vJ3u2U-gGmgP",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "best_poc_tubingen, best_loss = run_style_transfer('/tmp/nst/Tuebingen_Neckarfront.jpg', \n",
        "                                                  '/tmp/nst/Pillars_of_creation_2014_HST_WFC3-UVIS_full-res_denoised.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "pQUq3KxpGv2O",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "show_results(best_poc_tubingen, \n",
        "             '/tmp/nst/Tuebingen_Neckarfront.jpg',\n",
        "             '/tmp/nst/Pillars_of_creation_2014_HST_WFC3-UVIS_full-res_denoised.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "bTZdTOdW3s8H"
      },
      "cell_type": "markdown",
      "source": [
        "### 칸딘스키(Kandinsky) Composition #7 + 튀빙겐(Tuebingen)"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "bt9mbQfl7exl",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "best_kandinsky_tubingen, best_loss = run_style_transfer('/tmp/nst/Tuebingen_Neckarfront.jpg', \n",
        "                                                  '/tmp/nst/Vassily_Kandinsky,_1913_-_Composition_7.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "Qnz8HeXSXg6P",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "show_results(best_kandinsky_tubingen, \n",
        "             '/tmp/nst/Tuebingen_Neckarfront.jpg',\n",
        "             '/tmp/nst/Vassily_Kandinsky,_1913_-_Composition_7.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "cg68lW2A3s8N"
      },
      "cell_type": "markdown",
      "source": [
        "### 창조의 기둥(Pillars of Creation) + 바다 거북(Sea Turtle)"
      ]
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "dl0DUot_bFST",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "best_poc_turtle, best_loss = run_style_transfer('/tmp/nst/Green_Sea_Turtle_grazing_seagrass.jpg', \n",
        "                                                  '/tmp/nst/Pillars_of_creation_2014_HST_WFC3-UVIS_full-res_denoised.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "code",
        "id": "UzJfE0I1bQn8",
        "colab": {}
      },
      "cell_type": "code",
      "source": [
        "show_results(best_poc_turtle, \n",
        "             '/tmp/nst/Green_Sea_Turtle_grazing_seagrass.jpg',\n",
        "             '/tmp/nst/Pillars_of_creation_2014_HST_WFC3-UVIS_full-res_denoised.jpg')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "sElaeNX-4Vnc"
      },
      "cell_type": "markdown",
      "source": [
        "## 주요 요점\n",
        "\n",
        "### 우리가 다룬 내용:\n",
        "* 몇가지의 다른 손실(loss)함수를 정의하였고, 손실을 최소화하기 위한 입력 이미지 변형을 위해 역전파(backpropagation)을 사용하였습니다.\n",
        "  * 이를 위해서 우리는 사전학습된 모델에 적재해야 했으며, 이 모델의 학습된 특성맵을 이미지의 콘텐츠와 스타일 표현을 표현하기위해 사용했습니다.\n",
        "    * 메인 손실(loss) 함수는 표현 차이(different representations)의 관점에서 주로 거리를 계산하는 것이었습니다. \n",
        "* 사용자 지정 모델과 **즉시 실행(eager execution)**을 사용하여 실행하였습니다.\n",
        "  * Functional API를 사용하여 사용자 지정 모델을 빌드했습니다.\n",
        "  * 즉시 실행은 파이썬 자연제어흐름을 사용하여 `Tensor`와 함께 동적인 수행을 제공했습니다. \n",
        "  * `Tensor`를 직접적으로 제어했습니다. 이는 디버깅과 작업 쉽게 만들어줍니다.  \n",
        "* **tf.gradient**를 사용한 옵티마이저(optimizer) 업데이트 법칙을 적용하여 반복적으로 이미지를 업데이트했습니다. 옵티마이저는 주어진 입력 이미지의 손실과 관련하여 최적화됩니다."
      ]
    },
    {
      "metadata": {
        "colab_type": "text",
        "id": "U-y02GWonqnD"
      },
      "cell_type": "markdown",
      "source": [
        "\n",
        "**[Image of Tuebingen](https://commons.wikimedia.org/wiki/File:Tuebingen_Neckarfront.jpg)** \n",
        "Photo By: <NAME> [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC BY 3.0  (https://creativecommons.org/licenses/by/3.0)], from Wikimedia Commons\n",
        "\n",
        "**[Image of Green Sea Turtle](https://commons.wikimedia.org/wiki/File:Green_Sea_Turtle_grazing_seagrass.jpg)**\n",
        "By P.Lindgren [CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0)], from Wikimedia Commons\n",
        "\n"
      ]
    }
  ]
}
{'fraction_non_alphanumeric': 0.1647527228455793, 'fraction_numerical': 0.011077841027497176, 'mean_word_length': 1.8998441525396774, 'pattern_counts': {'":': 916, '<': 48, '<?xml version=': 0, '>': 46, 'https://': 76, 'lorem ipsum': 0, 'www.': 24, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '405137', 'n_tokens_mistral': 43720, 'n_tokens_neox': 48017, 'n_words': 7323}