Repository: keras-team/keras

Issue:
Variable-length Upsampling
I want to add an `UpSampling1D` but it seems it only accepts fixed-length input. Is there any chance to get it to work for variable-length input? Other layers work perfectly for my model.

Part of my model:

```
inp = Input(shape=(None,161),name="in")
zeropadded = ZeroPadding1D(padding=1)(inp)
upsampled = UpSampling1D(length=2)(zeropadded)
```

I get the following error:

```
ValueError: Axis 1 of input tensor should have a defined dimension, but is None. Full tensor shape: (None, None, 161). Typically you need to pass a fully-defined `input_shape` argument to your first layer.
```


Discussion:
The following works for tensorflow, which can be replaced with the original function in tensorflow backend to allow variable-length upsampling.

```
def repeat_elements(x,rep,axis):
    x_shape = x.get_shape().as_list()
    reps = np.ones(len(x_shape))
    reps[axis] = rep
    x_rep = tf.tile(x,reps)
    return x_rep
```

Resolving patch:
diff --git a/keras/backend/tensorflow_backend.py b/keras/backend/tensorflow_backend.py
--- a/keras/backend/tensorflow_backend.py
+++ b/keras/backend/tensorflow_backend.py
@@ -1823,24 +1823,43 @@ def repeat_elements(x, rep, axis):
         rep: Python integer, number of times to repeat.
         axis: Axis along which to repeat.
 
-    # Raises
-        ValueError: In case `x.shape[axis]` is undefined.
-
     # Returns
         A tensor.
     """
     x_shape = x.get_shape().as_list()
-    if x_shape[axis] is None:
-        raise ValueError('Axis ' + str(axis) + ' of input tensor '
-                         'should have a defined dimension, but is None. '
-                         'Full tensor shape: ' + str(tuple(x_shape)) + '. '
-                         'Typically you need to pass a fully-defined '
-                         '`input_shape` argument to your first layer.')
-    # slices along the repeat axis
-    splits = tf.split(value=x, num_or_size_splits=x_shape[axis], axis=axis)
-    # repeat each slice the given number of reps
-    x_rep = [s for s in splits for _ in range(rep)]
-    return concatenate(x_rep, axis)
+    # For static axis
+    if x_shape[axis] is not None:
+        # slices along the repeat axis
+        splits = tf.split(value=x, num_or_size_splits=x_shape[axis], axis=axis)
+        # repeat each slice the given number of reps
+        x_rep = [s for s in splits for _ in range(rep)]
+        return concatenate(x_rep, axis)
+
+    # Here we use tf.tile to mimic behaviour of np.repeat so that
+    # we can handle dynamic shapes (that include None).
+    # To do that, we need an auxiliary axis to repeat elements along
+    # it and then merge them along the desired axis.
+
+    # Repeating
+    auxiliary_axis = axis + 1
+    x_shape = tf.shape(x)
+    x_rep = tf.expand_dims(x, axis=auxiliary_axis)
+    reps = np.ones(len(x.get_shape()) + 1)
+    reps[auxiliary_axis] = rep
+    x_rep = tf.tile(x_rep, reps)
+
+    # Merging
+    reps = np.delete(reps, auxiliary_axis)
+    reps[axis] = rep
+    reps = tf.constant(reps, dtype='int32')
+    x_shape = x_shape * reps
+    x_rep = tf.reshape(x_rep, x_shape)
+
+    # Fix shape representation
+    x_shape = x.get_shape().as_list()
+    x_rep.set_shape(x_shape)
+    x_rep._keras_shape = tuple(x_shape)
+    return x_rep
 
 
 def repeat(x, n):


Test patch:
diff --git a/tests/keras/backend/backend_test.py b/tests/keras/backend/backend_test.py
--- a/tests/keras/backend/backend_test.py
+++ b/tests/keras/backend/backend_test.py
@@ -286,19 +286,14 @@ def test_repeat_elements(self):
                     if hasattr(z, '_keras_shape'):
                         assert z._keras_shape == z.shape
 
-                # test theano shape inference when
-                # input shape has None entries
-                if K.backend() == 'theano':
+                if K.backend() != 'cntk':
                     shape = list(shape)
                     shape[rep_axis] = None
                     x = K.placeholder(shape=shape)
                     y = K.repeat_elements(x, reps, axis=rep_axis)
                     assert y._keras_shape == tuple(shape)
-
-        # Test invalid use cases
-        with pytest.raises(ValueError):
-            ztf = KTF.placeholder(shape=(None, 2, 3))
-            KTF.repeat_elements(ztf, 5, axis=0)
+                if K.backend() == 'tensorflow':
+                    assert y._keras_shape == tuple(y.get_shape().as_list())
 
     def test_tile(self):
         shape = (3, 4)