[Из песочницы] Мозг изнутри (Визуализация прохождения паттерна через модель искусственной нейронной сети)
Введение
Статья предназначена для тех, кто когда-либо интересовался вопросом о том что же происходит внутри искусственной нейронной сети (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(...)
установить
def PLOT_PATTERN_PROCCESS(model, pattern, FOLDER_TO_SAVE, grid_size=(3, 3), limit_size_layer=(15, 15), PLOT_MODEL=True):
Основная идея
Необходимо выбрать один паттерн (прохождение, которого мы будем наблюдать), затем скомпилированная сеть разбивается на слои 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 классов.
_________________________________________________________________
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
постепенно заполняется данными:
- Номер слоя
num_layer
- Имя слоя
model.layers[num_layer].name
- Выходной двумерный массив
_output.tolist()
Исключая постепенно по одному результату из
SAVE_AR_LIST
и помещая его в ячейку холста
ax.imshow(np.array(ar), cmap='viridis', extent=(xmin, xmax, ymin, ymax))
На выходе создается файл (0.png)
Рекомендации
Успехов!