#python #tensorflow #keras #unity3d-unet #semantic-segmentation
#python #тензорный поток #keras #семантическая сегментация
Вопрос:
Я пытаюсь обучить UNET, чтобы обнаружить повреждения дороги после стихийного бедствия. Мои образы есть 256x256x3
, а маски есть 256x256x1
. Пиксели соответствуют 4 классам: 0 = фон, 1 = нет поврежденной дороги, 2 = поврежденная дорога и 3 = листва деревьев, мешающая обзору. Это выглядит примерно так — (на изображении видно только 3 класса):
У меня есть эти изображения в папках со следующей структурой. У меня также есть данные проверки в той же структуре
-- train images
-- img
-- 1.png
-- 2.png
-- train masks
-- img
-- 1_mask.png
-- 2_mask.png
Мои генераторы данных train и validation следующие
image_data_gen = image.ImageDataGenerator(rotation_range = 40, horizontal_flip = True,
vertical_flip = True, zoom_range = 0.2,
shear_range = 0.2,width_shift_range = 0.2,
height_shift_range = 0.2)
mask_data_gen = image.ImageDataGenerator(rotation_range = 40, horizontal_flip = True,
vertical_flip = True, zoom_range = 0.2,
shear_range = 0.2,width_shift_range = 0.2,
height_shift_range = 0.2)
validimg_data_gen = image.ImageDataGenerator()
validmask_data_gen = image.ImageDataGenerator()
image_array_gen = image_data_gen.flow_from_directory(directory=train_images_path, class_mode = None,
target_size = (256,256), seed = 909)
mask_array_gen = mask_data_gen.flow_from_directory(directory=train_segs_path, class_mode = None,
target_size = (256,256), seed = 909)
valid_image_array_gen = validimg_data_gen.flow_from_directory(directory= val_images_path, class_mode = None,
target_size = (256,256), seed = 909)
valid_mask_array_gen = validmask_data_gen.flow_from_directory(directory= val_segs_path, class_mode = None,
target_size = (256,256), seed = 909)
# combine generators into one which yields image and masks
train_generator = zip(image_array_gen, mask_array_gen)
valid_generator = zip(valid_image_array_gen, valid_mask_array_gen)
Я понимаю, что, поскольку это проблема сегментации, class_mode должно быть None . Когда я запускаю указанную выше ячейку Found x images belonging to 1 classes.
, появляется сообщение, где x — количество обучающих и проверочных изображений, которые у меня есть во вложенной папке «img». Я думаю, что ошибка может заключаться в том, что у меня есть мои данные во вложенных папках «img», и keras считает, что все изображения соответствуют классу img, когда на самом деле существует 4 класса пикселей. Но если я помещу свои данные в папки train images и train masks, я получу сообщение Found 0 images belonging to 0 classes.
Когда я пытаюсь обучить свою модель, results = model.fit_generator(train_generator, steps_per_epoch=int(train_samples/batch_size),epochs=30, validation_data=valid_generator,validation_steps=int(valid_samples/batch_size))
я получаю ошибку:
InvalidArgumentError: Incompatible shapes: [32,256,256,3] vs. [32,256,256,4]
[[node gradient_tape/categorical_crossentropy/mul/BroadcastGradientArgs (defined at <ipython-input-120-22f04a70298f>:3) ]] [Op:__inference_train_function_19597]
Function call stack:
train_function
Код, который я использовал для определения модели, находится в конце этого вопроса. Обратите внимание, что если я изменю последний слой conv10 = Conv2D(1, 1, activation = 'softmax')(conv9)
, ошибка исчезнет, и обучение будет выполнено без проблем. Также я думаю, что ошибка может заключаться в том, что у меня есть мои данные во вложенной папке img, но что я могу сделать, чтобы указать модель, в которой классы пикселей равны 4, без этой ошибки?
Модель
def unet(pretrained_weights = None,input_size = (256,256,3)):
inputs = Input(input_size)
conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(inputs)
conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1)
pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)
conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool1)
conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv2)
pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)
conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool2)
conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv3)
pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)
conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool3)
conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv4)
drop4 = Dropout(0.5)(conv4)
pool4 = MaxPooling2D(pool_size=(2, 2))(drop4)
conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool4)
conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv5)
drop5 = Dropout(0.5)(conv5)
up6 = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(drop5))
merge6 = concatenate([drop4,up6], axis = 3)
conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge6)
conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv6)
up7 = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv6))
merge7 = concatenate([conv3,up7], axis = 3)
conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge7)
conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv7)
up8 = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv7))
merge8 = concatenate([conv2,up8], axis = 3)
conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge8)
conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv8)
up9 = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv8))
merge9 = concatenate([conv1,up9], axis = 3)
conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge9)
conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
conv9 = Conv2D(2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
conv10 = Conv2D(4, 1, activation = 'softmax')(conv9)
model = Model(inputs = inputs, outputs = conv10)
return(model)
model = unet()
model.compile(optimizer='adam', loss='categorical_crossentropy' ,metrics=['categorical_accuracy'])
Ответ №1:
ваш train_images _path должен быть путем к каталогу, который содержит подкаталоги класса. То же самое для других каталогов, которые вы используете в потоке из каталога.Например, если у вас есть структура каталогов, как показано ниже, для классификации собак и кошек
c:
---train_images
---img
--- cats
--- dogs
then in flow from directory your path would be
r'c:train_imagesimg'
поток из каталога создаст классы cats и dogs
Комментарии:
1. Я понимаю, что для классификации изображений именно так и должно быть сделано, однако я пытаюсь семантическую сегментацию, где все классы могут встречаться в любом изображении. Так что это не так просто, как иметь папку для кошек, а другую для собак, нужно классифицировать каждый пиксель как принадлежащий к тому или иному классу
2. ОК неправильно понял вопрос