diff --git a/keras_core/layers/__init__.py b/keras_core/layers/__init__.py index c9f9fb8ea..78b22788c 100644 --- a/keras_core/layers/__init__.py +++ b/keras_core/layers/__init__.py @@ -68,3 +68,4 @@ from keras_core.layers.regularization.spatial_dropout import SpatialDropout2D from keras_core.layers.regularization.spatial_dropout import SpatialDropout3D from keras_core.layers.reshaping.flatten import Flatten from keras_core.layers.reshaping.reshape import Reshape +from keras_core.layers.reshaping.up_sampling1d import UpSampling1D diff --git a/keras_core/layers/reshaping/up_sampling1d.py b/keras_core/layers/reshaping/up_sampling1d.py new file mode 100644 index 000000000..10e81fdff --- /dev/null +++ b/keras_core/layers/reshaping/up_sampling1d.py @@ -0,0 +1,59 @@ +from keras_core import operations as ops +from keras_core.layers.input_spec import InputSpec +from keras_core.layers.layer import Layer + + +class UpSampling1D(Layer): + """Upsampling layer for 1D inputs. + + Repeats each temporal step `size` times along the time axis. + + Examples: + + >>> input_shape = (2, 2, 3) + >>> x = np.arange(np.prod(input_shape)).reshape(input_shape) + >>> print(x) + [[[ 0 1 2] + [ 3 4 5]] + [[ 6 7 8] + [ 9 10 11]]] + >>> y = keras_core.layers.UpSampling1D(size=2)(x) + >>> print(y) + [[[ 0. 1. 2.] + [ 0. 1. 2.] + [ 3. 4. 5.] + [ 3. 4. 5.]] + + [[ 6. 7. 8.] + [ 6. 7. 8.] + [ 9. 10. 11.] + [ 9. 10. 11.]]] + + Args: + size: Integer. Upsampling factor. + + Input shape: + 3D tensor with shape: `(batch_size, steps, features)`. + + Output shape: + 3D tensor with shape: `(batch_size, upsampled_steps, features)`. + """ + + def __init__(self, size=2, **kwargs): + super().__init__(**kwargs) + self.size = int(size) + self.input_spec = InputSpec(ndim=3) + + def compute_output_shape(self, input_shape): + size = ( + self.size * input_shape[1] if input_shape[1] is not None else None + ) + return [input_shape[0], size, input_shape[2]] + + def call(self, inputs): + return ops.repeat(x=inputs, repeats=self.size, axis=1) + + def get_config(self): + config = {"size": self.size} + base_config = super().get_config() + return {**base_config, **config} diff --git a/keras_core/layers/reshaping/up_sampling1d_test.py b/keras_core/layers/reshaping/up_sampling1d_test.py new file mode 100644 index 000000000..941755936 --- /dev/null +++ b/keras_core/layers/reshaping/up_sampling1d_test.py @@ -0,0 +1,66 @@ +import numpy as np +import pytest + +from keras_core import backend +from keras_core import layers +from keras_core import testing +from keras_core.backend.common.keras_tensor import KerasTensor + + +class UpSamplingTest(testing.TestCase): + def test_upsampling_1d(self): + self.run_layer_test( + layers.UpSampling1D, + init_kwargs={"size": 2}, + input_shape=(3, 5, 4), + expected_output_shape=(3, 10, 4), + expected_output_dtype="float32", + expected_num_trainable_weights=0, + expected_num_non_trainable_weights=0, + expected_num_seed_generators=0, + expected_num_losses=0, + supports_masking=False, + ) + + def test_upsampling_1d_correctness(self): + np.testing.assert_array_equal( + layers.UpSampling1D(size=2)(np.arange(12).reshape((2, 2, 3))), + np.array( + [ + [ + [0.0, 1.0, 2.0], + [0.0, 1.0, 2.0], + [3.0, 4.0, 5.0], + [3.0, 4.0, 5.0], + ], + [ + [6.0, 7.0, 8.0], + [6.0, 7.0, 8.0], + [9.0, 10.0, 11.0], + [9.0, 10.0, 11.0], + ], + ] + ), + ) + + def test_upsampling_1d_correctness_with_ones(self): + np.testing.assert_array_equal( + layers.UpSampling1D(size=3)(np.ones((2, 1, 5))), np.ones((2, 3, 5)) + ) + + @pytest.mark.skipif( + not backend.DYNAMIC_SHAPES_OK, + reason="Backend does not support dynamic shapes", + ) + def test_upsampling_1d_with_dynamic_shape(self): + x = KerasTensor([None, 2, 3]) + self.assertEqual(layers.UpSampling1D(size=2)(x).shape, (None, 4, 3)) + self.assertEqual(layers.UpSampling1D(size=4)(x).shape, (None, 8, 3)) + + y = KerasTensor([2, None, 3]) + self.assertEqual(layers.UpSampling1D(size=2)(y).shape, (2, None, 3)) + self.assertEqual(layers.UpSampling1D(size=4)(y).shape, (2, None, 3)) + + z = KerasTensor([2, 3, None]) + self.assertEqual(layers.UpSampling1D(size=2)(z).shape, (2, 6, None)) + self.assertEqual(layers.UpSampling1D(size=4)(z).shape, (2, 12, None))