Оптимизация модели для глубокого обучения Python

глубокое обучение Python

Это 12-й день моего участия в августовском испытании обновлений. Узнайте подробности события:Испытание августовского обновления

Deep Learning with Python

Эта статья — одна из серии заметок, которые я написал, изучая Deep Learning with Python (второе издание, Франсуа Шолле). Содержимое статьи конвертировано из блокнотов Jupyter в Markdown, вы можете перейти наGitHubилиGiteeнайти оригинал.ipynbноутбук.

ты можешь идтиЧитайте оригинальный текст этой книги онлайн на этом сайте(Английский). Автор этой книги также дает соответствиеJupyter notebooks.

Эта статьяГлава 7. Передовые методы расширенного глубокого обучения (Chapter 7. Advanced deep-learning best practices) одной из записок.

[TOC]

7.3 Getting the most out of your models

Максимальная производительность модели

Если вы просто хотите придумать хорошую модель, вы можете в основном попробовать различные сетевые архитектуры без мозгов. Но если вы собираетесь разрабатывать модели, которые работают хорошо и делают все возможное, вам необходимо рассмотреть методы, приведенные ниже.

Расширенные архитектурные шаблоны

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

остаточное соединение

Примечание: этот абзац был написан в 7.1, он просто скопирован сюда, чтобы сделать эту часть более полной.

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

  • Градиент исчезает: пройдено слишком много слоев, и ранее изученное представление становится размытым или даже полностью теряется, что делает сеть неспособной к обучению.
  • Представляет собой узкое место: слои сложены друг в друга, и последний уровень может получить доступ только к тому, чему научился предыдущий уровень. Если определенный слой слишком мал (в активацию можно вставить меньше информации), информация застрянет, и возникнет узкое место.

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

Примечание. Линейное преобразование можно выполнить с помощью плотных слоев без активации или свертки 1 × 1 без активации в CNN.

from keras import layers

x = ...

y = layers.Conv2D(128, 3, activation='relu', padding='same')(x)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
y = layers.MaxPooling2D(2, strides=2)(y)

# 形状不同,要做线性变换:
residual = layers.Conv2D(128, 1, strides=2, padding='same')(x)  # 使用 1×1 卷积,将 x 线性下采样为与 y 具有相同的形状

y = layers.add([y, residual])

стандартизация

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

Наиболее распространенная стандартизация данных заключается в том, чтобы данные имели среднее значение 0 и дисперсию 1:

normalized_data = (data - np.mean(data, axis=...)) / np.std(data, axis=...)

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

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

Пакетная нормализация в KerasBatchNormalizationРеализация слоя, обычно используемая после сверточных или плотно связанных слоев:

# Conv
conv_model.add(layers.Conv2D(32, 3, activation='relu'))
conv_model.add(layers.BatchNormalization())

# Dense
dense_model.add(layers.Dense(32, activation='relu'))
dense_model.add(layers.BatchNormalization())

Слой BatchNormalization принимает параметр оси, который указывает, какая ось объекта должна быть нормализована, значение по умолчанию равно -1. Это верно для Keras по умолчанию Dense, Conv1D, RNN и Conv2D. но дляdata_formatустановить как"channels_first"Для Conv2D ось функции равна 1, поэтому вам нужно установитьaxis=1.

Разделимая по глубине свертка

отделимая по глубине свертка, называемая письмом в КерасеSeparableConv2D, функция такая же, как и у обычного Conv2D. Но SeparableConv2D легче, быстрее обучается и точнее, чем Conv2D.

Слой SeparableConv2D выполняет пространственную свертку отдельно для каждого канала ввода, а затем интегрирует выходные результаты посредством точечной свертки (свертка 1×1). Это отделяет изучение пространственных признаков от изучения признаков канала, часто позволяя лучше изучить представления с использованием меньшего количества данных. (Это очень похоже на ранее упомянутую модель Xception. На самом деле в основе архитектуры Xception лежат разделяемые по глубине свертки)

深度可分离卷积: 深度卷积 + 逐点卷积

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

from tensorflow.keras.models import Sequential, Model
from tensorflow.keras import layers

height = 64
width = 64
channels = 3
num_classes = 10

model = Sequential()
model.add(layers.SeparableConv2D(32, 3, 
                                 activation='relu', 
                                 input_shape=(height, width, channels,)))
model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.MaxPooling2D(2))

model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.SeparableConv2D(128, 3, activation='relu'))
model.add(layers.MaxPooling2D(2))

model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.SeparableConv2D(128, 3, activation='relu'))
model.add(layers.GlobalAveragePooling2D())

model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dense(num_classes, activation='softmax'))

model.compile(optimizer='rmsprop', loss='categorical_crossentropy')

Для больших задач Xception лучше.

Оптимизация гиперпараметров

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

Сколько слоев сложено в модели, сколько единиц содержит каждый слой, какую функцию активации использовать...

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

  1. Выберите набор гиперпараметров
  2. Построить соответствующую модель
  3. Установите модель на мини-пакетных данных и проверьте производительность
  4. Выберите следующий набор гиперпараметров, чтобы попробовать
  5. Повторите вышеуказанный процесс (2-4)
  6. Выберите лучшие гиперпараметры из приведенных выше экспериментов.

Ключом к этому процессу является то, что при наличии нескольких наборов необязательных гиперпараметров «историческая производительность проверки» используется для автоматического выбора следующего набора гиперпараметров для оценки. Это можно сделать с помощью таких алгоритмов, как «Простой случайный поиск», «Байесовская оптимизация», «Генетический алгоритм» и т. д.

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

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

  • Hyperas, эта библиотека относительно старая и упоминается в книге, но по состоянию на август 2020 года она не подавалась уже несколько месяцев;
  • Keras Tuner, эта библиотека относительно новая, я видел ее несколько дней назад, и количество звезд было равно количеству звезд Hyperas.
  • ...

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

интеграция модели

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

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

Для модельных ансамблей важно разнообразие используемых моделей. Разнообразие - это сила.Модель, используемая для ансамбля, должна быть как можно лучше, но при этом максимально отличаться. Одна и та же сеть несколько раз независимо обучается с разными случайными инициализациями, а затем интегрируется, что не имеет особого смысла. Лучшим подходом должно быть использование моделей с очень разными архитектурами для интеграции, чтобы отклонения каждой модели были в разных направлениях, а интеграция позволяла отклонениям компенсировать друг друга, и результат был бы более стабильным и точным.

Взяв в качестве примера проблему классификации, во-первых, у нас есть несколько разных моделей:

preds_a = model_a.predict(x_val)
preds_b = model_b.predict(x_val)
preds_c = model_c.predict(x_val)
preds_d = model_d.predict(x_val)

Их можно интегрировать разными способами, самый простой способ — взять среднее значение:

final_preds = 0.25 * (preds_a + preds_b + preds_c + preds_d)

Поскольку производительность каждой модели будет разной, лучшим подходом является средневзвешенное значение:

final_preds = 0.5 * preds_a + 0.25 * preds_b + 0.1 * preds_c + 0.15 * preds_d

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