[Перевод] TensorFlow Tutorial #15 — Перенос стиля

искусственный интеллект TensorFlow алгоритм Нейронные сети
[Перевод] TensorFlow Tutorial #15 — Перенос стиля

Заглавное изображение из:Experiments with style transfer
Наконец-то написал это. За последние два года одно за другим появлялись различные приложения для обработки изображений в художественном стиле, такие как популярная Prisma.
Эта статья кратко представляет и реализует алгоритм переноса стилей.Более подробные описания см. в ранее переведенных статьях (Стилизация изображений, композиция ИИ, машинное обучение и искусство).
Тем не менее, оптимизация может потребоваться в определенных приложениях, таких как стабильность между кадрами в видео.

01 - Простая линейная модель | 02 - Сверточные нейронные сети | 03 - PrettyTensor | 04 - Сохранить и восстановить
05 – Интегрированное обучение | 06 - CIFAR 10 | 07 - Начальная модель | 08 – Перенос обучения
09 - Видеоданные | 11 - Состязательные примеры | 12 - МНИСТ от шума | 13 - Визуальный анализ
14 - DeepDream

by Magnus Erik Hvass Pedersen / GitHub / Videos on YouTube
китайский переводthrillerist / Github

При воспроизведении просьба прикрепить ссылку на эту статью.


вводить

В предыдущем уроке № 14 мы увидели, как максимизировать активацию функций внутри нейронной сети, чтобы усилить шаблоны во входном изображении. Этот называется DeepDream.

В этой статье используется аналогичная идея, но с двумя входными изображениями: изображением содержимого и изображением стиля. Затем мы хотим создать смешанное изображение,Он содержит схему карты содержимого и текстуру карты стиля.

Эта статья основана на предыдущем уроке. Вам потребуется общее знакомство с нейронными сетями (подробности см. в руководствах № 01 и № 02), а также полезно ознакомиться с DeepDream в руководстве № 14.

блок-схема

Эта блок-схема показывает общее представление об алгоритме передачи стиля, хотя используемая нами модель VGG-16 имеет больше слоев, чем показано на рисунке.

Введите в нейронную сеть два изображения: изображение контента и изображение стиля. Мы хотим создать смешанное изображение, содержащее контур карты содержимого и текстуру карты стиля.
Мы делаем это, создавая несколько функций потерь, которые можно оптимизировать.

Функция потерь для изображения контента пытается свести к минимуму разницу между функциями активации изображения контента и смешанного изображения на одном или нескольких уровнях сети.Это делает контуры смешанного изображения и изображения содержимого похожими.

Функция потерь для изображений стилей немного сложнее, так как она пытаетсяМинимизируйте разницу между матрицами Грамма изображения стиля и смешанного изображения.Это делается в одном или нескольких слоях сети. Грамматрицы измеряют, какие функции одновременно активируются в данном слое. Измените смешанное изображение, чтобы оно имитировало шаблоны активации изображения стиля, что приведет к миграции цветов и текстур.

Мы используем TensorFlow для автоматического получения градиентов этих функций потерь. Затем используйте градиент, чтобы обновить смешанное изображение. Повторяем этот процесс несколько раз, пока нас не удовлетворит полученное изображение.

Некоторые детали алгоритма передачи стиля не показаны на этой блок-схеме.Например, для расчета грамм-матриц вычисляется и сохраняется промежуточное значение для повышения эффективности, а также используется функция потерь для шумоподавления смешанного изображения. Функции нормализованы, чтобы их было легче масштабировать относительно друг друга.

from IPython.display import Image, display
Image('images/15_style_transfer_flowchart.png')

Imports

%matplotlib inline
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
import PIL.Image

Версия TensorFlow, разработанная с использованием Python 3.5.2 (Anaconda):

tf.__version__

'0.11.0rc0'

Модель ВГГ-16

Я потратил два дня, пытаясь реализовать алгоритм передачи стиля, используя модель Inception 5h, которую я использовал в DeepDream в предыдущем уроке № 14, но не смог получить изображение, которое выглядело бы достаточно хорошо. Это немного странно, потому что изображение, созданное в Уроке №14, выглядит нормально. Но, оглядываясь назад, мы также использовали некоторые приемы (в уроке № 14), чтобы получить это качество, такие как сглаживание градиентов и рекурсивное понижение дискретизации и обработка изображения.

оригинальная бумагаИспользовалась сверточная нейронная сеть VGG-19. По какой-то причине предварительно обученная модель VGG-19 недостаточно стабильна для TensorFlow в этом руководстве. Поэтому мы используем модель VGG-16, созданную другими, которую можно легко получить и загрузить в TensorFlow. Для удобства мы инкапсулируем класс.

import vgg16

Модель VGG-16 скачана из интернета. Это папка по умолчанию, в которой вы сохраняете файлы данных. Если папка не существует, она будет создана.

# vgg16.data_dir = 'vgg16/'

Download the data for the VGG-16 model if it doesn't already exist in the directory.

WARNING: It is 550 MB!

Если в папке нет модели VGG-16, она будет загружена автоматически.

Примечание: он имеет 500 МБ!

vgg16.maybe_download()

Downloading VGG16 Model ...
Data has apparently already been downloaded and unpacked.

Вспомогательные функции для работы с изображениями

Эта функция загружает изображение и возвращает пустой массив с плавающей запятой. Размер изображения может автоматически изменяться, поэтому максимальная ширина и высота равныmax_size.

def load_image(filename, max_size=None):
    image = PIL.Image.open(filename)

    if max_size is not None:
        # Calculate the appropriate rescale-factor for
        # ensuring a max height and width, while keeping
        # the proportion between them.
        factor = max_size / np.max(image.size)

        # Scale the image's height and width.
        size = np.array(image.size) * factor

        # The size is now floating-point because it was scaled.
        # But PIL requires the size to be integers.
        size = size.astype(int)

        # Resize the image.
        image = image.resize(size, PIL.Image.LANCZOS)

    # Convert to numpy floating-point array.
    return np.float32(image)

Сохраните изображение как файл jpeg. Данное изображение представляет собой массив numpy, содержащий значения пикселей от 0 до 255.

def save_image(image, filename):
    # Ensure the pixel-values are between 0 and 255.
    image = np.clip(image, 0.0, 255.0)

    # Convert to bytes.
    image = image.astype(np.uint8)

    # Write the image-file in jpeg-format.
    with open(filename, 'wb') as file:
        PIL.Image.fromarray(image).save(file, 'jpeg')

Эта функция рисует большое изображение. Данное изображение представляет собой массив numpy, содержащий значения пикселей от 0 до 255.

def plot_image_big(image):
    # Ensure the pixel-values are between 0 and 255.
    image = np.clip(image, 0.0, 255.0)

    # Convert pixels to bytes.
    image = image.astype(np.uint8)

    # Convert to a PIL-image and display it.
    display(PIL.Image.fromarray(image))

Эта функция рисует изображение содержимого, изображение слияния и изображение стиля.

def plot_images(content_image, style_image, mixed_image):
    # Create figure with sub-plots.
    fig, axes = plt.subplots(1, 3, figsize=(10, 10))

    # Adjust vertical spacing.
    fig.subplots_adjust(hspace=0.1, wspace=0.1)

    # Use interpolation to smooth pixels?
    smooth = True

    # Interpolation type.
    if smooth:
        interpolation = 'sinc'
    else:
        interpolation = 'nearest'

    # Plot the content-image.
    # Note that the pixel-values are normalized to
    # the [0.0, 1.0] range by dividing with 255.
    ax = axes.flat[0]
    ax.imshow(content_image / 255.0, interpolation=interpolation)
    ax.set_xlabel("Content")

    # Plot the mixed-image.
    ax = axes.flat[1]
    ax.imshow(mixed_image / 255.0, interpolation=interpolation)
    ax.set_xlabel("Mixed")

    # Plot the style-image
    ax = axes.flat[2]
    ax.imshow(style_image / 255.0, interpolation=interpolation)
    ax.set_xlabel("Style")

    # Remove ticks from all the plots.
    for ax in axes.flat:
        ax.set_xticks([])
        ax.set_yticks([])

    # Ensure the plot is shown correctly with multiple plots
    # in a single Notebook cell.
    plt.show()

функция потерь

Эти вспомогательные функции создают функции потерь, используемые в оптимизации TensorFlow.

Эта функция создает операцию TensorFlow, которая вычисляет минимальную среднеквадратичную ошибку двух входных тензоров.

def mean_squared_error(a, b):
    return tf.reduce_mean(tf.square(a - b))

Эта функция создает функцию потерь для изображения содержимого. Это минимальная средняя ошибка функций активации изображения контента и смешанного изображения в данном слое.Когда потери контента минимальны, это означает, что в данном слое функции активации смешанного изображения и изображения контента очень похожи.В зависимости от выбранного вами слоя, это перенесет контур изображения содержимого в смешанное изображение.

def create_content_loss(session, model, content_image, layer_ids):
    """
    Create the loss-function for the content-image.

    Parameters:
    session: An open TensorFlow session for running the model's graph.
    model: The model, e.g. an instance of the VGG16-class.
    content_image: Numpy float array with the content-image.
    layer_ids: List of integer id's for the layers to use in the model.
    """

    # Create a feed-dict with the content-image.
    feed_dict = model.create_feed_dict(image=content_image)

    # Get references to the tensors for the given layers.
    layers = model.get_layer_tensors(layer_ids)

    # Calculate the output values of those layers when
    # feeding the content-image to the model.
    values = session.run(layers, feed_dict=feed_dict)

    # Set the model's graph as the default so we can add
    # computational nodes to it. It is not always clear
    # when this is necessary in TensorFlow, but if you
    # want to re-use this code then it may be necessary.
    with model.graph.as_default():
        # Initialize an empty list of loss-functions.
        layer_losses = []

        # For each layer and its corresponding values
        # for the content-image.
        for value, layer in zip(values, layers):
            # These are the values that are calculated
            # for this layer in the model when inputting
            # the content-image. Wrap it to ensure it
            # is a const - although this may be done
            # automatically by TensorFlow.
            value_const = tf.constant(value)

            # The loss-function for this layer is the
            # Mean Squared Error between the layer-values
            # when inputting the content- and mixed-images.
            # Note that the mixed-image is not calculated
            # yet, we are merely creating the operations
            # for calculating the MSE between those two.
            loss = mean_squared_error(layer, value_const)

            # Add the loss-function for this layer to the
            # list of loss-functions.
            layer_losses.append(loss)

        # The combined loss for all layers is just the average.
        # The loss-functions could be weighted differently for
        # each layer. You can try it and see what happens.
        total_loss = tf.reduce_mean(layer_losses)

    return total_loss

Мы сделаем то же самое для слоя стиля,Но теперь нам нужно измерить, какие функции активированы как в слое стиля, так и в изображении стиля.Эти шаблоны активации затем копируются в смешанное изображение.

Один из способов — вычислить так называемую матрицу Грамма для выходных тензоров слоя стиля. Грамматрица — это, по сути, матрица точечного произведения векторов признаков активации в слое стиля.

Если элемент в Грам-матрице имеет значение, близкое к 0, это означает, что два признака данного слоя не активируются одновременно в образе стиля. И наоборот, если в Грам-матрице большое значение, это означает, что обе функции активируются одновременно. Далее мы попытаемся создать смешанное изображение, которое повторяет шаблон активации образа стиля.

Эта вспомогательная функция используется для вычисления матрицы Грамма выходных тензоров сверточных слоев в нейронной сети. Реальная функция потерь будет создана позже.

def gram_matrix(tensor):
    shape = tensor.get_shape()

    # Get the number of feature channels for the input tensor,
    # which is assumed to be from a convolutional layer with 4-dim.
    num_channels = int(shape[3])

    # Reshape the tensor so it is a 2-dim matrix. This essentially
    # flattens the contents of each feature-channel.
    matrix = tf.reshape(tensor, shape=[-1, num_channels])

    # Calculate the Gram-matrix as the matrix-product of
    # the 2-dim matrix with itself. This calculates the
    # dot-products of all combinations of the feature-channels.
    gram = tf.matmul(tf.transpose(matrix), matrix)

    return gram

Следующая функция создает функцию потерь для изображений стиля. это и вышеcreate_content_loss()Почти так же, за исключением того, что мы вычисляем наименьший квадрат ошибки матрицы Грамма вместо выходного тензора слоя.

def create_style_loss(session, model, style_image, layer_ids):
    """
    Create the loss-function for the style-image.

    Parameters:
    session: An open TensorFlow session for running the model's graph.
    model: The model, e.g. an instance of the VGG16-class.
    style_image: Numpy float array with the style-image.
    layer_ids: List of integer id's for the layers to use in the model.
    """

    # Create a feed-dict with the style-image.
    feed_dict = model.create_feed_dict(image=style_image)

    # Get references to the tensors for the given layers.
    layers = model.get_layer_tensors(layer_ids)

    # Set the model's graph as the default so we can add
    # computational nodes to it. It is not always clear
    # when this is necessary in TensorFlow, but if you
    # want to re-use this code then it may be necessary.
    with model.graph.as_default():
        # Construct the TensorFlow-operations for calculating
        # the Gram-matrices for each of the layers.
        gram_layers = [gram_matrix(layer) for layer in layers]

        # Calculate the values of those Gram-matrices when
        # feeding the style-image to the model.
        values = session.run(gram_layers, feed_dict=feed_dict)

        # Initialize an empty list of loss-functions.
        layer_losses = []

        # For each Gram-matrix layer and its corresponding values.
        for value, gram_layer in zip(values, gram_layers):
            # These are the Gram-matrix values that are calculated
            # for this layer in the model when inputting the
            # style-image. Wrap it to ensure it is a const,
            # although this may be done automatically by TensorFlow.
            value_const = tf.constant(value)

            # The loss-function for this layer is the
            # Mean Squared Error between the Gram-matrix values
            # for the content- and mixed-images.
            # Note that the mixed-image is not calculated
            # yet, we are merely creating the operations
            # for calculating the MSE between those two.
            loss = mean_squared_error(gram_layer, value_const)

            # Add the loss-function for this layer to the
            # list of loss-functions.
            layer_losses.append(loss)

        # The combined loss for all layers is just the average.
        # The loss-functions could be weighted differently for
        # each layer. You can try it and see what happens.
        total_loss = tf.reduce_mean(layer_losses)

    return total_loss

Функция потерь, используемая для шумоподавления смешанного изображения, создана ниже. Этот алгоритм называетсяTotal Variation Denoising, который фактически смещает изображение на один пиксель по осям x и y, вычисляет его отличие от исходного изображения, принимает абсолютное значение, чтобы убедиться, что разница положительна, и суммирует все пиксели по всему изображению. Этот шаг создает функцию потерь, которую можно минимизировать, чтобы подавить шум в изображении.

def create_denoise_loss(model):
    loss = tf.reduce_sum(tf.abs(model.input[:,1:,:,:] - model.input[:,:-1,:,:])) + \
           tf.reduce_sum(tf.abs(model.input[:,:,1:,:] - model.input[:,:,:-1,:]))

    return loss

алгоритм передачи стиля

Это основной алгоритм оптимизации передачи стиля. Это в основном делает градиентный спуск для тех функций потерь, которые определены выше.

Алгоритм также использует нормализацию функции потерь. Похоже, это новая идея, которая ранее не публиковалась. В каждой итерации оптимизации значения потерь корректируются так, чтобы они были равны единице. Это позволяет пользователю независимо устанавливать веса потерь для выбранного слоя стиля, а также слоя содержимого. В то же время веса также изменяются в процессе оптимизации, чтобы гарантировать сохранение желаемого веса между стилем, содержанием и шумоподавлением.

def style_transfer(content_image, style_image,
                   content_layer_ids, style_layer_ids,
                   weight_content=1.5, weight_style=10.0,
                   weight_denoise=0.3,
                   num_iterations=120, step_size=10.0):
    """
    Use gradient descent to find an image that minimizes the
    loss-functions of the content-layers and style-layers. This
    should result in a mixed-image that resembles the contours
    of the content-image, and resembles the colours and textures
    of the style-image.

    Parameters:
    content_image: Numpy 3-dim float-array with the content-image.
    style_image: Numpy 3-dim float-array with the style-image.
    content_layer_ids: List of integers identifying the content-layers.
    style_layer_ids: List of integers identifying the style-layers.
    weight_content: Weight for the content-loss-function.
    weight_style: Weight for the style-loss-function.
    weight_denoise: Weight for the denoising-loss-function.
    num_iterations: Number of optimization iterations to perform.
    step_size: Step-size for the gradient in each iteration.
    """

    # Create an instance of the VGG16-model. This is done
    # in each call of this function, because we will add
    # operations to the graph so it can grow very large
    # and run out of RAM if we keep using the same instance.
    model = vgg16.VGG16()

    # Create a TensorFlow-session.
    session = tf.InteractiveSession(graph=model.graph)

    # Print the names of the content-layers.
    print("Content layers:")
    print(model.get_layer_names(content_layer_ids))
    print()

    # Print the names of the style-layers.
    print("Style layers:")
    print(model.get_layer_names(style_layer_ids))
    print()

    # Create the loss-function for the content-layers and -image.
    loss_content = create_content_loss(session=session,
                                       model=model,
                                       content_image=content_image,
                                       layer_ids=content_layer_ids)

    # Create the loss-function for the style-layers and -image.
    loss_style = create_style_loss(session=session,
                                   model=model,
                                   style_image=style_image,
                                   layer_ids=style_layer_ids)    

    # Create the loss-function for the denoising of the mixed-image.
    loss_denoise = create_denoise_loss(model)

    # Create TensorFlow variables for adjusting the values of
    # the loss-functions. This is explained below.
    adj_content = tf.Variable(1e-10, name='adj_content')
    adj_style = tf.Variable(1e-10, name='adj_style')
    adj_denoise = tf.Variable(1e-10, name='adj_denoise')

    # Initialize the adjustment values for the loss-functions.
    session.run([adj_content.initializer,
                 adj_style.initializer,
                 adj_denoise.initializer])

    # Create TensorFlow operations for updating the adjustment values.
    # These are basically just the reciprocal values of the
    # loss-functions, with a small value 1e-10 added to avoid the
    # possibility of division by zero.
    update_adj_content = adj_content.assign(1.0 / (loss_content + 1e-10))
    update_adj_style = adj_style.assign(1.0 / (loss_style + 1e-10))
    update_adj_denoise = adj_denoise.assign(1.0 / (loss_denoise + 1e-10))

    # This is the weighted loss-function that we will minimize
    # below in order to generate the mixed-image.
    # Because we multiply the loss-values with their reciprocal
    # adjustment values, we can use relative weights for the
    # loss-functions that are easier to select, as they are
    # independent of the exact choice of style- and content-layers.
    loss_combined = weight_content * adj_content * loss_content + \
                    weight_style * adj_style * loss_style + \
                    weight_denoise * adj_denoise * loss_denoise

    # Use TensorFlow to get the mathematical function for the
    # gradient of the combined loss-function with regard to
    # the input image.
    gradient = tf.gradients(loss_combined, model.input)

    # List of tensors that we will run in each optimization iteration.
    run_list = [gradient, update_adj_content, update_adj_style, \
                update_adj_denoise]

    # The mixed-image is initialized with random noise.
    # It is the same size as the content-image.
    mixed_image = np.random.rand(*content_image.shape) + 128

    for i in range(num_iterations):
        # Create a feed-dict with the mixed-image.
        feed_dict = model.create_feed_dict(image=mixed_image)

        # Use TensorFlow to calculate the value of the
        # gradient, as well as updating the adjustment values.
        grad, adj_content_val, adj_style_val, adj_denoise_val \
        = session.run(run_list, feed_dict=feed_dict)

        # Reduce the dimensionality of the gradient.
        grad = np.squeeze(grad)

        # Scale the step-size according to the gradient-values.
        step_size_scaled = step_size / (np.std(grad) + 1e-8)

        # Update the image by following the gradient.
        mixed_image -= grad * step_size_scaled

        # Ensure the image has valid pixel-values between 0 and 255.
        mixed_image = np.clip(mixed_image, 0.0, 255.0)

        # Print a little progress-indicator.
        print(". ", end="")

        # Display status once every 10 iterations, and the last.
        if (i % 10 == 0) or (i == num_iterations - 1):
            print()
            print("Iteration:", i)

            # Print adjustment weights for loss-functions.
            msg = "Weight Adj. for Content: {0:.2e}, Style: {1:.2e}, Denoise: {2:.2e}"
            print(msg.format(adj_content_val, adj_style_val, adj_denoise_val))

            # Plot the content-, style- and mixed-images.
            plot_images(content_image=content_image,
                        style_image=style_image,
                        mixed_image=mixed_image)

    print()
    print("Final image:")
    plot_image_big(mixed_image)

    # Close the TensorFlow session to release its resources.
    session.close()

    # Return the mixed-image.
    return mixed_image

пример

В этом примере показано, как перенести стиль нескольких изображений на портрет.

Во-первых, мы загружаем изображение содержимого, которое имеет общий контур, необходимый для смешанного изображения.

content_filename = 'images/willy_wonka_old.jpg'
content_image = load_image(content_filename, max_size=None)

Затем мы загружаем изображение стиля, которое имеет желаемый цвет и текстуру для смешанного изображения.

style_filename = 'images/style7.jpg'
style_image = load_image(style_filename, max_size=300)

Затем мы определяем список целых чисел, представляющих слои в нейронной сети, которые мы используем для сопоставления с изображением контента. Это индексы слоев нейронной сети. Для модели VGG16 уровень 5 (индекс 4) кажется единственным допустимым уровнем содержимого.

content_layer_ids = [4]

Затем мы определяем еще один целочисленный массив для слоя стиля.

# The VGG16-model has 13 convolutional layers.
# This selects all those layers as the style-layers.
# This is somewhat slow to optimize.
style_layer_ids = list(range(13))

# You can also select a sub-set of the layers, e.g. like this:
# style_layer_ids = [1, 2, 3, 4]

Теперь выполните перенос стиля. Он автоматически создает соответствующие функции потерь для изображений стилей и изображений содержимого, а затем выполняет несколько итераций оптимизации. Это постепенно создаст смешанное изображение, имеющее общий контур изображения содержимого и похожее по текстуре, цвету и стилю на изображение.

Эта операция будет очень медленной на процессоре!

%%time
img = style_transfer(content_image=content_image,
                     style_image=style_image,
                     content_layer_ids=content_layer_ids,
                     style_layer_ids=style_layer_ids,
                     weight_content=1.5,
                     weight_style=10.0,
                     weight_denoise=0.3,
                     num_iterations=60,
                     step_size=10.0)

Content layers:
['conv3_1/conv3_1']

Style layers:
['conv1_1/conv1_1', 'conv1_2/conv1_2', 'conv2_1/conv2_1', 'conv2_2/conv2_2', 'conv3_1/conv3_1', 'conv3_2/conv3_2', 'conv3_3/conv3_3', 'conv4_1/conv4_1', 'conv4_2/conv4_2', 'conv4_3/conv4_3', 'conv5_1/conv5_1', 'conv5_2/conv5_2', 'conv5_3/conv5_3']

.
Iteration: 0
Weight Adj. for Content: 5.18e-11, Style: 2.14e-29, Denoise: 5.61e-06

. . . . . . . . . .
Iteration: 10
Weight Adj. for Content: 2.79e-11, Style: 4.13e-28, Denoise: 1.25e-07


. . . . . . . . . .
Iteration: 20
Weight Adj. for Content: 2.63e-11, Style: 1.09e-27, Denoise: 1.30e-07

. . . . . . . . . .
Iteration: 30
Weight Adj. for Content: 2.66e-11, Style: 1.27e-27, Denoise: 1.27e-07

. . . . . . . . . .
Iteration: 40
Weight Adj. for Content: 2.73e-11, Style: 1.16e-27, Denoise: 1.26e-07

. . . . . . . . . .
Iteration: 50
Weight Adj. for Content: 2.75e-11, Style: 1.12e-27, Denoise: 1.24e-07

. . . . . . . . .
Iteration: 59
Weight Adj. for Content: 1.85e-11, Style: 3.86e-28, Denoise: 1.01e-07

Final image:

CPU times: user 20min 1s, sys: 45.5 s, total: 20min 46s
Wall time: 3min 4s

Суммировать

Этот урок иллюстрирует основную идею использования нейронной сети для объединения содержимого и стиля двух изображений. К сожалению, результаты не так хороши, как в некоторых коммерческих системах, таких какDeepArt, который был разработан некоторыми из пионеров этой технологии. (Результат нехороший) Причина не ясна. Возможно, нам просто нужно больше вычислительной мощности, чтобы выполнять больше итераций оптимизации с меньшими шагами на изображениях с высоким разрешением. Возможно, нам нужны более сложные методы оптимизации. В следующем упражнении даются некоторые предложения, которые могут улучшить качество, и предлагается попробовать.

Упражнение

Ниже приведены некоторые рекомендуемые упражнения, которые могут улучшить ваши навыки работы с TensorFlow. Чтобы научиться правильно использовать TensorFlow, важен практический опыт.

Прежде чем вносить изменения в этот блокнот, вам может потребоваться создать его резервную копию.

  • Попробуйте использовать другие изображения. Некоторые образы стилей включены в эту статью. Вы можете использовать свои собственные изображения.
  • Попробуйте больше итераций (например, 1000–5000) и меньшие размеры шагов (например, 1,0–3,0). Улучшит ли это качество?
    * Измените вес слоя стиля, слоя содержимого и шумоподавления.
  • Попробуйте оптимизировать контент или стиль изображения, или, может быть, среднее из двух. Можно добавить немного шума.
  • Попробуйте изменить разрешение стиля и изображений содержимого. существуетload_image()функцию, вы можете использоватьmax_sizeпараметр для изменения размера изображения. Как это влияет на результаты?
  • Попробуйте использовать другие слои модели VGG-16.
  • Измените код так, чтобы он сохранял изображение каждые 10 итераций оптимизации.
  • Используйте постоянные веса во время оптимизации. Как это влияет на результаты?
  • Используйте разные веса в слое стиля. Опять же, попробуйте настроить веса автоматически, как и любую другую функцию потерь.
  • Замена базового градиентного спуска оптимизатором ADAM от TensorFlow.
  • Используйте оптимизатор L-BFGS. В настоящее время в TensorFlow нет реализации этого. Можно ли использовать оптимизатор, реализованный в SciPy, в алгоритме передачи стиля? Повышает ли это результаты?
  • Используйте другую предварительно обученную сеть, например модель Inception 5h, которую мы использовали в уроке №14, или модель VGG-19, которую вы нашли в Интернете.
  • Объясните другу, как работает программа.