Реализация модели перцептрона в Python

Для реализации модели полносвязного перцептрона на языке Python воспользуемся ранее созданным нами шаблоном. Как вы помните, в шаблоне мы оставили не заполненными описание нейронных слоев нашей модели.

# Создание модели нейронной сети
model = keras.Sequential([keras.Input(shape=inputs),
                         # Наполнить модель описанием нейронных слоев
                         ])

Для создания полносвязных слоев в нейронной сети библиотека Keras предоставляет класс layers.Dense. Внутри данного слоя реализуется операция:

где:

  • activation — функция активации, задается в параметрах;
  • input — массив исходных данных;
  • kernel — матрица весов;
  • dot — операция векторного умножения;
  • bias — элемент смещения.

Для управления процессом создания нейронного слоя Dense имеет ряд параметров:

  • units — размерность выходного пространства (количество нейронов в слое);
  • activation — используемая функция активации;
  • use_bias — необязательный параметр, указывающий на необходимость использования вектора элементов смещения;
  • kernel_initializer — метод инициализации матрицы весов;
  • bias_initializer — метод инициализации вектора элементов смещения;
  • kernel_regularizer — метод регуляризации матрицы весов;
  • bias_regularizer — метод регуляризации вектора смещения;
  • activity_regularizer — метод регуляризации функции активации;
  • kernel_constraint — функция ограничения матрицы весов;
  • bias_constraint — функция ограничения вектора смещения.

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

Дополнительно к указанным выше параметрам Dense может принимать параметр input_shape, указывающий на размер массива входных данных. Параметр допустим только для первого слоя нейронной сети. В случае использования параметра создается входной слой для вставки перед текущим слоем. Операцию можно рассматривать как эквивалент явного определения входного слоя.

Реализацию своей первой модели нейронной сети мы начнем с копирования нашего шаблона скрипта в новый файл perceptron.py. В созданном файле создадим первую модель с одним скрытым слоем из 40 нейронов и 2 нейронами в слое результатов. В скрытом слое будем использовать Swish в качестве функции активации. Нейроны выходного слоя будут активироваться гиперболическим тангенсом.

# Создание модели нейронной сети
model1 = keras.Sequential([keras.Input(shape=inputs),
                           keras.layers.Dense(40, activation=tf.nn.swish), 
                           keras.layers.Dense(targerts, activation=tf.nn.tanh) 
                          ])

В принципе этого достаточно для запуска обучения модели. Но мы с вами изучаем работу различных моделей и хотим понять влияние изменения архитектуры нейронной сети на способность модели обучаться и обобщать исходные данные. Поэтому я добавил еще две модели. В одной модели я добавил еще два скрытых слоя. В итоге получилась модель с тремя скрытыми слоями. Все три скрытых слоя полностью идентичны: имеют по 40 элементов и активируются функцией Swish. Первый и последний слои остались без изменений.

# Создание модели с тремя скрытыми слоями
model2 = keras.Sequential([keras.Input(shape=inputs),
                           keras.layers.Dense(40, activation=tf.nn.swish), 
                           keras.layers.Dense(40, activation=tf.nn.swish), 
                           keras.layers.Dense(40, activation=tf.nn.swish), 
                           keras.layers.Dense(targerts, activation=tf.nn.tanh) 
                         ])

И конечно, нам нужно будет повторить и все последующие шаги для каждой модели. Сначала подготовим модель к обучению с помощью метода compile.

model2.compile(optimizer='Adam'
               loss='mean_squared_error'
               metrics=['accuracy'])

После этого запустим процесс обучения модели и сохраним обученную модель.

history2 = model2.fit(train_data, train_target,
                      epochs=500, batch_size=1000,
                      callbacks=[callback],
                      verbose=2,
                      validation_split=0.2,
                      shuffle=True)
model2.save(os.path.join(path,'perceptron2.h5'))

Третью модель построим на базе второй с добавлением регуляризации. Для каждого нейронного слоя укажем в параметре kernel_regularizer объект класса keras.regularizers.l1_l2 с параметрами L1 и L2-регуляризации. Как видно из названия класса, мы будем использовать ElasticNet.

# Добавляем регуляризацию в модель с тремя скрытыми слоями
model3 = keras.Sequential([keras.Input(shape=inputs),
               keras.layers.Dense(40, activation=tf.nn.swish,
                  kernel_regularizer=keras.regularizers.l1_l2(l1=1e-7, l2=1e-5)), 
               keras.layers.Dense(40, activation=tf.nn.swish,
                  kernel_regularizer=keras.regularizers.l1_l2(l1=1e-7, l2=1e-5)), 
               keras.layers.Dense(40, activation=tf.nn.swish,
                  kernel_regularizer=keras.regularizers.l1_l2(l1=1e-7, l2=1e-5)), 
               keras.layers.Dense(targerts, activation=tf.nn.tanh) 
                         ])

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

model3.compile(optimizer='Adam'
               loss='mean_squared_error'
               metrics=['accuracy'])
history3 = model3.fit(train_data, train_target,
                      epochs=500, batch_size=1000,
                      callbacks=[callback],
                      verbose=2,
                      validation_split=0.2,
                      shuffle=True)
model3.save(os.path.join(path,'perceptron3.h5'))

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

# Отрисовка результатов обучения трех моделей
plt.figure()
plt.plot(history1.history['loss'],
         label='Train 1 hidden layer')
plt.plot(history1.history['val_loss'],
         label='Validation 1 hidden layer')
plt.plot(history2.history['loss'],
         label='Train 3 hidden layers')
plt.plot(history2.history['val_loss'],
         label='Validation 3 hidden layers')
plt.plot(history3.history['loss'],
         label='Train 3 hidden layers vs regularization')
plt.plot(history3.history['val_loss'],
         label='Validation 3 hidden layer vs regularization')
plt.ylabel('$MSE$ $Loss$')
plt.xlabel('$Epochs$')
plt.title('Dynamic of Models train')
plt.legend(loc='lower left')

plt.figure()
plt.plot(history1.history['accuracy'],
         label='Train 1 hidden layer')
plt.plot(history1.history['val_accuracy'],
         label='Validation 1 hidden layer')
plt.plot(history2.history['accuracy'],
         label='Train 3 hidden layers')
plt.plot(history2.history['val_accuracy'],
         label='Validation 3 hidden layers')
plt.plot(history3.history['accuracy'],
         label='Train 3 hidden layers\nvs regularization')
plt.plot(history3.history['val_accuracy'],
         label='Validation 3 hidden layer\nvs regularization')
plt.ylabel('$Accuracy$')
plt.xlabel('$Epochs$')
plt.title('Dynamic of Models train')
plt.legend(loc='upper left')

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

# Проверка результатов моделей на тестовой выборке
test_loss1, test_acc1 = model1.evaluate(test_data,
                                        test_target,
                                        verbose=2
test_loss2, test_acc2 = model2.evaluate(test_data,
                                        test_target,
                                        verbose=2
test_loss3, test_acc3 = model3.evaluate(test_data,
                                        test_target,
                                        verbose=2)

Результаты тестирования в шаблоне выводились в журнал. Но сейчас мы имеем результаты тестирования трех моделей. Более наглядно будет сравнить результаты на графике. Для построения графиков воспользуемся средствами библиотеки Matplolib.

В данном случае мы будем отображать не динамику процесса, как ранее, а сравнивать величины. Следовательно, для отображения величин удобнее будет приметь столбчатую диаграмму. Для построения диаграмм библиотека предлагает метод bar. Этот метод в параметрах принимает два массива: в первом мы укажем метки сравниваемых параметров, а во втором — их величины. Для полноты картины добавим название графика и вертикальной оси с помощью методов title и ylabel, соответственно.

plt.figure()
plt.bar(
    ['1 hidden layer','3 hidden layers''3 hidden layers\nvs regularization'],
    [test_loss1,test_loss2,test_loss3])
plt.ylabel('$MSE$ $Loss$')
plt.title('Result of test')
 
plt.figure()
plt.bar(
    ['1 hidden layer','3 hidden layers''3 hidden layers\nvs regularization'],
    [test_acc1,test_acc2,test_acc3])
plt.ylabel('$Accuracy$')
plt.title('Result of test')

С работой скрипта познакомимся немного позже. В следующей главе мы подготовим данные для обучения и тестирования моделей.