Skip to content

Commit 4ccf73b

Browse files
Quick fixes, reintroduced the min_size option and allow size=None in order to use the max possible size.
1 parent 30d4b46 commit 4ccf73b

1 file changed

Lines changed: 78 additions & 33 deletions

File tree

returnn/tf/layers/basic.py

Lines changed: 78 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -842,64 +842,93 @@ def get_out_data_from_opts(
842842

843843
class SliceNdLayer(_ConcatInputLayer):
844844
"""
845-
This takes out a slice-range from the time axis of the input,
846-
e.g. if the input is of shape (B,T,F) and start is of shape (B,T),
845+
This takes out a slice-range from the time axis,
846+
e.g. ``x[start:start + size]``.
847+
If the input is of shape (B,T,F) and start is of shape (B,),
848+
then the output will be of shape (B,size,F).
849+
If the input is of shape (B,T,F) and start is of shape (B,T),
847850
then the output will be of shape (B,T,size,F).
848-
This layers allows a different start slice point for each batch,
851+
This layer allows a different start slice point for each batch,
849852
in contrast to :class:`SliceLayer`, and the start is variable.
850853
See also :class:`GatherNdLayer`.
851854
:class:`PrefixInTimeLayer` can recover the original shape (by zero-padding).
852855
"""
853856
layer_class = "slice_nd"
854857
recurrent = True
855858

856-
def __init__(self, start, size, **kwargs):
859+
def __init__(self, start, size, min_size=None, **kwargs):
857860
"""
858861
:param LayerBase start: (B,...)
859-
:param int size: scalar
862+
:param int|None size: if None, it uses the max possible size, and it becomes a dynamic axis
863+
:param int|None min_size: if size is None, but we want to have a min-size
860864
"""
861865
super(SliceNdLayer, self).__init__(**kwargs)
862866
from returnn.tf.util.basic import where_bc, expand_multiple_dims
867+
from returnn.tf.util.data import DimensionTag
863868
x = self.input_data.copy_as_batch_major()
864-
seq_lens = x.get_sequence_lengths() if x.is_time_axis_dynamic() else None # (B,) or None
869+
seq_lens = x.get_sequence_lengths() if x.is_time_axis_dynamic() else None # (B,) or None
865870
self.start = start
866-
start = start.output.copy_as_batch_major() # e.g. (B,) or (B,T)
871+
start_data = start.output.copy_as_batch_major() # e.g. (B,) or (B,T)
872+
start_t = start_data.placeholder
873+
if size is None:
874+
if seq_lens is None:
875+
size = tf.maximum(tf.reduce_max(x.batch_shape[1] - start_t), 0) # scalar
876+
else:
877+
# make seq_lens compatible with start_t
878+
seq_lens = expand_multiple_dims( # e.g. (B,) or (B,1)
879+
x=seq_lens,
880+
axes=[-1] * (len(start_t.shape) - len(seq_lens.shape)))
881+
size = tf.maximum(tf.reduce_max(seq_lens - start_t), 0) # scalar
882+
if min_size is not None:
883+
size = tf.maximum(size, min_size)
867884
# build Data object for the position argument of GatherLayer
868-
indices_data = start.copy_template(name="%s_gather_indices" % self.name)
869-
indices_data = indices_data.copy_add_spatial_dim(spatial_dim_axis=start.batch_ndim, auto_time_dim_axis=False,
870-
dim=size)
871-
start = start.placeholder # e.g. (B,) or (B,T)
885+
indices_data = start_data.copy_template(name="%s_gather_indices" % self.name)
886+
if isinstance(size, int):
887+
tag = DimensionTag(
888+
kind=DimensionTag.Types.Spatial,
889+
description="time_sliced",
890+
batch=start_data.batch,
891+
dimension=size)
892+
else:
893+
# in this case, size is not known before runtime and becomes dynamic
894+
if len(seq_lens.shape) == 1:
895+
dyn_size = tf.maximum(seq_lens - start_t, 0) # (B,)
896+
else:
897+
# in this case, we would normally get a dynamic size of shape (B,T) for the slices
898+
# in order to get shape (B,) instead, we reduce all other axes except the batch axis
899+
reduce_axes = range(1, len(seq_lens.shape))
900+
dyn_size = tf.maximum(tf.reduce_max(seq_lens - start_t, axis=reduce_axes), 0) # (B,)
901+
tag = DimensionTag(
902+
kind=DimensionTag.Types.Spatial,
903+
description="time_sliced",
904+
batch=start_data.batch,
905+
dyn_size=dyn_size)
906+
indices_data = indices_data.copy_add_dim_by_tag(tag, unbroadcast=True, axis=start_data.batch_ndim)
872907
# [start+0, start+1, ...]
873-
indices = tf.expand_dims(start, -1) + tf.range(0, size) # e.g. (B, size) or (B, T, size)
908+
indices = tf.expand_dims(start_t, -1) + tf.range(0, size) # e.g. (B, size) or (B, T, size)
874909
if seq_lens is not None:
875910
# broadcast from (B,) to the shape of the indices
876-
seq_lens = expand_multiple_dims(seq_lens, [-1] * len(indices.shape[1:])) # e.g. (B,1) or (B,1,1)
877-
# mask if an index is larger than the length of a sequence
878-
pad_mask = tf.logical_or(tf.greater(indices, seq_lens-1), tf.less(indices, 0)) # shape like indices
879-
# clip indices to the min and max indices of the sequences so that GatherLayer does not fail
880-
indices = tf.clip_by_value(indices, 0, seq_lens-1)
911+
seq_lens = expand_multiple_dims( # e.g. (B,1) or (B,1,1)
912+
x=seq_lens,
913+
axes=[-1] * (len(indices.shape) - len(seq_lens.shape)))
914+
pad_mask = tf.logical_or(tf.greater(indices, seq_lens - 1), tf.less(indices, 0)) # shape like indices
915+
indices = tf.clip_by_value(indices, 0, seq_lens - 1)
881916
else:
882-
# mask if an index is larger than the length of a sequence
883-
pad_mask = tf.logical_or(tf.greater(indices, x.batch_shape[1] - 1), tf.less(indices, 0))
884-
# clip indices to the min and max indices of the sequences
917+
pad_mask = tf.logical_or(tf.greater(indices, x.batch_shape[1] - 1), tf.less(indices, 0)) # shape like indices
885918
indices = tf.clip_by_value(indices, 0, x.batch_shape[1] - 1)
886919
indices_data.placeholder = indices
887-
position = InternalLayer(
888-
network=self.network,
889-
name="%s_internal" % indices_data.name,
890-
output=indices_data
891-
)
920+
position = InternalLayer(network=self.network, name="%s_internal" % indices_data.name, output=indices_data)
892921
gather_layer = GatherLayer(
893922
name="%s_gather" % self.name,
894923
network=self.network,
895924
output=self.output,
896925
sources=self.sources,
897926
position=position,
898-
axis="T"
899-
)
927+
axis=x.get_time_dim_tag())
900928
placeholder = gather_layer.output.placeholder
901929
self.output.size_placeholder = gather_layer.output.size_placeholder
902930
# zero padding
931+
pad_mask = expand_multiple_dims(pad_mask, [-1] * (len(placeholder.shape) - len(pad_mask.shape)))
903932
self.output.placeholder = where_bc(pad_mask, tf.zeros_like(placeholder), placeholder)
904933

905934
def get_dep_layers(self):
@@ -917,17 +946,33 @@ def get_out_data_from_opts(cls, name, sources=(), start=None, size=None, **kwarg
917946
:param int|None size:
918947
:rtype: Data
919948
"""
920-
start = start.output.copy_as_batch_major()
921-
indices_data = start.copy_template(name="%s_gather_indices" % name)
922-
indices_data = indices_data.copy_add_spatial_dim(spatial_dim_axis=start.batch_ndim, dim=size,
923-
auto_time_dim_axis=False)
949+
from returnn.tf.util.data import DimensionTag
950+
start_data = start.output.copy_as_batch_major()
951+
input_data = sources[0].output.copy_as_batch_major()
952+
input_t = input_data.placeholder
953+
indices_data = start_data.copy_template(name="%s_gather_indices" % name)
954+
if isinstance(size, int):
955+
tag = DimensionTag(
956+
kind=DimensionTag.Types.Spatial,
957+
description="time_sliced",
958+
batch=start_data.batch,
959+
dimension=size)
960+
else:
961+
# get tensor of shape (B,) from input to use as dynamic size (start_data has no placeholder here)
962+
dyn_size = tf.repeat(tf.reduce_max(input_t), tf.shape(input_t)[0])
963+
dyn_size = tf.cast(dyn_size, tf.int32)
964+
tag = DimensionTag(
965+
kind=DimensionTag.Types.Spatial,
966+
description="time_sliced",
967+
batch=start_data.batch,
968+
dyn_size=dyn_size)
969+
indices_data = indices_data.copy_add_dim_by_tag(tag, unbroadcast=True, axis=start_data.batch_ndim)
924970
position = InternalLayer(network=sources[0].network, name="%s_internal" % indices_data.name, output=indices_data)
925971
return GatherLayer.get_out_data_from_opts(
926972
name="%s_gather" % name,
927973
sources=sources,
928974
position=position,
929-
axis="T"
930-
)
975+
axis=input_data.get_time_dim_tag())
931976

932977
@classmethod
933978
def transform_config_dict(cls, d, network, get_layer):

0 commit comments

Comments
 (0)