Активация в моем CNN выглядит неправильно - или проблема в тепловой карте? - PullRequest
3 голосов
/ 06 июня 2019

Я делаю тепловые карты для моих сверточных нейронных сетей, сделанных через Keras, , как описано здесь .Когда я запускаю этот алгоритм для ванильной VGG16 сети, тепловая карта выглядит нормально:

enter image description here

Затем я создал свою собственную пользовательскую модель, основанную на этомVGG16 сеть, но с пользовательскими верхними слоями:

input_layer = layers.Input(shape=(img_size, img_size, 3), name="model_input")
vgg16_base = VGG16(weights="imagenet", include_top=False, input_tensor=input_layer)
temp_model = vgg16_base.output
temp_model = layers.Flatten()(temp_model)
temp_model = layers.Dense(256, activation="relu")(temp_model)
temp_model = layers.Dense(1, activation="sigmoid")(temp_model)
custom = models.Model(inputs=input_layer, outputs=temp_model)

Тем не менее, когда я генерирую тепловую карту для того же слоя моей собственной пользовательской сети (то есть последнего конвективного слоя из базы VGG16,будучи частью моей новой сети), используя тот же код / ​​функцию, тепловая карта выглядит неправильно:

enter image description here

Точность проверки / тестированияМоя пользовательская сеть на 97-98%, поэтому я предполагаю, что она работает нормально.Почему же тогда активация / тепловая карта отключена?Или я что-то здесь упускаю?

PS: Для справки, тепловая карта создается с помощью функции , перечисленной здесь .Это называется так:

# Load the image from disk and preprocess it via Keras tools
img_path = "/path/to/image.jpg"
img = image.load_img(img_path, target_size=(224, 224))
img_tensor = image.img_to_array(img)
img_tensor = np.expand_dims(img_tensor, axis=0)
img_tensor = preprocess_input(img_tensor)

# At this point I either load the VGG16 model directly (heatmapü works),
# or I create my own custom VGG16-based model (heatmap does not work)
# The model itself is then stored into the variable "model"

preds = model.predict(img_tensor)
model_prediction = model.output[:, np.argmax(preds[0])]

# Then I call the custom function referred to above
input_layer = model.get_layer("model_input")
conv_layer = model.get_layer("block5_conv3")
plot_conv_heat_map(model_prediction, input_layer, conv_layer, img_tensor, img_path)

1 Ответ

3 голосов
/ 18 июня 2019

Краткий ответ: Поскольку на этапе обучения вы пометили собаку как 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)

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

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