[Из песочницы] Мозг изнутри (Визуализация прохождения паттерна через модель искусственной нейронной сети)

Введение


Статья предназначена для тех, кто когда-либо интересовался вопросом о том что же происходит внутри искусственной нейронной сети (artificial neural network) — ИНС. Сейчас разработать собственную ИНС может практически каждый используя уже готовые библиотеки, в большинстве языков программирования. В рассматриваемой статье я постараюсь показать как именно выглядит объект (Паттерн) проходящий через слои ИНС, разработанной и скомпилированной при помощи библиотеки глубокого обучения Tensorflow с надстройкой Keras.

Используемое ПО


Необходимы следующие компоненты (версии я указал для своего случая):

  • tensorflow 1.10.0
  • keras 2.2.4
  • matplotlib 2.2.0
  • modul-os
  • numpy1.14.3


Также есть возможность нарисовать архитектуру сети, но для этого необходимо установить средства визуализации keras и в методе

PLOT_PATTERN_PROCCESS(...)


установить

PLOT_MODEL=True
def PLOT_PATTERN_PROCCESS(model, pattern, FOLDER_TO_SAVE, grid_size=(3, 3), limit_size_layer=(15, 15), PLOT_MODEL=True):

image


Основная идея


Необходимо выбрать один паттерн (прохождение, которого мы будем наблюдать), затем скомпилированная сеть разбивается на слои tensor’ов. В цикле от второго до последнего слоя, создается новая сеть где ее выход это номер слоя по циклу и пропуская паттерн, на её выходе получается результат в виде n-мерного массива.

Реализация


Подключение библиотек

from keras.models import *
from keras.layers import *
import matplotlib.pyplot as plt
import os
import numpy as np


Используемые методы:

  • def PLOT_PATTERN_PROCCESS (model, pattern, FOLDER_TO_SAVE, grid_size=(3, 3), limit_size_layer=(15, 15), PLOT_MODEL=True):
    def PLOT_PATTERN_PROCCESS(model, pattern, FOLDER_TO_SAVE, grid_size=(3, 3), limit_size_layer=(15, 15), PLOT_MODEL=True):
        """
        :param model: Модель нейроархитектуры keras
        :type model: Sequential
        :param pattern: Входной паттерн, массив данных соответвующий размеру входных слоев
        :type pattern: np.array
        :param FOLDER_TO_SAVE: Папка в которую будет сохраняться результат
        :type FOLDER_TO_SAVE: str
        :param grid_size: Размер отображаемой сетки слоев
        :type grid_size: tuple
        :param limit_size_layer: Минимальный размер для отображения слоя
        :type limit_size_layer: tuple
        :param PLOT_MODEL: Выполнить построение модели
        :type PLOT_MODEL: PLOT_MODEL
        """
        SAVE_AR_LIST = []
        for num_layer in range(1, len(model.layers)):
            LO = model.layers[num_layer].output
            _model = Model(inputs=model.input, outputs=LO)
            if (
                    len(_model.output_shape) == 3 and
                    _model.output_shape[1] > limit_size_layer[0] and
                    _model.output_shape[2] > limit_size_layer[1]
            ):
                _output = _model.predict(pattern)[0]
                SAVE_AR_LIST.append(
                    [
                        num_layer,
                        model.layers[num_layer].name,
                        _output.tolist()
                    ]
                )
        ###
        PIC_NUM = 0
        while len(SAVE_AR_LIST) > 0:
            fig, axs = plt.subplots(nrows=grid_size[0], ncols=grid_size[1], figsize=(10, 10), tight_layout=True)
            xmin, xmax = plt.xlim()
            ymin, ymax = plt.ylim()
            for ax in axs.flat:
                [num_layer, layer_name, ar] = SAVE_AR_LIST.pop(0)
                ax.imshow(np.array(ar), cmap='viridis', extent=(xmin, xmax, ymin, ymax))
                ax.set_title(layer_name + " " + str(np.array(ar).shape))
                if len(SAVE_AR_LIST) == 0:
                    break
            # plt.show()
            plt.savefig(os.path.join(FOLDER_TO_SAVE, str(PIC_NUM) + '.png'), fmt='png')
            plt.close(fig)
            PIC_NUM += 1
        ###
        if PLOT_MODEL:
            from keras.utils.vis_utils import plot_model
            plot_model(
                model=model,
                to_file=os.path.join(FOLDER_TO_SAVE, model.name + " neural network architecture.png"),
                show_shapes=True,
                show_layer_names=True
            )
        ###
    
    

  • def build_model (IN_SHAPE=50, CLASSES=5) → Sequential:
    def build_model(IN_SHAPE=50,CLASSES=5) -> Sequential:
    
        inputs_LAYER0 = Input(shape=(IN_SHAPE,IN_SHAPE))
        Dense_2_2 = Dense(75, activation='relu')(inputs_LAYER0)
        Dense_2_3 = Dense(50, activation='relu', name="my_dense")(Dense_2_2)
        Dense_2_4 = Dense(25, activation='relu')(Dense_2_3)
        Dense_2_5 = Dense(10, activation='relu')(Dense_2_4)
        flat_f_0 = Flatten()(Dense_2_5)
        final_layer= Dense(CLASSES, activation='softmax')(flat_f_0)
        #
        model = Model(input=inputs_LAYER0, output=final_layer, name="simple model")
        model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
        model.summary()
        return model
    


Код программы

model_ = build_model()
pattern = np.random.sample((1,50,50))
os.makedirs("PLOT_PATTERN_PROCCESS")
PLOT_PATTERN_PROCCESS(
    model = model_,
    pattern = pattern,
    FOLDER_TO_SAVE = "PLOT_PATTERN_PROCCESS",
    PLOT_MODEL=False,
    grid_size=(2, 2)
)


Описание работы программы


Метод

build_model()


возвращвет модель ИНС в формате Sequential, предназначенную для классификации чего-либо в 5 классов.

model.summary ()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 50, 50)            0         
_________________________________________________________________
dense_1 (Dense)              (None, 50, 75)            3825      
_________________________________________________________________
my_dense (Dense)             (None, 50, 50)            3800      
_________________________________________________________________
dense_2 (Dense)              (None, 50, 25)            1275      
_________________________________________________________________
dense_3 (Dense)              (None, 50, 10)            260       
_________________________________________________________________
flatten_1 (Flatten)          (None, 500)               0         
_________________________________________________________________
dense_4 (Dense)              (None, 5)                 2505      
=================================================================
Total params: 11,665
Trainable params: 11,665
Non-trainable params: 0
_________________________________________________________________


Как видно из архитектуры паттерн, это массив размером 50×50. Переменная

pattern


и есть наблюдаемый объект.
Далее создается директория

os.makedirs("PLOT_PATTERN_PROCCESS")


куда будет сохранятья весть результат.

Описание метода PLOT_PATTERN_PROCCESS


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

_output = _model.predict(pattern)[0]


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

limit_size_layer


Поочередно перебирая слои модели ИНС переменная

SAVE_AR_LIST

постепенно заполняется данными:

  1. Номер слоя
    num_layer
    
  2. Имя слоя
    model.layers[num_layer].name
    
  3. Выходной двумерный массив
     _output.tolist()
    


Исключая постепенно по одному результату из

SAVE_AR_LIST


и помещая его в ячейку холста

ax.imshow(np.array(ar), cmap='viridis', extent=(xmin, xmax, ymin, ymax))


На выходе создается файл (0.png)

image

Рекомендации


Успехов!

© Habrahabr.ru