5
$\begingroup$

I am trying to apply convolutional autoencdeor on a odd size image. Below is the code:

from keras.layers import Input, Dense, Conv2D, MaxPooling2D, UpSampling2D
from keras.models import Model
# from keras import backend as K

input_img = Input(shape=(91, 91, 1))  # adapt this if using `channels_first` image data format

x = Conv2D(16, (3, 3), activation='relu', padding='same')(input_img)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
encoded = MaxPooling2D((2, 2), padding='same')(x)

# at this point the representation is (4, 4, 8) i.e. 128-dimensional

x = Conv2D(8, (3, 3), activation='relu', padding='same')(encoded)
x = UpSampling2D((2, 2))(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
x = UpSampling2D((2, 2))(x)
x = Conv2D(16, (3, 3), activation='relu')(x)
x = UpSampling2D((2, 2))(x)
decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)

autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.summary()

which gives the following:

Layer (type)                 Output Shape              Param #   
=================================================================
input_22 (InputLayer)        (None, 91, 91, 1)         0         
_________________________________________________________________
conv2d_145 (Conv2D)          (None, 91, 91, 16)        160       
_________________________________________________________________
max_pooling2d_64 (MaxPooling (None, 46, 46, 16)        0         
_________________________________________________________________
conv2d_146 (Conv2D)          (None, 46, 46, 8)         1160      
_________________________________________________________________
max_pooling2d_65 (MaxPooling (None, 23, 23, 8)         0         
_________________________________________________________________
conv2d_147 (Conv2D)          (None, 23, 23, 8)         584       
_________________________________________________________________
max_pooling2d_66 (MaxPooling (None, 12, 12, 8)         0         
_________________________________________________________________
conv2d_148 (Conv2D)          (None, 12, 12, 8)         584       
_________________________________________________________________
up_sampling2d_64 (UpSampling (None, 24, 24, 8)         0         
_________________________________________________________________
conv2d_149 (Conv2D)          (None, 24, 24, 8)         584       
_________________________________________________________________
up_sampling2d_65 (UpSampling (None, 48, 48, 8)         0         
_________________________________________________________________
conv2d_150 (Conv2D)          (None, 46, 46, 16)        1168      
_________________________________________________________________
up_sampling2d_66 (UpSampling (None, 92, 92, 16)        0         
_________________________________________________________________
conv2d_151 (Conv2D)          (None, 92, 92, 1)         145       
=================================================================
Total params: 4,385
Trainable params: 4,385
Non-trainable params: 0

We can see that the input and output are not of the same size. So when i do the autoencoder.fit, it throws the error that ValueError: Error when checking target: expected conv2d_7 to have shape (92, 92, 1) but got array with shape (91, 91, 1). What to do to rectify this?

$\endgroup$

2 Answers 2

1
$\begingroup$

I got the solution by adding a cropping layer in the end. The full code is as below:

from keras.layers import Input, Dense, Conv2D, MaxPooling2D, UpSampling2D
from keras.models import Model
# from keras import backend as K

input_img = Input(shape=(91, 91, 1))  # adapt this if using `channels_first` image data format

x = Conv2D(16, (3, 3), activation='relu', padding='same')(input_img)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
x = MaxPooling2D((2, 2), padding='same')(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
encoded = MaxPooling2D((2, 2), padding='same')(x)

# at this point the representation is (4, 4, 8) i.e. 128-dimensional

x = Conv2D(8, (3, 3), activation='relu', padding='same')(encoded)
x = UpSampling2D((2, 2))(x)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
x = UpSampling2D((2, 2))(x)
x = Conv2D(16, (3, 3), activation='relu')(x)
x = UpSampling2D((2, 2))(x)
x = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)
decoded = Cropping2D(cropping=((1, 0), (1, 0)), data_format=None)(x) # this is the added step

autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
$\endgroup$
0
$\begingroup$

You can add ZeroPadding to the input layer so that the expected input dimension is even. Or you have to add padding layers in between so that you do not need to do divisions on odd numbers (which will get rounded off).

Maybe this will help you to manually pre-calculate the effective spatial dimensions of your feature maps using the following formulae:

out_width = (W - F_w + 2P)/S_w + 1
out_height = (H - F_h + 2P)/S_h + 1

where (W, H) = spatial dimensions of the input
(F_w, F_h) = spatial dimensions of filters
P = the amount of zero padding added to the border of the image
S_w, S_h are horizontal and vertical stride of the convolution, respectively
$\endgroup$
3
  • $\begingroup$ okay. I solved this by adding a cropping layer in the end x = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x) decoded = Cropping2D(cropping=((1, 0), (1, 0)), data_format=None)(x) $\endgroup$
    – prashanth
    Nov 14, 2018 at 16:26
  • $\begingroup$ But if it is a semantic segmentation problem, should this not mess up your predictions at the edges? Since you are cropping at the very last layer. $\endgroup$
    – Anakin
    Nov 15, 2018 at 12:25
  • $\begingroup$ Convolutional layers are transforming information from input plain image to stacks of features. Cropping away some these stacks at latest steps is 'suboptimal' decision. If you need cheap way to save this information, consider use pooling with non-symmetric padding like (0,1). $\endgroup$ Aug 6, 2021 at 6:41

Not the answer you're looking for? Browse other questions tagged or ask your own question.