@@ -842,64 +842,93 @@ def get_out_data_from_opts(
842842
843843class 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