This iPython notebook is an implementation of a popular paper (Gatys et al., 2015) that demonstrates how to use neural networks to transfer artistic style from one image onto another.
The implementation is slightly modified to use Densenet instead of VGG-Net and an additional regularization term is also added to the overall loss function.
The main objective of the algorithm is to merge two images, namely content image (C) and style image (S) to create a generated image (G) , which combines the content of the image C with the style of the image S.
For example, we can see the stylized image from the Taj Mahal (content image C), mixed with a painting by Van Gogh (style image S).
The algorithm uses neural representations which are obtained by using Convolution Neural Networks (CNN), which are most powerful in image processing tasks. A CNN consists of a number of convolutional and subsampling layers optionally followed by fully connected layers, where each layer can be understood as a collection of image filters, each of which extracts a certain feature from the input image also called as feature maps.
On training CNN for object recognition, along the processing hierarchy of the network, the feature maps represents more actual content of the image compared to its detailed pixel values. This can be visualized by reconstructing the image only from feature maps. This is called content reconstruction.
By including the feature correlations of multiple layers, we obtain a style representation of the input image, which captures its texture information but not the global arrangement. This representation captures general appearance in terms of colour and localised structures. This can be visualized by reconstructiing the image from feature maps showing texture information. This is called style reconstruction.
Style transfer is posed as an optimization problem: $$g^* = \operatorname*{argmin}_g (\alpha L_{content}(c,g) + \beta L_{style}(s,g))$$
Above means that we are looking for an image g that differs very little in terms of content from image c, while simultaneously differing as little as possible in terms of style from image s.
Content loss at layer l is calculated as follows with respect to content image (c) and generated image (g): $$L_{content}(c,g,l) = \frac12 \sum_{i,j} (F^l_{ij} - P^l_{ij})^2$$
Style loss is slightly tricky and is calculated as follows with respect to style image (s) and generated image (g):
from __future__ import print_function
import time
from PIL import Image
import numpy as np
from keras import backend
from keras.models import Model
from keras.applications.densenet import DenseNet121, preprocess_input
from scipy.optimize import fmin_l_bfgs_b
from scipy.misc import imsave
import requests
from io import BytesIO
from cStringIO import StringIO
import IPython.display
Using TensorFlow backend.
224 x 224
size image to choose appropriate layers for content and style representations.height = 244
width = 244
img_url = "https://upload.wikimedia.org/wikipedia/commons/c/c8/Taj_Mahal_in_March_2004.jpg"
response = requests.get(img_url)
content_image = Image.open(BytesIO(response.content))
content_image = content_image.resize((width, height))
content_image
img_url = "http://1.bp.blogspot.com/-yqh8zFJh_OU/UcDJKe-EYaI/AAAAAAAAS2I/Qb3bm2zwQu0/s1600/%5BIMG+SRC=%22URL%22+ALT=picasso+painting,+romancing+picasso+painting,+colorful+oil+painting,k+madison+moore+artist%5D++copy.jpg"
response = requests.get(img_url)
style_image = Image.open(BytesIO(response.content))
style_image = style_image.resize((width, height))
style_image
Then, we convert these images into a form suitable for numerical processing. In particular, we add another dimension (beyond the classic height x width x 3 dimensions) so that we can later concatenate the representations of these two images into a common data structure.
content_array = np.asarray(content_image, dtype='float32')
content_array = np.expand_dims(content_array, axis=0)
print(content_array.shape)
style_array = np.asarray(style_image, dtype='float32')
style_array = np.expand_dims(style_array, axis=0)
print(style_array.shape)
(1, 244, 244, 3) (1, 244, 244, 3)
Now we're ready to use these arrays to define variables in Keras' backend (the TensorFlow graph). We also introduce a placeholder variable to store the combination image (generated image) that retains the content of the content image while incorporating the style of the style image.
content_image = backend.variable(content_array)
style_image = backend.variable(style_array)
combination_image = backend.placeholder((1, height, width, 3))
Finally, we concatenate all this image data into a single tensor that's suitable for processing by Keras' DenseNet121 model.
input_tensor = backend.concatenate([content_image,
style_image,
combination_image], axis=0)
The core idea introduced by Gatys et al. (2015) is that convolutional neural networks (CNNs) pre-trained for image classification already know how to encode perceptual and semantic information about images. We're going to follow their idea, and use the feature spaces provided by one such model to independently work with content and style of images.
The original paper uses the 19 layer VGG network model from Simonyan and Zisserman (2015), but we're going to instead use DenseNet121 model.
Also, since we're not interested in the classification problem, we don't need the fully connected layers or the final softmax classifier. We only need the part of the model before "Classification Layer" from below architectures:
As seen above, for DenseNet-121 there are 121 layers (1 right after Input Layer, 116 from Dense Block, 3 from Transition Layer & 1 FC Layer). Note: Pooling is not considered as a layer.
It is trivial for us to get access to this truncated model because Keras comes with a set of pretrained models, including the DenseNet121 model we're interested in. Note that by setting include_top=False
in the code below, we don't include any of the fully connected layers.
model = DenseNet121(input_tensor=input_tensor, weights='imagenet',
include_top=False)
As it is clear from the table above, the model we're working with has a lot of layers. Keras has its own names for these layers. Let's make a list of these names so that we can easily refer to individual layers later.
layers = dict([(layer.name, layer.output) for layer in model.layers])
layers
{'bn': <tf.Tensor 'bn/cond/Merge:0' shape=(3, 7, 7, 1024) dtype=float32>, 'conv1/bn': <tf.Tensor 'conv1/bn/cond/Merge:0' shape=(3, 122, 122, 64) dtype=float32>, 'conv1/conv': <tf.Tensor 'conv1/conv/convolution:0' shape=(3, 122, 122, 64) dtype=float32>, 'conv1/relu': <tf.Tensor 'conv1/relu/Relu:0' shape=(3, 122, 122, 64) dtype=float32>, 'conv2_block1_0_bn': <tf.Tensor 'conv2_block1_0_bn/cond/Merge:0' shape=(3, 61, 61, 64) dtype=float32>, 'conv2_block1_0_relu': <tf.Tensor 'conv2_block1_0_relu/Relu:0' shape=(3, 61, 61, 64) dtype=float32>, 'conv2_block1_1_bn': <tf.Tensor 'conv2_block1_1_bn/cond/Merge:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block1_1_conv': <tf.Tensor 'conv2_block1_1_conv/convolution:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block1_1_relu': <tf.Tensor 'conv2_block1_1_relu/Relu:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block1_2_conv': <tf.Tensor 'conv2_block1_2_conv/convolution:0' shape=(3, 61, 61, 32) dtype=float32>, 'conv2_block1_concat': <tf.Tensor 'conv2_block1_concat/concat:0' shape=(3, 61, 61, 96) dtype=float32>, 'conv2_block2_0_bn': <tf.Tensor 'conv2_block2_0_bn/cond/Merge:0' shape=(3, 61, 61, 96) dtype=float32>, 'conv2_block2_0_relu': <tf.Tensor 'conv2_block2_0_relu/Relu:0' shape=(3, 61, 61, 96) dtype=float32>, 'conv2_block2_1_bn': <tf.Tensor 'conv2_block2_1_bn/cond/Merge:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block2_1_conv': <tf.Tensor 'conv2_block2_1_conv/convolution:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block2_1_relu': <tf.Tensor 'conv2_block2_1_relu/Relu:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block2_2_conv': <tf.Tensor 'conv2_block2_2_conv/convolution:0' shape=(3, 61, 61, 32) dtype=float32>, 'conv2_block2_concat': <tf.Tensor 'conv2_block2_concat/concat:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block3_0_bn': <tf.Tensor 'conv2_block3_0_bn/cond/Merge:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block3_0_relu': <tf.Tensor 'conv2_block3_0_relu/Relu:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block3_1_bn': <tf.Tensor 'conv2_block3_1_bn/cond/Merge:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block3_1_conv': <tf.Tensor 'conv2_block3_1_conv/convolution:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block3_1_relu': <tf.Tensor 'conv2_block3_1_relu/Relu:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block3_2_conv': <tf.Tensor 'conv2_block3_2_conv/convolution:0' shape=(3, 61, 61, 32) dtype=float32>, 'conv2_block3_concat': <tf.Tensor 'conv2_block3_concat/concat:0' shape=(3, 61, 61, 160) dtype=float32>, 'conv2_block4_0_bn': <tf.Tensor 'conv2_block4_0_bn/cond/Merge:0' shape=(3, 61, 61, 160) dtype=float32>, 'conv2_block4_0_relu': <tf.Tensor 'conv2_block4_0_relu/Relu:0' shape=(3, 61, 61, 160) dtype=float32>, 'conv2_block4_1_bn': <tf.Tensor 'conv2_block4_1_bn/cond/Merge:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block4_1_conv': <tf.Tensor 'conv2_block4_1_conv/convolution:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block4_1_relu': <tf.Tensor 'conv2_block4_1_relu/Relu:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block4_2_conv': <tf.Tensor 'conv2_block4_2_conv/convolution:0' shape=(3, 61, 61, 32) dtype=float32>, 'conv2_block4_concat': <tf.Tensor 'conv2_block4_concat/concat:0' shape=(3, 61, 61, 192) dtype=float32>, 'conv2_block5_0_bn': <tf.Tensor 'conv2_block5_0_bn/cond/Merge:0' shape=(3, 61, 61, 192) dtype=float32>, 'conv2_block5_0_relu': <tf.Tensor 'conv2_block5_0_relu/Relu:0' shape=(3, 61, 61, 192) dtype=float32>, 'conv2_block5_1_bn': <tf.Tensor 'conv2_block5_1_bn/cond/Merge:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block5_1_conv': <tf.Tensor 'conv2_block5_1_conv/convolution:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block5_1_relu': <tf.Tensor 'conv2_block5_1_relu/Relu:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block5_2_conv': <tf.Tensor 'conv2_block5_2_conv/convolution:0' shape=(3, 61, 61, 32) dtype=float32>, 'conv2_block5_concat': <tf.Tensor 'conv2_block5_concat/concat:0' shape=(3, 61, 61, 224) dtype=float32>, 'conv2_block6_0_bn': <tf.Tensor 'conv2_block6_0_bn/cond/Merge:0' shape=(3, 61, 61, 224) dtype=float32>, 'conv2_block6_0_relu': <tf.Tensor 'conv2_block6_0_relu/Relu:0' shape=(3, 61, 61, 224) dtype=float32>, 'conv2_block6_1_bn': <tf.Tensor 'conv2_block6_1_bn/cond/Merge:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block6_1_conv': <tf.Tensor 'conv2_block6_1_conv/convolution:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block6_1_relu': <tf.Tensor 'conv2_block6_1_relu/Relu:0' shape=(3, 61, 61, 128) dtype=float32>, 'conv2_block6_2_conv': <tf.Tensor 'conv2_block6_2_conv/convolution:0' shape=(3, 61, 61, 32) dtype=float32>, 'conv2_block6_concat': <tf.Tensor 'conv2_block6_concat/concat:0' shape=(3, 61, 61, 256) dtype=float32>, 'conv3_block10_0_bn': <tf.Tensor 'conv3_block10_0_bn/cond/Merge:0' shape=(3, 30, 30, 416) dtype=float32>, 'conv3_block10_0_relu': <tf.Tensor 'conv3_block10_0_relu/Relu:0' shape=(3, 30, 30, 416) dtype=float32>, 'conv3_block10_1_bn': <tf.Tensor 'conv3_block10_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block10_1_conv': <tf.Tensor 'conv3_block10_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block10_1_relu': <tf.Tensor 'conv3_block10_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block10_2_conv': <tf.Tensor 'conv3_block10_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block10_concat': <tf.Tensor 'conv3_block10_concat/concat:0' shape=(3, 30, 30, 448) dtype=float32>, 'conv3_block11_0_bn': <tf.Tensor 'conv3_block11_0_bn/cond/Merge:0' shape=(3, 30, 30, 448) dtype=float32>, 'conv3_block11_0_relu': <tf.Tensor 'conv3_block11_0_relu/Relu:0' shape=(3, 30, 30, 448) dtype=float32>, 'conv3_block11_1_bn': <tf.Tensor 'conv3_block11_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block11_1_conv': <tf.Tensor 'conv3_block11_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block11_1_relu': <tf.Tensor 'conv3_block11_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block11_2_conv': <tf.Tensor 'conv3_block11_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block11_concat': <tf.Tensor 'conv3_block11_concat/concat:0' shape=(3, 30, 30, 480) dtype=float32>, 'conv3_block12_0_bn': <tf.Tensor 'conv3_block12_0_bn/cond/Merge:0' shape=(3, 30, 30, 480) dtype=float32>, 'conv3_block12_0_relu': <tf.Tensor 'conv3_block12_0_relu/Relu:0' shape=(3, 30, 30, 480) dtype=float32>, 'conv3_block12_1_bn': <tf.Tensor 'conv3_block12_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block12_1_conv': <tf.Tensor 'conv3_block12_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block12_1_relu': <tf.Tensor 'conv3_block12_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block12_2_conv': <tf.Tensor 'conv3_block12_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block12_concat': <tf.Tensor 'conv3_block12_concat/concat:0' shape=(3, 30, 30, 512) dtype=float32>, 'conv3_block1_0_bn': <tf.Tensor 'conv3_block1_0_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block1_0_relu': <tf.Tensor 'conv3_block1_0_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block1_1_bn': <tf.Tensor 'conv3_block1_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block1_1_conv': <tf.Tensor 'conv3_block1_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block1_1_relu': <tf.Tensor 'conv3_block1_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block1_2_conv': <tf.Tensor 'conv3_block1_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block1_concat': <tf.Tensor 'conv3_block1_concat/concat:0' shape=(3, 30, 30, 160) dtype=float32>, 'conv3_block2_0_bn': <tf.Tensor 'conv3_block2_0_bn/cond/Merge:0' shape=(3, 30, 30, 160) dtype=float32>, 'conv3_block2_0_relu': <tf.Tensor 'conv3_block2_0_relu/Relu:0' shape=(3, 30, 30, 160) dtype=float32>, 'conv3_block2_1_bn': <tf.Tensor 'conv3_block2_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block2_1_conv': <tf.Tensor 'conv3_block2_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block2_1_relu': <tf.Tensor 'conv3_block2_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block2_2_conv': <tf.Tensor 'conv3_block2_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block2_concat': <tf.Tensor 'conv3_block2_concat/concat:0' shape=(3, 30, 30, 192) dtype=float32>, 'conv3_block3_0_bn': <tf.Tensor 'conv3_block3_0_bn/cond/Merge:0' shape=(3, 30, 30, 192) dtype=float32>, 'conv3_block3_0_relu': <tf.Tensor 'conv3_block3_0_relu/Relu:0' shape=(3, 30, 30, 192) dtype=float32>, 'conv3_block3_1_bn': <tf.Tensor 'conv3_block3_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block3_1_conv': <tf.Tensor 'conv3_block3_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block3_1_relu': <tf.Tensor 'conv3_block3_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block3_2_conv': <tf.Tensor 'conv3_block3_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block3_concat': <tf.Tensor 'conv3_block3_concat/concat:0' shape=(3, 30, 30, 224) dtype=float32>, 'conv3_block4_0_bn': <tf.Tensor 'conv3_block4_0_bn/cond/Merge:0' shape=(3, 30, 30, 224) dtype=float32>, 'conv3_block4_0_relu': <tf.Tensor 'conv3_block4_0_relu/Relu:0' shape=(3, 30, 30, 224) dtype=float32>, 'conv3_block4_1_bn': <tf.Tensor 'conv3_block4_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block4_1_conv': <tf.Tensor 'conv3_block4_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block4_1_relu': <tf.Tensor 'conv3_block4_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block4_2_conv': <tf.Tensor 'conv3_block4_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block4_concat': <tf.Tensor 'conv3_block4_concat/concat:0' shape=(3, 30, 30, 256) dtype=float32>, 'conv3_block5_0_bn': <tf.Tensor 'conv3_block5_0_bn/cond/Merge:0' shape=(3, 30, 30, 256) dtype=float32>, 'conv3_block5_0_relu': <tf.Tensor 'conv3_block5_0_relu/Relu:0' shape=(3, 30, 30, 256) dtype=float32>, 'conv3_block5_1_bn': <tf.Tensor 'conv3_block5_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block5_1_conv': <tf.Tensor 'conv3_block5_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block5_1_relu': <tf.Tensor 'conv3_block5_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block5_2_conv': <tf.Tensor 'conv3_block5_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block5_concat': <tf.Tensor 'conv3_block5_concat/concat:0' shape=(3, 30, 30, 288) dtype=float32>, 'conv3_block6_0_bn': <tf.Tensor 'conv3_block6_0_bn/cond/Merge:0' shape=(3, 30, 30, 288) dtype=float32>, 'conv3_block6_0_relu': <tf.Tensor 'conv3_block6_0_relu/Relu:0' shape=(3, 30, 30, 288) dtype=float32>, 'conv3_block6_1_bn': <tf.Tensor 'conv3_block6_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block6_1_conv': <tf.Tensor 'conv3_block6_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block6_1_relu': <tf.Tensor 'conv3_block6_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block6_2_conv': <tf.Tensor 'conv3_block6_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block6_concat': <tf.Tensor 'conv3_block6_concat/concat:0' shape=(3, 30, 30, 320) dtype=float32>, 'conv3_block7_0_bn': <tf.Tensor 'conv3_block7_0_bn/cond/Merge:0' shape=(3, 30, 30, 320) dtype=float32>, 'conv3_block7_0_relu': <tf.Tensor 'conv3_block7_0_relu/Relu:0' shape=(3, 30, 30, 320) dtype=float32>, 'conv3_block7_1_bn': <tf.Tensor 'conv3_block7_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block7_1_conv': <tf.Tensor 'conv3_block7_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block7_1_relu': <tf.Tensor 'conv3_block7_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block7_2_conv': <tf.Tensor 'conv3_block7_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block7_concat': <tf.Tensor 'conv3_block7_concat/concat:0' shape=(3, 30, 30, 352) dtype=float32>, 'conv3_block8_0_bn': <tf.Tensor 'conv3_block8_0_bn/cond/Merge:0' shape=(3, 30, 30, 352) dtype=float32>, 'conv3_block8_0_relu': <tf.Tensor 'conv3_block8_0_relu/Relu:0' shape=(3, 30, 30, 352) dtype=float32>, 'conv3_block8_1_bn': <tf.Tensor 'conv3_block8_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block8_1_conv': <tf.Tensor 'conv3_block8_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block8_1_relu': <tf.Tensor 'conv3_block8_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block8_2_conv': <tf.Tensor 'conv3_block8_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block8_concat': <tf.Tensor 'conv3_block8_concat/concat:0' shape=(3, 30, 30, 384) dtype=float32>, 'conv3_block9_0_bn': <tf.Tensor 'conv3_block9_0_bn/cond/Merge:0' shape=(3, 30, 30, 384) dtype=float32>, 'conv3_block9_0_relu': <tf.Tensor 'conv3_block9_0_relu/Relu:0' shape=(3, 30, 30, 384) dtype=float32>, 'conv3_block9_1_bn': <tf.Tensor 'conv3_block9_1_bn/cond/Merge:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block9_1_conv': <tf.Tensor 'conv3_block9_1_conv/convolution:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block9_1_relu': <tf.Tensor 'conv3_block9_1_relu/Relu:0' shape=(3, 30, 30, 128) dtype=float32>, 'conv3_block9_2_conv': <tf.Tensor 'conv3_block9_2_conv/convolution:0' shape=(3, 30, 30, 32) dtype=float32>, 'conv3_block9_concat': <tf.Tensor 'conv3_block9_concat/concat:0' shape=(3, 30, 30, 416) dtype=float32>, 'conv4_block10_0_bn': <tf.Tensor 'conv4_block10_0_bn/cond/Merge:0' shape=(3, 15, 15, 544) dtype=float32>, 'conv4_block10_0_relu': <tf.Tensor 'conv4_block10_0_relu/Relu:0' shape=(3, 15, 15, 544) dtype=float32>, 'conv4_block10_1_bn': <tf.Tensor 'conv4_block10_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block10_1_conv': <tf.Tensor 'conv4_block10_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block10_1_relu': <tf.Tensor 'conv4_block10_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block10_2_conv': <tf.Tensor 'conv4_block10_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block10_concat': <tf.Tensor 'conv4_block10_concat/concat:0' shape=(3, 15, 15, 576) dtype=float32>, 'conv4_block11_0_bn': <tf.Tensor 'conv4_block11_0_bn/cond/Merge:0' shape=(3, 15, 15, 576) dtype=float32>, 'conv4_block11_0_relu': <tf.Tensor 'conv4_block11_0_relu/Relu:0' shape=(3, 15, 15, 576) dtype=float32>, 'conv4_block11_1_bn': <tf.Tensor 'conv4_block11_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block11_1_conv': <tf.Tensor 'conv4_block11_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block11_1_relu': <tf.Tensor 'conv4_block11_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block11_2_conv': <tf.Tensor 'conv4_block11_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block11_concat': <tf.Tensor 'conv4_block11_concat/concat:0' shape=(3, 15, 15, 608) dtype=float32>, 'conv4_block12_0_bn': <tf.Tensor 'conv4_block12_0_bn/cond/Merge:0' shape=(3, 15, 15, 608) dtype=float32>, 'conv4_block12_0_relu': <tf.Tensor 'conv4_block12_0_relu/Relu:0' shape=(3, 15, 15, 608) dtype=float32>, 'conv4_block12_1_bn': <tf.Tensor 'conv4_block12_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block12_1_conv': <tf.Tensor 'conv4_block12_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block12_1_relu': <tf.Tensor 'conv4_block12_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block12_2_conv': <tf.Tensor 'conv4_block12_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block12_concat': <tf.Tensor 'conv4_block12_concat/concat:0' shape=(3, 15, 15, 640) dtype=float32>, 'conv4_block13_0_bn': <tf.Tensor 'conv4_block13_0_bn/cond/Merge:0' shape=(3, 15, 15, 640) dtype=float32>, 'conv4_block13_0_relu': <tf.Tensor 'conv4_block13_0_relu/Relu:0' shape=(3, 15, 15, 640) dtype=float32>, 'conv4_block13_1_bn': <tf.Tensor 'conv4_block13_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block13_1_conv': <tf.Tensor 'conv4_block13_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block13_1_relu': <tf.Tensor 'conv4_block13_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block13_2_conv': <tf.Tensor 'conv4_block13_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block13_concat': <tf.Tensor 'conv4_block13_concat/concat:0' shape=(3, 15, 15, 672) dtype=float32>, 'conv4_block14_0_bn': <tf.Tensor 'conv4_block14_0_bn/cond/Merge:0' shape=(3, 15, 15, 672) dtype=float32>, 'conv4_block14_0_relu': <tf.Tensor 'conv4_block14_0_relu/Relu:0' shape=(3, 15, 15, 672) dtype=float32>, 'conv4_block14_1_bn': <tf.Tensor 'conv4_block14_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block14_1_conv': <tf.Tensor 'conv4_block14_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block14_1_relu': <tf.Tensor 'conv4_block14_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block14_2_conv': <tf.Tensor 'conv4_block14_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block14_concat': <tf.Tensor 'conv4_block14_concat/concat:0' shape=(3, 15, 15, 704) dtype=float32>, 'conv4_block15_0_bn': <tf.Tensor 'conv4_block15_0_bn/cond/Merge:0' shape=(3, 15, 15, 704) dtype=float32>, 'conv4_block15_0_relu': <tf.Tensor 'conv4_block15_0_relu/Relu:0' shape=(3, 15, 15, 704) dtype=float32>, 'conv4_block15_1_bn': <tf.Tensor 'conv4_block15_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block15_1_conv': <tf.Tensor 'conv4_block15_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block15_1_relu': <tf.Tensor 'conv4_block15_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block15_2_conv': <tf.Tensor 'conv4_block15_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block15_concat': <tf.Tensor 'conv4_block15_concat/concat:0' shape=(3, 15, 15, 736) dtype=float32>, 'conv4_block16_0_bn': <tf.Tensor 'conv4_block16_0_bn/cond/Merge:0' shape=(3, 15, 15, 736) dtype=float32>, 'conv4_block16_0_relu': <tf.Tensor 'conv4_block16_0_relu/Relu:0' shape=(3, 15, 15, 736) dtype=float32>, 'conv4_block16_1_bn': <tf.Tensor 'conv4_block16_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block16_1_conv': <tf.Tensor 'conv4_block16_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block16_1_relu': <tf.Tensor 'conv4_block16_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block16_2_conv': <tf.Tensor 'conv4_block16_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block16_concat': <tf.Tensor 'conv4_block16_concat/concat:0' shape=(3, 15, 15, 768) dtype=float32>, 'conv4_block17_0_bn': <tf.Tensor 'conv4_block17_0_bn/cond/Merge:0' shape=(3, 15, 15, 768) dtype=float32>, 'conv4_block17_0_relu': <tf.Tensor 'conv4_block17_0_relu/Relu:0' shape=(3, 15, 15, 768) dtype=float32>, 'conv4_block17_1_bn': <tf.Tensor 'conv4_block17_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block17_1_conv': <tf.Tensor 'conv4_block17_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block17_1_relu': <tf.Tensor 'conv4_block17_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block17_2_conv': <tf.Tensor 'conv4_block17_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block17_concat': <tf.Tensor 'conv4_block17_concat/concat:0' shape=(3, 15, 15, 800) dtype=float32>, 'conv4_block18_0_bn': <tf.Tensor 'conv4_block18_0_bn/cond/Merge:0' shape=(3, 15, 15, 800) dtype=float32>, 'conv4_block18_0_relu': <tf.Tensor 'conv4_block18_0_relu/Relu:0' shape=(3, 15, 15, 800) dtype=float32>, 'conv4_block18_1_bn': <tf.Tensor 'conv4_block18_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block18_1_conv': <tf.Tensor 'conv4_block18_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block18_1_relu': <tf.Tensor 'conv4_block18_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block18_2_conv': <tf.Tensor 'conv4_block18_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block18_concat': <tf.Tensor 'conv4_block18_concat/concat:0' shape=(3, 15, 15, 832) dtype=float32>, 'conv4_block19_0_bn': <tf.Tensor 'conv4_block19_0_bn/cond/Merge:0' shape=(3, 15, 15, 832) dtype=float32>, 'conv4_block19_0_relu': <tf.Tensor 'conv4_block19_0_relu/Relu:0' shape=(3, 15, 15, 832) dtype=float32>, 'conv4_block19_1_bn': <tf.Tensor 'conv4_block19_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block19_1_conv': <tf.Tensor 'conv4_block19_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block19_1_relu': <tf.Tensor 'conv4_block19_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block19_2_conv': <tf.Tensor 'conv4_block19_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block19_concat': <tf.Tensor 'conv4_block19_concat/concat:0' shape=(3, 15, 15, 864) dtype=float32>, 'conv4_block1_0_bn': <tf.Tensor 'conv4_block1_0_bn/cond/Merge:0' shape=(3, 15, 15, 256) dtype=float32>, 'conv4_block1_0_relu': <tf.Tensor 'conv4_block1_0_relu/Relu:0' shape=(3, 15, 15, 256) dtype=float32>, 'conv4_block1_1_bn': <tf.Tensor 'conv4_block1_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block1_1_conv': <tf.Tensor 'conv4_block1_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block1_1_relu': <tf.Tensor 'conv4_block1_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block1_2_conv': <tf.Tensor 'conv4_block1_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block1_concat': <tf.Tensor 'conv4_block1_concat/concat:0' shape=(3, 15, 15, 288) dtype=float32>, 'conv4_block20_0_bn': <tf.Tensor 'conv4_block20_0_bn/cond/Merge:0' shape=(3, 15, 15, 864) dtype=float32>, 'conv4_block20_0_relu': <tf.Tensor 'conv4_block20_0_relu/Relu:0' shape=(3, 15, 15, 864) dtype=float32>, 'conv4_block20_1_bn': <tf.Tensor 'conv4_block20_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block20_1_conv': <tf.Tensor 'conv4_block20_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block20_1_relu': <tf.Tensor 'conv4_block20_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block20_2_conv': <tf.Tensor 'conv4_block20_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block20_concat': <tf.Tensor 'conv4_block20_concat/concat:0' shape=(3, 15, 15, 896) dtype=float32>, 'conv4_block21_0_bn': <tf.Tensor 'conv4_block21_0_bn/cond/Merge:0' shape=(3, 15, 15, 896) dtype=float32>, 'conv4_block21_0_relu': <tf.Tensor 'conv4_block21_0_relu/Relu:0' shape=(3, 15, 15, 896) dtype=float32>, 'conv4_block21_1_bn': <tf.Tensor 'conv4_block21_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block21_1_conv': <tf.Tensor 'conv4_block21_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block21_1_relu': <tf.Tensor 'conv4_block21_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block21_2_conv': <tf.Tensor 'conv4_block21_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block21_concat': <tf.Tensor 'conv4_block21_concat/concat:0' shape=(3, 15, 15, 928) dtype=float32>, 'conv4_block22_0_bn': <tf.Tensor 'conv4_block22_0_bn/cond/Merge:0' shape=(3, 15, 15, 928) dtype=float32>, 'conv4_block22_0_relu': <tf.Tensor 'conv4_block22_0_relu/Relu:0' shape=(3, 15, 15, 928) dtype=float32>, 'conv4_block22_1_bn': <tf.Tensor 'conv4_block22_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block22_1_conv': <tf.Tensor 'conv4_block22_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block22_1_relu': <tf.Tensor 'conv4_block22_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block22_2_conv': <tf.Tensor 'conv4_block22_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block22_concat': <tf.Tensor 'conv4_block22_concat/concat:0' shape=(3, 15, 15, 960) dtype=float32>, 'conv4_block23_0_bn': <tf.Tensor 'conv4_block23_0_bn/cond/Merge:0' shape=(3, 15, 15, 960) dtype=float32>, 'conv4_block23_0_relu': <tf.Tensor 'conv4_block23_0_relu/Relu:0' shape=(3, 15, 15, 960) dtype=float32>, 'conv4_block23_1_bn': <tf.Tensor 'conv4_block23_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block23_1_conv': <tf.Tensor 'conv4_block23_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block23_1_relu': <tf.Tensor 'conv4_block23_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block23_2_conv': <tf.Tensor 'conv4_block23_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block23_concat': <tf.Tensor 'conv4_block23_concat/concat:0' shape=(3, 15, 15, 992) dtype=float32>, 'conv4_block24_0_bn': <tf.Tensor 'conv4_block24_0_bn/cond/Merge:0' shape=(3, 15, 15, 992) dtype=float32>, 'conv4_block24_0_relu': <tf.Tensor 'conv4_block24_0_relu/Relu:0' shape=(3, 15, 15, 992) dtype=float32>, 'conv4_block24_1_bn': <tf.Tensor 'conv4_block24_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block24_1_conv': <tf.Tensor 'conv4_block24_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block24_1_relu': <tf.Tensor 'conv4_block24_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block24_2_conv': <tf.Tensor 'conv4_block24_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block24_concat': <tf.Tensor 'conv4_block24_concat/concat:0' shape=(3, 15, 15, 1024) dtype=float32>, 'conv4_block2_0_bn': <tf.Tensor 'conv4_block2_0_bn/cond/Merge:0' shape=(3, 15, 15, 288) dtype=float32>, 'conv4_block2_0_relu': <tf.Tensor 'conv4_block2_0_relu/Relu:0' shape=(3, 15, 15, 288) dtype=float32>, 'conv4_block2_1_bn': <tf.Tensor 'conv4_block2_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block2_1_conv': <tf.Tensor 'conv4_block2_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block2_1_relu': <tf.Tensor 'conv4_block2_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block2_2_conv': <tf.Tensor 'conv4_block2_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block2_concat': <tf.Tensor 'conv4_block2_concat/concat:0' shape=(3, 15, 15, 320) dtype=float32>, 'conv4_block3_0_bn': <tf.Tensor 'conv4_block3_0_bn/cond/Merge:0' shape=(3, 15, 15, 320) dtype=float32>, 'conv4_block3_0_relu': <tf.Tensor 'conv4_block3_0_relu/Relu:0' shape=(3, 15, 15, 320) dtype=float32>, 'conv4_block3_1_bn': <tf.Tensor 'conv4_block3_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block3_1_conv': <tf.Tensor 'conv4_block3_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block3_1_relu': <tf.Tensor 'conv4_block3_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block3_2_conv': <tf.Tensor 'conv4_block3_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block3_concat': <tf.Tensor 'conv4_block3_concat/concat:0' shape=(3, 15, 15, 352) dtype=float32>, 'conv4_block4_0_bn': <tf.Tensor 'conv4_block4_0_bn/cond/Merge:0' shape=(3, 15, 15, 352) dtype=float32>, 'conv4_block4_0_relu': <tf.Tensor 'conv4_block4_0_relu/Relu:0' shape=(3, 15, 15, 352) dtype=float32>, 'conv4_block4_1_bn': <tf.Tensor 'conv4_block4_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block4_1_conv': <tf.Tensor 'conv4_block4_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block4_1_relu': <tf.Tensor 'conv4_block4_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block4_2_conv': <tf.Tensor 'conv4_block4_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block4_concat': <tf.Tensor 'conv4_block4_concat/concat:0' shape=(3, 15, 15, 384) dtype=float32>, 'conv4_block5_0_bn': <tf.Tensor 'conv4_block5_0_bn/cond/Merge:0' shape=(3, 15, 15, 384) dtype=float32>, 'conv4_block5_0_relu': <tf.Tensor 'conv4_block5_0_relu/Relu:0' shape=(3, 15, 15, 384) dtype=float32>, 'conv4_block5_1_bn': <tf.Tensor 'conv4_block5_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block5_1_conv': <tf.Tensor 'conv4_block5_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block5_1_relu': <tf.Tensor 'conv4_block5_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block5_2_conv': <tf.Tensor 'conv4_block5_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block5_concat': <tf.Tensor 'conv4_block5_concat/concat:0' shape=(3, 15, 15, 416) dtype=float32>, 'conv4_block6_0_bn': <tf.Tensor 'conv4_block6_0_bn/cond/Merge:0' shape=(3, 15, 15, 416) dtype=float32>, 'conv4_block6_0_relu': <tf.Tensor 'conv4_block6_0_relu/Relu:0' shape=(3, 15, 15, 416) dtype=float32>, 'conv4_block6_1_bn': <tf.Tensor 'conv4_block6_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block6_1_conv': <tf.Tensor 'conv4_block6_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block6_1_relu': <tf.Tensor 'conv4_block6_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block6_2_conv': <tf.Tensor 'conv4_block6_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block6_concat': <tf.Tensor 'conv4_block6_concat/concat:0' shape=(3, 15, 15, 448) dtype=float32>, 'conv4_block7_0_bn': <tf.Tensor 'conv4_block7_0_bn/cond/Merge:0' shape=(3, 15, 15, 448) dtype=float32>, 'conv4_block7_0_relu': <tf.Tensor 'conv4_block7_0_relu/Relu:0' shape=(3, 15, 15, 448) dtype=float32>, 'conv4_block7_1_bn': <tf.Tensor 'conv4_block7_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block7_1_conv': <tf.Tensor 'conv4_block7_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block7_1_relu': <tf.Tensor 'conv4_block7_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block7_2_conv': <tf.Tensor 'conv4_block7_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block7_concat': <tf.Tensor 'conv4_block7_concat/concat:0' shape=(3, 15, 15, 480) dtype=float32>, 'conv4_block8_0_bn': <tf.Tensor 'conv4_block8_0_bn/cond/Merge:0' shape=(3, 15, 15, 480) dtype=float32>, 'conv4_block8_0_relu': <tf.Tensor 'conv4_block8_0_relu/Relu:0' shape=(3, 15, 15, 480) dtype=float32>, 'conv4_block8_1_bn': <tf.Tensor 'conv4_block8_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block8_1_conv': <tf.Tensor 'conv4_block8_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block8_1_relu': <tf.Tensor 'conv4_block8_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block8_2_conv': <tf.Tensor 'conv4_block8_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block8_concat': <tf.Tensor 'conv4_block8_concat/concat:0' shape=(3, 15, 15, 512) dtype=float32>, 'conv4_block9_0_bn': <tf.Tensor 'conv4_block9_0_bn/cond/Merge:0' shape=(3, 15, 15, 512) dtype=float32>, 'conv4_block9_0_relu': <tf.Tensor 'conv4_block9_0_relu/Relu:0' shape=(3, 15, 15, 512) dtype=float32>, 'conv4_block9_1_bn': <tf.Tensor 'conv4_block9_1_bn/cond/Merge:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block9_1_conv': <tf.Tensor 'conv4_block9_1_conv/convolution:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block9_1_relu': <tf.Tensor 'conv4_block9_1_relu/Relu:0' shape=(3, 15, 15, 128) dtype=float32>, 'conv4_block9_2_conv': <tf.Tensor 'conv4_block9_2_conv/convolution:0' shape=(3, 15, 15, 32) dtype=float32>, 'conv4_block9_concat': <tf.Tensor 'conv4_block9_concat/concat:0' shape=(3, 15, 15, 544) dtype=float32>, 'conv5_block10_0_bn': <tf.Tensor 'conv5_block10_0_bn/cond/Merge:0' shape=(3, 7, 7, 800) dtype=float32>, 'conv5_block10_0_relu': <tf.Tensor 'conv5_block10_0_relu/Relu:0' shape=(3, 7, 7, 800) dtype=float32>, 'conv5_block10_1_bn': <tf.Tensor 'conv5_block10_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block10_1_conv': <tf.Tensor 'conv5_block10_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block10_1_relu': <tf.Tensor 'conv5_block10_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block10_2_conv': <tf.Tensor 'conv5_block10_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block10_concat': <tf.Tensor 'conv5_block10_concat/concat:0' shape=(3, 7, 7, 832) dtype=float32>, 'conv5_block11_0_bn': <tf.Tensor 'conv5_block11_0_bn/cond/Merge:0' shape=(3, 7, 7, 832) dtype=float32>, 'conv5_block11_0_relu': <tf.Tensor 'conv5_block11_0_relu/Relu:0' shape=(3, 7, 7, 832) dtype=float32>, 'conv5_block11_1_bn': <tf.Tensor 'conv5_block11_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block11_1_conv': <tf.Tensor 'conv5_block11_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block11_1_relu': <tf.Tensor 'conv5_block11_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block11_2_conv': <tf.Tensor 'conv5_block11_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block11_concat': <tf.Tensor 'conv5_block11_concat/concat:0' shape=(3, 7, 7, 864) dtype=float32>, 'conv5_block12_0_bn': <tf.Tensor 'conv5_block12_0_bn/cond/Merge:0' shape=(3, 7, 7, 864) dtype=float32>, 'conv5_block12_0_relu': <tf.Tensor 'conv5_block12_0_relu/Relu:0' shape=(3, 7, 7, 864) dtype=float32>, 'conv5_block12_1_bn': <tf.Tensor 'conv5_block12_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block12_1_conv': <tf.Tensor 'conv5_block12_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block12_1_relu': <tf.Tensor 'conv5_block12_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block12_2_conv': <tf.Tensor 'conv5_block12_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block12_concat': <tf.Tensor 'conv5_block12_concat/concat:0' shape=(3, 7, 7, 896) dtype=float32>, 'conv5_block13_0_bn': <tf.Tensor 'conv5_block13_0_bn/cond/Merge:0' shape=(3, 7, 7, 896) dtype=float32>, 'conv5_block13_0_relu': <tf.Tensor 'conv5_block13_0_relu/Relu:0' shape=(3, 7, 7, 896) dtype=float32>, 'conv5_block13_1_bn': <tf.Tensor 'conv5_block13_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block13_1_conv': <tf.Tensor 'conv5_block13_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block13_1_relu': <tf.Tensor 'conv5_block13_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block13_2_conv': <tf.Tensor 'conv5_block13_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block13_concat': <tf.Tensor 'conv5_block13_concat/concat:0' shape=(3, 7, 7, 928) dtype=float32>, 'conv5_block14_0_bn': <tf.Tensor 'conv5_block14_0_bn/cond/Merge:0' shape=(3, 7, 7, 928) dtype=float32>, 'conv5_block14_0_relu': <tf.Tensor 'conv5_block14_0_relu/Relu:0' shape=(3, 7, 7, 928) dtype=float32>, 'conv5_block14_1_bn': <tf.Tensor 'conv5_block14_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block14_1_conv': <tf.Tensor 'conv5_block14_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block14_1_relu': <tf.Tensor 'conv5_block14_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block14_2_conv': <tf.Tensor 'conv5_block14_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block14_concat': <tf.Tensor 'conv5_block14_concat/concat:0' shape=(3, 7, 7, 960) dtype=float32>, 'conv5_block15_0_bn': <tf.Tensor 'conv5_block15_0_bn/cond/Merge:0' shape=(3, 7, 7, 960) dtype=float32>, 'conv5_block15_0_relu': <tf.Tensor 'conv5_block15_0_relu/Relu:0' shape=(3, 7, 7, 960) dtype=float32>, 'conv5_block15_1_bn': <tf.Tensor 'conv5_block15_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block15_1_conv': <tf.Tensor 'conv5_block15_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block15_1_relu': <tf.Tensor 'conv5_block15_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block15_2_conv': <tf.Tensor 'conv5_block15_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block15_concat': <tf.Tensor 'conv5_block15_concat/concat:0' shape=(3, 7, 7, 992) dtype=float32>, 'conv5_block16_0_bn': <tf.Tensor 'conv5_block16_0_bn/cond/Merge:0' shape=(3, 7, 7, 992) dtype=float32>, 'conv5_block16_0_relu': <tf.Tensor 'conv5_block16_0_relu/Relu:0' shape=(3, 7, 7, 992) dtype=float32>, 'conv5_block16_1_bn': <tf.Tensor 'conv5_block16_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block16_1_conv': <tf.Tensor 'conv5_block16_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block16_1_relu': <tf.Tensor 'conv5_block16_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block16_2_conv': <tf.Tensor 'conv5_block16_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block16_concat': <tf.Tensor 'conv5_block16_concat/concat:0' shape=(3, 7, 7, 1024) dtype=float32>, 'conv5_block1_0_bn': <tf.Tensor 'conv5_block1_0_bn/cond/Merge:0' shape=(3, 7, 7, 512) dtype=float32>, 'conv5_block1_0_relu': <tf.Tensor 'conv5_block1_0_relu/Relu:0' shape=(3, 7, 7, 512) dtype=float32>, 'conv5_block1_1_bn': <tf.Tensor 'conv5_block1_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block1_1_conv': <tf.Tensor 'conv5_block1_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block1_1_relu': <tf.Tensor 'conv5_block1_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block1_2_conv': <tf.Tensor 'conv5_block1_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block1_concat': <tf.Tensor 'conv5_block1_concat/concat:0' shape=(3, 7, 7, 544) dtype=float32>, 'conv5_block2_0_bn': <tf.Tensor 'conv5_block2_0_bn/cond/Merge:0' shape=(3, 7, 7, 544) dtype=float32>, 'conv5_block2_0_relu': <tf.Tensor 'conv5_block2_0_relu/Relu:0' shape=(3, 7, 7, 544) dtype=float32>, 'conv5_block2_1_bn': <tf.Tensor 'conv5_block2_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block2_1_conv': <tf.Tensor 'conv5_block2_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block2_1_relu': <tf.Tensor 'conv5_block2_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block2_2_conv': <tf.Tensor 'conv5_block2_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block2_concat': <tf.Tensor 'conv5_block2_concat/concat:0' shape=(3, 7, 7, 576) dtype=float32>, 'conv5_block3_0_bn': <tf.Tensor 'conv5_block3_0_bn/cond/Merge:0' shape=(3, 7, 7, 576) dtype=float32>, 'conv5_block3_0_relu': <tf.Tensor 'conv5_block3_0_relu/Relu:0' shape=(3, 7, 7, 576) dtype=float32>, 'conv5_block3_1_bn': <tf.Tensor 'conv5_block3_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block3_1_conv': <tf.Tensor 'conv5_block3_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block3_1_relu': <tf.Tensor 'conv5_block3_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block3_2_conv': <tf.Tensor 'conv5_block3_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block3_concat': <tf.Tensor 'conv5_block3_concat/concat:0' shape=(3, 7, 7, 608) dtype=float32>, 'conv5_block4_0_bn': <tf.Tensor 'conv5_block4_0_bn/cond/Merge:0' shape=(3, 7, 7, 608) dtype=float32>, 'conv5_block4_0_relu': <tf.Tensor 'conv5_block4_0_relu/Relu:0' shape=(3, 7, 7, 608) dtype=float32>, 'conv5_block4_1_bn': <tf.Tensor 'conv5_block4_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block4_1_conv': <tf.Tensor 'conv5_block4_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block4_1_relu': <tf.Tensor 'conv5_block4_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block4_2_conv': <tf.Tensor 'conv5_block4_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block4_concat': <tf.Tensor 'conv5_block4_concat/concat:0' shape=(3, 7, 7, 640) dtype=float32>, 'conv5_block5_0_bn': <tf.Tensor 'conv5_block5_0_bn/cond/Merge:0' shape=(3, 7, 7, 640) dtype=float32>, 'conv5_block5_0_relu': <tf.Tensor 'conv5_block5_0_relu/Relu:0' shape=(3, 7, 7, 640) dtype=float32>, 'conv5_block5_1_bn': <tf.Tensor 'conv5_block5_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block5_1_conv': <tf.Tensor 'conv5_block5_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block5_1_relu': <tf.Tensor 'conv5_block5_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block5_2_conv': <tf.Tensor 'conv5_block5_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block5_concat': <tf.Tensor 'conv5_block5_concat/concat:0' shape=(3, 7, 7, 672) dtype=float32>, 'conv5_block6_0_bn': <tf.Tensor 'conv5_block6_0_bn/cond/Merge:0' shape=(3, 7, 7, 672) dtype=float32>, 'conv5_block6_0_relu': <tf.Tensor 'conv5_block6_0_relu/Relu:0' shape=(3, 7, 7, 672) dtype=float32>, 'conv5_block6_1_bn': <tf.Tensor 'conv5_block6_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block6_1_conv': <tf.Tensor 'conv5_block6_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block6_1_relu': <tf.Tensor 'conv5_block6_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block6_2_conv': <tf.Tensor 'conv5_block6_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block6_concat': <tf.Tensor 'conv5_block6_concat/concat:0' shape=(3, 7, 7, 704) dtype=float32>, 'conv5_block7_0_bn': <tf.Tensor 'conv5_block7_0_bn/cond/Merge:0' shape=(3, 7, 7, 704) dtype=float32>, 'conv5_block7_0_relu': <tf.Tensor 'conv5_block7_0_relu/Relu:0' shape=(3, 7, 7, 704) dtype=float32>, 'conv5_block7_1_bn': <tf.Tensor 'conv5_block7_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block7_1_conv': <tf.Tensor 'conv5_block7_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block7_1_relu': <tf.Tensor 'conv5_block7_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block7_2_conv': <tf.Tensor 'conv5_block7_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block7_concat': <tf.Tensor 'conv5_block7_concat/concat:0' shape=(3, 7, 7, 736) dtype=float32>, 'conv5_block8_0_bn': <tf.Tensor 'conv5_block8_0_bn/cond/Merge:0' shape=(3, 7, 7, 736) dtype=float32>, 'conv5_block8_0_relu': <tf.Tensor 'conv5_block8_0_relu/Relu:0' shape=(3, 7, 7, 736) dtype=float32>, 'conv5_block8_1_bn': <tf.Tensor 'conv5_block8_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block8_1_conv': <tf.Tensor 'conv5_block8_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block8_1_relu': <tf.Tensor 'conv5_block8_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block8_2_conv': <tf.Tensor 'conv5_block8_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block8_concat': <tf.Tensor 'conv5_block8_concat/concat:0' shape=(3, 7, 7, 768) dtype=float32>, 'conv5_block9_0_bn': <tf.Tensor 'conv5_block9_0_bn/cond/Merge:0' shape=(3, 7, 7, 768) dtype=float32>, 'conv5_block9_0_relu': <tf.Tensor 'conv5_block9_0_relu/Relu:0' shape=(3, 7, 7, 768) dtype=float32>, 'conv5_block9_1_bn': <tf.Tensor 'conv5_block9_1_bn/cond/Merge:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block9_1_conv': <tf.Tensor 'conv5_block9_1_conv/convolution:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block9_1_relu': <tf.Tensor 'conv5_block9_1_relu/Relu:0' shape=(3, 7, 7, 128) dtype=float32>, 'conv5_block9_2_conv': <tf.Tensor 'conv5_block9_2_conv/convolution:0' shape=(3, 7, 7, 32) dtype=float32>, 'conv5_block9_concat': <tf.Tensor 'conv5_block9_concat/concat:0' shape=(3, 7, 7, 800) dtype=float32>, 'input_1': <tf.Tensor 'concat:0' shape=(3, 244, 244, 3) dtype=float32>, 'pool1': <tf.Tensor 'pool1/MaxPool:0' shape=(3, 61, 61, 64) dtype=float32>, 'pool2_bn': <tf.Tensor 'pool2_bn/cond/Merge:0' shape=(3, 61, 61, 256) dtype=float32>, 'pool2_conv': <tf.Tensor 'pool2_conv/convolution:0' shape=(3, 61, 61, 128) dtype=float32>, 'pool2_pool': <tf.Tensor 'pool2_pool/AvgPool:0' shape=(3, 30, 30, 128) dtype=float32>, 'pool2_relu': <tf.Tensor 'pool2_relu/Relu:0' shape=(3, 61, 61, 256) dtype=float32>, 'pool3_bn': <tf.Tensor 'pool3_bn/cond/Merge:0' shape=(3, 30, 30, 512) dtype=float32>, 'pool3_conv': <tf.Tensor 'pool3_conv/convolution:0' shape=(3, 30, 30, 256) dtype=float32>, 'pool3_pool': <tf.Tensor 'pool3_pool/AvgPool:0' shape=(3, 15, 15, 256) dtype=float32>, 'pool3_relu': <tf.Tensor 'pool3_relu/Relu:0' shape=(3, 30, 30, 512) dtype=float32>, 'pool4_bn': <tf.Tensor 'pool4_bn/cond/Merge:0' shape=(3, 15, 15, 1024) dtype=float32>, 'pool4_conv': <tf.Tensor 'pool4_conv/convolution:0' shape=(3, 15, 15, 512) dtype=float32>, 'pool4_pool': <tf.Tensor 'pool4_pool/AvgPool:0' shape=(3, 7, 7, 512) dtype=float32>, 'pool4_relu': <tf.Tensor 'pool4_relu/Relu:0' shape=(3, 15, 15, 1024) dtype=float32>, 'zero_padding2d_1': <tf.Tensor 'zero_padding2d_1/Pad:0' shape=(3, 250, 250, 3) dtype=float32>, 'zero_padding2d_2': <tf.Tensor 'zero_padding2d_2/Pad:0' shape=(3, 124, 124, 64) dtype=float32>}
If you stare at the list above, you'll convince yourself that we covered all items we wanted in the table. Notice also that because we provided Keras with a concrete input tensor, the various TensorFlow tensors get well-defined shapes.
The crux of the paper we're trying to reproduce is that the style transfer problem can be posed as an optimisation problem, where the loss function we want to minimise can be decomposed into three distinct parts: the content loss, the style loss and the total variation loss.
visualize_img
, which will help us visualise image during content and style reconstruction.Evaluator
class that computes loss and gradients in one pass while retrieving them via two separate functions, loss
and grads
. This is done because scipy.optimize
requires separate functions for loss and gradients, but computing them separately would be inefficient.Evaluator
class is defined here so that this can be used for content and style reconstruction later.import copy
def visualise_img(x, h, w):
x_p = copy.deepcopy(x)
x_p = x_p.reshape((h, w, 3))
x_p = np.clip(x_p, 0, 255).astype('uint8')
f = StringIO()
Image.fromarray(x_p).save(f, 'png')
IPython.display.display(IPython.display.Image(data=f.getvalue()))
class Evaluator(object):
def __init__(self, f_outputs, height, width):
self.loss_value = None
self.grads_values = None
self.f_outputs = f_outputs
self.height = height
self.width = width
def eval_loss_and_grads(self, x):
x = x.reshape((1, self.height, self.width, 3))
outs = self.f_outputs([x])
loss_value = outs[0]
grad_values = outs[1].flatten().astype('float64')
return loss_value, grad_values
def loss(self, x):
assert self.loss_value is None
loss_value, grad_values = self.eval_loss_and_grads(x)
self.loss_value = loss_value
self.grad_values = grad_values
return self.loss_value
def grads(self, x):
assert self.loss_value is not None
grad_values = np.copy(self.grad_values)
self.loss_value = None
self.grad_values = None
return grad_values
The content loss is the (scaled, squared) Euclidean distance between feature representations of the content and combination images.
def content_loss(content, combination):
return backend.sum(backend.square(combination - content)) / 2
For the content loss, we see that in Gatys et al. (2015) the choice of layer is made based on the ability of the feature-map to reconstruct image which preserves the high-level content of the original image but loses the exact pixel information.
For example:
relu5_1
in above figure is a perfect choice.content_image_reconstruction
, which reconstructs image from a specific layer.def content_image_reconstruction(layer_name):
layer_features = layers[layer_name]
content_image_features = layer_features[0, :, :, :]
combination_features = layer_features[2, :, :, :]
loss = content_loss(content_image_features,
combination_features)
grads = backend.gradients(loss, combination_image)
outputs = [loss]
outputs += grads
f_outputs = backend.function([combination_image], outputs)
evaluator = Evaluator(f_outputs, height, width)
# white noise image
x = np.random.uniform(0, 255, (1, height, width, 3))
iterations = 20
# print layer-name & number of iterations
title = layer_name + " (%d-iters):" % (iterations)
print(title)
print('='*len(title))
for i in range(iterations):
start_time = time.time()
x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x.flatten(),
fprime=evaluator.grads, maxfun=20)
end_time = time.time()
# print loss value & time taken in iteration i
# format: loss(time-in-seconds)
print("%.2f(%ds)," % (min_val, end_time - start_time), end='')
# print new line so that we don't exceed way out of screen
if (i % 10 == 9):
print()
print()
visualise_img(x, height, width)
loss(time-in-secs), ...
along with reconstructed image.layer_names = ['conv1/bn', 'conv2_block1_0_bn', 'conv3_block1_0_bn',
'conv4_block1_0_bn', 'conv5_block1_0_bn']
for cnn_layer in layer_names:
content_image_reconstruction(cnn_layer)
conv1/bn (20-iters): ==================== 504451.44(1s),54016.53(0s),21806.86(0s),13166.37(0s),9437.42(0s),6982.75(0s),5386.82(0s),4158.95(0s),3109.65(0s),2455.14(0s), 2033.72(0s),1779.91(0s),1520.70(0s),1321.28(0s),1168.30(0s),1025.88(0s),915.91(0s),784.21(0s),684.80(0s),525.69(0s),
conv2_block1_0_bn (20-iters): ============================= 2099155.00(1s),1302239.75(0s),1073201.00(0s),964935.75(0s),908546.75(0s),878296.38(0s),855785.00(0s),840794.62(0s),829856.00(0s),823038.12(0s), 817763.12(0s),813637.31(0s),810452.50(0s),807842.88(0s),805559.62(0s),803567.00(0s),801567.69(0s),799761.75(0s),798208.88(0s),796698.38(0s),
conv3_block1_0_bn (20-iters): ============================= 59121.86(3s),20934.72(1s),12289.31(1s),8775.93(1s),6765.74(1s),5525.70(1s),4726.24(1s),4207.69(1s),3810.63(1s),3520.18(1s), 3282.23(1s),3091.47(1s),2933.25(1s),2785.52(1s),2663.71(1s),2560.16(1s),2460.03(1s),2377.12(1s),2304.42(1s),2233.12(1s),
conv4_block1_0_bn (20-iters): ============================= 4873.72(4s),2298.30(2s),1560.49(2s),1242.56(2s),1071.88(2s),956.79(2s),875.81(2s),815.86(2s),771.03(2s),736.74(2s), 708.67(2s),685.85(2s),665.94(2s),647.98(2s),631.72(2s),618.58(2s),605.77(2s),594.61(2s),584.03(2s),573.88(2s),
conv5_block1_0_bn (20-iters): ============================= 2552.67(7s),1157.08(3s),744.59(3s),563.09(3s),468.59(3s),411.64(3s),367.81(3s),336.28(3s),308.74(3s),287.58(3s), 270.67(3s),256.22(3s),244.51(3s),233.93(3s),225.00(3s),216.94(3s),209.04(3s),202.07(3s),195.47(3s),189.71(3s),
conv1
& conv2
gives better image. And other layers are losing too much information.conv1
is not losing pixel information and hence we drop looking at that layer as well. And go through other layers with prefix conv1
conv2
.layer_names = ['conv1/conv', 'conv1/relu', 'conv2_block1_0_bn',
'conv2_block2_0_bn', 'conv2_block3_0_bn',
'conv2_block4_0_bn', 'conv2_block5_0_bn', 'conv2_block6_0_bn']
for cnn_layer in layer_names:
content_image_reconstruction(cnn_layer)
conv1/conv (20-iters): ====================== 35026064.00(1s),7755294.00(0s),3448869.00(0s),1467268.62(0s),883612.62(0s),472427.69(0s),321004.12(0s),218103.91(0s),161746.41(0s),119318.51(0s), 90769.68(0s),72925.20(0s),55592.41(0s),43786.45(0s),33882.90(0s),27347.78(0s),23114.65(0s),19281.43(0s),16026.20(0s),12873.27(0s),
conv1/relu (20-iters): ====================== 281166.06(1s),81516.14(0s),46891.80(0s),33692.56(0s),26099.80(0s),21621.65(0s),17963.58(0s),15170.31(0s),12876.07(0s),11070.13(0s), 9584.99(0s),8359.66(0s),7392.63(0s),6632.40(0s),5918.79(0s),5271.10(0s),4731.73(0s),4249.26(0s),3773.69(0s),3353.47(0s),
conv2_block1_0_bn (20-iters): ============================= 2077690.75(1s),1297634.75(0s),1065118.12(0s),969035.62(0s),920423.62(0s),888340.19(0s),866674.75(0s),849609.81(0s),839982.88(0s),831954.75(0s), 825746.25(0s),820688.69(0s),817111.38(0s),814180.12(0s),811574.75(0s),809443.44(0s),807770.81(0s),806163.25(0s),804859.00(0s),803798.56(0s),
conv2_block2_0_bn (20-iters): ============================= 3599795.50(1s),1965514.75(0s),1461563.62(0s),1239990.12(0s),1118528.25(0s),1056392.25(0s),1013804.81(0s),985027.50(0s),963646.00(0s),948284.88(0s), 936322.75(0s),927495.06(0s),919769.19(0s),913719.81(0s),908395.69(0s),903713.88(0s),900069.62(0s),897239.88(0s),894766.38(0s),892832.25(0s),
conv2_block3_0_bn (20-iters): ============================= 1509662.50(1s),987315.94(0s),812963.69(0s),711396.81(0s),642204.38(0s),595465.50(0s),558131.25(0s),531091.50(0s),512692.69(0s),497926.28(0s), 487139.50(0s),479659.16(0s),473916.38(0s),469242.09(0s),465266.41(0s),461887.88(0s),458609.19(0s),456144.53(0s),453932.72(0s),452020.09(0s),
conv2_block4_0_bn (20-iters): ============================= 817334.50(2s),529482.31(0s),433458.34(0s),379504.44(0s),345619.38(0s),321840.25(1s),303609.03(1s),286879.56(1s),275201.00(1s),266681.84(1s), 260808.50(1s),256007.50(1s),251813.97(1s),248534.69(1s),243034.58(1s),239398.09(1s),236626.31(1s),232848.12(1s),230219.53(1s),227750.44(1s),
conv2_block5_0_bn (20-iters): ============================= 994609.19(2s),476686.19(1s),329458.12(1s),261226.70(1s),225156.59(1s),199299.69(1s),179828.27(1s),165918.86(1s),155851.05(1s),147724.77(1s), 140955.98(1s),135559.48(1s),130695.29(1s),127008.45(1s),123451.01(1s),119789.25(1s),116530.19(1s),114030.12(1s),112105.78(1s),110456.91(1s),
conv2_block6_0_bn (20-iters): ============================= 628167.31(2s),322538.91(1s),222755.81(1s),176006.09(1s),150722.50(1s),133302.77(1s),121408.76(1s),112559.49(1s),105638.49(1s),100142.46(1s), 95568.89(1s),91572.27(1s),88298.80(1s),85593.97(1s),83545.72(1s),81780.02(1s),80179.57(1s),78643.95(1s),77178.64(1s),75794.45(1s),
block 5,6
are also loosing enough information. And hence we now go through all the layers with prefix conv1/relu
, conv2_block1
, conv2_block2
, conv2_block3
, conv2_block4
layer_names = ['conv2_block1_0_bn', 'conv2_block1_0_relu', 'conv2_block1_1_bn',
'conv2_block1_1_conv', 'conv2_block1_1_relu',
'conv2_block1_2_conv', 'conv2_block1_concat',
'conv2_block2_0_bn', 'conv2_block2_0_relu', 'conv2_block2_1_bn',
'conv2_block2_1_conv', 'conv2_block2_1_relu',
'conv2_block2_2_conv', 'conv2_block2_concat',
'conv2_block3_0_bn', 'conv2_block3_0_relu', 'conv2_block3_1_bn',
'conv2_block3_1_conv', 'conv2_block3_1_relu',
'conv2_block3_2_conv', 'conv2_block3_concat',
'conv2_block4_0_bn', 'conv2_block4_0_relu',
'conv2_block4_1_bn', 'conv2_block4_1_conv',
'conv2_block4_1_relu', 'conv2_block4_2_conv',
'conv2_block4_concat']
# for cnn_layer in layer_names:
# content_image_reconstruction(cnn_layer)
candidate_layer_names = ['conv1/relu', 'conv2_block1_0_bn',
'conv2_block3_0_relu', 'conv2_block4_0_relu']
for cnn_layer in candidate_layer_names:
content_image_reconstruction(cnn_layer)
conv1/relu (20-iters): ====================== 283393.78(1s),80843.02(0s),45512.79(0s),32863.82(0s),25849.57(0s),21302.49(0s),17761.21(0s),15102.54(0s),13017.16(0s),11305.61(0s), 9991.99(0s),8932.42(0s),7975.80(0s),7025.18(0s),6240.68(0s),5567.58(0s),4957.10(0s),4435.38(0s),3983.34(0s),3550.30(0s),
conv2_block1_0_bn (20-iters): ============================= 1993353.62(1s),1245978.12(0s),1037743.12(0s),933809.50(0s),884536.88(0s),853225.94(0s),830494.62(0s),814567.50(0s),802769.69(0s),792622.75(0s), 786744.00(0s),782000.62(0s),778532.50(0s),775759.00(0s),773358.31(0s),771393.62(0s),769798.12(0s),768275.88(0s),766808.38(0s),765604.62(0s),
conv2_block3_0_relu (20-iters): =============================== 1045752.00(2s),634954.62(0s),503958.69(0s),441780.16(0s),410652.59(0s),392736.31(0s),381095.34(0s),373464.69(0s),367590.84(0s),363492.09(0s), 360444.66(0s),357877.94(0s),355596.84(0s),353869.19(0s),352315.56(0s),351064.62(0s),349965.62(0s),349030.81(0s),348211.38(0s),347308.59(0s),
conv2_block4_0_relu (20-iters): =============================== 338053.59(2s),203081.78(0s),157104.81(0s),136329.53(0s),123336.94(0s),115110.89(0s),110023.11(0s),106145.56(1s),102738.68(1s),100126.80(1s), 98208.25(1s),96893.77(1s),95898.84(1s),94809.66(1s),94005.39(1s),93363.84(1s),92789.95(1s),92230.16(1s),91775.02(1s),91475.80(1s),
conv2_block1_0_bn
, conv2_block3_0_relu
, conv2_block4_0_relu
can be good choice.This is where things start to get a bit intricate.
For the style loss, we first define something called a Gram matrix. The terms of this matrix are proportional to the covariances of corresponding sets of features, and thus captures information about which features tend to activate together. By only capturing these aggregate statistics across the image, they are blind to the specific arrangement of objects inside the image. This is what allows them to capture information about style independent of content. (This is not trivial at all, and I refer you to a paper that attempts to explain the idea.)
The Gram matrix can be computed efficiently by reshaping the feature spaces suitably and taking an outer product.
def gram_matrix(x):
features = backend.batch_flatten(backend.permute_dimensions(x, (2, 0, 1)))
gram = backend.dot(features, backend.transpose(features))
return gram
The style loss is then the (scaled, squared) Frobenius norm of the difference between the Gram matrices of the style and combination images.
def style_loss(style, combination, height, width):
S = gram_matrix(style)
C = gram_matrix(combination)
channels = 3
size = height * width
return backend.sum(backend.square(S - C)) / (4. * (channels ** 2) * (size ** 2))
For style loss, we see that in Gatys et al. (2015) they select layers which reconstructs images that match the style of a given image on an increasing scale while discarding information of the global arrangement of the scene.
For example:
style_image_reconstruction
, which reconstructs image from a specific layer.def style_image_reconstruction(layer_names):
loss = backend.variable(0.)
for layer_name in layer_names:
layer_features = layers[layer_name]
style_features = layer_features[1, :, :, :]
combination_features = layer_features[2, :, :, :]
loss += style_loss(style_features, combination_features, height, width)
grads = backend.gradients(loss, combination_image)
outputs = [loss]
outputs += grads
f_outputs = backend.function([combination_image], outputs)
evaluator = Evaluator(f_outputs, height, width)
# white noise image
x = np.random.uniform(0, 255, (1, height, width, 3))
iterations = 20
# print layer-name & number of iterations
title = ",".join(layer_names) + " (%d-iters):" % (iterations)
print(title)
print('='*len(title))
for i in range(iterations):
start_time = time.time()
x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x.flatten(),
fprime=evaluator.grads, maxfun=20)
end_time = time.time()
# print loss value & time taken in iteration i
# format: loss(time-in-seconds)
print("%.2f(%ds)," % (min_val, end_time - start_time), end='')
# print new line so that we don't exceed way out of screen
if (i % 10 == 9):
print()
print()
visualise_img(x, height, width)
loss(time-in-secs), ...
along with reconstructed image.layer_names = [('conv1/bn',), ('conv2_block1_0_bn',), ('conv3_block1_0_bn',),
('conv4_block1_0_bn',), ('conv5_block1_0_bn',)]
for cnn_layer in layer_names:
style_image_reconstruction(cnn_layer)
WARNING:tensorflow:Variable += will be deprecated. Use variable.assign_add if you want assignment to the variable value or 'x = x + y' if you want a new python Tensor object. conv1/bn (20-iters): ==================== 165.48(2s),16.24(0s),6.71(0s),4.08(0s),2.65(0s),1.80(0s),1.42(0s),1.16(0s),1.01(0s),0.89(0s), 0.89(0s),0.89(0s),0.89(0s),0.89(0s),0.89(0s),0.89(0s),0.89(0s),0.89(0s),0.89(0s),0.89(0s),
conv2_block1_0_bn (20-iters): ============================= 362.33(2s),80.49(0s),26.57(0s),12.37(0s),7.04(0s),4.88(0s),3.55(0s),2.96(0s),2.52(0s),2.21(0s), 1.97(0s),1.80(0s),1.64(0s),1.50(0s),1.40(0s),1.22(0s),1.03(0s),0.94(0s),0.87(0s),0.79(0s),
conv3_block1_0_bn (20-iters): ============================= 15.48(4s),3.47(1s),0.68(1s),0.26(1s),0.19(0s),0.19(0s),0.19(0s),0.19(0s),0.19(0s),0.19(0s), 0.19(0s),0.19(0s),0.19(0s),0.19(0s),0.19(0s),0.19(0s),0.19(0s),0.19(0s),0.19(0s),0.19(0s),
conv4_block1_0_bn (20-iters): ============================= 0.60(3s),0.60(0s),0.60(0s),0.60(0s),0.60(0s),0.60(0s),0.60(0s),0.60(0s),0.60(0s),0.60(0s), 0.60(0s),0.60(0s),0.60(0s),0.60(0s),0.60(0s),0.60(0s),0.60(0s),0.60(0s),0.60(0s),0.60(0s),
conv5_block1_0_bn (20-iters): ============================= 0.16(6s),0.16(0s),0.16(0s),0.16(0s),0.16(0s),0.16(0s),0.16(0s),0.16(0s),0.16(0s),0.16(0s), 0.16(0s),0.16(0s),0.16(0s),0.16(0s),0.16(0s),0.16(0s),0.16(0s),0.16(0s),0.16(0s),0.16(0s),
conv2
gives better texture. And other layers are losing too much information.conv2
.layer_names = [('conv2_block1_0_bn',), ('conv2_block1_0_relu',),
('conv2_block1_1_bn',), ('conv2_block1_1_conv',),
('conv2_block1_1_relu',), ('conv2_block1_2_conv',),
('conv2_block1_concat',), ('conv2_block2_0_bn',),
('conv2_block2_0_relu',), ('conv2_block2_1_bn',),
('conv2_block2_1_conv',), ('conv2_block2_1_relu',),
('conv2_block2_2_conv',), ('conv2_block2_concat',),
('conv2_block3_0_bn',), ('conv2_block3_0_relu',),
('conv2_block3_1_bn',), ('conv2_block3_1_conv',),
('conv2_block3_1_relu',), ('conv2_block3_2_conv',),
('conv2_block3_concat',), ('conv2_block4_0_bn',),
('conv2_block4_0_relu',), ('conv2_block4_1_bn',),
('conv2_block4_1_conv',), ('conv2_block4_1_relu',),
('conv2_block4_2_conv',), ('conv2_block4_concat',),
('conv2_block5_0_bn',), ('conv2_block5_0_relu',),
('conv2_block5_1_bn',), ('conv2_block5_1_conv',),
('conv2_block5_1_relu',), ('conv2_block5_2_conv',),
('conv2_block5_concat',), ('conv2_block6_0_bn',),
('conv2_block6_0_relu',), ('conv2_block6_1_bn',),
('conv2_block6_1_conv',), ('conv2_block6_1_relu',),
('conv2_block6_2_conv',), ('conv2_block6_concat',)]
# for cnn_layer in layer_names:
# style_image_reconstruction(cnn_layer)
layer_names = [('conv2_block1_concat',), ('conv2_block2_concat',),
('conv2_block3_concat',), ('conv2_block4_concat',),
('conv2_block5_concat',), ('conv2_block6_concat',)]
for cnn_layer in layer_names:
style_image_reconstruction(cnn_layer)
conv2_block1_concat (20-iters): =============================== 117.61(3s),16.94(0s),5.65(0s),3.22(0s),2.28(0s),1.72(0s),1.24(0s),1.01(0s),0.86(0s),0.72(0s), 0.64(0s),0.58(0s),0.58(0s),0.58(0s),0.58(0s),0.58(0s),0.58(0s),0.58(0s),0.58(0s),0.58(0s),
conv2_block2_concat (20-iters): =============================== 150.56(3s),22.24(0s),9.89(0s),5.86(0s),4.20(0s),3.21(0s),2.64(0s),2.23(0s),1.82(0s),1.53(0s), 1.31(0s),1.16(0s),1.03(0s),0.91(0s),0.80(0s),0.73(0s),0.68(0s),0.68(0s),0.68(0s),0.68(0s),
conv2_block3_concat (20-iters): =============================== 156.10(3s),26.77(0s),10.92(0s),7.17(0s),5.22(1s),3.98(0s),3.02(1s),2.51(1s),2.12(1s),1.84(1s), 1.56(1s),1.40(1s),1.28(1s),1.17(1s),1.07(1s),1.00(1s),0.93(1s),0.87(1s),0.81(1s),0.75(1s),
conv2_block4_concat (20-iters): =============================== 161.71(4s),28.41(1s),12.62(1s),7.71(1s),5.61(1s),4.46(1s),3.53(1s),2.78(1s),2.35(1s),2.05(1s), 1.84(1s),1.65(1s),1.49(1s),1.37(1s),1.27(1s),1.19(1s),1.12(1s),1.03(1s),0.97(1s),0.90(1s),
conv2_block5_concat (20-iters): =============================== 166.99(4s),30.05(1s),13.19(1s),8.02(1s),5.81(1s),4.49(1s),3.62(1s),3.06(1s),2.63(1s),2.35(1s), 2.08(1s),1.89(1s),1.72(1s),1.56(1s),1.42(1s),1.32(1s),1.24(1s),1.15(1s),1.09(1s),1.03(1s),
conv2_block6_concat (20-iters): =============================== 170.13(4s),34.24(1s),15.10(1s),9.14(1s),6.63(1s),5.13(1s),4.18(1s),3.52(1s),2.93(1s),2.58(1s), 2.30(1s),2.06(1s),1.88(1s),1.74(1s),1.59(1s),1.48(1s),1.39(1s),1.30(1s),1.22(1s),1.15(1s),
layer_names = [('conv2_block1_concat', 'conv2_block2_concat',
'conv2_block3_concat', 'conv2_block4_concat',
'conv2_block5_concat', 'conv2_block6_concat',)]
for cnn_layer in layer_names:
style_image_reconstruction(cnn_layer)
conv2_block1_concat,conv2_block2_concat,conv2_block3_concat,conv2_block4_concat,conv2_block5_concat,conv2_block6_concat (20-iters): =================================================================================================================================== 864.17(5s),145.26(1s),61.66(1s),39.21(1s),29.22(1s),22.85(1s),19.01(1s),16.11(1s),13.71(1s),12.04(1s), 10.92(1s),9.90(1s),8.97(1s),8.23(1s),7.66(1s),7.17(1s),6.75(1s),6.35(1s),6.01(1s),5.69(1s),
Note: As it can be seen in above reconstructed style image that the texture is not yet fully concise and hence the output combination image is not that of good quality as compared to the results in original paper.
Now we're back on simpler ground.
If you were to solve the optimisation problem with only the two loss terms we've introduced so far (style and content), you'll find that the output is quite noisy. We thus add another term, called the total variation loss (a regularisation term) that encourages spatial smoothness.
You can experiment with reducing the total_variation_weight
and play with the noise-level of the generated image.
Total variation loss works as regularizer for smoothing the generated image.
def total_variation_loss(x, height, width):
a = backend.square(x[:, :height-1, :width-1, :] - x[:, 1:, :width-1, :])
b = backend.square(x[:, :height-1, :width-1, :] - x[:, :height-1, 1:, :])
return backend.sum(backend.pow(a + b, 1.25))
As seen earlier, Style transfer is posed as an optimization problem with following loss function: $$L_{total}(c,s,g) = \alpha L_{content}(c,g) + \beta L_{style}(s,g)$$
We also introduce total variation loss and hence following is the overall loss function: $$L_{total}(c,s,g) = \alpha L_{content}(c,g) + \beta L_{style}(s,g) + \gamma L_{tv}(g)$$
def style_transfer_loss(layers, content_weight, style_weight, tv_weight,
content_layer_name, style_layer_names, height, width):
layer_features = layers[content_layer_name]
content_image_features = layer_features[0, :, :, :]
combination_features = layer_features[2, :, :, :]
# alpha * L_content
final_content_loss = content_weight * content_loss(content_image_features,
combination_features)
final_style_loss = backend.variable(0.)
feature_layers = style_layer_names
for layer_name in feature_layers:
layer_features = layers[layer_name]
style_features = layer_features[1, :, :, :]
combination_features = layer_features[2, :, :, :]
sl = style_loss(style_features, combination_features, height, width)
# beta * L_style
final_style_loss += (style_weight / len(feature_layers)) * sl
# gamma * L_tv
final_tv_loss = tv_weight * total_variation_loss(combination_image, height,
width)
# L_total
final_loss = final_content_loss + final_style_loss + final_tv_loss
return final_loss
We'll now use the feature spaces provided by specific layers of our model to define these three loss functions:
The relative importance of loss terms are determined by a set of scalar weights. These are arbitrary, but the following set have been chosen after quite a bit of experimentation to find a set that generates output that's aesthetically pleasing to me.
content_weight = 4
style_weight = 500000
tv_weight = 0.0001
content_layer_name = 'conv2_block3_0_relu'
style_layer_names = ['conv2_block1_concat', 'conv2_block2_concat',
'conv2_block3_concat', 'conv2_block4_concat',
'conv2_block5_concat', 'conv2_block6_concat']
final_loss = style_transfer_loss(layers, content_weight, style_weight,
tv_weight, content_layer_name,
style_layer_names, height, width)
The goal of this journey was to setup an optimisation problem that aims to solve for a combination image that contains the content of the content image, while having the style of the style image. Now that we have our input images massaged and our loss function calculators in place, all we have left to do is define gradients of the total loss relative to the combination image, and use these gradients to iteratively improve upon our combination image to minimise the loss.
We start by defining the gradients.
grads = backend.gradients(final_loss, combination_image)
outputs = [final_loss]
outputs += grads
f_outputs = backend.function([combination_image], outputs)
Now we're finally ready to solve our optimisation problem. This combination image begins its life as a random collection of (valid) pixels, and we use the L-BFGS algorithm (a quasi-Newton algorithm that's significantly quicker to converge than standard gradient descent) to iteratively improve upon it.
We stop after 80 iterations because the output looks good to me and the loss stops reducing significantly.
# Subtracting with 128. gradients converge faster
x = np.random.uniform(0, 255, (1, height, width, 3)) - 128.
iterations = 80
evaluator = Evaluator(f_outputs, height, width)
# print number of iterations
title = "%d-iters [format: loss(time-in-seconds)]:" % (iterations)
print(title)
print('='*len(title))
for i in range(iterations):
start_time = time.time()
x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x.flatten(),
fprime=evaluator.grads, maxfun=20)
end_time = time.time()
# print loss value & time taken in iteration i
# format: loss(time-in-seconds)
print("%.2f(%ds)," % (min_val, end_time - start_time), end='')
# print new line so that we don't exceed way out of screen
if ((i+1) % 5 == 0):
print()
80-iters [format: loss(time-in-seconds)]: ========================================= 483531008.00(7s),269808896.00(7s),206679456.00(7s),176362096.00(7s),159217648.00(7s), 146495056.00(7s),138160496.00(7s),132035136.00(7s),127673504.00(7s),124130248.00(7s), 121484848.00(7s),118959768.00(7s),116933888.00(7s),115401248.00(7s),114164640.00(7s), 113161784.00(7s),112361808.00(7s),111742976.00(7s),111228368.00(7s),110811784.00(7s), 110486784.00(7s),110225296.00(7s),110015504.00(7s),109833736.00(7s),109683008.00(7s), 109558064.00(7s),109452888.00(7s),109363936.00(7s),109283280.00(7s),109210024.00(7s), 109146464.00(7s),109089016.00(7s),109037920.00(7s),108993464.00(7s),108954744.00(7s), 108920664.00(7s),108890296.00(7s),108863688.00(7s),108840480.00(7s),108820352.00(7s), 108801976.00(7s),108785352.00(7s),108770760.00(7s),108756600.00(7s),108742704.00(7s), 108730368.00(7s),108718808.00(7s),108708120.00(7s),108698352.00(7s),108689312.00(7s), 108680848.00(7s),108672960.00(7s),108665272.00(7s),108658176.00(7s),108651440.00(7s), 108645320.00(7s),108639496.00(7s),108633840.00(7s),108628600.00(7s),108623448.00(7s), 108618480.00(7s),108613616.00(7s),108608808.00(7s),108604344.00(7s),108599920.00(7s), 108595584.00(7s),108591432.00(7s),108587504.00(7s),108583752.00(7s),108579800.00(7s), 108575688.00(7s),108571616.00(7s),108567424.00(7s),108563352.00(7s),108559320.00(7s), 108555336.00(7s),108551456.00(7s),108547632.00(7s),108543880.00(7s),108540120.00(7s),
print("Content Image")
visualise_img(content_array, height, width)
print("Style Image")
visualise_img(style_array, height, width)
print("Combination Image")
visualise_img(x, height, width)
Content Image
Style Image
Combination Image
content_img_url = "https://upload.wikimedia.org/wikipedia/commons/c/c8/Taj_Mahal_in_March_2004.jpg"
style_img_url = "http://1.bp.blogspot.com/-yqh8zFJh_OU/UcDJKe-EYaI/AAAAAAAAS2I/Qb3bm2zwQu0/s1600/%5BIMG+SRC=%22URL%22+ALT=picasso+painting,+romancing+picasso+painting,+colorful+oil+painting,k+madison+moore+artist%5D++copy.jpg"
height = 512
width = 512
content_weight = 4
style_weight = 500000
total_variation_weight = 0.0001
content_layer_name = 'conv2_block3_0_relu'
style_layer_names = ['conv2_block1_concat', 'conv2_block2_concat',
'conv2_block3_concat', 'conv2_block4_concat',
'conv2_block5_concat', 'conv2_block6_concat']
response = requests.get(content_img_url)
content_image = Image.open(BytesIO(response.content))
content_image = content_image.resize((height, width))
response = requests.get(style_img_url)
style_image = Image.open(BytesIO(response.content))
style_image = style_image.resize((height, width))
content_array = np.asarray(content_image, dtype='float32')
content_array = np.expand_dims(content_array, axis=0)
style_array = np.asarray(style_image, dtype='float32')
style_array = np.expand_dims(style_array, axis=0)
content_image = backend.variable(content_array)
style_image = backend.variable(style_array)
combination_image = backend.placeholder((1, height, width, 3))
input_tensor = backend.concatenate([content_image,
style_image,
combination_image], axis=0)
model = DenseNet121(input_tensor=input_tensor, weights='imagenet',
include_top=False)
layers = dict([(layer.name, layer.output) for layer in model.layers])
final_loss = style_transfer_loss(layers, content_weight, style_weight,
total_variation_weight, content_layer_name,
style_layer_names, height, width)
grads = backend.gradients(final_loss, combination_image)
outputs = [final_loss]
outputs += grads
f_outputs = backend.function([combination_image], outputs)
# Subtracting with 128. gradients converge faster
x = np.random.uniform(0, 255, (1, height, width, 3)) - 128.
iterations = 80
evaluator = Evaluator(f_outputs, height, width)
# print number of iterations
title = "%d-iters [format: loss(time-in-seconds)]:" % (iterations)
print(title)
print('='*len(title))
for i in range(iterations):
start_time = time.time()
x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x.flatten(),
fprime=evaluator.grads, maxfun=20)
end_time = time.time()
# print loss value & time taken in iteration i
# format: loss(time-in-seconds)
print("%.2f(%ds)," % (min_val, end_time - start_time), end='')
# print new line so that we don't exceed way out of screen
if ((i+1) % 5 == 0):
print()
print("Content Image")
visualise_img(content_array, height, width)
print("Style Image")
visualise_img(style_array, height, width)
print("Combination Image")
visualise_img(x, height, width)
80-iters [format: loss(time-in-seconds)]: ========================================= 458811040.00(15s),259987328.00(7s),198441568.00(7s),168911088.00(7s),152311504.00(7s), 141540128.00(7s),133982568.00(7s),128701864.00(7s),124901520.00(7s),121879168.00(7s), 119514528.00(7s),117662016.00(7s),116207408.00(7s),115085936.00(7s),114127504.00(7s), 113356024.00(7s),112755912.00(7s),112249864.00(7s),111836104.00(7s),111510152.00(7s), 111243368.00(7s),111025440.00(7s),110852296.00(7s),110709584.00(7s),110582872.00(7s), 110472336.00(7s),110379424.00(7s),110298392.00(7s),110227680.00(7s),110165160.00(7s), 110112216.00(7s),110066416.00(7s),110027112.00(7s),109993128.00(7s),109961272.00(7s), 109931888.00(7s),109905376.00(7s),109881208.00(7s),109858928.00(7s),109838496.00(7s), 109819424.00(7s),109801856.00(7s),109785784.00(7s),109770832.00(7s),109756888.00(7s), 109743448.00(7s),109731296.00(7s),109719816.00(7s),109709184.00(7s),109699336.00(7s), 109690440.00(7s),109682160.00(7s),109674072.00(7s),109666704.00(7s),109659744.00(7s), 109653200.00(7s),109647024.00(7s),109641280.00(7s),109635712.00(7s),109630472.00(7s), 109625512.00(7s),109620704.00(7s),109616048.00(7s),109611592.00(7s),109607384.00(7s), 109603128.00(7s),109599016.00(7s),109594888.00(7s),109590976.00(7s),109587232.00(7s), 109583608.00(7s),109579952.00(7s),109576432.00(7s),109572968.00(7s),109569640.00(7s), 109566320.00(7s),109563256.00(7s),109560176.00(7s),109557168.00(7s),109554168.00(7s), Content Image
Style Image
Combination Image
The system consists of two networks:
Given this system, we can then train the image transformation network to reduce the total style transfer loss. To train the network, one can pick a fixed style image and use a large batch of different content images as training examples. In their paper, Johnson et. al trained their network on the Microsoft COCO dataset - which is an object recognition dataset of 80,000 different images.
Training involves using the loss network to evaluate the loss for a given training example and then propagating this error back through every layer in the image transformation network. So the first part of the structure is a “Image Transform Net” which generate new image from the input image. And the second part is simply a “Loss Network”, which is the feeding forward part.The weight of the loss network is fixed and will not be updated during training.
Testing involves passing the image through transformation network and after final layer it will produce a valid image which has style imposed on content image. This means the generated output must contain values that are all in the valid pixel range of 0 to 255. To achieve this there is tanh layer in transformation network which is scaled to have output in the range of 0 to 255.
As this requires enough compute power to train the network, we won't be implementing this here. The whole idea of describing this paper here is to see an alternative faster approach which can be used in real-time for style transfer. It can be seen above that during testing we are only doing single forward pass and hence the speed compared to the approach seen in Gatys et al. (2015).
As seen in Gatys et al paper, the optimization-based methods can handle arbitrary styles with pleasing visual quality but at the expense of high computational costs. And as seen in Perceptual Loss function based style transfer paper that feed-forward approaches can be executed efficiently but are limited to a fixed number of styles or compromised visual quality.
This paper proposes a simple yet effective method for universal style transfer which achieves the style-agnostic generalization ability with marginally compromised visual quality and execution efficiency.
It formulates style transfer as an image reconstruction process coupled with feature transformation, i.e., whitening and coloring. The reconstruction part is responsible for inverting features back to the RGB space and the feature transformation matches the statistics of a content image to a style image.
Hence there are two parts to this approach:
Recontruction decoder:
Whitening and coloring transforms (WCT)
For improved results the paper also present a multi-level stylization pipeline, which takes all level of information of a style into account.
In short, the algorithm works by unfolding the image generation process via training an auto-encoder for image reconstruction. And then integrates the whitening and coloring transforms in the feed-forward passes to match the statistical distributions and correlations between the intermediate features of content and style.
Given time constraints and compute resources the code is not implemented for this algorithm. Also paper uses MS COCO dataset to train the decoder.