@@ -859,39 +859,50 @@ class SliceNdLayer(_ConcatInputLayer):
859859 def __init__ (self , start , size , min_size = None , ** kwargs ):
860860 """
861861 :param LayerBase start: (B,...)
862- :param int|None size: if None, it uses the max possible size, and it becomes a dynamic axis
862+ :param int|LayerBase|None size: if None, it uses the max possible size,
863+ and it becomes a dynamic axis.
863864 :param int|None min_size: if size is None, but we want to have a min-size
864865 """
865866 super (SliceNdLayer , self ).__init__ (** kwargs )
866- from returnn .tf .util .basic import where_bc , expand_multiple_dims
867- x = self .input_data .copy_as_batch_major ()
868- seq_lens = x .get_sequence_lengths () if x .is_time_axis_dynamic () else None # (B,) or None
867+ from returnn .tf .util .basic import where_bc
868+ from returnn .tf .util .data import Data
869+ x = self .input_data .copy ()
870+ seq_lens_data = x .get_time_dim_tag ().dyn_size_ext # (B,) or None
869871 self .start = start
870- start_data = start .output .copy_as_batch_major () # e.g. (B,) or (B,T)
872+ self .size = size
873+ start_data = start .output .copy () # e.g. (B,) or (B,T)
874+ data_objs = [start_data ]
875+ data_objs += [size .output ] if isinstance (size , LayerBase ) else []
876+ data_objs += [seq_lens_data ] if isinstance (seq_lens_data , Data ) else []
877+ common_data = Data .get_common_data (data_objs )
878+ start_data = start_data .copy_compatible_to (common_data , check_sparse = False )
871879 start_t = start_data .placeholder
872880 if size is None :
873881 if min_size is None :
874882 min_size = 0
875- if seq_lens is None :
883+ if seq_lens_data is None :
876884 assert isinstance (x .batch_shape [x .time_dim_axis ], int )
877- size = tf . maximum ( tf . reduce_max ( x .batch_shape [x .time_dim_axis ] - start_t ), min_size ) # scalar
885+ size_t = x .batch_shape [x .time_dim_axis ] - start_t
878886 else :
879- # make seq_lens compatible with start_t
880- seq_lens = expand_multiple_dims ( # e.g. (B,) or (B,1)
881- x = seq_lens ,
882- axes = [- 1 ] * (len (start_t .shape ) - len (seq_lens .shape )))
883- size = tf .maximum (tf .reduce_max (seq_lens - start_t ), min_size ) # scalar
887+ seq_lens_t = seq_lens_data .copy_compatible_to (common_data , check_sparse = False ).placeholder
888+ size_t = seq_lens_t - start_t
889+ size = tf .maximum (tf .reduce_max (size_t ), min_size ) # scalar
890+ elif isinstance (size , LayerBase ):
891+ size_data = size .output .copy_compatible_to (common_data , check_sparse = False )
892+ size_t = size_data .placeholder
893+ min_size = 0
894+ size = tf .maximum (tf .reduce_max (size_t ), min_size ) # scalar
895+ else :
896+ size_t = None
884897 # for each start index in start_data, we want to gather a slice
885898 # therefore, the output's first axes are the same as the ones from start_data
886899 # and the next axis will therefore be the slice axis
887900 slice_tag = self .output .dim_tags [start_data .batch_ndim ]
888901 assert slice_tag .description .startswith ("sliced-time:" )
889- if not isinstance ( size , int ) :
902+ if size_t is not None :
890903 # in this case, size is not known before runtime and becomes dynamic and we need to set dyn_size
891- if seq_lens is None :
892- dyn_size = tf .maximum (x .batch_shape [x .time_dim_axis ] - start_t , min_size ) # (B,) or (B,T)
893- else :
894- dyn_size = tf .maximum (seq_lens - start_t , min_size ) # (B,) or (B,T)
904+ assert not isinstance (size , int )
905+ dyn_size = tf .maximum (size_t , min_size ) # (B,) or (B,T)
895906 dyn_size_ext = Data (
896907 name = ("%s:dyn_size" % slice_tag .description ),
897908 dtype = Data .size_dtype ,
@@ -909,20 +920,25 @@ def __init__(self, start, size, min_size=None, **kwargs):
909920 axis = start_data .batch_ndim )
910921 # [start+0, start+1, ...]
911922 gather_positions = tf .expand_dims (start_t , - 1 ) + tf .range (0 , size ) # e.g. (B, size) or (B, T, size)
912- if seq_lens is not None :
913- # broadcast from (B,) to the shape of the indices
914- seq_lens = expand_multiple_dims ( # e.g. (B,1) or (B,1,1)
915- x = seq_lens ,
916- axes = [- 1 ] * (len (gather_positions .shape ) - len (seq_lens .shape )))
923+ if seq_lens_data is not None :
924+ seq_lens_t = seq_lens_data .copy_compatible_to (
925+ gather_positions_data ,
926+ check_sparse = False ).placeholder
917927 pad_mask = tf .logical_or ( # shape like gather_positions
918- tf .greater (gather_positions , seq_lens - 1 ),
928+ tf .greater (gather_positions , seq_lens_t - 1 ),
919929 tf .less (gather_positions , 0 ))
920- gather_positions = tf .clip_by_value (gather_positions , 0 , seq_lens - 1 )
930+ gather_positions = tf .clip_by_value (gather_positions , 0 , seq_lens_t - 1 )
921931 else :
922932 pad_mask = tf .logical_or ( # shape like gather_positions
923933 tf .greater (gather_positions , x .batch_shape [1 ] - 1 ),
924934 tf .less (gather_positions , 0 ))
925935 gather_positions = tf .clip_by_value (gather_positions , 0 , x .batch_shape [1 ] - 1 )
936+ if isinstance (self .size , LayerBase ):
937+ pad_mask = tf .logical_or (tf .greater (gather_positions , tf .expand_dims (start_t + size_t - 1 , - 1 )), pad_mask )
938+ pad_mask_data = gather_positions_data .copy_template (
939+ name = "%s_gather_positions" % self .name ,
940+ dtype = "bool" )
941+ pad_mask_data .placeholder = pad_mask
926942 gather_positions_data .placeholder = gather_positions
927943 position = InternalLayer (
928944 network = self .network ,
@@ -944,28 +960,34 @@ def __init__(self, start, size, min_size=None, **kwargs):
944960 # the gradient flow would go into wrong frames
945961 # and might lead to unexpected behavior.
946962 # So to be on the safe side, we do the masking here.
947- pad_mask = expand_multiple_dims (pad_mask , [- 1 ] * (len (placeholder .shape ) - len (pad_mask .shape )))
963+ pad_mask_data = pad_mask_data .copy_compatible_to (gather_layer .output , check_sparse = False , check_dtype = False )
964+ pad_mask = pad_mask_data .placeholder
948965 self .output .placeholder = where_bc (pad_mask , tf .zeros_like (placeholder ), placeholder )
949966
950967 def get_dep_layers (self ):
951968 """
952969 :rtype: list[LayerBase]
953970 """
954- return super (SliceNdLayer , self ).get_dep_layers () + [self .start ]
971+ dep_layers = super (SliceNdLayer , self ).get_dep_layers () + [self .start ]
972+ if isinstance (self .size , LayerBase ):
973+ dep_layers += [self .size ]
974+ return dep_layers
955975
956976 @classmethod
957977 def get_out_data_from_opts (cls , name , sources = (), start = None , size = None , ** kwargs ):
958978 """
959979 :param str name:
960980 :param list[LayerBase] sources:
961981 :param LayerBase|None start:
962- :param int|None size:
982+ :param int|LayerBase| None size:
963983 :rtype: Data
964984 """
965985 from ..util .data import DimensionTag
966- start_data = start .output .copy_as_batch_major ()
967- input_data = sources [0 ].output .copy_as_batch_major ()
986+ start_data = start .output .copy ()
987+ input_data = sources [0 ].output .copy ()
968988 gather_positions_data = start_data .copy_template (name = "%s_gather_positions" % name )
989+ if isinstance (size , LayerBase ):
990+ size = None
969991 # size might be None here in which case we set the dyn_size in __init__
970992 tag = DimensionTag (
971993 kind = DimensionTag .Types .Spatial ,
@@ -991,6 +1013,8 @@ def transform_config_dict(cls, d, network, get_layer):
9911013 """
9921014 super (SliceNdLayer , cls ).transform_config_dict (d , network = network , get_layer = get_layer )
9931015 d ["start" ] = get_layer (d ["start" ])
1016+ if isinstance (d ["size" ], str ):
1017+ d ["size" ] = get_layer (d ["size" ])
9941018
9951019
9961020class GatherLayer (_ConcatInputLayer ):
0 commit comments