Предсказание одного примера в Keras возвращает разные значения

Вопрос или проблема

Я использую предобученную модель 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() возвращает разные значения в зависимости от того, передаете ли вы одиночный образец или группу образцов, вполне обоснованы. Вот основные причины:

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

  2. Эффект размерности: Если вы передаете модель менее чем 16 или более образцов (например, 1, 2, или 4), может происходить эффект, который обычно возникает на небольших батчах — значения вероятностей могут колебаться. Это может быть связано с тем, что модель настраивает свои внутренние параметры для лучшего распознавания при обработке данных в группах.

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

  • Переключение на режим оценки: Убедитесь, что модель находится в режиме оценки, когда вы делаете предсказания. Перед вызовом model.predict() можете использовать model.evaluate() для получения более стабильных результатов. Однако, возможно, это не полностью решит вашу проблему, если в модели по-прежнему использованы слои BatchNormalization.

  • Используйте model.predict() с фиксированным размером батча: Если вы хотите избежать влияния BatchNormalization, используйте model.predict() с батчами, размером, равным 1, чтобы получать стабильные предсказания. Это, как вы уже пробовали, уменьшит колебания предсказаний.

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

  • Переобучение модели: Если ваш набор данных сильно отличается по размеру батча, возможно, стоит переобучить модель с учетом меньших батчей (например, 1 или 2) во время тренировки, чтобы результаты в inferencing были более согласованными.

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

Оцените материал
Добавить комментарий

Капча загружается...