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

1. Основные сведения

Перед этим я хотел бы уточнить некоторые моменты.

  1. В этом посте предполагается, что у вас есть определенные знания о многослойных перцептронах и о том, как они работают.
  2. Вы знакомы с базовой терминологией, такой как обратное распространение, а также знаете о функциях активации, таких как сигмоид, tanh, relu.
  3. Вы знаете или, по крайней мере, хорошо разбираетесь в том, что такое различные алгоритмы оптимизации, такие как SGD, Adam.
  4. У вас есть идея о Mnist Dataset.
  5. У вас есть знания о переобучении и недогрузке.

Если вы сомневаетесь в каком-либо из вышеперечисленных, пожалуйста, изучите / исправьте их, вы можете получить информацию о них, и в Интернете есть множество ресурсов, таких как Medium, Towards Data Science, Analytics Vidhya.

2. Проблема работы с моделями глубокого обучения:

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

Я получил эту красивую шпаргалку от одной из групп Facebook, и она мне очень помогла при работе с набором данных Mnist с использованием многослойного перцептрона. Я буду использовать «иллюстрацию глубокого обучения» на этом изображении для получения различных результатов.

3. Выбор подходящих функций активации

Есть много функций вроде.

  1. Сигмовидная.
  2. Тань.
  3. Релу и Дырявый Релу.

Здесь я решил использовать Relu, который присутствует в Tensorflow, на самом деле он использует версию Leaky Relu, чтобы избежать проблем с мертвой активацией. Рекомендуется использовать Relu, поскольку они не подвержены проблемам с исчезающим и взрывающимся градиентом.

4. Соответствующая инициализация весов

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

Формула инициализации He проста и приведена ниже.

Wi ~ N (0, sqrt (2 / fan_in)

Это просто означает, что наши веса будут соответствовать нормальному распределению с центром в 0 и стандартным отклонением = sqrt (2 / fan_in).

Здесь fan_in: обозначает количество входов.

5. Выбор лучшего алгоритма оптимизации

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

6. Игра с кодом.

Сначала мы начнем с обработки нашего набора данных Mnist и преобразования каждого изображения набора данных Mnist в 2D-массив.

(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], X_train.shape[1]*X_train.shape[2]) 
X_test = X_test.reshape(X_test.shape[0], X_test.shape[1]*X_test.shape[2])

Нормализовать наши Данные - всегда хорошая привычка.

X_train = X_train/255
X_test = X_test/255

Мы также конвертируем метки классов в векторы с горячим кодированием.

Y_train = np_utils.to_categorical(y_train, 10) 
Y_test = np_utils.to_categorical(y_test, 10)

Пример для изображения, представляющего число 5, он будет закодирован как {0,0,0,0,0,1,0,0,0,0}

model_relu = Sequential()
model_relu.add(Dense(512, activation='relu', input_shape=(input_dim,), kernel_initializer=RandomNormal(mean=0.0, stddev=0.0625, seed=None)))
model_relu.add(Dense(256, activation='relu', kernel_initializer=RandomNormal(mean=0.0, stddev=0.088, seed=None)) )
model_relu.add(Dense(128, activation='relu', kernel_initializer=RandomNormal(mean=0.0, stddev=0.125, seed=None)) )
model_relu.add(Dense(output_dim, activation='softmax'))

Наконец, мы обучаем нашу модель, используя приведенные ниже линии.

model_relu.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
history = model_relu.fit(X_train, Y_train, batch_size=batch_size, epochs=nb_epoch, verbose=1, validation_data=(X_test, Y_test))

Получаем следующие цифры.

Результат теста: 0,1187

Точность теста: 0,98

На поверхности эти цифры могут выглядеть достаточно хорошо.

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

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

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

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

from keras import regularizers
model_relu = Sequential()
model_relu.add(Dense(512, activation='relu', input_shape=(input_dim,), kernel_initializer=RandomNormal(mean=0.0, stddev=0.0625, seed=None), kernel_regularizer=regularizers.l2(0.0001)))
model_relu.add(Dense(256, activation='relu', kernel_initializer=RandomNormal(mean=0.0, stddev=0.088, seed=None), kernel_regularizer=regularizers.l2(0.0001)) )
model_relu.add(Dense(128, activation='relu', kernel_initializer=RandomNormal(mean=0.0, stddev=0.125, seed=None), kernel_regularizer=regularizers.l2(0.0001)) )
model_relu.add(Dense(output_dim, activation='softmax'))

В приведенном выше коде единственное изменение, которое я сделал, заключается в том, что я добавил регуляризатор L2 с lamda, равным 0,0001 (я получил это значение, исчерпывающе обучив свою модель для разных значений lamda: P), а затем я обучил указанную выше модель с помощью кода, приведенного ниже.

model_relu.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
history = model_relu.fit(X_train, Y_train, batch_size=batch_size, epochs=nb_epoch, verbose=1, validation_data=(X_test, Y_test))

Я получу следующие цифры.

Оценка: 0,127

Точность теста: 0,9758

Цифры более или менее похожи на те, что мы получили ранее, но давайте посмотрим на график.

Мы заметили, что преодолели проблему переобучения, поскольку Train и Validation Cross разумно отличаются.

7. Работа для регуляризаторов

Можем ли мы использовать какой-то другой метод, чтобы избежать переобучения, вместо использования других скважин? Я только что обнаружил, что Dropouts и Batch Normalization являются лучшими методами для достижения этого.

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

from keras.layers import Dropout
model_drop = Sequential()
model_drop.add(Dense(512, activation='relu', input_shape=(input_dim,), kernel_initializer=RandomNormal(mean=0.0, stddev=0.0625, seed=None)))
model_drop.add(BatchNormalization())
model_drop.add(Dropout(0.5))
model_drop.add(Dense(256, activation='relu', kernel_initializer=RandomNormal(mean=0.0, stddev=0.088, seed=None)) )
model_drop.add(BatchNormalization())
model_drop.add(Dropout(0.5))
model_drop.add(Dense(128, activation='relu', kernel_initializer=RandomNormal(mean=0.0, stddev=0.125, seed=None)) )
model_drop.add(BatchNormalization())
model_drop.add(Dropout(0.5))
model_drop.add(Dense(output_dim, activation='softmax'))

Когда мы тестируем эту модель, мы получаем следующие результаты.

Оценка: 0,059

Точность теста: 0,9845

Посмотрим на сюжет.

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

Итак, в основном выпадение и пакетная нормализация действует как регуляризатор, который помогает нам строить более обобщенные модели.

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

8. Использование как отсева, так и нормализации и регуляризатора

Давайте используем все, что мы получили в этом блоге, и посмотрим, если мы еще сможем улучшить этот результат, однако здесь я использовал другую модель с 5 скрытыми слоями с 512 нейронами в первом слое, 256 нейронами во втором, 128 в третьем. , 64 в 4-м и 32 в пятом слое.

from keras.layers import Dropout
model_drop = Sequential()
model_drop.add(Dense(512, activation='relu', input_shape=(input_dim,), kernel_initializer=RandomNormal(mean=0.0, stddev=0.0625, seed=None), kernel_regularizer=regularizers.l2(0.0001)))
model_drop.add(BatchNormalization())
model_drop.add(Dropout(0.5))
model_drop.add(Dense(256, activation='relu', kernel_initializer=RandomNormal(mean=0.0, stddev=0.088, seed=None), kernel_regularizer=regularizers.l2(0.0001)))
model_drop.add(BatchNormalization())
model_drop.add(Dropout(0.5))
model_drop.add(Dense(128, activation='relu', kernel_initializer=RandomNormal(mean=0.0, stddev=0.125, seed=None),kernel_regularizer=regularizers.l2(0.0001)) )
model_drop.add(BatchNormalization())
model_drop.add(Dropout(0.5))
model_drop.add(Dense(64, activation='relu', kernel_initializer=RandomNormal(mean=0.0, stddev=0.1767, seed=None),kernel_regularizer=regularizers.l2(0.0001)) )
model_drop.add(BatchNormalization())
model_drop.add(Dropout(0.5))
model_drop.add(Dense(32, activation='relu', kernel_initializer=RandomNormal(mean=0.0, stddev=0.25, seed=None),kernel_regularizer=regularizers.l2(0.0001)) )
model_drop.add(BatchNormalization())
model_drop.add(Dropout(0.5))
model_drop.add(Dense(output_dim, activation='softmax'))
model_drop.summary()

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

Мы обучаем модель, используя приведенный ниже код.

model_drop.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
history = model_drop.fit(X_train, Y_train, batch_size=batch_size, epochs=nb_epoch, verbose=1, validation_data=(X_test, Y_test))model_drop.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
history = model_drop.fit(X_train, Y_train, batch_size=batch_size, epochs=nb_epoch, verbose=1, validation_data=(X_test, Y_test))

После тренировки мы получаем следующие результаты.

Оценка: 0,234

Точность теста: 0,9781

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

Если мы рассмотрим вышеприведенную модель, мы увидим, что она преодолела и переоснащение, и недостаточное оснащение.

Такие модели хорошо использовать, поскольку они в значительной степени обобщают.

9. Выводы и выводы

  1. Одна из ключевых задач работы с многослойным персептроном - избежать переобучения любой ценой.
  2. Регуляризация может помочь избежать переобучения, однако параметр lamda должен быть выбран правильно, иначе вы можете не соответствовать.

3. Отсев и пакетная нормализация могут действовать как регуляризатор и преодолевать переоснащение.

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

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

Спасибо за прочтение : ).

Следите за новостями, чтобы увидеть больше таких сообщений.

PS: Это мой первый блог о глубоком обучении, пожалуйста, оставьте отзыв :).