Keras - Сохранить встраивание изображения из набора данных mnist

Я написал следующую простую сеть MLP для MNIST дб.

from __future__ import print_function

import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras import callbacks


batch_size = 100
num_classes = 10
epochs = 20

tb = callbacks.TensorBoard(log_dir='/Users/shlomi.shwartz/tensorflow/notebooks/logs/minist', histogram_freq=10, batch_size=32,
                           write_graph=True, write_grads=True, write_images=True,
                           embeddings_freq=10, embeddings_layer_names=None,
                           embeddings_metadata=None)

early_stop = callbacks.EarlyStopping(monitor='val_loss', min_delta=0,
                     patience=3, verbose=1, mode='auto')


# the data, shuffled and split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()

x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

model = Sequential()
model.add(Dense(200, activation='relu', input_shape=(784,)))
model.add(Dropout(0.2))
model.add(Dense(100, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(60, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(30, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(10, activation='softmax'))

model.summary()

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

history = model.fit(x_train, y_train,
                    callbacks=[tb,early_stop],
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

Модель работала нормально, и я мог видеть информацию о скалярах на TensorBoard. Однако, когда я изменил embeddings_freq=10, чтобы попытаться визуализировать изображения (как показано здесь), я получил следующую ошибку:

Traceback (most recent call last):
  File "/Users/shlomi.shwartz/IdeaProjects/TF/src/minist.py", line 65, in <module>
    validation_data=(x_test, y_test))
  File "/Users/shlomi.shwartz/tensorflow/lib/python3.6/site-packages/keras/models.py", line 870, in fit
    initial_epoch=initial_epoch)
  File "/Users/shlomi.shwartz/tensorflow/lib/python3.6/site-packages/keras/engine/training.py", line 1507, in fit
    initial_epoch=initial_epoch)
  File "/Users/shlomi.shwartz/tensorflow/lib/python3.6/site-packages/keras/engine/training.py", line 1117, in _fit_loop
    callbacks.set_model(callback_model)
  File "/Users/shlomi.shwartz/tensorflow/lib/python3.6/site-packages/keras/callbacks.py", line 52, in set_model
    callback.set_model(model)
  File "/Users/shlomi.shwartz/tensorflow/lib/python3.6/site-packages/keras/callbacks.py", line 719, in set_model
    self.saver = tf.train.Saver(list(embeddings.values()))
  File "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/tensorflow/python/training/saver.py", line 1139, in __init__
    self.build()
  File "/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/tensorflow/python/training/saver.py", line 1161, in build
    raise ValueError("No variables to save")
ValueError: No variables to save

Q: Чего мне не хватает? Это правильный способ сделать это в Керасе?

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

3 ответа

Решение

Что называется "встраивание" здесь в callbacks.TensorBoard в широком смысле, любой слой веса. Согласно документации Keras:

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

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

В приведенном вами примере MLP не хватает embeddings_layer_names аргумент. Вы должны выяснить, какие слои вы собираетесь визуализировать. Предположим, вы хотите визуализировать вес (или, kernel в Керасе) всего Dense слои, вы можете указать embeddings_layer_names как это:

model = Sequential()
model.add(Dense(200, activation='relu', input_shape=(784,)))
model.add(Dropout(0.2))
model.add(Dense(100, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(60, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(30, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(10, activation='softmax'))

embedding_layer_names = set(layer.name
                            for layer in model.layers
                            if layer.name.startswith('dense_'))

tb = callbacks.TensorBoard(log_dir='temp', histogram_freq=10, batch_size=32,
                           write_graph=True, write_grads=True, write_images=True,
                           embeddings_freq=10, embeddings_metadata=None,
                           embeddings_layer_names=embedding_layer_names)

model.compile(...)
model.fit(...)

Затем вы можете увидеть что-то вроде этого в TensorBoard: tensorboard

Вы можете увидеть соответствующие строки в источнике Keras, если вы хотите выяснить, что происходит с embeddings_layer_names,


Редактировать:

Итак, вот грязное решение для визуализации выходных данных слоя. Так как оригинал TensorBoard обратный вызов не поддерживает это, реализация нового обратного вызова кажется неизбежной.

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

import tensorflow as tf
from tensorflow.contrib.tensorboard.plugins import projector
from keras import backend as K
from keras.models import Model
from keras.callbacks import TensorBoard

class TensorResponseBoard(TensorBoard):
    def __init__(self, val_size, img_path, img_size, **kwargs):
        super(TensorResponseBoard, self).__init__(**kwargs)
        self.val_size = val_size
        self.img_path = img_path
        self.img_size = img_size

    def set_model(self, model):
        super(TensorResponseBoard, self).set_model(model)

        if self.embeddings_freq and self.embeddings_layer_names:
            embeddings = {}
            for layer_name in self.embeddings_layer_names:
                # initialize tensors which will later be used in `on_epoch_end()` to
                # store the response values by feeding the val data through the model
                layer = self.model.get_layer(layer_name)
                output_dim = layer.output.shape[-1]
                response_tensor = tf.Variable(tf.zeros([self.val_size, output_dim]),
                                              name=layer_name + '_response')
                embeddings[layer_name] = response_tensor

            self.embeddings = embeddings
            self.saver = tf.train.Saver(list(self.embeddings.values()))

            response_outputs = [self.model.get_layer(layer_name).output
                                for layer_name in self.embeddings_layer_names]
            self.response_model = Model(self.model.inputs, response_outputs)

            config = projector.ProjectorConfig()
            embeddings_metadata = {layer_name: self.embeddings_metadata
                                   for layer_name in embeddings.keys()}

            for layer_name, response_tensor in self.embeddings.items():
                embedding = config.embeddings.add()
                embedding.tensor_name = response_tensor.name

                # for coloring points by labels
                embedding.metadata_path = embeddings_metadata[layer_name]

                # for attaching images to the points
                embedding.sprite.image_path = self.img_path
                embedding.sprite.single_image_dim.extend(self.img_size)

            projector.visualize_embeddings(self.writer, config)

    def on_epoch_end(self, epoch, logs=None):
        super(TensorResponseBoard, self).on_epoch_end(epoch, logs)

        if self.embeddings_freq and self.embeddings_ckpt_path:
            if epoch % self.embeddings_freq == 0:
                # feeding the validation data through the model
                val_data = self.validation_data[0]
                response_values = self.response_model.predict(val_data)
                if len(self.embeddings_layer_names) == 1:
                    response_values = [response_values]

                # record the response at each layers we're monitoring
                response_tensors = []
                for layer_name in self.embeddings_layer_names:
                    response_tensors.append(self.embeddings[layer_name])
                K.batch_set_value(list(zip(response_tensors, response_values)))

                # finally, save all tensors holding the layer responses
                self.saver.save(self.sess, self.embeddings_ckpt_path, epoch)

Чтобы использовать это:

tb = TensorResponseBoard(log_dir=log_dir, histogram_freq=10, batch_size=10,
                         write_graph=True, write_grads=True, write_images=True,
                         embeddings_freq=10,
                         embeddings_layer_names=['dense_1'],
                         embeddings_metadata='metadata.tsv',
                         val_size=len(x_test), img_path='images.jpg', img_size=[28, 28])

Перед запуском TensorBoard вам необходимо сохранить метки и изображения в log_dir для визуализации:

from PIL import Image
img_array = x_test.reshape(100, 100, 28, 28)
img_array_flat = np.concatenate([np.concatenate([x for x in row], axis=1) for row in img_array])
img = Image.fromarray(np.uint8(255 * (1. - img_array_flat)))
img.save(os.path.join(log_dir, 'images.jpg'))
np.savetxt(os.path.join(log_dir, 'metadata.tsv'), np.where(y_test)[1], fmt='%d')

Вот результат:

TensorResponseBoard

Вам нужен как минимум один слой для встраивания в Keras. На статистике было хорошее объяснение о них. Это не напрямую для Кераса, но концепции примерно одинаковы. Что такое слой встраивания в нейронную сеть?

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

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

Итог: хотя это нетривиально, это действительно возможно сделать с помощью Keras. Вам не нужны обратные вызовы Keras; все, что вам нужно, это предсказания вашей модели, необходимые метаданные и изображение спрайта, а также некоторый чистый код TensorFlow. Так,

Шаг 1 - получите прогнозы вашей модели для тестового набора:

emb = model.predict(x_test) # 'emb' for embedding

Шаг 2а - создайте файл метаданных с реальными метками тестового набора:

import numpy as np

LOG_DIR = '/home/herc/SO/tmp'  # FULL PATH HERE!!!

metadata_file = os.path.join(LOG_DIR, 'metadata.tsv')
with open(metadata_file, 'w') as f:
    for i in range(len(y_test)):
        c = np.nonzero(y_test[i])[0][0]
        f.write('{}\n'.format(c))

Шаг 2б - получить спрайт-изображение mnist_10k_sprite.png как предусмотрено парнями из TensorFlow здесь, и поместите его в свой LOG_DIR

Шаг 3 - написать код Tensorflow:

import tensorflow as tf
from tensorflow.contrib.tensorboard.plugins import projector

embedding_var = tf.Variable(emb,  name='final_layer_embedding')
sess = tf.Session()
sess.run(embedding_var.initializer)
summary_writer = tf.summary.FileWriter(LOG_DIR)
config = projector.ProjectorConfig()
embedding = config.embeddings.add()
embedding.tensor_name = embedding_var.name

# Specify the metadata file:
embedding.metadata_path = os.path.join(LOG_DIR, 'metadata.tsv')

# Specify the sprite image: 
embedding.sprite.image_path = os.path.join(LOG_DIR, 'mnist_10k_sprite.png')
embedding.sprite.single_image_dim.extend([28, 28]) # image size = 28x28

projector.visualize_embeddings(summary_writer, config)
saver = tf.train.Saver([embedding_var])
saver.save(sess, os.path.join(LOG_DIR, 'model2.ckpt'), 1)

Затем, запустив Tensorboard в вашем LOG_DIRи выбрав цвет по метке, вот что вы получите:

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

Другие вопросы по тегам