Методы тонкой настройки нейросети. Keras

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

Главная проблемы многослойних нейросите и вообще глубокого обучения это эффект переобучения. Когда сеть перестает улучщать свои прособности обобщать на тестовых данных. По сути она запоминает тренировочные данные и только на них показывать хороший резутьтат.

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

Базовая модель.

Задача будет у нас достаточно типичканая : Классификация картинок (самолет, автомобиль, птица, кошка, олень, собака, лягушка, лошадь, корабль и грузовик). Для этого мы будем использовать CIFAR-10, датасет который входит в библиотеку Keras.

База CIFAR-10 это набор цветных изображений 32 x 32 x 3 и имеет десят классов ( самолет, автомобиль, птица, кошка, олень, собака, лягушка, лошадь, корабль и грузовик ) 50 000 тренировочных картонок,10 000 тестовых.

from keras.datasets import cifar10

Модель будет выглядеть так :

  • Сверточный слой ( 32 карты, ядро 3х3, функция активации RuLu).
  • Сверточный слой ( 32 карты, ядро 3х3, функция активации RuLu).
  • МаксПуллинг слой ( ядро 2х2).
  • Сверточный слой ( 64 карты, ядро 3х3, функция активации RuLu).
  • Сверточный слой ( 64 карты, ядро 3х3, функция активации RuLu).
  • МаксПуллинг слой ( ядро 2х2).
  • Создаем вектор для полносвязного слоя( Flatten() ).
  • Полносвязный слой (512 нейронов вход , 10 классов выход)
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same',
                 input_shape=x_train.shape[1:]))
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dense(num_classes))
model.add(Activation('softmax'))

Запускаем обучение, 30 эпох. Результат конечно печальный.

L_2-регуляризация.

Одной из основных проблем машинного обучения является проблема переобучения (overfitting), когда модель в погоне за минимизацией затрат на обучение теряет способность к обобщению. Простой способ держать переобучение под контролем — метод dropout. Который вы скорей всего уже видели или пробовали.

Но есть и другие регуляризаторы, которые можно применить к нашей сети. Возможно, самый популярный из них L_2 -регуляризация (также называемая сокращением весов, англ. weight decay), которая использует более прямой подход к регуляризации, чем dropout.

Обычно основной причиной являеться сложность модели( количество параметров) для набора данных. В некотором смысле, задача регуляризатора — понизить сложность модели, сохранив количество ее параметров. Регуляризация выполняется посредством наложения штрафов (penalising) на веса с наибольшими значениями, минимизируя их L2 — норму с использованием параметра λ — коэффициент регуляризации, выражающий предпочтение минимизации нормы относительно минимизации потерь на обучающем множестве.

Обратите внимание, что крайне важно правильно выбрать λ. Если коэффициент слишком мал, то эффект от регуляризации будет ничтожен, если же слишком велик — модель обнулит все веса. Здесь мы возьмем λ = 0.0001; чтобы добавить этот метод регуляризации в нашу модель, нам понадобится еще один импорт, после чего достаточно всего лишь добавить параметр к каждому слою, где мы хотим применять регуляризацию.

from keras import regularizers

model.add(Conv2D(32, (3, 3), kernel_regularizer=regularizers.l2(0.0001)))

Первоначальные параметры.

Выбора начальных значений весов для слоев, составляющих модель. Очевидно, что этот вопрос очень важен: установка всех весов в 0 будет серьезным препятствием для обучения, так как ни один из весов изначально не будет активен. Присваивать весам значения из интервала ±1 — уже более лучший вариант.

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

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

Метод инициализации Ге (He) — это вариация метода Завьера, больше подходящая функции активации ReLU, компенсирующая тот факт, что эта функция возвращает нуль для половины области определения.

Указать способ инициализации для слоя не сложно: вам всего лишь надо указать параметр kernel_initializer , как описано ниже. Мы будем использовать равномерную инициализацию Ге (he_uniform) для всех слоев ReLU и равномерную инициализацию Завьера (glorot_uniform) для выходного softmax слоя (так как по сути он представляет собой обобщение логистической функции на множественные сходные данные).

# Add He initialisation to a layer
model.add(Dense(500, kernel_initializer="he_uniform"))

Батч-нормализация (batch normalization).

Батч-нормализация — метод ускорения глубокого обучения, предложенный Ioffe и Szegedy в начале 2015 года. Метод решает следующую проблему, препятствующую эффективному обучению нейронных сетей: по мере распространения сигнала по сети, даже если мы нормализовали его на входе, пройдя через внутренние слои, он может сильно исказиться как по матожиднию, так и по дисперсии (данное явление называется внутренним ковариационным сдвигом), что чревато серьезными несоответствиями между градиентами на различных уровнях. Поэтому нам приходится использовать более сильные регуляризаторы, замедляя тем самым темп обучения.

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

В Keras добавить батч-нормализацию к вашей сети очень просто: за нее отвечает слой BatchNormalization, которому мы передадим несколько параметров, самый важный из которых — axis (вдоль какой оси данных будут вычислять статистические характеристики). В частности, во время работы со сверточными слоями, нам лучше нормализовать вдоль отдельных каналов, следовательно, выбираем axis=1.

from keras.layers.normalization import BatchNormalization # batch normalisation

model.add(BatchNormalization(axis=1))

Заключение.

Здесь мы увеличили число эпох обучения до 45. Но значимого прогресса не добились. Хотя обучение пошло ровней и в целом ошибка на тестовых данных уменьшилась.

Весь код полностью :

import keras
from keras.datasets import cifar10
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import regularizers
from keras.layers.normalization import BatchNormalization
import matplotlib.pyplot as plt


num_classes = 10

# Загрузка базы данных.
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
# Преобразование векторов классов в двоичные матрицы классов.
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
# Создание модели сети
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=x_train.shape[1:], kernel_initializer='he_uniform', kernel_regularizer=regularizers.l2(0.0001)))
model.add(Activation('relu'))
model.add(BatchNormalization(axis=1))
model.add(Conv2D(32, (3, 3), kernel_regularizer=regularizers.l2(0.0001), kernel_initializer='he_uniform'))
model.add(Activation('relu'))
model.add(BatchNormalization(axis=1))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Conv2D(64, (3, 3), kernel_regularizer=regularizers.l2(0.0001), kernel_initializer='he_uniform'))
model.add(Activation('relu'))
model.add(BatchNormalization(axis=1))
model.add(Conv2D(64, (3, 3), kernel_regularizer=regularizers.l2(0.0001), kernel_initializer='he_uniform'))
model.add(Activation('relu'))
model.add(BatchNormalization(axis=1))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Flatten())
model.add(Dense(512, kernel_regularizer=regularizers.l2(0.0001), kernel_initializer='he_uniform'))
model.add(Activation('relu'))
model.add(BatchNormalization(axis=1))
model.add(Dense(num_classes, kernel_regularizer=regularizers.l2(0.0001)))
model.add(Activation('softmax'))


# обучим модель с помощью RMSprop
model.compile(loss='categorical_crossentropy',
              optimizer='RMSprop',
              metrics=['accuracy'])

history = model.fit(x_train, y_train,
              epochs=45,
              validation_data=(x_test, y_test),
              verbose=1, batch_size=128)
# Вывод графика обучения
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model accuracy')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.