Пошаговое руководство по добавлению и настройке пакетной нормализации

В этой статье мы сосредоточимся на добавлении и настройке пакетной нормализации в нашей модели машинного обучения и рассмотрим пример того, как мы делаем это на практике с Keras и TensorFlow 2.0.

Мягкое введение в пакетную нормализацию

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

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

Автор Джейсон Браунли

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

Формально алгоритм пакетной нормализации [1] определяется как:

В алгоритме B используется для обозначения мини-пакета размером m всего обучающего набора. Таким образом, среднее значение и дисперсию B можно рассчитать как:

Для слоя с входными данными d, x = (x_1, …, x_d), каждое измерение входных данных может быть нормализовано (центрировано и масштабировано) отдельно. Таким образом, нормализация для d -мерных входных данных может быть рассчитана как:

ε добавляется в знаменатель для числовой устойчивости и является сколь угодно малой константой.

И, наконец, чтобы восстановить репрезентативную способность сети, этап преобразования определяется как:

где параметры β и γ впоследствии изучаются в процессе оптимизации.

Преимущества пакетной нормализации [2]:

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

Если вы ищете полное объяснение, вам могут быть полезны следующие ресурсы:

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

Настройка среды, исходный код и подготовка набора данных

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

Чтобы запустить это руководство, вам необходимо установить

TensorFlow 2, numpy, pandas, sklean, matplotlib

Все они могут быть установлены непосредственно через PyPI, и я настоятельно рекомендую создать новую виртуальную среду. Для учебника по созданию виртуальной среды Python

Исходный код

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

Подготовка набора данных

В этом руководстве для демонстрации используется набор данных Цветок ириса Андерсона (ирис). Набор данных содержит набор из 150 записей с пятью атрибутами: длина чашелистика, ширина чашелистика, длина лепестка, ширина лепестка, и класс (известный как target из наборов данных sklearn).

Во-первых, давайте импортируем библиотеки и получим набор данных радужной оболочки глаза из библиотеки scikit-learn. Вы также можете скачать его из набора данных UCI Iris.

import tensorflow as tf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris = load_iris()

Чтобы изучить данные, давайте загрузим данные в DataFrame.

# Load data into a DataFrame
df = pd.DataFrame(iris.data, columns=iris.feature_names)
# Convert datatype to float
df = df.astype(float)
# append "target" and name it "label"
df['label'] = iris.target
# Use string label instead
df['label'] = df.label.replace(dict(enumerate(iris.target_names)))

И df должен выглядеть так:

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

# label -> one-hot encoding
label = pd.get_dummies(df['label'], prefix='label')
df = pd.concat([df, label], axis=1)
# drop old label
df.drop(['label'], axis=1, inplace=True)

Теперь df должен выглядеть так:

Затем давайте создадим X и y. Keras и TensorFlow 2.0 принимают только массив Numpy в качестве входных данных, поэтому нам придется преобразовать DataFrame обратно в массив Numpy.

# Creating X and yX = df[['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']]
# Convert DataFrame into np array
X = np.asarray(X)y = df[['label_setosa', 'label_versicolor', 'label_virginica']]
# Convert DataFrame into np array
y = np.asarray(y)

Наконец, давайте разделим набор данных на обучающий набор (80%) и тестовый набор (20%), используя train_test_split() из библиотеки sklearn.

X_train, X_test, y_train, y_test = train_test_split(
  X,
  y,
  test_size=0.20
)

Большой! наши данные готовы для построения модели машинного обучения.

Постройте модель нейронной сети с пакетной нормализацией

Есть 3 способа создать модель машинного обучения с помощью Keras и TensorFlow 2.0. Поскольку мы строим простую полностью связанную нейронную сеть и для простоты, давайте воспользуемся самым простым способом: последовательной моделью с Sequential().

Сначала импортируем Sequential и BatchNormalization

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, BatchNormalization

Давайте продолжим и создадим последовательную модель

model = Sequential([
    Dense(64, input_shape=(4,), activation="relu"),
    Dense(128, activation='relu'),
    Dense(128, activation='relu'),
    Dense(64, activation='relu'),
    Dense(64, activation='relu'),
    Dense(3, activation='softmax')
]);

Наша модель имеет следующие характеристики:

  • Первый уровень (также известный как входной слой) имеет input_shape для установки размера входа (4,).
  • Входной слой состоит из 64 единиц, за которыми следуют 2 плотных слоя по 128 единиц в каждом. Затем идут еще 2 плотных слоя по 64 блока в каждом. Все эти уровни используют функцию активации relu.
  • Выходной плотный слой имеет 3 единицы и функцию активации softmax.

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

model = Sequential([
    Dense(64, input_shape=(4,), activation="relu"),
    BatchNormalization(),
    Dense(128, activation='relu'),
    BatchNormalization(),
    Dense(128, activation='relu'),
    BatchNormalization(),
    Dense(64, activation='relu'),
    BatchNormalization(),
    Dense(64, activation='relu'),
    BatchNormalization(),
    Dense(3, activation='softmax')
]);

BatchNormalization() нормализует активацию предыдущего уровня в каждом пакете и по умолчанию использует следующие значения [3]:

  • По умолчанию моментум 0.99.
  • По умолчанию гиперпараметр ε равен 0.001.
  • По умолчанию гиперпараметр β равен all-zeros вектору.
  • По умолчанию гиперпараметр γ равен all-ones вектору.

Все это можно изменить, добавив необязательные аргументы в BatchNormalization(). Например

from tensorflow.keras.initializers import RandomNormal, Constant
# Model with default batch normalization
model = Sequential([
    Dense(64, input_shape=(4,), activation="relu"),
    BatchNormalization(),
    Dense(128, activation='relu'),
    BatchNormalization(),
    Dense(128, activation='relu'),
    BatchNormalization(),
    Dense(64, activation='relu'),
    BatchNormalization(),
    Dense(64, activation='relu'),
    BatchNormalization(
        momentum=0.95, 
        epsilon=0.005,
        beta_initializer=RandomNormal(mean=0.0, stddev=0.05), 
        gamma_initializer=Constant(value=0.9)
    ),
    Dense(3, activation='softmax')
]);

RandomNormal() генерирует тензор с нормальным распределением, а Constant() генерирует тензор с постоянными значениями. Запустив model.summary(), вы должны получить сводку модели, как показано ниже:

Обучение

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

  • Используйте алгоритм оптимизации Adam (adam) в качестве оптимизатора
  • Используйте категориальную функцию кросс-энтропийных потерь (categorical_crossentropy) для нашей задачи многоклассовой классификации.
  • Для простоты используйте accuracy в качестве показателей оценки для оценки модели во время обучения и тестирования.
model.compile(
    optimizer='adam', 
    loss='categorical_crossentropy', 
    metrics=['accuracy']
)

После этого мы можем вызвать model.fit(), чтобы подогнать нашу модель к обучающим данным.

history = model.fit(
    X_train, 
    y_train, 
    epochs=200, 
    validation_split=0.25, 
    batch_size=40, 
    verbose=2
)

Если все работает нормально, мы должны получить результат, как показано ниже

Train on 90 samples, validate on 30 samples
Epoch 1/200
90/90 - 3s - loss: 0.8735 - accuracy: 0.5778 - val_loss: 1.0685 - val_accuracy: 0.6667
Epoch 2/200
90/90 - 0s - loss: 0.1983 - accuracy: 0.9333 - val_loss: 1.0640 - val_accuracy: 0.4667
......
......
Epoch 200/200
90/90 - 0s - loss: 0.0532 - accuracy: 0.9778 - val_loss: 0.1453 - val_accuracy: 0.9333

Оценка модели

Наконец, пора проверить, хороша ли модель

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

Давайте создадим функцию plot_metric() для построения показателей.

%matplotlib inline
%config InlineBackend.figure_format = 'svg'
def plot_metric(history, metric):
    train_metrics = history.history[metric]
    val_metrics = history.history['val_'+metric]
    epochs = range(1, len(train_metrics) + 1)
    plt.plot(epochs, train_metrics)
    plt.plot(epochs, val_metrics)
    plt.title('Training and validation '+ metric)
    plt.xlabel("Epochs")
    plt.ylabel(metric)
    plt.legend(["train_"+metric, 'val_'+metric])
    plt.show()

Запустив plot_metric(history, 'accuracy'), чтобы отобразить прогресс точности.

Запустив plot_metric(history, 'loss'), чтобы отобразить прогресс по убыткам.

Оценить модель на тестовом наборе

# Evaluate the model on the test set
model.evaluate(X_test, y_test, verbose=2)

И мы должны получить результат, как показано ниже

30/1 - 0s - loss: 0.1192 - accuracy: 0.9667
[0.11924469470977783, 0.96666664]

Вот и все

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

Пожалуйста, загляните в записную книжку на моем Github, чтобы найти исходный код.

Следите за обновлениями, если вас интересует практический аспект машинного обучения.

Использованная литература: