diff --git a/CHANEGLOG.md b/CHANEGLOG.md index 2d78a11..69e0e2e 100644 --- a/CHANEGLOG.md +++ b/CHANEGLOG.md @@ -6,9 +6,6 @@ - `ScanRNN` changes: - - `backward_cell=...` is deprecated , instead use `ScanRNN(forward_cell, backward_cell, reverse=(False,True))` - - `ScanRNN` now accepts arbitrary number of cells as input and an argument `reverse` to decide whether to reverse the corresponding cell or not. - - `return_state` is added to control whether the final carry is returned or not. - `cell.init_state` is deprecated use `sk.tree_state(cell, ...)` instead. - Naming changes: diff --git a/docs/notebooks/bilstm.ipynb b/docs/notebooks/bilstm.ipynb index 43c813c..c4d06e4 100644 --- a/docs/notebooks/bilstm.ipynb +++ b/docs/notebooks/bilstm.ipynb @@ -77,21 +77,16 @@ "k1, k2, k3 = jr.split(jr.PRNGKey(0), 3)\n", "\n", "nn = sk.nn.Sequential(\n", - " # here we have forward and backward(i.e. reverse=Trye) cells\n", + " # here we have forward and backward cells\n", " sk.nn.ScanRNN(\n", " sk.nn.LSTMCell(1, 64),\n", " sk.nn.LSTMCell(1, 64),\n", " return_sequences=True,\n", - " reverse=(False, True),\n", " ),\n", " # here we have only forward cell\n", " # here the in_features is 64*2 (64 for each cell from previous layer)\n", " # we set return_sequences=False to return only the last output of the sequence\n", - " sk.nn.ScanRNN(\n", - " sk.nn.LSTMCell(128, 1, key=k3),\n", - " return_sequences=False,\n", - " # return_state=True,\n", - " ),\n", + " sk.nn.ScanRNN(sk.nn.LSTMCell(128, 1, key=k3)),\n", ")\n", "\n", "# 1) mask the non-jaxtype parameters\n", @@ -161,13 +156,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "Epoch: 100/100\tBatch: 100/100\tBatch loss: 1.760744e-03\tTime: 0.025\r" + "Epoch: 100/100\tBatch: 100/100\tBatch loss: 1.485198e-03\tTime: 0.021\r" ] }, { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 5, @@ -176,7 +171,7 @@ }, { "data": { - "image/png": "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", + "image/png": "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oEGD+PDDDxkxYkSp2v/444/4+fnx5Zdf0rx5c6ZOncojjzzC3LlzTRmmqEQsLCyIiYnh8OHDBeaouLq68thjj5V5W4BLly7x0UcfFShYV5Qnn3ySRo0akZiYyHfffXdXsQshRHh4ODNmzCA4OJjly5czb948XnrppWLbv/766xw4cICpU6cSFBTEpUuXWLdunXFibNOmTRk4cCCTJk0iMDCQY8eO8dxzz5XYu/L444/j7e3N8OHD2b9/P6GhoaxevZqDBw8C+T3RYWFhBAUFkZiYSHZ2dqF7jBkzBmtra5566inOnDnDrl27+N///sfYsWMLlZyoCJVqzsvBgwfp379/gWMDBgwwvuFFyc7OJiUlpcBDVG1arZbOnTuXanPFkmzatIkmTZrw9ttv88UXX5TYVqPR8O677wKwaNEis3R7CiGqn3HjxpGZmUmXLl2YMmUKL730knFJdFHatGnDnj17uHjxIj179qR9+/bMmjULHx8fY5tFixbh4+ND7969efjhh5k4cSKenp7F3lOr1bJ161Y8PT158MEHad26NZ988omx92fkyJEMHDiQ++67Dw8PD5YvX17oHra2tmzZsoXr16/TuXNnHnnkEfr161dpvtxVqtVGsbGxhTI6Ly8vUlJSyMzMLDLTnDNnDu+//765QhRVyK3dvaXp5nz88ce5ceMGTz311D0nTkKImsnS0pKvv/6aH374odA5Q6Xv23Xu3JmtW7cWe09vb2/Wr19f4NjYsWML/Hz7ku569eqxatWqIu9nZWVV5Lnb79G6dWt27txZbFxFLYm+dcsCU6ryv6FnzpxJcnKy8REREVHRIYl70L9/fyZOnEhcXNw938uwTQBA7dq179jewsKCqVOn4uDgcM/PLYQQwnQqVfLi7e1d6EMrLi4OR0fHYsf3rKyscHR0LPAQVVNERAQ7duxg4cKF2Nvb3/P9bu3F8/X1LdO1iqJw/vz5e45BCCFE+atUyUtAQAA7duwocGzbtm0EBARUUETCnAxLpNu1a1doOeHdWrduHa+88gpjxowp9TWxsbF07tyZli1b8vHHHxdbYXPXrl0FVgAIIWq23bt3m23YpKYzafKSlpZGUFCQcTM9w+zm8PBwIH/IZ9y4ccb2zz//PKGhobz22mtcuHCB77//nr/++ovp06ebMkxRSRiSl27dupXbPR966CE+//zzYpcp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", "text/plain": [ "
" ] @@ -198,7 +193,6 @@ " end=\"\\r\",\n", " )\n", "\n", - "# Epoch: 001/001\tBatch: 467/468\tBatch loss: 2.040178e-01\tBatch accuracy: 0.984375\tTime: 19.284\n", "\n", "# 6) un-mask the trained network\n", "eval_nn = sk.tree_unmask(nn)\n", diff --git a/serket/__init__.py b/serket/__init__.py index 06a382e..fe677f7 100644 --- a/serket/__init__.py +++ b/serket/__init__.py @@ -88,4 +88,4 @@ ) -__version__ = "0.5.0b1" +__version__ = "0.6.0b0" diff --git a/serket/nn/recurrent.py b/serket/nn/recurrent.py index 1b8006b..86853e5 100644 --- a/serket/nn/recurrent.py +++ b/serket/nn/recurrent.py @@ -22,7 +22,6 @@ import jax.numpy as jnp import jax.random as jr import jax.tree_util as jtu -from jax.util import unzip2 import serket as sk from serket.nn.activation import ActivationType, resolve_activation @@ -33,6 +32,8 @@ KernelSizeType, PaddingType, StridesType, + maybe_lazy_call, + maybe_lazy_init, positive_int_cb, validate_axis_shape, validate_spatial_ndim, @@ -41,6 +42,21 @@ """Defines RNN related classes.""" +def is_lazy_call(instance, *_, **__) -> bool: + return instance.in_features is None + + +def is_lazy_init(_, in_features: int | None, *__, **___) -> bool: + return in_features is None + + +def infer_in_features(_, x: jax.Array, *__, **___) -> int: + return x.shape[0] + + +rnn_updates = dict(in_features=infer_in_features) + + @sk.autoinit class RNNState(sk.TreeClass): hidden_state: jax.Array @@ -75,6 +91,7 @@ class SimpleRNNState(RNNState): class SimpleRNNCell(RNNCell): + @ft.partial(maybe_lazy_init, is_lazy=is_lazy_init) def __init__( self, in_features: int, @@ -107,6 +124,23 @@ def __init__( >>> result.hidden_state.shape # 20 features (20,) + Note: + :class:`.SimpleRNNCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> lazy_layer = sk.nn.ScanRNN(sk.nn.SimpleRNNCell(None, 20), return_sequences=True) + >>> x = jnp.ones((5, 10)) # 5 timesteps, 10 features + >>> _, materialized_layer = lazy_layer.at["__call__"](x) + >>> materialized_layer(x).shape + (5, 20) + Reference: - https://www.tensorflow.org/api_docs/python/tf/keras/layers/SimpleRNNCell. """ @@ -135,6 +169,7 @@ def __init__( self.ih2h_weight = jnp.concatenate([i2h.weight, h2h.weight], axis=0) self.ih2h_bias = i2h.bias + @ft.partial(maybe_lazy_call, is_lazy=is_lazy_call, updates=rnn_updates) @ft.partial(validate_spatial_ndim, attribute_name="spatial_ndim") @ft.partial(validate_axis_shape, attribute_name="in_features", axis=0) def __call__(self, x: jax.Array, state: SimpleRNNState, **k) -> SimpleRNNState: @@ -177,8 +212,26 @@ class DenseCell(RNNCell): >>> result = cell(x, dummy_state) >>> result.hidden_state.shape # 20 features (20,) + + Note: + :class:`.DenseCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> lazy_layer = sk.nn.ScanRNN(sk.nn.DenseCell(None, 20), return_sequences=True) + >>> x = jnp.ones((5, 10)) # 5 timesteps, 10 features + >>> _, materialized_layer = lazy_layer.at["__call__"](x) + >>> materialized_layer(x).shape + (5, 20) """ + @ft.partial(maybe_lazy_init, is_lazy=is_lazy_init) def __init__( self, in_features: int, @@ -201,6 +254,7 @@ def __init__( key=key, ) + @ft.partial(maybe_lazy_call, is_lazy=is_lazy_call, updates=rnn_updates) @ft.partial(validate_spatial_ndim, attribute_name="spatial_ndim") @ft.partial(validate_axis_shape, attribute_name="in_features", axis=0) def __call__(self, x: jax.Array, state: DenseState, **k) -> DenseState: @@ -233,11 +287,41 @@ class LSTMCell(RNNCell): recurrent_act_func: the activation function to use for the cell state update key: the key to use to initialize the weights + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> # 10-dimensional input, 20-dimensional hidden state + >>> cell = sk.nn.LSTMCell(10, 20) + >>> # 20-dimensional hidden state + >>> dummy_state = sk.tree_state(cell) + >>> x = jnp.ones((10,)) # 10 features + >>> result = cell(x, dummy_state) + >>> result.hidden_state.shape # 20 features + (20,) + + Note: + :class:`.LSTMCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> lazy_layer = sk.nn.ScanRNN(sk.nn.LSTMCell(None, 20), return_sequences=True) + >>> x = jnp.ones((5, 10)) # 5 timesteps, 10 features + >>> _, materialized_layer = lazy_layer.at["__call__"](x) + >>> materialized_layer(x).shape + (5, 20) + Reference: - https://www.tensorflow.org/api_docs/python/tf/keras/layers/LSTMCell - https://github.com/deepmind/dm-haiku/blob/main/haiku/_src/recurrent.py """ + @ft.partial(maybe_lazy_init, is_lazy=is_lazy_init) def __init__( self, in_features: int, @@ -276,6 +360,7 @@ def __init__( self.ih2h_weight = jnp.concatenate([i2h.weight, h2h.weight], axis=0) self.ih2h_bias = i2h.bias + @ft.partial(maybe_lazy_call, is_lazy=is_lazy_call, updates=rnn_updates) @ft.partial(validate_spatial_ndim, attribute_name="spatial_ndim") @ft.partial(validate_axis_shape, attribute_name="in_features", axis=0) def __call__(self, x: jax.Array, state: LSTMState, **k) -> LSTMState: @@ -316,10 +401,40 @@ class GRUCell(RNNCell): recurrent_act_func: the activation function to use for the cell state update key: the key to use to initialize the weights + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> # 10-dimensional input, 20-dimensional hidden state + >>> cell = sk.nn.GRUCell(10, 20) + >>> # 20-dimensional hidden state + >>> dummy_state = sk.tree_state(cell) + >>> x = jnp.ones((10,)) # 10 features + >>> result = cell(x, dummy_state) + >>> result.hidden_state.shape # 20 features + (20,) + + Note: + :class:`.GRUCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> lazy_layer = sk.nn.ScanRNN(sk.nn.GRUCell(None, 20), return_sequences=True) + >>> x = jnp.ones((5, 10)) # 5 timesteps, 10 features + >>> _, materialized_layer = lazy_layer.at["__call__"](x) + >>> materialized_layer(x).shape + (5, 20) + Reference: - https://keras.io/api/layers/recurrent_layers/gru/ """ + @ft.partial(maybe_lazy_init, is_lazy=is_lazy_init) def __init__( self, in_features: int, @@ -355,6 +470,7 @@ def __init__( key=k2, ) + @ft.partial(maybe_lazy_call, is_lazy=is_lazy_call, updates=rnn_updates) @ft.partial(validate_spatial_ndim, attribute_name="spatial_ndim") @ft.partial(validate_axis_shape, attribute_name="in_features", axis=0) def __call__(self, x: jax.Array, state: GRUState, **k) -> GRUState: @@ -381,26 +497,7 @@ class ConvLSTMNDState(RNNState): class ConvLSTMNDCell(RNNCell): - """Convolution LSTM cell that defines the update rule for the hidden state and cell state - - Args: - in_features: Number of input features - hidden_features: Number of output features - kernel_size: Size of the convolutional kernel - strides: Stride of the convolution - padding: Padding of the convolution - dilation: Dilation of the convolutional kernel - weight_init: Weight initialization function - bias_init: Bias initialization function - recurrent_weight_init: Recurrent weight initialization function - act_func: Activation function - recurrent_act_func: Recurrent activation function - key: PRNG key - - Reference: - - https://www.tensorflow.org/api_docs/python/tf/keras/layers/ConvLSTM1D - """ - + @ft.partial(maybe_lazy_init, is_lazy=is_lazy_init) def __init__( self, in_features: int, @@ -448,6 +545,7 @@ def __init__( key=k2, ) + @ft.partial(maybe_lazy_call, is_lazy=is_lazy_call, updates=rnn_updates) @ft.partial(validate_spatial_ndim, attribute_name="spatial_ndim") @ft.partial(validate_axis_shape, attribute_name="in_features", axis=0) def __call__(self, x: jax.Array, state: ConvLSTMNDState, **k) -> ConvLSTMNDState: @@ -488,6 +586,33 @@ class ConvLSTM1DCell(ConvLSTMNDCell): recurrent_act_func: Recurrent activation function key: PRNG key + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvLSTM1DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4) + + Note: + :class:`.ConvLSTM1DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvLSTM1DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4) + Reference: https://www.tensorflow.org/api_docs/python/tf/keras/layers/ConvLSTM1D """ @@ -518,6 +643,33 @@ class FFTConvLSTM1DCell(ConvLSTMNDCell): recurrent_act_func: Recurrent activation function key: PRNG key + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvLSTM1DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4) + + Note: + :class:`.FFTConvLSTM1DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvLSTM1DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4) + Reference: - https://www.tensorflow.org/api_docs/python/tf/keras/layers/ConvLSTM1D """ @@ -548,6 +700,33 @@ class ConvLSTM2DCell(ConvLSTMNDCell): recurrent_act_func: Recurrent activation function key: PRNG key + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvLSTM2DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4, 4) + + Note: + :class:`.ConvLSTM2DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvLSTM2DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4, 4) + Reference: - https://www.tensorflow.org/api_docs/python/tf/keras/layers/ConvLSTM2D """ @@ -578,6 +757,33 @@ class FFTConvLSTM2DCell(ConvLSTMNDCell): recurrent_act_func: Recurrent activation function key: PRNG key + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvLSTM2DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4, 4) + + Note: + :class:`.FFTConvLSTM2DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvLSTM2DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4, 4) + Reference: - https://www.tensorflow.org/api_docs/python/tf/keras/layers/ConvLSTM2D """ @@ -608,6 +814,33 @@ class ConvLSTM3DCell(ConvLSTMNDCell): recurrent_act_func: Recurrent activation function key: PRNG key + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvLSTM3DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4, 4, 4) + + Note: + :class:`.ConvLSTM3DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvLSTM3DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4, 4, 4) + Reference: - https://www.tensorflow.org/api_docs/python/tf/keras/layers/ConvLSTM3D """ @@ -638,6 +871,33 @@ class FFTConvLSTM3DCell(ConvLSTMNDCell): recurrent_act_func: Recurrent activation function key: PRNG key + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvLSTM3DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4, 4, 4) + + Note: + :class:`.FFTConvLSTM3DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvLSTM3DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4, 4, 4) + Reference: - https://www.tensorflow.org/api_docs/python/tf/keras/layers/ConvLSTM3D """ @@ -656,24 +916,7 @@ class ConvGRUNDState(RNNState): class ConvGRUNDCell(RNNCell): - """Convolution GRU cell that defines the update rule for the hidden state and cell state - - Args: - in_features: Number of input features - hidden_features: Number of output features - kernel_size: Size of the convolutional kernel - strides: Stride of the convolution - padding: Padding of the convolution - dilation: Dilation of the convolutional kernel - weight_init: Weight initialization function - bias_init: Bias initialization function - recurrent_weight_init: Recurrent weight initialization function - act_func: Activation function - recurrent_act_func: Recurrent activation function - key: PRNG key - spatial_ndim: Number of spatial dimensions. - """ - + @ft.partial(maybe_lazy_init, is_lazy=is_lazy_init) def __init__( self, in_features: int, @@ -721,7 +964,9 @@ def __init__( key=k2, ) + @ft.partial(maybe_lazy_call, is_lazy=is_lazy_call, updates=rnn_updates) @ft.partial(validate_spatial_ndim, attribute_name="spatial_ndim") + @ft.partial(validate_axis_shape, attribute_name="in_features", axis=0) def __call__(self, x: jax.Array, state: ConvGRUNDState, **k) -> ConvGRUNDState: if not isinstance(state, ConvGRUNDState): raise TypeError(f"Expected {state=} to be an instance of `GRUState`") @@ -758,6 +1003,33 @@ class ConvGRU1DCell(ConvGRUNDCell): recurrent_act_func: Recurrent activation function key: PRNG key spatial_ndim: Number of spatial dimensions. + + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvGRU1DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4) + + Note: + :class:`.ConvGRU1DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvGRU1DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4) """ @property @@ -786,6 +1058,33 @@ class FFTConvGRU1DCell(ConvGRUNDCell): recurrent_act_func: Recurrent activation function key: PRNG key spatial_ndim: Number of spatial dimensions. + + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvGRU1DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4) + + Note: + :class:`.FFTConvGRU1DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvGRU1DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4) """ @property @@ -814,6 +1113,33 @@ class ConvGRU2DCell(ConvGRUNDCell): recurrent_act_func: Recurrent activation function key: PRNG key spatial_ndim: Number of spatial dimensions. + + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvGRU2DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4, 4) + + Note: + :class:`.ConvGRU2DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvGRU2DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4, 4) """ @property @@ -842,6 +1168,33 @@ class FFTConvGRU2DCell(ConvGRUNDCell): recurrent_act_func: Recurrent activation function key: PRNG key spatial_ndim: Number of spatial dimensions. + + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvGRU2DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4, 4) + + Note: + :class:`.FFTConvGRU2DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvGRU2DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4, 4) """ @property @@ -869,6 +1222,33 @@ class ConvGRU3DCell(ConvGRUNDCell): act_func: Activation function recurrent_act_func: Recurrent activation function key: PRNG key + + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvGRU3DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4, 4, 4) + + Note: + :class:`.ConvGRU3DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.ConvGRU3DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4, 4, 4) """ @property @@ -896,6 +1276,33 @@ class FFTConvGRU3DCell(ConvGRUNDCell): act_func: Activation function recurrent_act_func: Recurrent activation function key: PRNG key + + Example: + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvGRU3DCell(10, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> layer(x).shape + (1, 2, 4, 4, 4) + + Note: + :class:`.FFTConvGRU3DCell` supports lazy initialization, meaning that the + weights and biases are not initialized until the first call to the layer. + This is useful when the input shape is not known at initialization time. + + To use lazy initialization, pass ``None`` as the ``in_features`` argument + and use the ``.at["calling_method_name"]`` attribute to call the layer + with an input of known shape. + + >>> import serket as sk + >>> import jax.numpy as jnp + >>> cell = sk.nn.FFTConvGRU3DCell(None, 2, 3) + >>> x = jnp.ones((1, 10, 4, 4, 4)) # time, in_features, spatial dimensions + >>> layer = sk.nn.ScanRNN(cell, return_sequences=True) # return time steps results + >>> _, layer = layer.at["__call__"](x) # materialize the layer + >>> layer(x).shape + (1, 2, 4, 4, 4) """ @property @@ -910,20 +1317,49 @@ def convolution_layer(self): # Scanning API +def materialize_cell(instance, x: jax.Array, state=None, **__) -> RNNCell: + # in case of lazy initialization, we need to materialize the cell + # before it can be passed to the scan function + cell = instance.cell + state = state if state is not None else sk.tree_state(instance, x) + state = split_state(state, 2) if instance.backward_cell is not None else [state] + _, cell = cell.at["__call__"](x[0], state[0]) + return cell + + +def materialize_backward_cell(instance, x, state=None, **__) -> RNNCell | None: + if instance.backward_cell is None: + return None + cell = instance.cell + state = state if state is not None else sk.tree_state(instance, x) + state = split_state(state, 2) if instance.backward_cell is not None else [state] + _, cell = cell.at["__call__"](x[0], state[-1]) + return cell + + +def is_lazy_init(_, cell, backward_cell=None, **__) -> bool: + lhs = cell.in_features is None + rhs = getattr(backward_cell, "in_features", False) is None + return lhs or rhs + + +def is_lazy_call(instance, x, state=None, **_) -> bool: + lhs = instance.cell.in_features is None + rhs = getattr(instance.backward_cell, "in_features", False) is None + return lhs or rhs + + +scan_updates = dict(cell=materialize_cell, backward_cell=materialize_backward_cell) + + class ScanRNN(sk.TreeClass): """Scans RNN cell over a sequence. Args: - cells: the RNN cell(s) to use. + cell: the RNN cell to scan. + backward_cell: (optional) the backward RNN cell to scan in case of bidirectional RNN. return_sequences: whether to return the output for each timestep. return_state: whether to return the final state of the RNN cell(s). - reverse: a tuple of booleans indicating whether to reverse the input - sequence for each cell. for example, if `cells` is a tuple of - two cells, and `reverse=(True, False)`, then the first cell will - scan the input sequence in reverse, and the second cell will scan - the input sequence in the original order. or a single boolean - indicating whether to reverse the input sequence for all cells. - default is `False`. Example: >>> import jax.numpy as jnp @@ -946,38 +1382,34 @@ class ScanRNN(sk.TreeClass): (5, 20) """ + @ft.partial(maybe_lazy_init, is_lazy=is_lazy_init) def __init__( self, - *cells: RNNCell, + cell: RNNCell, + backward_cell: RNNCell | None = None, return_sequences: bool = False, return_state: bool = False, - reverse: tuple[bool, ...] | bool = False, ): - cell0, *_ = cells - - for cell in cells: - if not isinstance(cell, RNNCell): - raise TypeError(f"Expected {cell=} to be an instance of `RNNCell`.") - - if cell.in_features != cell0.in_features: - raise ValueError(f"{cell.in_features=} != {cell0.in_features=}.") - - if cell.hidden_features != cell0.hidden_features: + if not isinstance(cell, RNNCell): + raise TypeError(f"Expected {cell=} to be an instance of `RNNCell`.") + + if backward_cell is not None: + # bidirectional + if not isinstance(backward_cell, RNNCell): + raise TypeError(f"{backward_cell=} to be an instance of `RNNCell`.") + if cell.in_features != backward_cell.in_features: + raise ValueError(f"{cell.in_features=} != {backward_cell.in_features=}") + if cell.hidden_features != backward_cell.hidden_features: raise ValueError( - f"{cell.hidden_features=} != {cell0.hidden_features=}." + f"{cell.hidden_features=} != {backward_cell.hidden_features=}." ) - if isinstance(reverse, bool): - reverse = (reverse,) * len(cells) - - if len(reverse) != len(cells): - raise ValueError(f"{len(reverse)=} != {len(cells)=}.") - - self.cells: tuple[RNNCell, ...] | RNNCell = cells + self.cell = cell + self.backward_cell = backward_cell self.return_sequences = return_sequences self.return_state = return_state - self.reverse = reverse + @ft.partial(maybe_lazy_call, is_lazy=is_lazy_call, updates=scan_updates) def __call__( self, x: jax.Array, @@ -989,57 +1421,52 @@ def __call__( Args: x: the input sequence. state: the initial state. if None, state is initialized by the rule - defined using `tree_state`. + defined using :func:`.tree_state`. Returns: return the result and state if ``return_state`` is True. otherwise, - return only the result. + return only the result. """ if not isinstance(state, (RNNState, type(None))): raise TypeError(f"Expected state to be an instance of RNNState, {state=}") - # non-spatial RNN : (time steps, in_features) - # spatial RNN : (time steps, in_features, *spatial_dims) - cell0, *_ = self.cells - - if x.ndim != cell0.spatial_ndim + 2: + if x.ndim != self.cell.spatial_ndim + 2: raise ValueError( - f"Expected x to have {(cell0.spatial_ndim + 2)=} dimensions corresponds to " - f"(timesteps, in_features, {','.join('...'*cell0.spatial_ndim)})," + f"Expected x to have {(self.cell.spatial_ndim + 2)=} dimensions corresponds to " + f"(timesteps, in_features, {','.join('...'*self.cell.spatial_ndim)})," f" got {x.ndim=}" ) - if cell0.in_features != x.shape[1]: + if self.cell.in_features != x.shape[1]: raise ValueError( - f"Expected x to have shape (timesteps, {cell0.in_features}," - f"{'*'*cell0.spatial_ndim}), got {x.shape=}" + f"Expected x to have shape (timesteps, {self.cell.in_features}," + f"{'*'*self.cell.spatial_ndim}), got {x.shape=}" ) - splits = len(self.cells) - state: RNNState = tree_state(self, array=x) if state is None else state + state: RNNState = tree_state(self, array=x) # if state is None else state scan_func = _accumulate_scan if self.return_sequences else _no_accumulate_scan - result_states: list[tuple[jax.Array, RNNState]] = [ - scan_func(x, ci, si, reverse=ri) - for ci, ri, si in zip(self.cells, self.reverse, _split(state, splits)) - ] + if self.backward_cell is None: + result, state = scan_func(x, self.cell, state) + return (result, state) if self.return_state else result - results, states = unzip2(result_states) - result = jnp.concatenate(results, axis=int(self.return_sequences)) + # bidirectional + lhs_state, rhs_state = split_state(state, splits=2) + lhs_result, lhs_state = scan_func(x, self.cell, lhs_state, False) + rhs_result, rhs_state = scan_func(x, self.backward_cell, rhs_state, True) + concat_axis = int(self.return_sequences) + result = jnp.concatenate((lhs_result, rhs_result), axis=concat_axis) + state: RNNState = concat_state((lhs_state, rhs_state)) + return (result, state) if self.return_state else result - if self.return_state: - state: RNNState = _merge(states) - return result, state - return result - -def _split(state: RNNState, splits: int) -> list[RNNState]: +def split_state(state: RNNState, splits: int) -> list[RNNState]: flat_arrays: list[jax.Array] = jtu.tree_leaves(state) return [type(state)(*x) for x in zip(*(jnp.split(x, splits) for x in flat_arrays))] -def _merge(states: list[RNNState]) -> RNNState: +def concat_state(states: list[RNNState]) -> RNNState: # undo the split return ( states[0] @@ -1109,20 +1536,19 @@ def gru_init_state(cell: GRUCell) -> GRUState: def _check_rnn_cell_tree_state_input(cell: RNNCell, array): if not (hasattr(array, "ndim") and hasattr(array, "shape")): raise TypeError( - f"Expected {array=} to have `ndim` and `shape` attributes.", - f"To initialize the `{type(cell).__name__}` state.\n", - "Pass a single sample array to `tree_state(..., array=)`.", + f"Expected {array=} to have `ndim` and `shape` attributes." + f"To initialize the `{type(cell).__name__}` state.\n" + "Pass a single sample array to `tree_state(..., array=)`." ) if array.ndim != cell.spatial_ndim + 1: raise ValueError( - f"{array.ndim=} != {(cell.spatial_ndim + 1)=}.", + f"{array.ndim=} != {(cell.spatial_ndim + 1)=}." f"Expected input to have `shape` (in_features, {'...'*cell.spatial_dim})." - "Pass a single sample array to `tree_state", + "Pass a single sample array to `tree_state" ) - spatial_dim = array.shape[1:] - if len(spatial_dim) != cell.spatial_ndim: + if len(spatial_dim := array.shape[1:]) != cell.spatial_ndim: raise ValueError(f"{len(spatial_dim)=} != {cell.spatial_ndim=}.") return array @@ -1132,19 +1558,29 @@ def _check_rnn_cell_tree_state_input(cell: RNNCell, array): def conv_lstm_init_state(cell: ConvLSTMNDCell, x: Any) -> ConvLSTMNDState: x = _check_rnn_cell_tree_state_input(cell, x) shape = (cell.hidden_features, *x.shape[1:]) - return ConvLSTMNDState(jnp.zeros(shape), jnp.zeros(shape)) + zeros = jnp.zeros(shape).astype(x.dtype) + return ConvLSTMNDState(zeros, zeros) @tree_state.def_state(ConvGRUNDCell) def conv_gru_init_state(cell: ConvGRUNDCell, x: Any) -> ConvGRUNDState: x = _check_rnn_cell_tree_state_input(cell, x) shape = (cell.hidden_features, *x.shape[1:]) - return ConvGRUNDState(jnp.zeros(shape), jnp.zeros(shape)) + return ConvGRUNDState(jnp.zeros(shape).astype(x.dtype)) @tree_state.def_state(ScanRNN) -def scan_rnn_init_state(rnn: ScanRNN, x: Any) -> RNNState: - if not hasattr(x, "shape") or not hasattr(x, "ndim"): - raise TypeError("Pass an array to `tree_state(..., array=...)`.") - # should pass a single sample array to `tree_state` - return _merge(tree_state(rnn.cells, array=x[0])) +def scan_rnn_init_state(rnn: ScanRNN, x: jax.Array | None = None) -> RNNState: + # the idea here is to combine the state of the forward and backward cells + # if backward cell exists. to have single state input for `ScanRNN` and + # single state output not to complicate the ``__call__`` signature on the + # user side. + x = [None] if x is None else x + # non-spatial cells don't need an input instead + # pass `None` to `tree_state` + # otherwise pass the a single time step input to the cells + return ( + tree_state(rnn.cell, array=x[0]) + if rnn.backward_cell is None + else concat_state(tree_state((rnn.cell, rnn.backward_cell), array=x[0])) + ) diff --git a/serket/nn/utils.py b/serket/nn/utils.py index a43822c..d6fc5bd 100644 --- a/serket/nn/utils.py +++ b/serket/nn/utils.py @@ -17,6 +17,7 @@ import functools as ft import inspect import operator as op +from types import MethodType from typing import Any, Callable, Sequence, Tuple, TypeVar, Union import jax @@ -307,7 +308,7 @@ def wrapper(self, array, *a, **k): @ft.lru_cache(maxsize=128) -def get_params(func: type) -> tuple[inspect.Parameter, ...]: +def get_params(func: MethodType) -> tuple[inspect.Parameter, ...]: """Get the arguments of func.""" return tuple(inspect.signature(func).parameters.values()) @@ -316,7 +317,12 @@ def maybe_lazy_init( func: Callable[P, T], is_lazy: Callable[..., bool], ) -> Callable[P, T]: - """Sets input argumet to instance attribute if lazy initialization is ``True``. + """Sets input argumets to instance attribute if lazy initialization is ``True``. + + The key idea is to store the input arguments to the instance attribute to + be used later when the instance is re-initialized using ``maybe_lazy_call`` + decorator. ``maybe_lazy_call`` assumes that the input arguments are stored + in the instance attribute and can be retrieved using ``vars(instance)``. Args: func: The ``__init__`` method of a class. @@ -364,7 +370,7 @@ def inner(instance, *a, **k): return inner -LAZY_CALL_ERROR = """ +LAZY_CALL_ERROR = """\ Cannot call ``{func_name}`` directly on a lazy layer. use ``layer.at['{func_name}'](...)`` instead to return a tuple of: - The layer output. @@ -378,7 +384,7 @@ def inner(instance, *a, **k): >>> _, materialized_layer = layer.at['{func_name}'](x) >>> materialized_layer(x) ... -""".lstrip() +""" def maybe_lazy_call( @@ -388,6 +394,8 @@ def maybe_lazy_call( ) -> Callable[P, T]: """Reinitialize the instance if it is lazy. + Accompanying decorator for ``maybe_lazy_init``. + Args: func: The method to decorate that accepts the arguments needed to re-initialize the instance. @@ -399,7 +407,7 @@ def maybe_lazy_call( as ``func``. """ - @ft.wraps(func) + # @ft.wraps(func) def inner(instance, *a, **k): if not is_lazy(instance, *a, **k): return func(instance, *a, **k) diff --git a/tests/test_rnn.py b/tests/test_rnn.py index aa8a48c..e6af153 100644 --- a/tests/test_rnn.py +++ b/tests/test_rnn.py @@ -760,7 +760,7 @@ def test_bilstm(): reverse_cell = reverse_cell.at["ih2h_weight"].set(combined_w_reverse) reverse_cell = reverse_cell.at["ih2h_bias"].set(b_hidden_to_hidden_reverse) - res = ScanRNN(cell, reverse_cell, reverse=(False, True), return_sequences=False)(x) + res = ScanRNN(cell, reverse_cell, return_sequences=False)(x) y = jnp.array([0.35901642, 0.00826644, -0.3015435, -0.13661332]) @@ -768,7 +768,7 @@ def test_bilstm(): def test_rnn_error(): - with pytest.raises(TypeError): + with pytest.raises(AttributeError): ScanRNN(None) with pytest.raises(TypeError):