Учебное пособие по TensorFlow № 13 — Визуальный анализ

искусственный интеллект TensorFlow Нейронные сети модульный тест
Учебное пособие по TensorFlow № 13 — Визуальный анализ

Заглавное изображение из:toyota.csail.mit.edu
В данной работе в основном проводится визуальный анализ сверточной нейронной сети.

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

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

Если воспроизводится, пожалуйста, прикрепите ссылку на эту статью.


вводить

В некоторых предыдущих руководствах по сверточным нейронным сетям мы показывали веса сверточных фильтров, например, в руководствах № 02 и № 06. Но, глядя только на веса фильтров, невозможно определить, что сверточный фильтр может идентифицировать из входного изображения.

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

Методы визуального анализа нейронных сетей также известны какмаксимизация возможностейили максимизация активации**.

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

блок-схема

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

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

from IPython.display import Image, display
Image('images/13_visual_analysis_flowchart.png')

Импортировать

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

# Functions and classes for loading and using the Inception model.
import inception

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

tf.__version__

'1.1.0'

Начальная модель

Загрузите модель Inception из Интернета.

Загрузите модель Inception из Интернета. Это папка по умолчанию, в которой вы сохраняете файлы данных. Папка создается автоматически, если она не существует.

# inception.data_dir = 'inception/'

Если исходной модели в папке нет, она будет загружена автоматически. Он имеет 85 МБ.

inception.maybe_download()

Downloading Inception v3 Model ...
Download progress: 100.0%
Download finished. Extracting files.
Done.

Имя свёрточного слоя

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

def get_conv_layer_names():
    # Load the Inception model.
    model = inception.Inception()

    # Create a list of names for the operations in the graph
    # for the Inception model where the operator-type is 'Conv2D'.
    names = [op.name for op in model.graph.get_operations() if op.type=='Conv2D']

    # Close the TensorFlow session inside the model-object.
    model.close()

    return names
conv_names = get_conv_layer_names()

Всего в начальной модели 94 сверточных слоя.

len(conv_names)

94

Запишите имена первых 5 сверточных слоев.

conv_names[:5]

['conv/Conv2D',
'conv_1/Conv2D',
'conv_2/Conv2D',
'conv_3/Conv2D',
'conv_4/Conv2D']

Напишите имена последних 5 сверточных слоев.

conv_names[-5:]

['mixed_10/tower_1/conv/Conv2D',
'mixed_10/tower_1/conv_1/Conv2D',
'mixed_10/tower_1/mixed/conv/Conv2D',
'mixed_10/tower_1/mixed/conv_1/Conv2D',
'mixed_10/tower_2/conv/Conv2D']

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

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

def optimize_image(conv_id=None, feature=0,
                   num_iterations=30, show_progress=True):
    """
    Find an image that maximizes the feature
    given by the conv_id and feature number.

    Parameters:
    conv_id: Integer identifying the convolutional layer to
             maximize. It is an index into conv_names.
             If None then use the last fully-connected layer
             before the softmax output.
    feature: Index into the layer for the feature to maximize.
    num_iteration: Number of optimization iterations to perform.
    show_progress: Boolean whether to show the progress.
    """

    # Load the Inception model. This is done for each call of
    # this function because we will add a lot to the graph
    # which will cause the graph to grow and eventually the
    # computer will run out of memory.
    model = inception.Inception()

    # Reference to the tensor that takes the raw input image.
    resized_image = model.resized_image

    # Reference to the tensor for the predicted classes.
    # This is the output of the final layer's softmax classifier.
    y_pred = model.y_pred

    # Create the loss-function that must be maximized.
    if conv_id is None:
        # If we want to maximize a feature on the last layer,
        # then we use the fully-connected layer prior to the
        # softmax-classifier. The feature no. is the class-number
        # and must be an integer between 1 and 1000.
        # The loss-function is just the value of that feature.
        loss = model.y_logits[0, feature]
    else:
        # If instead we want to maximize a feature of a
        # convolutional layer inside the neural network.

        # Get the name of the convolutional operator.
        conv_name = conv_names[conv_id]

        # Get a reference to the tensor that is output by the
        # operator. Note that ":0" is added to the name for this.
        tensor = model.graph.get_tensor_by_name(conv_name + ":0")

        # Set the Inception model's graph as the default
        # so we can add an operator to it.
        with model.graph.as_default():
            # The loss-function is the average of all the
            # tensor-values for the given feature. This
            # ensures that we generate the whole input image.
            # You can try and modify this so it only uses
            # a part of the tensor.
            loss = tf.reduce_mean(tensor[:,:,:,feature])

    # Get the gradient for the loss-function with regard to
    # the resized input image. This creates a mathematical
    # function for calculating the gradient.
    gradient = tf.gradients(loss, resized_image)

    # Create a TensorFlow session so we can run the graph.
    session = tf.Session(graph=model.graph)

    # Generate a random image of the same size as the raw input.
    # Each pixel is a small random value between 128 and 129,
    # which is about the middle of the colour-range.
    image_shape = resized_image.get_shape()
    image = np.random.uniform(size=image_shape) + 128.0

    # Perform a number of optimization iterations to find
    # the image that maximizes the loss-function.
    for i in range(num_iterations):
        # Create a feed-dict. This feeds the image to the
        # tensor in the graph that holds the resized image, because
        # this is the final stage for inputting raw image data.
        feed_dict = {model.tensor_name_resized_image: image}

        # Calculate the predicted class-scores,
        # as well as the gradient and the loss-value.
        pred, grad, loss_value = session.run([y_pred, gradient, loss],
                                             feed_dict=feed_dict)

        # Squeeze the dimensionality for the gradient-array.
        grad = np.array(grad).squeeze()

        # The gradient now tells us how much we need to change the
        # input image in order to maximize the given feature.

        # Calculate the step-size for updating the image.
        # This step-size was found to give fast convergence.
        # The addition of 1e-8 is to protect from div-by-zero.
        step_size = 1.0 / (grad.std() + 1e-8)

        # Update the image by adding the scaled gradient
        # This is called gradient ascent.
        image += step_size * grad

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

        if show_progress:
            print("Iteration:", i)

            # Convert the predicted class-scores to a one-dim array.
            pred = np.squeeze(pred)

            # The predicted class for the Inception model.
            pred_cls = np.argmax(pred)

            # Name of the predicted class.
            cls_name = model.name_lookup.cls_to_name(pred_cls,
                                               only_first_name=True)

            # The score (probability) for the predicted class.
            cls_score = pred[pred_cls]

            # Print the predicted score etc.
            msg = "Predicted class-name: {0} (#{1}), score: {2:>7.2%}"
            print(msg.format(cls_name, pred_cls, cls_score))

            # Print statistics for the gradient.
            msg = "Gradient min: {0:>9.6f}, max: {1:>9.6f}, stepsize: {2:>9.2f}"
            print(msg.format(grad.min(), grad.max(), step_size))

            # Print the loss-value.
            print("Loss:", loss_value)

            # Newline.
            print()

    # Close the TensorFlow session inside the model-object.
    model.close()

    return image.squeeze()

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

Функция нормализует изображение, чтобы значение пикселя находилось в диапазоне от 0,0 до 1,0.

def normalize_image(x):
    # Get the min and max values for all pixels in the input.
    x_min = x.min()
    x_max = x.max()

    # Normalize so all values are between 0.0 and 1.0
    x_norm = (x - x_min) / (x_max - x_min)

    return x_norm

Эта функция рисует изображение.

def plot_image(image):
    # Normalize the image so pixels are between 0.0 and 1.0
    img_norm = normalize_image(image)

    # Plot the image.
    plt.imshow(img_norm, interpolation='nearest')
    plt.show()

Эта функция рисует 6 графиков в системе координат.

def plot_images(images, show_size=100):
    """
    The show_size is the number of pixels to show for each image.
    The max value is 299.
    """

    # Create figure with sub-plots.
    fig, axes = plt.subplots(2, 3)

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

    # Use interpolation to smooth pixels?
    smooth = True

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

    # For each entry in the grid.
    for i, ax in enumerate(axes.flat):
        # Get the i'th image and only use the desired pixels.
        img = images[i, 0:show_size, 0:show_size, :]

        # Normalize the image so its pixels are between 0.0 and 1.0
        img_norm = normalize_image(img)

        # Plot the image.
        ax.imshow(img_norm, interpolation=interpolation)

        # Remove ticks.
        ax.set_xticks([])
        ax.set_yticks([])

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

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

Эта функция оптимизирует несколько изображений и рисует их.

def optimize_images(conv_id=None, num_iterations=30, show_size=100):
    """
    Find 6 images that maximize the 6 first features in the layer
    given by the conv_id.

    Parameters:
    conv_id: Integer identifying the convolutional layer to
             maximize. It is an index into conv_names.
             If None then use the last layer before the softmax output.
    num_iterations: Number of optimization iterations to perform.
    show_size: Number of pixels to show for each image. Max 299.
    """

    # Which layer are we using?
    if conv_id is None:
        print("Final fully-connected layer before softmax.")
    else:
        print("Layer:", conv_names[conv_id])

    # Initialize the array of images.
    images = []

    # For each feature do the following. Note that the
    # last fully-connected layer only supports numbers
    # between 1 and 1000, while the convolutional layers
    # support numbers between 0 and some other number.
    # So we just use the numbers between 1 and 7.
    for feature in range(1,7):
        print("Optimizing image for feature no.", feature)

        # Find the image that maximizes the given feature
        # for the network layer identified by conv_id (or None).
        image = optimize_image(conv_id=conv_id, feature=feature,
                               show_progress=False,
                               num_iterations=num_iterations)

        # Squeeze the dim of the array.
        image = image.squeeze()

        # Append to the list of images.
        images.append(image)

    # Convert to numpy-array so we can index all dimensions easily.
    images = np.array(images)

    # Plot the images.
    plot_images(images=images, show_size=show_size)

результат

Оптимизация изображений для неглубоких сверточных слоев

Например, ищем сверточный слойconv_names[conv_id]Входное изображение, в котором функция номер 2 максимизирована, гдеconv_id=5.

image = optimize_image(conv_id=5, feature=2,
                       num_iterations=30, show_progress=True)

Iteration: 0
Predicted class-name: dishwasher (#667), score: 4.81%
Gradient min: -0.000083, max: 0.000100, stepsize: 76290.32
Loss: 4.83793

Iteration: 1
Predicted class-name: kite (#397), score: 15.12%
Gradient min: -0.000142, max: 0.000126, stepsize: 71463.42
Loss: 5.59611

Iteration: 2
Predicted class-name: wall clock (#524), score: 6.85%
Gradient min: -0.000119, max: 0.000121, stepsize: 80427.39
Loss: 6.91725

...
Iteration: 28
Predicted class-name: bib (#941), score: 19.26%
Gradient min: -0.000043, max: 0.000043, stepsize: 214742.82
Loss: 17.7469

Iteration: 29
Predicted class-name: bib (#941), score: 18.87%
Gradient min: -0.000047, max: 0.000059, stepsize: 218511.00
Loss: 17.9321

plot_image(image)

Оптимизация нескольких изображений для сверточных слоев

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

optimize_images(conv_id=0, num_iterations=10)

Layer: conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5

optimize_images(conv_id=3, num_iterations=30)

Layer: conv_3/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=4, num_iterations=30)

Layer: conv_4/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=5, num_iterations=30)

Layer: mixed/conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=6, num_iterations=30)

Layer: mixed/tower/conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=7, num_iterations=30)

Layer: mixed/tower/conv_1/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=8, num_iterations=30)

Layer: mixed/tower_1/conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=9, num_iterations=30)

Layer: mixed/tower_1/conv_1/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=10, num_iterations=30)

Layer: mixed/tower_1/conv_2/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=20, num_iterations=30)

Layer: mixed_2/tower/conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=30, num_iterations=30)

Layer: mixed_4/conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=40, num_iterations=30)

Layer: mixed_5/conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=50, num_iterations=30)

Layer: mixed_6/conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=60, num_iterations=30)

Layer: mixed_7/conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=70, num_iterations=30)

Layer: mixed_8/tower/conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=80, num_iterations=30)

Layer: mixed_9/tower_1/conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=90, num_iterations=30)

Layer: mixed_10/tower_1/conv_1/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

optimize_images(conv_id=93, num_iterations=30)

Layer: mixed_10/tower_2/conv/Conv2D
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6

Последний полносвязный слой перед Softmax

Теперь мы оптимизируем и рисуем изображение для последнего слоя в исходной модели. Это полносвязный слой перед классификатором softmax.Особенность этого слоя соответствует категории выхода.

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

optimize_images(conv_id=None, num_iterations=30)

Final fully-connected layer before softmax.
Optimizing image for feature no. 1
Optimizing image for feature no. 2
Optimizing image for feature no. 3
Optimizing image for feature no. 4
Optimizing image for feature no. 5
Optimizing image for feature no. 6


Выше показано только изображение размером 100x100 пикселей, но на самом деле оно имеет размер 299x299 пикселей. Если мы выполним больше итераций оптимизации и отрисуем полное изображение, могут быть некоторые узнаваемые закономерности. Итак, давайте еще раз оптимизируем первое изображение и нарисуем его в полном разрешении.

Начальная модель классифицирует результирующее изображение примерно со 100% уверенностью как «гладкая лиса», но для человеческого глаза изображение представляет собой просто какой-то абстрактный узор.

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

image = optimize_image(conv_id=None, feature=1,
                       num_iterations=100, show_progress=True)

Iteration: 0
Predicted class-name: dishwasher (#667), score: 4.98%
Gradient min: -0.006252, max: 0.004451, stepsize: 3734.48
Loss: -0.837608

Iteration: 1
Predicted class-name: ballpoint (#907), score: 8.52%
Gradient min: -0.007303, max: 0.006427, stepsize: 2152.89
Loss: -0.416723
...
Iteration: 98
Predicted class-name: kit fox (#1), score: 100.00%
Gradient min: -0.007732, max: 0.010692, stepsize: 1286.44
Loss: 67.5603

Iteration: 99
Predicted class-name: kit fox (#1), score: 100.00%
Gradient min: -0.005850, max: 0.006159, stepsize: 1863.65
Loss: 75.6356

plot_image(image=image)

Закройте сеанс TensorFlow.

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

Суммировать

В этом руководстве показано, как оптимизировать входное изображение, чтобы максимально использовать возможности нейронной сети.Поскольку данный признак (или нейрон) в нейронной сети наиболее сильно реагирует на конкретное изображение, что позволяет нам визуализировать то, что ему «нравится видеть».

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

это почему? Напомним из урока № 11, что модель Inception легко обмануть каким-то враждебным шумом и классифицировать любое входное изображение в другой целевой класс. Поэтому нетрудно представить, что модель Inception может распознавать эти абстрактные паттерны изображения, непонятные человеческому глазу. Может быть бесконечное количество изображений, которые максимизируют внутренние особенности нейронной сети, и люди могут распознавать только небольшое их подмножество. Это может быть причиной того, что процесс оптимизации находит только абстрактные шаблоны изображений.

Другие методы

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

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

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

Упражнение

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

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

  • Попробуйте запустить несколько оптимизаций для функций нижних слоев в сети. Всегда ли результирующее изображение одинаково?
  • Попробуйте повторить с меньшим или большим количеством оптимизаций. Как это влияет на качество изображения?
  • Попробуйте изменить функцию потерь сверточных функций. Это можно сделать разными способами. Как это повлияет на режим шаблона? Зачем?
  • Как вы думаете, расширит ли оптимизатор другие функции помимо той, которую мы хотим максимизировать? Как вы собираетесь это измерять? Вы уверены, что оптимизатор будет максимизировать только одну функцию за раз?
  • Постарайтесь максимально использовать несколько функций одновременно.
  • Обучите меньшую сеть на наборе данных MNIST и попытайтесь визуализировать функции и слои. Было бы легче увидеть узор на изображении?
  • Попробуйте реализовать метод из приведенной выше статьи.
  • Попробуйте свой собственный способ улучшить оптимизированное изображение.
  • Объясните другу, как работает программа.