Краткий ответ: Поскольку на этапе обучения вы пометили собаку как 1, а кошку - как 0, вам необходимо заменить model_prediction
на 1 - model_prediction
, чтобы найти области, относящиеся к кошке:
plot_conv_heat_map(1 - model_prediction, ...)
Длинный ответ: Когда вы используете исходную модель VGG, в последнем слое имеется 1000 нейронов (если вы используете предварительно обученную модель ImageNet), одиндля каждого из 1000 различных классов:
# last layer in VGG model
x = layers.Dense(classes, activation='softmax', name='predictions')(x)
Каждый из этих нейронов имеет выходное значение от нуля до единицы (при условии, что сумма выходов должна быть равна единице).Таким образом, наиболее активированный нейрон (то есть тот, который имеет самый высокий выход) соответствует предсказанному классу.Таким образом, вы находите это так:
model_prediction = model.output[:, np.argmax(preds[0])]
\
\___ finds the index of the neuron with maximum output
и затем передаете его в функцию визуализации, чтобы вычислить его градиент относительно выбранного слоя свертки и визуализировать тепловую карту:
plot_conv_heat_map(model_prediction, ...)
Все идет нормально.Однако в вашей пользовательской модели вы превратили задачу из задачи классификации с несколькими классами в задачу двоичной классификации, то есть «собака против кошки».Вы используете сигмовидный слой с одним единичным блоком в качестве последнего слоя и рассматриваете активное состояние (т.е. выходы около 1) нейрона как собаку, а неактивное состояние (то есть выходы около 0) нейрона как кошку.Таким образом, ваша сеть по сути является детектором собак, и если собаки нет, то мы предполагаем, что на изображении есть кошка.
Хорошо, вы можете спросить "в чем проблема?"Ответ заключается в том, что нет проблем с точки зрения обучения модели, и, как вы предположили, вы получили хорошую точность обучения.Однако помните предположение, стоящее за функцией визуализации: она принимает в качестве входных данных нейрон с наивысшим выходным значением, которое соответствует классу, обнаруженному на изображении.Таким образом, учитывая изображение кошки для вашей пользовательской модели, вывод последнего слоя будет очень низким, скажем, 0,01.Итак, одна интерпретация этого числа заключается в том, что вероятность того, что это изображение собаки, составляет 0,01.Так что же происходит, когда вы даете это непосредственно вашей функции визуализации?Да, вы уже догадались: он найдет все области изображения, которые наиболее актуальны для собаки!Вы могли бы все еще возразить "Но я дал этому изображение кошки !!!"Это не имеет значения, поскольку этот нейрон активируется, когда присутствует собака, и поэтому, когда вы берете ее градиент относительно слоя свертки, области, наиболее важные для собаки, будут широко представлены и показаны на тепловой карте.Тем не менее, визуализация будет правильной, если вы дадите модели изображение собаки.
«Так что же нам делать, когда нужно визуализировать области, наиболее важные для кошки?»Это просто: просто сделайте этот нейрон детектором кошек."Как?"Просто создайте его дополнение: 1 - model_prediction
.Это дает вам вероятность того, что кошка присутствует на изображении.И вы можете легко использовать его, как показано ниже, для нанесения на изображение областей, соответствующих кошкам:
plot_conv_heat_map(1 - model_prediction, ...)
В качестве альтернативы, вы можете изменить последний слой вашей модели, чтобы иметь 2 нейрона с активацией softmax
изатем заново обучите его:
temp_model = layers.Dense(2, activation="softmax")(temp_model)
Таким образом, у каждого из классов, то есть собаки и кошки, будет свой собственный нейрон, и поэтому при визуализации карты активации не возникнет никаких проблем.