Вопрос или проблема
Я использую предобученную модель ResNet50 в Keras и пытаюсь получить прогнозы для одиночных образцов
Проблема в том, что model.predict()
возвращает разные значения в зависимости от того, подаю ли я одиночный образец или тот же образец в более широком массиве.
Моя модель выдает вектор вероятностей 1×6
Я запускаю на одном образце:
Случай A: model.predict(X_test[0:1])
и в массиве из нескольких образцов:
Случай B: model.predict(X_test[0:2])
Прогноз для X_test[0]
таков:
Случай A: [9.999e-01, 4.228e-06, 8.278e-05, 1.121e-06, 1.061e-06, 2.958e-05]
Случай B: [1.000e+00, 7.702e-13, 2.969e-13, 6.343e-11, 7.477e-14, 1.304e-10]
Почему прогнозы различаются? (прогнозы здесь – это сами вероятности, а не argmax
)
Как запросили, основной код приведен ниже:
from keras.models import load_model
from my_utils import load_dataset
import numpy as np
X_train, Y_train, X_test, Y_test, classes = load_dataset()
model = load_model('model.h5') # ResNet50 с предобученными весами
model.predict(X_test[0:1]) # Случай A
model.predict(X_test[0:2]) # Случай B
Архитектуру модели ResNet50 можно найти здесь; единственное отличие в реализации, которую я использую, заключается в том, что у меня 6 классов, а не 1000
Спасибо!
Я не знаю, как это исправить правильно, но, похоже, причина в том, что модель обучалась на пакетах, а не на отдельных примерах. Моя модель также основана на ResNet50 (частично) и у меня есть функция, которая подготавливает датасеты таким образом:
def to_ds(xy, batch_size=1):
return tf.data.Dataset.from_tensor_slices(xy).batch(batch_size)
Мои наблюдения: если я обучаю свою модель с размером партии 32, то у меня очень высокая точность на валидации на первых эпохах (например, 98-99). Однако, когда я проверяю свою модель на тестовом наборе данных, я вижу, что точность значительно ниже (75-85).
Чтобы понять, что происходит, мне пришлось написать две версии валидации:
def ensure(x, y):
# если я уберу batch_size=1, эти две функции дадут разные результаты
predicted = model.predict(x, batch_size=1)
diff = np.round(abs(predicted-y))
print(np.sum(diff))
print((x.shape[0] - np.sum(diff)) / x.shape[0])
print(np.where(np.any(diff > 0, axis=1)))
def ensure2(x, y, limit=None):
count = 0
wrong_count = 0
wrong_indexes = []
for i in range(x.shape[0]):
x_i = x[i]
y_i = y[i]
rez = model.predict(np.array([x_i]))[0, 0]
rez_text = "ok" if abs(rez - y_i) < 0.5 else "wrong"
if rez_text != "ok":
show_as_image(x_i)
print(rez, abs(rez - y_i), rez_text)
wrong_count += 1
wrong_indexes.append(i)
count += 1
if limit is not None and count > limit:
break
print(f'accuracy={(count - wrong_count)/count}')
print(wrong_indexes)
Я немного погуглил это, и на данный момент я понимаю, что слой BatchNormalization каким-то образом нормализует значения по всем образцам в пакете, а не по отдельным частям данных, принадлежащим одному образцу. Это довольно печально и не то, что мне нужно (в моем случае я хочу, чтобы моя модель была максимально точной для одного переданного изображения, а не для 32). Поэтому на данный момент я просто переключаюсь на использование batch_size=1 для своих экспериментов.
Ответ или решение
Когда вы работаете с моделями глубокого обучения, такими как ResNet50, важно учитывать, как обрабатываются различные слои, особенно слои нормализации, такие как BatchNormalization.
Ваши наблюдения о том, что model.predict()
возвращает разные значения в зависимости от того, передаете ли вы одиночный образец или группу образцов, вполне обоснованы. Вот основные причины:
-
BatchNormalization: Если в вашей модели присутствует слой BatchNormalization, он вычисляет статистику (среднее и стандартное отклонение) на основе всего батча входных данных. Это значит, что при передаче одного образца (например, с помощью
X_test[0:1]
), модель будет использовать статистику, соответствующую всему батчу, и предсказание может быть искажено этим эффектом. Сравнение с батчем, содержащим два образца (например,X_test[0:2]
), может привести к более стабильным или другим значениям предсказаний, поскольку статистика будет другой. -
Эффект размерности: Если вы передаете модель менее чем 16 или более образцов (например, 1, 2, или 4), может происходить эффект, который обычно возникает на небольших батчах — значения вероятностей могут колебаться. Это может быть связано с тем, что модель настраивает свои внутренние параметры для лучшего распознавания при обработке данных в группах.
Чтобы устранить эту проблему, вы можете попробовать несколько подходов:
-
Переключение на режим оценки: Убедитесь, что модель находится в режиме оценки, когда вы делаете предсказания. Перед вызовом
model.predict()
можете использоватьmodel.evaluate()
для получения более стабильных результатов. Однако, возможно, это не полностью решит вашу проблему, если в модели по-прежнему использованы слои BatchNormalization. -
Используйте
model.predict()
с фиксированным размером батча: Если вы хотите избежать влияния BatchNormalization, используйтеmodel.predict()
с батчами, размером, равным 1, чтобы получать стабильные предсказания. Это, как вы уже пробовали, уменьшит колебания предсказаний. -
Деинстанцирование BatchNormalization при обучении: В некоторых случаях может быть целесообразно заменить BatchNormalization на GroupNormalization или отключить его для вашего применения, если это не критически важно для производительности модели.
-
Переобучение модели: Если ваш набор данных сильно отличается по размеру батча, возможно, стоит переобучить модель с учетом меньших батчей (например, 1 или 2) во время тренировки, чтобы результаты в inferencing были более согласованными.
В итоге, разница в предсказаниях между запуском для отдельных выборок и групп может быть решена с помощью корректировки способа, которым вы используете модель для предсказаний, приоритетное внимание следует уделить слоям нормализации.