def get_batch_size(global_batch_size, distribution): batch_size = global_batch_size # TODO(b/118776054): Use global batch size for Keras/DS support. use_per_core_batch_size = ( distribution and not distributed_training_utils.global_batch_size_supported(distribution)) if use_per_core_batch_size: batch_size //= distribution.num_replicas_in_sync return batch_size
def __init__( self, input_shape=None, batch_size=None, dtype=None, input_tensor=None, sparse=None, name=None, ragged=None, type_spec=None, **kwargs, ): self._init_input_shape = input_shape self._init_batch_size = batch_size self._init_dtype = dtype self._init_sparse = sparse self._init_ragged = ragged self._init_type_spec = type_spec strategy = tf.distribute.get_strategy() if (strategy and batch_size is not None and distributed_training_utils.global_batch_size_supported( strategy)): if batch_size % strategy.num_replicas_in_sync != 0: raise ValueError( "The `batch_size` argument ({}) must be divisible by " "the number of replicas ({})".format( batch_size, strategy.num_replicas_in_sync)) batch_size = batch_size // strategy.num_replicas_in_sync if "batch_input_shape" in kwargs: batch_input_shape = kwargs.pop("batch_input_shape") if input_shape and batch_input_shape: raise ValueError("Only provide the input_shape OR " "batch_input_shape argument to " "InputLayer, not both at the same time.") # Set the input shape and batch size from the batch_input_shape. # Note that batch_input_shape can be None (unknown rank) or [] (scalar), # in which case the batch size must be None. if batch_input_shape: batch_size = batch_input_shape[0] input_shape = batch_input_shape[1:] if kwargs: raise ValueError( f"Unrecognized keyword arguments: {list(kwargs.keys())}") if sparse and ragged: raise ValueError( "Cannot set both sparse and ragged to True in a Keras input.") if not name: prefix = "input" name = prefix + "_" + str(backend.get_uid(prefix)) if not dtype: if input_tensor is None: dtype = backend.floatx() else: dtype = backend.dtype(input_tensor) elif input_tensor is not None and input_tensor.dtype != dtype: raise ValueError( "`input_tensor.dtype` differs from `dtype`. Received: " f"input_tensor.dtype={input_tensor.dtype} " f"but expected dtype={dtype}") super().__init__(dtype=dtype, name=name) self.built = True self.sparse = True if sparse else False self.ragged = True if ragged else False self.batch_size = batch_size self.supports_masking = True if isinstance(input_shape, tf.TensorShape): input_shape = tuple(input_shape.as_list()) elif isinstance(input_shape, int): input_shape = (input_shape, ) if type_spec is not None: args_that_must_be_none = [ ("(input_)shape", self._init_input_shape), ("batch_size", self._init_batch_size), ("dtype", self._init_dtype), ("input_tensor", input_tensor), ("sparse", self._init_sparse), ("ragged", self._init_ragged), ] for arg_name, arg in args_that_must_be_none: _assert_other_arg_none(arg_name, arg) if not tf.compat.v1.executing_eagerly_outside_functions(): raise ValueError( "Creating Keras inputs from a type_spec is only " "supported when eager execution is enabled.") input_tensor = keras_tensor.keras_tensor_from_type_spec(type_spec) if isinstance(input_tensor, keras_tensor.SparseKerasTensor): self.sparse = True if isinstance(input_tensor, keras_tensor.RaggedKerasTensor): self.ragged = True self.is_placeholder = True try: self._batch_input_shape = tuple(input_tensor.shape.as_list()) except ValueError: # If the shape cannot be represented as a tuple (e.g. unknown rank) self._batch_input_shape = None elif input_tensor is None: if input_shape is not None: batch_input_shape = (batch_size, ) + tuple(input_shape) else: batch_input_shape = None graph = backend.get_graph() with graph.as_default(): input_tensor = backend.placeholder( shape=batch_input_shape, dtype=dtype, name=self.name, sparse=sparse, ragged=ragged, ) self.is_placeholder = True self._batch_input_shape = batch_input_shape else: if tf.compat.v1.executing_eagerly_outside_functions(): if not isinstance(input_tensor, keras_tensor.KerasTensor): input_tensor = keras_tensor.keras_tensor_from_tensor( input_tensor) else: if not tf_utils.is_symbolic_tensor(input_tensor): raise ValueError( "You should not pass an EagerTensor to `Input`. " "For example, instead of creating an " "`InputLayer`, you should instantiate your model " "and directly call it on your input.") self.is_placeholder = False try: self._batch_input_shape = tuple(input_tensor.shape.as_list()) except ValueError: # If the shape cannot be represented as a tuple (e.g. unknown rank) self._batch_input_shape = None # Create an input node. input_tensor._keras_mask = None node_module.Node(layer=self, outputs=input_tensor) # Store type spec if isinstance(input_tensor, keras_tensor.KerasTensor) or ( tf_utils.is_extension_type(input_tensor)): self._type_spec = (input_tensor._type_spec) # pylint: disable=protected-access else: self._type_spec = tf.TensorSpec( shape=input_tensor.shape, dtype=input_tensor.dtype, name=self.name, )
def get_input_params(distribution_strategy, num_samples, steps, batch_size, mode=None): """Calculate the number of batches and steps/steps_per_epoch. Args: distribution_strategy: The DistributionStrategy used to compile the model. num_samples: The number of samples from which we determine the batch size and steps. steps: The specified number of steps. batch_size: The specified batch_size. mode: ModeKey representing whether input will be used for training, evaluation, or prediction. This is used to relax the constraints on consuming all the training samples to keep compatibility till we support partial batches. If none, then partial batches are not allowed. Returns: steps: The steps or steps_per_epoch argument depending on if a user is calling `fit`, `evaluate` or `predict`. If the is_training flag is set we don't require the number of samples to be used completely. batch_size: The batch size to be used in model iterations. Raises: ValueError: If the number of batches or steps evaluates to 0. """ # TODO(b/118776054): Use global batch size for Keras/DS support. # Currently this is only supported in TPUStrategy and CoreMirroredStrategy. use_per_replica_batch = not dist_utils.global_batch_size_supported( distribution_strategy) # TODO(b/128995245): In eager mode, uneven batch sizes are allowed except for # `fit()` on TPUStrategy. # In graph mode, the zero batch case in batch norm is not handled due to # XLA-GPU regression. Uneven batch sizes are not allowed except # for `test()` and `predict()` on TPUStrategy. if tf.executing_eagerly(): allow_partial_batch = ( mode != ModeKeys.TRAIN or not backend.is_tpu_strategy(distribution_strategy)) else: allow_partial_batch = (mode == ModeKeys.TRAIN or ( (mode == ModeKeys.PREDICT or mode == ModeKeys.TEST) and backend.is_tpu_strategy(distribution_strategy))) if steps is None: if batch_size is None: # If neither the batch size or number of steps are set. We choose the # global batch size as the minimum of number of samples and 32. 32 is # chosen to provide backward compatibility. global_batch_size = min(num_samples, 32) else: # If the user provided the batch size we need to handle the case # between different strategies that use the global/per-replica batch size global_batch_size = batch_size if use_per_replica_batch: global_batch_size *= distribution_strategy.num_replicas_in_sync if allow_partial_batch: steps = np.ceil(num_samples / global_batch_size).astype(int) else: if num_samples % global_batch_size: raise ValueError( 'The number of samples %s is not divisible by ' 'batch size %s.' % (num_samples, global_batch_size)) steps = num_samples // global_batch_size else: if batch_size is None: # We calculate the batch size based on the number of steps specified if num_samples % steps: raise ValueError( 'The number of samples %s is not divisible by ' 'steps %s. Please change the number of steps to a ' 'value that can consume all the samples' % (num_samples, steps)) global_batch_size = num_samples // steps else: # If the user provided the batch size we need to handle the case # between different strategies that use the global/per-replica batch size global_batch_size = batch_size if use_per_replica_batch: global_batch_size *= distribution_strategy.num_replicas_in_sync min_num_samples = global_batch_size * steps if allow_partial_batch: min_num_samples = global_batch_size * ( steps - 1) + 1 if steps > 1 else 0 if num_samples < min_num_samples: raise ValueError( 'Number of samples %s is less than samples required ' 'for specified batch_size %s and steps %s' % (num_samples, global_batch_size, steps)) # We need to return the per replica or global batch size based on the strategy if use_per_replica_batch: if global_batch_size % distribution_strategy.num_replicas_in_sync: raise ValueError( 'The batch size (%s) could not be sharded evenly across the sync ' 'replicas (%s) in the distribution strategy.' % (global_batch_size, distribution_strategy.num_replicas_in_sync)) batch_size = global_batch_size // distribution_strategy.num_replicas_in_sync else: batch_size = global_batch_size return steps, batch_size
def __init__(self, input_shape=None, batch_size=None, dtype=None, input_tensor=None, sparse=None, name=None, ragged=None, type_spec=None, **kwargs): self._init_input_shape = input_shape self._init_batch_size = batch_size self._init_dtype = dtype self._init_sparse = sparse self._init_ragged = ragged self._init_type_spec = type_spec strategy = tf.distribute.get_strategy() if strategy and batch_size is not None and \ distributed_training_utils.global_batch_size_supported(strategy): if batch_size % strategy.num_replicas_in_sync != 0: raise ValueError( 'The `batch_size` argument ({}) must be divisible by ' 'the number of replicas ({})'.format( batch_size, strategy.num_replicas_in_sync)) batch_size = batch_size // strategy.num_replicas_in_sync if 'batch_input_shape' in kwargs: batch_input_shape = kwargs.pop('batch_input_shape') if input_shape and batch_input_shape: raise ValueError('Only provide the input_shape OR ' 'batch_input_shape argument to ' 'InputLayer, not both at the same time.') batch_size = batch_input_shape[0] input_shape = batch_input_shape[1:] if kwargs: raise ValueError('Unrecognized keyword arguments:', kwargs.keys()) if sparse and ragged: raise ValueError( 'Cannot set both sparse and ragged to True in a Keras input.') if not name: prefix = 'input' name = prefix + '_' + str(backend.get_uid(prefix)) if not dtype: if input_tensor is None: dtype = backend.floatx() else: dtype = backend.dtype(input_tensor) elif input_tensor is not None and input_tensor.dtype != dtype: raise ValueError( '`input_tensor.dtype` differs from `dtype`: %s vs. %s' % (input_tensor.dtype, dtype)) super(InputLayer, self).__init__(dtype=dtype, name=name) self.built = True self.sparse = True if sparse else False self.ragged = True if ragged else False self.batch_size = batch_size self.supports_masking = True if isinstance(input_shape, tf.TensorShape): input_shape = tuple(input_shape.as_list()) elif isinstance(input_shape, int): input_shape = (input_shape, ) if type_spec is not None: args_that_must_be_none = [ ('(input_)shape', self._init_input_shape), ('batch_size', self._init_batch_size), ('dtype', self._init_dtype), ('input_tensor', input_tensor), ('sparse', self._init_sparse), ('ragged', self._init_ragged), ] for arg_name, arg in args_that_must_be_none: _assert_other_arg_none(arg_name, arg) if not keras_tensor.keras_tensors_enabled(): raise ValueError( 'Creating Keras inputs from a type_spec is only ' 'supported when eager execution is enabled.') input_tensor = keras_tensor.keras_tensor_from_type_spec(type_spec) if isinstance(input_tensor, keras_tensor.SparseKerasTensor): self.sparse = True if isinstance(input_tensor, keras_tensor.RaggedKerasTensor): self.ragged = True self.is_placeholder = True try: self._batch_input_shape = tuple(input_tensor.shape.as_list()) except ValueError: # If the shape cannot be represented as a tuple (e.g. unknown rank) self._batch_input_shape = None elif input_tensor is None: if input_shape is not None: batch_input_shape = (batch_size, ) + tuple(input_shape) else: batch_input_shape = None graph = backend.get_graph() with graph.as_default(): input_tensor = backend.placeholder(shape=batch_input_shape, dtype=dtype, name=self.name, sparse=sparse, ragged=ragged) self.is_placeholder = True self._batch_input_shape = batch_input_shape else: if keras_tensor.keras_tensors_enabled(): if not isinstance(input_tensor, keras_tensor.KerasTensor): input_tensor = keras_tensor.keras_tensor_from_tensor( input_tensor) else: if not tf_utils.is_symbolic_tensor(input_tensor): raise ValueError( 'You should not pass an EagerTensor to `Input`. ' 'For example, instead of creating an ' 'InputLayer, you should instantiate your model and ' 'directly call it on your input.') self.is_placeholder = False try: self._batch_input_shape = tuple(input_tensor.shape.as_list()) except ValueError: # If the shape cannot be represented as a tuple (e.g. unknown rank) self._batch_input_shape = None # Create an input node. input_tensor._keras_mask = None node_module.Node(layer=self, outputs=input_tensor) # Store type spec if isinstance(input_tensor, keras_tensor.KerasTensor) or ( tf_utils.is_extension_type(input_tensor)): self._type_spec = input_tensor._type_spec # pylint: disable=protected-access else: self._type_spec = tf.TensorSpec(shape=input_tensor.shape, dtype=input_tensor.dtype, name=self.name)