Repository: keras-team/keras

Issue:
Upsampling2D does not work when image dimensions are not sepcified
Upsampling seems to fail when the image dimensions are not specified, even though upsampling should be scale invariant.

When the 2nd and 3rd dim of input_shape is set to None as shown below the error at the bottom occurs.

I think it's missing an if statement for when the input_shape is None like the convolutional layers

```
model = Sequential()
model.add(Convolution2D(16, 17, 17, border_mode='valid', activation='linear', input_shape=(1, None, None))) # 1376 x 496
model.add(ELU())
model.add(AveragePooling2D((2,2))) # 688 x 248
model.add(Convolution2D(8, 9, 9, border_mode='valid', activation='linear')) # 680 x 240
model.add(ELU())
model.add(AveragePooling2D((2,2))) # 340 x 120
model.add(Convolution2D(4, 5, 5, border_mode='valid', activation='linear')) # 336 x 116
model.add(ELU())
model.add(AveragePooling2D((2,2))) # 168 x 58

model.add(Convolution2D(1, 5, 5, border_mode='valid', activation='linear')) # 164 x 54
model.add(ELU())

model.add(ZeroPadding2D((4, 4)))
model.add(Convolution2D(4, 5, 5, border_mode='valid', activation='linear')) # 168 x 58
model.add(ELU())

model.add(UpSampling2D((2, 2))) # 336 x 116
model.add(ZeroPadding2D((4, 4)))
model.add(Convolution2D(8, 5, 5, border_mode='valid', activation='linear')) # 340 x 120
model.add(ELU())

model.add(UpSampling2D((2, 2))) # 680 x 240
model.add(ZeroPadding2D((4, 4)))
model.add(Convolution2D(16, 9, 9, border_mode='valid', activation='linear')) # 688 x 248
model.add(ELU())

model.add(UpSampling2D((2, 2))) # 1376 x 496
model.add(ZeroPadding2D((4, 4)))
model.add(Convolution2D(1, 17, 17, border_mode='valid', activation='sigmoid')) #1392 x 512
```

```
Traceback (most recent call last):
  File "conv_autosegment.py", line 211, in <module>
    model.add(UpSampling2D((2, 2))) # 336 x 116
  File "/home/henry/.local/lib/python2.7/site-packages/Keras-1.0.1-py2.7.egg/keras/models.py", line 139, in add
    output_tensor = layer(self.outputs[0])
  File "/home/henry/.local/lib/python2.7/site-packages/Keras-1.0.1-py2.7.egg/keras/engine/topology.py", line 485, in __call__
    self.add_inbound_node(inbound_layers, node_indices, tensor_indices)
  File "/home/henry/.local/lib/python2.7/site-packages/Keras-1.0.1-py2.7.egg/keras/engine/topology.py", line 543, in add_inbound_node
    Node.create_node(self, inbound_layers, node_indices, tensor_indices)
  File "/home/henry/.local/lib/python2.7/site-packages/Keras-1.0.1-py2.7.egg/keras/engine/topology.py", line 151, in create_node
    output_shapes = to_list(outbound_layer.get_output_shape_for(input_shapes[0]))
  File "/home/henry/.local/lib/python2.7/site-packages/Keras-1.0.1-py2.7.egg/keras/layers/convolutional.py", line 1010, in get_output_shape_for
    self.size[0] * input_shape[2],
TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'
```



Discussion:
Ran into this myself but it fails even if the input dimensions are specified (with TensorFlow dim ordering):

```
# generator of GAN:
inputs = Input(shape=(100,))
x = Dense(1024 * 3 * 4)(inputs)
x = BatchNormalization(axis=-1)(x)
x = Activation('relu')(x)

x = Reshape(target_shape=(3, 4, 1024))(x)
print('Reshape', x.get_shape()) # (None, 3, 4, 1024)

x = UpSampling2D(size=(2, 2), dim_ordering='tf')(x)
print('UpSampling2D', x.get_shape()) # (None, None, None, 1024)

x = Convolution2D(1024 // 2, 5, 5, border_mode='same', dim_ordering='tf')(x)
x = BatchNormalization(axis=-1)(x)
x = Activation('relu')(x)

x = UpSampling2D(size=(2, 2), dim_ordering='tf')(x)
x = Convolution2D(1024 // 4, 5, 5, border_mode='same', dim_ordering='tf')(x)
x = BatchNormalization(axis=-1)(x)
x = Activation('relu')(x)

x = UpSampling2D(size=(2, 2), dim_ordering='tf')(x)
x = Convolution2D(1024 // 8, 5, 5, border_mode='same', dim_ordering='tf')(x)
x = BatchNormalization(axis=-1)(x)
x = Activation('relu')(x)

x = UpSampling2D(size=(2, 2), dim_ordering='tf')(x)
x = Convolution2D(3, 5, 5, border_mode='same', dim_ordering='tf')(x)
x = Activation('tanh')(x)

model = Model(input=inputs, output=x)
```

I've verified that `get_output_shape_for` of `UpSampling2D` returns the correct shape. Looking into why it isn't propagated to the output tensor now.

EDIT: Looks like `K.resize_images` of `UpSampling2D#call` does not retain dimensions of its output.

EDIT2: I'm not sure about the Theano case but in TF, replacing `tf.image.resize_nearest_neighbor` of `K.resize_images` with `tf.image.resize_images(..., method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)` appears to fix the issue and dimensions are propagated correctly. I presume this is a bug in TF since `resize_images` just proxies to `resize_nearest_neighbor`.


Resolving patch:
diff --git a/keras/layers/convolutional.py b/keras/layers/convolutional.py
--- a/keras/layers/convolutional.py
+++ b/keras/layers/convolutional.py
@@ -1045,7 +1045,8 @@ def __init__(self, length=2, **kwargs):
         super(UpSampling1D, self).__init__(**kwargs)
 
     def get_output_shape_for(self, input_shape):
-        return (input_shape[0], self.length * input_shape[1], input_shape[2])
+        length = self.length * input_shape[1] if input_shape[1] is not None else None
+        return (input_shape[0], length, input_shape[2])
 
     def call(self, x, mask=None):
         output = K.repeat_elements(x, self.length, axis=1)
@@ -1094,14 +1095,18 @@ def __init__(self, size=(2, 2), dim_ordering='default', **kwargs):
 
     def get_output_shape_for(self, input_shape):
         if self.dim_ordering == 'th':
+            width = self.size[0] * input_shape[2] if input_shape[2] is not None else None
+            height = self.size[1] * input_shape[3] if input_shape[3] is not None else None
             return (input_shape[0],
                     input_shape[1],
-                    self.size[0] * input_shape[2],
-                    self.size[1] * input_shape[3])
+                    width,
+                    height)
         elif self.dim_ordering == 'tf':
+            width = self.size[0] * input_shape[1] if input_shape[1] is not None else None
+            height = self.size[1] * input_shape[2] if input_shape[2] is not None else None
             return (input_shape[0],
-                    self.size[0] * input_shape[1],
-                    self.size[1] * input_shape[2],
+                    width,
+                    height,
                     input_shape[3])
         else:
             raise Exception('Invalid dim_ordering: ' + self.dim_ordering)
@@ -1153,16 +1158,22 @@ def __init__(self, size=(2, 2, 2), dim_ordering='default', **kwargs):
 
     def get_output_shape_for(self, input_shape):
         if self.dim_ordering == 'th':
+            dim1 = self.size[0] * input_shape[2] if input_shape[2] is not None else None
+            dim2 = self.size[1] * input_shape[3] if input_shape[3] is not None else None
+            dim3 = self.size[2] * input_shape[4] if input_shape[4] is not None else None
             return (input_shape[0],
                     input_shape[1],
-                    self.size[0] * input_shape[2],
-                    self.size[1] * input_shape[3],
-                    self.size[2] * input_shape[4])
+                    dim1,
+                    dim2,
+                    dim3)
         elif self.dim_ordering == 'tf':
+            dim1 = self.size[0] * input_shape[1] if input_shape[1] is not None else None
+            dim2 = self.size[1] * input_shape[2] if input_shape[2] is not None else None
+            dim3 = self.size[2] * input_shape[3] if input_shape[3] is not None else None
             return (input_shape[0],
-                    self.size[0] * input_shape[1],
-                    self.size[1] * input_shape[2],
-                    self.size[2] * input_shape[3],
+                    dim1,
+                    dim2,
+                    dim3,
                     input_shape[4])
         else:
             raise Exception('Invalid dim_ordering: ' + self.dim_ordering)


Test patch: