Я использую реализацию FCN-8 на основе VGG16 в Keras с Tensorflow в качестве Backend для выполнения сегментации c на изображениях. Цель состоит в том, чтобы выделить железнодорожную колею на изображении, взятом из кабины, что по существу делает ее проблемой двоичной сегментации: колея является передним планом (класс 1), а все остальное относится к фону (класс 0). Результат должен быть похож на на следующем изображении , где результирующая сегментация накладывается на исходное изображение (я пока не могу публиковать изображения).
Я адаптировал FCN, указанный в это сообщение в блоге путем изменения следующих частей:
- Уменьшено количество ядер на каждом слое, поскольку FCN выглядело излишне большим.
- Изменена функция потерь в binary_crossentropy , так как я имею дело с двумя различными классами.
- Удален импорт VGG16, поскольку я хочу обучить модель на моих данных с самого начала.
Это дало следующую модель:
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_33 (InputLayer) (None, 224, 224, 3) 0
__________________________________________________________________________________________________
block1_conv1 (Conv2D) (None, 224, 224, 2) 56 input_33[0][0]
__________________________________________________________________________________________________
block1_conv2 (Conv2D) (None, 224, 224, 2) 38 block1_conv1[0][0]
__________________________________________________________________________________________________
block1_pool (MaxPooling2D) (None, 112, 112, 2) 0 block1_conv2[0][0]
__________________________________________________________________________________________________
block2_conv1 (Conv2D) (None, 112, 112, 4) 76 block1_pool[0][0]
__________________________________________________________________________________________________
block2_conv2 (Conv2D) (None, 112, 112, 4) 148 block2_conv1[0][0]
__________________________________________________________________________________________________
block2_pool (MaxPooling2D) (None, 56, 56, 4) 0 block2_conv2[0][0]
__________________________________________________________________________________________________
block3_conv1 (Conv2D) (None, 56, 56, 8) 296 block2_pool[0][0]
__________________________________________________________________________________________________
block3_conv2 (Conv2D) (None, 56, 56, 8) 584 block3_conv1[0][0]
__________________________________________________________________________________________________
block3_conv3 (Conv2D) (None, 56, 56, 8) 584 block3_conv2[0][0]
__________________________________________________________________________________________________
block3_pool (MaxPooling2D) (None, 28, 28, 8) 0 block3_conv3[0][0]
__________________________________________________________________________________________________
block4_conv1 (Conv2D) (None, 28, 28, 16) 1168 block3_pool[0][0]
__________________________________________________________________________________________________
block4_conv2 (Conv2D) (None, 28, 28, 16) 2320 block4_conv1[0][0]
__________________________________________________________________________________________________
block4_conv3 (Conv2D) (None, 28, 28, 16) 2320 block4_conv2[0][0]
__________________________________________________________________________________________________
block4_pool (MaxPooling2D) (None, 14, 14, 16) 0 block4_conv3[0][0]
__________________________________________________________________________________________________
block5_conv1 (Conv2D) (None, 14, 14, 16) 2320 block4_pool[0][0]
__________________________________________________________________________________________________
block5_conv2 (Conv2D) (None, 14, 14, 16) 2320 block5_conv1[0][0]
__________________________________________________________________________________________________
block5_conv3 (Conv2D) (None, 14, 14, 16) 2320 block5_conv2[0][0]
__________________________________________________________________________________________________
block5_pool (MaxPooling2D) (None, 7, 7, 16) 0 block5_conv3[0][0]
__________________________________________________________________________________________________
conv6 (Conv2D) (None, 7, 7, 128) 100480 block5_pool[0][0]
__________________________________________________________________________________________________
pool4_11 (Conv2D) (None, 14, 14, 2) 34 block4_pool[0][0]
__________________________________________________________________________________________________
conv7 (Conv2D) (None, 7, 7, 128) 16512 conv6[0][0]
__________________________________________________________________________________________________
conv2d_transpose_98 (Conv2DTran (None, 28, 28, 2) 16 pool4_11[0][0]
__________________________________________________________________________________________________
pool3_11 (Conv2D) (None, 28, 28, 2) 18 block3_pool[0][0]
__________________________________________________________________________________________________
conv2d_transpose_97 (Conv2DTran (None, 28, 28, 2) 4096 conv7[0][0]
__________________________________________________________________________________________________
add (Add) (None, 28, 28, 2) 0 conv2d_transpose_98[0][0]
pool3_11[0][0]
conv2d_transpose_97[0][0]
__________________________________________________________________________________________________
conv2d_transpose_99 (Conv2DTran (None, 224, 224, 2) 256 add[0][0]
__________________________________________________________________________________________________
activation_33 (Activation) (None, 224, 224, 2) 0 conv2d_transpose_99[0][0]
==================================================================================================
Total params: 135,962
Trainable params: 135,962
Non-trainable params: 0
__________________________________________________________________________________________________
Однако результаты недостаточно точны, как видно здесь . В то время как часть, близкая к поезду, кажется более или менее нормальной, часть пути, которая находится дальше, вообще не распознается.
Проблема заключается в том, что путь становится более узким в направлении горизонт, но отдаленная часть также содержит более актуальную информацию о кривизне дорожки. На всех изображениях ближняя часть выглядит очень похоже.
Мой вопрос: как я могу заставить данную модель FCN-8 также распознавать детали, которые находятся на ie дальше?
Поскольку очевидно, что фона намного больше, чем переднего плана (в ~ 40 раз), я попытался использовать параметр class_weights
функции model.fit()
. Однако, поскольку мои цели закодированы в горячем виде, они являются трехмерными (высота изображения х ширина изображения х количество классов). Для этой фигуры Keras, похоже, не поддерживает веса классов:
`class_weight` not supported for 3+ dimensional targets.
Я также пытался следовать инструкциям, приведенным в в этом выпуске , и использовать веса выборок вместо весов классов, но как Я очень новичок в Keras и Deep Learning в целом, я не понимал, какие части моей модели мне нужно настроить, чтобы заставить это работать. Более того, я даже не уверен, решит ли это мою проблему.
Уменьшение изображения до соответствующей части дало следующий результат , который определенно является более точным и "плавным". Соотношение между классами 0 и 1 в этом случае составляет около 4: 1, что может указывать на то, что соотношение между классами в исходном изображении действительно отрицательно влияет на результат. Однако поиск соответствующей области на изображении добавляет дополнительный процесс к процессу, который я хочу избежать, чтобы он оставался общим.
Наконец, увеличение количества эпох не кажется решением, так как видел в этом графике потерь . Не было заметной разницы при использовании 30 или 3000 изображений.
Итак, чтобы подвести итог моего вопроса: Если модель FCN-8 теоретически способна удовлетворительно решить мою задачу, что я могу изменить обнаружить важные детали дальше? Если это модель, ограничивающая точность, какая другая модель будет лучше для поставленной задачи? И если решение действительно вводит выборочные веса, какую форму они должны были бы применить? Поскольку я очень новичок в Keras, я бы хотел пока не писать собственные функции потерь и вместо этого просто передавать веса в качестве параметра функции model.fit()
, если только эту проблему не решить очень легко.
Спасибо!