Странное поведение функции потерь в модели Кераса с предварительно обученной сверточной базой - PullRequest
0 голосов
/ 01 июля 2018

Я пытаюсь создать модель в Керасе, чтобы делать численные прогнозы по фотографиям. Моя модель имеет densenet121 сверточную основу, с несколькими дополнительными слоями сверху. Все слои, кроме двух последних, установлены на layer.trainable = False. Моя потеря - среднеквадратическая ошибка, так как это регрессионная задача. Во время обучения я получаю loss: ~3, а оценка по той же партии данных дает loss: ~30:

model.fit(x=dat[0],y=dat[1],batch_size=32)

Epoch 1/1 32/32 [====================================] - 0 с 11 мс / шаг - потери: 2.5571

model.evaluate(x=dat[0],y=dat[1])

32/32 [==============================] - 2 с 59 мс / шаг 29,276123046875

Во время обучения и оценки я кормлю точно такие же 32 картинки. И я также рассчитал потери, используя предсказанные значения из y_pred=model.predict(dat[0]), а затем построил среднеквадратическую ошибку, используя numpy. Результат был таким же, как то, что я получил от оценки (то есть 29.276123 ...).

Было высказано предположение, что такое поведение может быть связано с BatchNormalization слоями в сверточной основе ( обсуждение на github ). Конечно, все слои BatchNormalization в моей модели также были установлены на layer.trainable=False. Может, кто-то сталкивался с этой проблемой и нашел решение?

Ответы [ 2 ]

0 голосов
/ 01 июля 2018

Похоже, я нашел решение. Как я уже говорил, проблема в слоях BatchNormalization. Они делают вещи из дерева 1) вычитают среднее и нормализуют по стандартному стандарту 2) собирают статистику по среднему и стандартному стандарту с использованием скользящего среднего 3) обучают двум дополнительным параметрам (по два на узел). Когда каждый устанавливает обучаемое значение False, эти два параметра останавливаются, и слой также прекращает сбор статистики о среднем и стандартном значении. Но похоже, что слой все еще выполняет нормализацию во время тренировки с использованием тренировочной партии . Скорее всего, это ошибка в керасе или, может быть, они сделали это специально по какой-то причине. В результате расчеты прямого распространения во время обучения отличаются от времени предсказания , даже если для обучаемого атрибута установлено значение False .

Существует два возможных решения:

  1. Чтобы установить все слои BatchNormalization для обучения. В этом случае эти слои будут собирать статистику из вашего набора данных вместо использования предварительно подготовленного (который может существенно отличаться!). В этом случае вы отрегулируете все слои BatchNorm под свой пользовательский набор данных во время обучения.
  2. Разделить модель на две части model=model_base+model_top. После этого используйте model_base, чтобы извлечь элементы с помощью model_base.predict(), а затем введите эти функции в model_top и обучите только model_top.

Я только что попробовал первое решение, и похоже, что оно работает:

model.fit(x=dat[0],y=dat[1],batch_size=32)

Epoch 1/1
32/32 [==============================] - 1s 28ms/step - loss: **3.1053**

model.evaluate(x=dat[0],y=dat[1])

32/32 [==============================] - 0s 10ms/step
**2.487905502319336**

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

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

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

0 голосов
/ 01 июля 2018

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

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

Если вы продолжите тренировать модель для большего количества эпох, вы можете ожидать, что потеря тренировки и тестовая потеря ( на тех же данных ) станут более или менее одинаковыми.

Поэкспериментируйте сами: просто установите параметр trainable слоя (ов) Dropout на False и посмотрите, происходит ли это или нет.


Кто-то может быть сбит с толку (как и я), видя, что после одной эпохи обучения потеря обучения не равна потере оценки в той же партии данных. И это не относится к моделям со слоями Dropout или BatchNormalization. Рассмотрим этот пример:

from keras import layers, models
import numpy as np

model = models.Sequential()
model.add(layers.Dense(1000, activation='relu', input_dim=100))
model.add(layers.Dense(1))

model.compile(loss='mse', optimizer='adam')
x = np.random.rand(32, 100)
y = np.random.rand(32, 1)

print("Training:")
model.fit(x, y, batch_size=32, epochs=1)

print("\nEvaluation:")
loss = model.evaluate(x, y)
print(loss)

Выход:

Training:
Epoch 1/1
32/32 [==============================] - 0s 7ms/step - loss: 0.1520

Evaluation:
32/32 [==============================] - 0s 2ms/step
0.7577340602874756

Так почему потери различаются, если они были рассчитаны по одним и тем же данным, т.е. 0.1520 != 0.7577?

Если вы спросите это, то это потому, что вы, как и я, не уделили достаточного внимания: 0.1520 - это потеря до обновления параметров модели (то есть до выполнения обратного прохода или обратного распространения). И 0.7577 - это потеря после обновления весов модели. Несмотря на то, что используемые данные одинаковы, состояние модели при вычислении этих значений потерь не одинаково (Другой вопрос: почему потеря увеличилась после обратного распространения? Это просто потому, что вы обучили ее только за одну эпоху и поэтому обновления весов еще недостаточно стабильны).

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

model.fit(x, y, batch_size=32, epochs=1, validation_data=(x,y))

Если вы запустите приведенный выше код с измененной строкой выше, вы получите вывод наподобие этого (очевидно, точные значения могут отличаться для вас):

Training:
Train on 32 samples, validate on 32 samples
Epoch 1/1
32/32 [==============================] - 0s 15ms/step - loss: 0.1273 - val_loss: 0.5344

Evaluation:
32/32 [==============================] - 0s 89us/step
0.5344240665435791

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...