Nuwanda
Nuwanda

Reputation: 51

Problem with training U-Net while using ImageDataGenerator on multiclass segmentation

The task I am dealing with is multiclass segmentation (0-3 classes on each image). I had a working U-Net model and could train on small dataset just fine, then I augmented the dataset and now I have almost 15k 512x512 grayscale images. I've naturally encountered an issue with not having enough HW resources (RAM, GPU), so I decided to switch to google colab and to go with ImageDataGenerator. I've encountered this issue not being able to solve so far.

InvalidArgumentError: Conv2DSlowBackpropInput: Size of out_backprop doesn't match computed: actual = 16, computed = 32 spatial_dim: 2 input: 64 filter: 2 output: 16 stride: 2 dilation: 1 [[node model/conv2d_transpose_1/conv2d_transpose (defined at /usr/local/lib/python3.7/dist-packages/keras/backend.py:5360) ]] [Op:__inference_train_function_3151]

The only explanation for me is I am not using the generator well. I've structured the data as:

path_to_dataset
│
└───images_dir
│   │
│   └─── images_subdir
│       │   img1.png
│       │   img2.png
│       │   ...
│   
└───masks_dir
│   │
│   └─── masks_subdir
│       │   img1.png
│       │   img2.png
│       │   ...

The subdirectories are there to only make ImageDataGenerator work.

data_gen_args = dict(rescale=1./255,)
image_datagen = ImageDataGenerator(**data_gen_args)
mask_datagen = ImageDataGenerator(**data_gen_args)
# image_datagen.fit(images)
# mask_datagen.fit(masks)
# Provide the same seed and keyword arguments to the fit and flow methods
seed = 1
image_generator = image_datagen.flow_from_directory(
    '/content/drive/MyDrive/DP/preprocess_images/images/final_ds/orig_folder/',
    batch_size=16,
    class_mode=None,
    # color_mode='grayscale',
    seed=seed)
mask_generator = mask_datagen.flow_from_directory(
    '/content/drive/MyDrive/DP/preprocess_images/images/final_ds/seg_greyscale_folder/',
    batch_size=16,
    class_mode=None,
    # color_mode='grayscale',
    seed=seed)
# combine generators into one which yields image and masks
train_generator = zip(image_generator, mask_generator)
callbacks = [
    ModelCheckpoint('unet_512.h5', verbose=1, save_best_only=True),
    EarlyStopping(patience=5, monitor='val_loss'),
    TensorBoard(log_dir='logs_unet512')
]

history = model.fit(train_generator,
                    verbose=1,
                    epochs=50,
                    callbacks=callbacks,
                    # class_weight=class_weights,
                    shuffle=False)

I didn't deal with creating data generator for validation data so far, since I'm not even able to make this part work.

And for the curious ones, here's the model.

    # IMG_HEIGHT=512, IMG_WIDTH=512, IMG_CHANNELS=1
    inputs = Input((IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS))
    s = inputs

    # Contraction path
    c1 = Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(s)
    c1 = Dropout(0.1)(c1)
    c1 = Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c1)
    p1 = MaxPooling2D((2, 2))(c1)

    c2 = Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p1)
    c2 = Dropout(0.1)(c2)
    c2 = Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c2)
    p2 = MaxPooling2D((2, 2))(c2)

    c3 = Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p2)
    c3 = Dropout(0.2)(c3)
    c3 = Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c3)
    p3 = MaxPooling2D((2, 2))(c3)

    c4 = Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p3)
    c4 = Dropout(0.2)(c4)
    c4 = Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c4)
    p4 = MaxPooling2D(pool_size=(2, 2))(c4)

    c5 = Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p4)
    c5 = Dropout(0.3)(c5)
    c5 = Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c5)

    # Expansive path
    u6 = Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(c5)
    u6 = concatenate([u6, c4])
    c6 = Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u6)
    c6 = Dropout(0.2)(c6)
    c6 = Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c6)

    u7 = Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(c6)
    u7 = concatenate([u7, c3])
    c7 = Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u7)
    c7 = Dropout(0.2)(c7)
    c7 = Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c7)

    u8 = Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(c7)
    u8 = concatenate([u8, c2])
    c8 = Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u8)
    c8 = Dropout(0.1)(c8)
    c8 = Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c8)

    u9 = Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same')(c8)
    u9 = concatenate([u9, c1], axis=3)
    c9 = Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u9)
    c9 = Dropout(0.1)(c9)
    c9 = Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c9)

    # n_classes=4
    outputs = Conv2D(n_classes, (1, 1), activation='softmax')(c9)

    model = Model(inputs=[inputs], outputs=[outputs])

Edit: also planning to get the number of filters up, so far I'm running the model that worked before on my personal laptop

Upvotes: 2

Views: 779

Answers (1)

Nuwanda
Nuwanda

Reputation: 51

I didn't find a way to make it work with keras built in implementations, however custom generator does the trick. Seems like most of the tasks are handled very well, but multiclass semantic segmentation will be added, hopefully, some day

Upvotes: 0

Related Questions