Реализация Dropout в Python

Для построения моделей на языке Python ранее мы использовали библиотеку Keras для TensorFlow. В этой библиотеке уже есть готовая реализация слоя Dropout.

tf.keras.layers.Dropout(
    rate, noise_shape=None, seed=None, **kwargs
)

Слой Dropout случайным образом устанавливает входные единицы на 0 с частотой rate на каждой итерации в процессе обучения. Это помогает предотвратить переобучение модели. Исходные данные, не установленные на 0, масштабируются на 1/(1 - rate). Поэтому сумма передаваемых всех исходных данным остается неизменной.

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

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

Конструктор слоя Dropout имеет следующие аргументы:

  • rate — число с плавающей запятой в диапазоне от 0 до 1, которое представляет собой долю элементов исходных данных, маскируемых в процессе обучения;
  • noise_shape — одномерный целочисленный тензор, представляющий форму двоичной маски исключения в виде (batch_size, timesteps, features). Форма будет умножена на тензор исходных данных. Например, если исходные данные имеют форму и вы хотите, чтобы маска исключения была одинаковой для всех временных шагов, вы можете использовать noise_shape=(batch_size, 1, features);
  • seed — целое число для использования в качестве случайного начального числа.

При вызове слоя допускается использование двух аргументов:

  • inputs — тензор исходных данных, допускается использование тензора любого ранга;
  • training — логический флаг, указывающий режим работы слоя.

Для проверки эффективности использования технологии Dropout мы создадим скрипт и обучим несколько моделей с использованием данного слоя. Мы не будем создавать слишком сложных моделей. Вместо этого возьмем скрипт batch_norm.py, который использовали при тестировании пакетной нормализации. Создадим копию данного скрипта в файле dropout.py. В каждую модель добавим слои Dropout.

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

# Добавление Dropout в модель с одним скрытым слоем
model1do = keras.Sequential([keras.layers.InputLayer(input_shape=inputs),
                           keras.layers.Dropout(0.3),
                           keras.layers.Dense(40, activation=tf.nn.swish), 
                           keras.layers.Dropout(0.3),
                           keras.layers.Dense(targets, activation=tf.nn.tanh) 
                         ])
model1do.compile(optimizer='Adam'
               loss='mean_squared_error'
               metrics=['accuracy'])
model1do.summary()

Обратите внимание, что во всех слоях Dropout мы будем маскировать 30% нейронов предыдущего слоя.

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

# Добавление Dropout в модель с пакетной нормализацией исходных данных 
# и одним скрытым слоем
model1bndo = keras.Sequential([keras.layers.InputLayer(input_shape=inputs),
                             keras.layers.BatchNormalization(),
                             keras.layers.Dropout(0.3),
                             keras.layers.Dense(40, activation=tf.nn.swish), 
                             keras.layers.Dropout(0.3),
                             keras.layers.Dense(targets, activation=tf.nn.tanh) 
                            ])
model1bndo.compile(optimizer='Adam'
               loss='mean_squared_error'
               metrics=['accuracy'])
model1bndo.summary()

Аналогичным образом мы добавим пакеты Dropout в модели с тремя скрытыми слоями.

# Добавление Dropout в модель с тремя скрытыми слоями
model2do = keras.Sequential([keras.layers.InputLayer(input_shape=inputs),
                           keras.layers.Dropout(0.3),
                           keras.layers.Dense(40, activation=tf.nn.swish), 
                           keras.layers.Dropout(0.3),
                           keras.layers.Dense(40, activation=tf.nn.swish), 
                           keras.layers.Dropout(0.3),
                           keras.layers.Dense(40, activation=tf.nn.swish), 
                           keras.layers.Dropout(0.3),
                           keras.layers.Dense(targets, activation=tf.nn.tanh) 
                         ])
model2do.compile(optimizer='Adam'
               loss='mean_squared_error'
               metrics=['accuracy'])
model2do.summary()

# Добавление Dropout в модель с пакетной нормализацией исходных данных 
# и трех скрытых слоев
model2bndo = keras.Sequential([keras.layers.InputLayer(input_shape=inputs),
                             keras.layers.BatchNormalization(),
                             keras.layers.Dropout(0.3),
                             keras.layers.Dense(40, activation=tf.nn.swish), 
                             keras.layers.BatchNormalization(),
                             keras.layers.Dropout(0.3),
                             keras.layers.Dense(40, activation=tf.nn.swish), 
                             keras.layers.BatchNormalization(),
                             keras.layers.Dropout(0.3),
                             keras.layers.Dense(40, activation=tf.nn.swish), 
                             keras.layers.Dropout(0.3),
                             keras.layers.Dense(targets, activation=tf.nn.tanh) 
                            ])
model2bndo.compile(optimizer='Adam'
               loss='mean_squared_error'
               metrics=['accuracy'])
model2bndo.summary()

После создания моделей добавим код для запуска процесса обучения новых моделей.

history1do = model1do.fit(train_data, train_target,
                      epochs=500, batch_size=1000,
                      callbacks=[callback],
                      verbose=2,
                      validation_split=0.1,
                      shuffle=True)
model1do.save(os.path.join(path,'perceptron1do.h5'))

history1bndo = model1bndo.fit(train_nn_data, train_nn_target,
                      epochs=500, batch_size=1000,
                      callbacks=[callback],
                      verbose=2,
                      validation_split=0.1,
                      shuffle=True)
model1bndo.save(os.path.join(path,'perceptron1bndo.h5'))

history2do = model2do.fit(train_data, train_target,
                      epochs=500, batch_size=1000,
                      callbacks=[callback],
                      verbose=2,
                      validation_split=0.1,
                      shuffle=True)
model2do.save(os.path.join(path,'perceptron2do.h5'))

history2bndo = model2bndo.fit(train_nn_data, train_nn_target,
                      epochs=500, batch_size=1000,
                      callbacks=[callback],
                      verbose=2,
                      validation_split=0.1,
                      shuffle=True)
model2bndo.save(os.path.join(path,'perceptron2bndo.h5'))

Обязательно добавим запуск моделей на тестовой выборке.

test_loss1do, test_acc1do = model1do.evaluate(test_data, test_target,
                                                            verbose=2
test_loss1bndo, test_acc1bndo = model1bndo.evaluate(test_nn_data, 
                                                    test_nn_target,
                                                    verbose=2
test_loss2do, test_acc2do = model2do.evaluate(test_data, test_target, 
                                                            verbose=2
test_loss2bndo, test_acc2bndo = model2bndo.evaluate(test_nn_data,
                                                    test_nn_target,
                                                    verbose=2)

Кроме изменений в части обучения и тестирования моделей, дополним и блок отрисовки результатов работы моделей. Сначала изменим код, который создает графики динамики среднеквадратичной ошибки и Accuracy в процессе обучения. Изменения здесь не глобальные — мы лишь добавляем новые показатели на график.

# Отрисовка результатов обучения моделей с одним скрытым слоем
plt.figure()
plt.plot(history1.history['loss'], label='Normalized inputs train')
plt.plot(history1.history['val_loss'], label='Normalized inputs validation')
plt.plot(history1do.history['loss'], label='Normalized inputs\nvs Dropout train')
plt.plot(history1do.history['val_loss'],
                                label='Normalized inputs\nvs Dropout validation')
plt.plot(history1bn.history['loss'],
                        label='Unnormalized inputs\nvs BatchNormalization train')
plt.plot(history1bn.history['val_loss'],
                   label='Unnormalized inputs\nvs BatchNormalization validation')
plt.plot(history1bndo.history['loss'],
            label='Unnormalized inputs\nvs BatchNormalization and Dropout train')
plt.plot(history1bndo.history['val_loss'],
       label='Unnormalized inputs\nvs BatchNormalization and Dropout validation')
plt.ylabel('$MSE$ $loss$')
plt.xlabel('$Epochs$')
plt.title('Model training dynamics\n1 hidden layer')
plt.legend(loc='upper right',ncol=2)

plt.figure()
plt.plot(history1.history['accuracy'], label='Normalized inputs trin')
plt.plot(history1.history['val_accuracy'], label='Normalized inputs validation')
plt.plot(history1do.history['accuracy'],
                                    label='Normalized inputs\nvs Dropout train')
plt.plot(history1do.history['val_accuracy'],
                               label='Normalized inputs\nvs Dropout validation')
plt.plot(history1bn.history['accuracy'],
                       label='Unnormalized inputs\nvs BatchNormalization train')
plt.plot(history1bn.history['val_accuracy'],
                  label='Unnormalized inputs\nvs BatchNormalization validation')
plt.plot(history1bndo.history['accuracy'],
           label='Unnormalized inputs\nvs BatchNormalization and Dropout train')
plt.plot(history1bndo.history['val_accuracy'],
      label='Unnormalized inputs\nvs BatchNormalization and Dropout validation')
plt.ylabel('$Accuracy$')
plt.xlabel('$Epochs$')
plt.title('Model training dynamics\n1 hidden layer')
plt.legend(loc='lower right',ncol=2)

# Отрисовка результатов обучения моделей с тремя скрытыми слоями
plt.figure()
plt.plot(history2.history['loss'], label='Normalized inputs train')
plt.plot(history2.history['val_loss'], label='Normalized inputs validation')
plt.plot(history2do.history['loss'], label='Normalized inputs\nvs Dropout train')
plt.plot(history2do.history['val_loss'], 
                                 label='Normalizedinputs\nvs Dropout validation')
plt.plot(history2bn.history['loss'],
                        label='Unnormalized inputs\nvs BatchNormalization train')
plt.plot(history2bn.history['val_loss'],
                   label='Unnormalized inputs\nvs BatchNormalization validation')
plt.plot(history2bndo.history['loss'],
            label='Unnormalized inputs\nvs BatchNormalization and Dropout train')
plt.plot(history2bndo.history['val_loss'],
       label='Unnormalized inputs\nvs BatchNormalization and Dropout validation')
plt.ylabel('$MSE$ $loss$')
plt.xlabel('$Epochs$')
plt.title('Model training dynamics\n3 hidden layers')
plt.legend(loc='upper right',ncol=2)

plt.figure()
plt.plot(history2.history['accuracy'], label='Normalized inputs train')
plt.plot(history2.history['val_accuracy'], label='Normalized inputs validation')
plt.plot(history2do.history['accuracy'], label='Normalized inputs\nvs Dropout train')
plt.plot(history2do.history['val_accuracy'],
                                    label='Normalized inputs\nvs Dropout validation')
plt.plot(history2bn.history['accuracy'],
                            label='Unnormalized inputs\nvs BatchNormalization train')
plt.plot(history2bn.history['val_accuracy'],
                       label='Unnormalized inputs\nvs BatchNormalization validation')
plt.plot(history2bndo.history['accuracy'],
                label='Unnormalized inputs\nvs BatchNormalization and Dropout train')
plt.plot(history2bndo.history['val_accuracy'],
           label='Unnormalized inputs\nvs BatchNormalization and Dropout validation')
plt.ylabel('$Accuracy$')
plt.xlabel('$Epochs$')
plt.title('Model training dynamics\n3 hidden layers')
plt.legend(loc='lower right',ncol=2)

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

plt.figure()
plt.bar(['Normalized inputs','\n\nNormalized inputs\nvs Dropout',
         'Unnormalized inputs\nvs BatchNornalization',
         '\n\nUnnormalized inputs\nvs BatchNornalization and Dropout'],
        [test_loss1,test_loss1do,
         test_loss1bn,test_loss1bndo])
plt.ylabel('$MSE$ $loss$')
plt.title('Test results\n1 hidden layer')

plt.figure()
plt.bar(['Normalized inputs','\n\nNormalized inputs\nvs Dropout',
         'Unnormalized inputs\nvs BatchNornalization',
         '\n\nUnnormalized inputs\nvs BatchNornalization and Dropout'],
        [test_loss2,test_loss2do,
         test_loss2bn,test_loss2bndo])
plt.ylabel('$MSE$ $loss$')
plt.title('Test results\n3 hidden layers')

plt.figure()
plt.bar(['Normalized inputs','\n\nNormalized inputs\nvs Dropout',
         'Unnormalized inputs\nvs BatchNornalization',
         '\n\nUnnormalized inputs\nvs BatchNornalization and Dropout'],
        [test_acc1,test_acc1do,
         test_acc1bn,test_acc1bndo])
plt.ylabel('$Accuracy$')
plt.title('Test results\n1 hidden layer')

plt.figure()
plt.bar(['Normalized inputs','\n\nNormalized inputs\nvs Dropout',
         'Unnormalized inputs\nvs BatchNornalization',
         '\n\nUnnormalized inputs\nvs BatchNornalization and Dropout'],
        [test_acc2,test_acc2do,
         test_acc2bn,test_acc2bndo])
plt.ylabel('$Accuracy$')
plt.title('Test results\n3 hidden layers')
 
plt.show()

В остальном код скрипта остался без изменений.

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