def data_generator(x, y, batch_size): x = to_list(x) y = to_list(y) max_batch_index = len(x[0]) // batch_size i = 0 while 1: x_batch = [array[i * batch_size:(i + 1) * batch_size] for array in x] x_batch = unpack_singleton(x_batch) y_batch = [array[i * batch_size:(i + 1) * batch_size] for array in y] y_batch = unpack_singleton(y_batch) yield x_batch, y_batch i += 1 i = i % max_batch_index
def data_generator(x, y, batch_size): x = to_list(x) y = to_list(y) max_batch_index = len(x[0]) // batch_size i = 0 while 1: x_batch = [array[i * batch_size: (i + 1) * batch_size] for array in x] x_batch = unpack_singleton(x_batch) y_batch = [array[i * batch_size: (i + 1) * batch_size] for array in y] y_batch = unpack_singleton(y_batch) yield x_batch, y_batch i += 1 i = i % max_batch_index
def avg_loss(losses, batch_sizes): averages = [] for i in range(len(losses[0])): if i not in stateful_metric_indices: averages.append( np.average([out[i] for out in losses], weights=batch_sizes)) else: averages.append(np.float64(losses[-1][i])) return unpack_singleton(averages)
def _collect_input_shape(input_tensors): """Collects the output shape(s) of a list of Keras tensors. # Arguments input_tensors: list of input tensors (or single input tensor). # Returns List of shape tuples (or single tuple), one tuple per input. """ input_tensors = to_list(input_tensors) shapes = [] for x in input_tensors: try: shapes.append(K.int_shape(x)) except TypeError: shapes.append(None) return unpack_singleton(shapes)
def test_on_batch_custom(self, x, y, sample_weight=None, class_weight=None): """ only diff to `test_on_batch`: extra `class_weight` """ x, y, sample_weights = self._standardize_user_data( x, y, sample_weight=sample_weight, class_weight=class_weight) if self._uses_dynamic_learning_phase(): ins = x + y + sample_weights + [0.] else: ins = x + y + sample_weights self._make_test_function() outputs = self.test_function(ins) return unpack_singleton(outputs)
def _collect_previous_mask(input_tensors): """Retrieves the output mask(s) of the previous node. # Arguments input_tensors: A tensor or list of tensors. # Returns A mask tensor or list of mask tensors. """ input_tensors = to_list(input_tensors) masks = [] for x in input_tensors: if hasattr(x, '_keras_history'): inbound_layer, node_index, tensor_index = x._keras_history node = inbound_layer._inbound_nodes[node_index] mask = node.output_masks[tensor_index] masks.append(mask) else: masks.append(None) return unpack_singleton(masks)
def call(self, inputs): if isinstance(inputs, GraphWrapper): nodes = inputs.nodes adjacency = inputs.adjacency else: raise ValueError() new_nodes = Dropout(rate=self.nodes_rate, noise_shape=self.nodes_noise_shape, seed=self.nodes_seed)(nodes) new_adj = to_list(adjacency) if self.use_adjacency_dropout: new_adj = [ Dropout(rate=self.adjacency_rate, noise_shape=self.adjacency_noise_shape, seed=self.nodes_seed)(a) for a in new_adj ] new_adj = unpack_singleton(new_adj) else: new_adj = adjacency return self.make_output_graph(adjacency=new_adj, nodes=new_nodes)
def predict_loop(model, f, ins, batch_size=32, verbose=0, steps=None): """Abstract method to loop over some data in batches. # Arguments model: Keras model instance. f: Keras function returning a list of tensors. ins: list of tensors to be fed to `f`. batch_size: integer batch size. verbose: verbosity mode. steps: Total number of steps (batches of samples) before declaring `predict_loop` finished. Ignored with the default value of `None`. # Returns Array of predictions (if the model has a single output) or list of arrays of predictions (if the model has multiple outputs). """ num_samples = check_num_samples(ins, batch_size=batch_size, steps=steps, steps_name='steps') if verbose == 1: if steps is not None: progbar = Progbar(target=steps) else: progbar = Progbar(target=num_samples) indices_for_conversion_to_dense = [] is_sparse = False for i in range(len(model._feed_inputs)): if issparse(ins[i]) and not K.is_sparse(model._feed_inputs[i]): indices_for_conversion_to_dense.append(i) elif issparse(ins[i]) and K.is_sparse(model._feed_inputs[i]): is_sparse = True if steps is not None: # Step-based predictions. # Since we do not know how many samples # we will see, we cannot pre-allocate # the returned Numpy arrays. # Instead, we store one array per batch seen # and concatenate them upon returning. unconcatenated_outs = [] for step in range(steps): batch_outs = f(ins) batch_outs = to_list(batch_outs) if step == 0: for batch_out in batch_outs: unconcatenated_outs.append([]) for i, batch_out in enumerate(batch_outs): unconcatenated_outs[i].append(batch_out) if verbose == 1: progbar.update(step + 1) if is_sparse: if len(unconcatenated_outs) == 1: return vstack(unconcatenated_outs[0], 'csr') return [ vstack(unconcatenated_outs[i], 'csr') for i in range(len(unconcatenated_outs)) ] if len(unconcatenated_outs) == 1: return np.concatenate(unconcatenated_outs[0], axis=0) return [ np.concatenate(unconcatenated_outs[i], axis=0) for i in range(len(unconcatenated_outs)) ] else: # Sample-based predictions. outs = [] batches = make_batches(num_samples, batch_size) index_array = np.arange(num_samples) for batch_index, (batch_start, batch_end) in enumerate(batches): batch_ids = index_array[batch_start:batch_end] if ins and isinstance(ins[-1], float): # Do not slice the training phase flag. ins_batch = slice_arrays(ins[:-1], batch_ids) + [ins[-1]] else: ins_batch = slice_arrays(ins, batch_ids) for i in indices_for_conversion_to_dense: ins_batch[i] = ins_batch[i].toarray() batch_outs = f(ins_batch) batch_outs = to_list(batch_outs) if batch_index == 0: # Pre-allocate the results arrays. for batch_out in batch_outs: shape = (num_samples, ) + batch_out.shape[1:] if is_sparse: outs.append(lil_matrix(shape, dtype=batch_out.dtype)) else: outs.append(np.zeros(shape, dtype=batch_out.dtype)) for i, batch_out in enumerate(batch_outs): outs[i][batch_start:batch_end] = batch_out if verbose == 1: progbar.update(batch_end) if is_sparse: return unpack_singleton(list(map(lambda oo: oo.tocsr(), outs))) return unpack_singleton(outs)
def evaluate_generator_custom(model, generator, steps=None, max_queue_size=10, class_weight=None, workers=1, use_multiprocessing=False, verbose=0): """See docstring for `Model.evaluate_generator`.""" model._make_test_function() stateful_metric_indices = [] if hasattr(model, 'metrics'): for m in model.stateful_metric_functions: m.reset_states() stateful_metric_indices = [ i for i, name in enumerate(model.metrics_names) if str(name) in model.stateful_metric_names ] else: stateful_metric_indices = [] steps_done = 0 wait_time = 0.01 outs_per_batch = [] batch_sizes = [] is_sequence = isinstance(generator, Sequence) if not is_sequence and use_multiprocessing and workers > 1: warnings.warn( UserWarning('Using a generator with `use_multiprocessing=True`' ' and multiple workers may duplicate your data.' ' Please consider using the`keras.utils.Sequence' ' class.')) if steps is None: if is_sequence: steps = len(generator) else: raise ValueError('`steps=None` is only valid for a generator' ' based on the `keras.utils.Sequence` class.' ' Please specify `steps` or use the' ' `keras.utils.Sequence` class.') enqueuer = None try: if workers > 0: if is_sequence: enqueuer = OrderedEnqueuer( generator, use_multiprocessing=use_multiprocessing) else: enqueuer = GeneratorEnqueuer( generator, use_multiprocessing=use_multiprocessing, wait_time=wait_time) enqueuer.start(workers=workers, max_queue_size=max_queue_size) output_generator = enqueuer.get() else: if is_sequence: output_generator = iter(generator) else: output_generator = generator if verbose == 1: progbar = Progbar(target=steps) while steps_done < steps: generator_output = next(output_generator) if not hasattr(generator_output, '__len__'): raise ValueError('Output of generator should be a tuple ' '(x, y, sample_weight) ' 'or (x, y). Found: ' + str(generator_output)) if len(generator_output) == 2: x, y = generator_output sample_weight = None elif len(generator_output) == 3: x, y, sample_weight = generator_output else: raise ValueError('Output of generator should be a tuple ' '(x, y, sample_weight) ' 'or (x, y). Found: ' + str(generator_output)) # Ken: to weight validation examples, testing must re-weight the examples ############################################################### outs = model.test_on_batch_custom(x, y, sample_weight=sample_weight, class_weight=class_weight) ############################################################### outs = to_list(outs) outs_per_batch.append(outs) if x is None or len(x) == 0: # Handle data tensors support when no input given # step-size = 1 for data tensors batch_size = 1 elif isinstance(x, list): batch_size = x[0].shape[0] elif isinstance(x, dict): batch_size = list(x.values())[0].shape[0] else: batch_size = x.shape[0] if batch_size == 0: raise ValueError('Received an empty batch. ' 'Batches should contain ' 'at least one item.') steps_done += 1 batch_sizes.append(batch_size) if verbose == 1: progbar.update(steps_done) finally: if enqueuer is not None: enqueuer.stop() averages = [] for i in range(len(outs)): if i not in stateful_metric_indices: averages.append( np.average([out[i] for out in outs_per_batch], weights=batch_sizes)) else: averages.append(np.float64(outs_per_batch[-1][i])) return unpack_singleton(averages)
def run_internal_graph(model, inputs, mode, mask=None): """Computes output tensors for new inputs. # Note: - Expects `inputs` to be a list (potentially with 1 element). - Can be run on non-Keras tensors. # Arguments inputs: List of tensors masks: List of masks (tensors or None). # Returns Three lists: output_tensors, output_masks, output_shapes """ is_training = mode == tf.estimator.ModeKeys.TRAIN inputs = to_list(inputs) if mask is None: masks = [None for _ in range(len(inputs))] else: masks = to_list(mask) cache_key = object_list_uid(inputs) cache_key += "_" + object_list_uid(masks) if cache_key in model._output_tensor_cache: return model._output_tensor_cache[cache_key] # Dictionary mapping reference tensors to tuples # (computed tensor, compute mask) # we assume a 1:1 mapping from tensor to mask # TODO: raise exception when a `.compute_mask()` call # does not return a list the same size as `call` tensor_map = {} for x, y, mask in zip(model.inputs, inputs, masks): tensor_map[str(id(x))] = (y, mask) depth_keys = list(model._nodes_by_depth.keys()) depth_keys.sort(reverse=True) for depth in depth_keys: nodes = model._nodes_by_depth[depth] for node in nodes: # This is always a single layer, never a list. layer = node.outbound_layer reference_input_tensors = node.input_tensors reference_output_tensors = node.output_tensors # issue: https://github.com/tensorflow/tensorflow/issues/30208 if not isinstance(reference_input_tensors, list): reference_input_tensors = [reference_input_tensors] if not isinstance(reference_output_tensors, list): reference_output_tensors = [reference_output_tensors] # If all previous input tensors are available in tensor_map, # then call node.inbound_layer on them. computed_data = [] # List of tuples (input, mask). for x in reference_input_tensors: if str(id(x)) in tensor_map: computed_data.append(tensor_map[str(id(x))]) if len(computed_data) == len(reference_input_tensors): # call layer with K.name_scope(layer.name): if node.arguments: kwargs = node.arguments else: kwargs = {} if len(computed_data) == 1: computed_tensor, computed_mask = computed_data[0] if has_arg(layer.call, "mask"): if "mask" not in kwargs: kwargs["mask"] = computed_mask if has_arg(layer.call, "training"): kwargs["training"] = is_training output_tensors = to_list( layer.call(computed_tensor, **kwargs)) output_masks = layer.compute_mask( computed_tensor, computed_mask) if output_masks is None: output_masks = [None for _ in output_tensors] else: output_masks = to_list(output_masks) computed_tensors = [computed_tensor] # computed_masks might be used in the future. computed_masks = [computed_mask] else: computed_tensors = [x[0] for x in computed_data] computed_masks = [x[1] for x in computed_data] if has_arg(layer.call, "mask"): if "mask" not in kwargs: kwargs["mask"] = computed_masks output_tensors = to_list( layer.call(computed_tensors, **kwargs)) output_masks = layer.compute_mask( computed_tensors, computed_masks) if output_masks is None: output_masks = [None for _ in output_tensors] else: output_masks = to_list(output_masks) # Apply activity regularizer if any: if (hasattr(layer, "activity_regularizer") and layer.activity_regularizer is not None): with K.name_scope("activity_regularizer"): regularization_losses = [ layer.activity_regularizer(x) for x in output_tensors ] layer.add_loss(regularization_losses, inputs=computed_tensors) if len(output_masks) != len(output_tensors): raise Exception( "Layers should have equal number of output tensors " "and output masks. Layer " + str(layer.name) + " has" " " + str(len(output_tensors)) + " output tensors " "and " + str(len(output_masks)) + " output masks.") # Update model updates and losses: # Keep track of updates that depend on the inputs # (e.g. BN updates). model.add_update(layer.get_updates_for(computed_tensors), inputs) # Keep track of unconditional updates (e.g. a counter). model.add_update(layer.get_updates_for(None), None) # Keep track of losses that depend on the inputs # (e.g. activity regularizers). model.add_loss(layer.get_losses_for(computed_tensors), inputs) # Keep track of unconditional losses # (e.g. weight regularizers). model.add_loss(layer.get_losses_for(None), None) # Update _keras_shape. if all([hasattr(x, "_keras_shape") for x in computed_tensors]): input_shapes = unpack_singleton( [x._keras_shape for x in computed_tensors]) shapes = to_list(layer.compute_output_shape(input_shapes)) uses_learning_phase = any( [x._uses_learning_phase for x in computed_tensors]) for x, s in zip(output_tensors, shapes): x._keras_shape = s _u = getattr(x, "_uses_learning_phase", False) x._uses_learning_phase = _u or uses_learning_phase # Update tensor_map. for x, y, mask in zip(reference_output_tensors, output_tensors, output_masks): tensor_map[str(id(x))] = (y, mask) output_tensors = [] output_masks = [] output_shapes = [] for x in model.outputs: assert str(id(x)) in tensor_map, "Could not compute output " + str(x) tensor, mask = tensor_map[str(id(x))] if hasattr(tensor, "_keras_shape") and output_shapes is not None: shape = tensor._keras_shape output_shapes.append(shape) else: output_shapes = None output_tensors.append(tensor) output_masks.append(mask) # Update cache; # keys are based on ids on input tensors and inputs masks. cache_key = object_list_uid(inputs) cache_key += "_" + object_list_uid(masks) output_tensors = unpack_singleton(output_tensors) model._output_tensor_cache[cache_key] = output_tensors output_masks = unpack_singleton(output_masks) model._output_mask_cache[cache_key] = output_masks if output_shapes is not None: input_shapes = [x._keras_shape for x in inputs] cache_key = ", ".join([str(x) for x in input_shapes]) output_shapes = unpack_singleton(output_shapes) model._output_shape_cache[cache_key] = output_shapes return output_tensors
async def evaluate_generator(model, generator, steps=None, verbose=1): """See docstring for `Model.evaluate_generator`.""" model._make_test_function() if hasattr(model, 'metrics'): for m in model.stateful_metric_functions: m.reset_states() stateful_metric_indices = [ i for i, name in enumerate(model.metrics_names) if str(name) in model.stateful_metric_names ] else: stateful_metric_indices = [] steps_done = 0 outs_per_batch = [] batch_sizes = [] if steps is None: steps = len(generator) output_generator = generator.async_next if verbose == 1: progbar = Progbar(target=steps) while steps_done < steps: generator_output = await output_generator() if not hasattr(generator_output, '__len__'): raise ValueError('Output of generator should be a tuple ' '(x, y, sample_weight) ' 'or (x, y). Found: ' + str(generator_output)) if len(generator_output) == 2: x, y = generator_output sample_weight = None elif len(generator_output) == 3: x, y, sample_weight = generator_output else: raise ValueError('Output of generator should be a tuple ' '(x, y, sample_weight) ' 'or (x, y). Found: ' + str(generator_output)) outs = model.test_on_batch(x, y, sample_weight=sample_weight) outs = to_list(outs) outs_per_batch.append(outs) if x is None or len(x) == 0: # Handle data tensors support when no input given # step-size = 1 for data tensors batch_size = 1 elif isinstance(x, list): batch_size = x[0].shape[0] elif isinstance(x, dict): batch_size = list(x.values())[0].shape[0] else: batch_size = x.shape[0] if batch_size == 0: raise ValueError('Received an empty batch. ' 'Batches should contain ' 'at least one item.') steps_done += 1 batch_sizes.append(batch_size) if verbose == 1: progbar.update(steps_done) generator.on_epoch_end() averages = [] for i in range(len(outs)): if i not in stateful_metric_indices: averages.append( np.average([out[i] for out in outs_per_batch], weights=batch_sizes)) else: averages.append(np.float64(outs_per_batch[-1][i])) return unpack_singleton(averages)
def __call__(self, inputs, **kwargs): """ Overriding the Layer's __call__ method for graph wrappers. The overriding is mostly based on the __call__ code from 'keras/engine/base_layer.py' for the moment, with some changes to handle graphs. """ # If arguments are 'keras-style' arguments, let keras to the job if K.is_tensor(inputs) or (isinstance(inputs, list) and not isinstance(inputs, GraphWrapper)): output = super(GraphLayer, self).__call__(inputs, **kwargs) else: if isinstance(inputs, list) and not isinstance(inputs, GraphWrapper): inputs = inputs[:] with K.name_scope(self.name): # Build the layer if not self.built: # Check the comatibilty of inputs for each inputs (some can # be GraphWrapper) if isinstance(inputs, list) and not isinstance( inputs, GraphWrapper): for i in inputs: self.assert_input_compatibility(i) # Collect input shapes to build layer. input_shapes = [] if isinstance(inputs, GraphWrapper): inputs = [inputs] for x_elem in inputs: if hasattr(x_elem, '_keras_shape'): # For a GraphWrapper, _keras_shape is a GraphShape # object input_shapes.append(x_elem._keras_shape) elif hasattr(K, 'int_shape'): input_shapes.append(K.int_shape(x_elem)) else: raise ValueError( 'You tried to call layer "' + self.name + '". This layer has no information' ' about its expected input shape, ' 'and thus cannot be built. ' 'You can build it manually via: ' '`layer.build(batch_input_shape)`') self.build(unpack_singleton(input_shapes)) self._built = True # Load weights that were specified at layer instantiation. if self._initial_weights is not None: self.set_weights(self._initial_weights) # Raise exceptions in case the input is not compatible # with the input_spec set at build time. if isinstance(inputs, list) and not isinstance(inputs, GraphWrapper): for i in inputs: self.assert_input_compatibility(i) # Handle mask propagation. previous_mask = _collect_previous_mask(inputs) user_kwargs = copy.copy(kwargs) if not is_all_none(previous_mask): # The previous layer generated a mask. if has_arg(self.call, 'mask'): if 'mask' not in kwargs: # If mask is explicitly passed to __call__, # we should override the default mask. kwargs['mask'] = previous_mask # Handle automatic shape inference (only useful for Theano). input_shape = _collect_input_shape(inputs) # Actually call the layer, # collecting output(s), mask(s), and shape(s). # Note that inpputs can hold graph wrappers now output = self.call(unpack_singleton(inputs), **kwargs) output_mask = self.compute_mask(inputs, previous_mask) # If the layer returns tensors from its inputs, unmodified, # we copy them to avoid loss of tensor metadata. # output_ls = to_list(output) if isinstance(output, GraphWrapper): output_ls = [output] # Unpack wrappers inputs_ls = list( chain.from_iterable( [i for i in inputs if isinstance(i, GraphWrapper)])) inputs_ls += [ i for i in inputs if not isinstance(i, GraphWrapper) ] # Unpack adjacency and nodes inpadj_ls = list( chain.from_iterable([ to_list(i.adjacency) for i in inputs if isinstance(i, GraphWrapper) ])) inpnod_ls = to_list( [i.nodes for i in inputs if isinstance(i, GraphWrapper)]) output_ls_copy = [] adj_ls = [] for x in output_ls: if K.is_tensor(x) and x in inputs_ls: x = K.identity(x) # Apply adjacency-wise and node-wise identity elif isinstance(x, GraphWrapper): # Store changed or copy of unchanged adjacency matrices adj_ls.clear() for adj in to_list(x.adjacency): if adj in inpadj_ls: adj_ls.append(K.identity(adj)) else: adj_ls.append(adj) # Assign to output graph x.adjacency = unpack_singleton(adj_ls) # Store unchanged nodes if x.nodes in inpnod_ls: x.nodes = K.identity(x.nodes) output_ls_copy.append(x) output = unpack_singleton(output_ls_copy) # Inferring the output shape is only relevant for Theano. if all([s is not None for s in to_list(input_shape)]): output_shape = self.compute_output_shape(input_shape) else: if isinstance(input_shape, list): output_shape = [None for _ in input_shape] else: output_shape = None if (not isinstance(output_mask, (list, tuple)) and len(output_ls) > 1): # Augment the mask to match the length of the output. output_mask = [output_mask] * len(output_ls) # Add an inbound node to the layer, so that it keeps track # of the call and of all new variables created during the call. # This also updates the layer history of the output tensor(s). # If the input tensor(s) had not previous Keras history, # this does nothing. self._add_inbound_node(input_tensors=inputs_ls, output_tensors=unpack_singleton(output), input_masks=previous_mask, output_masks=output_mask, input_shapes=input_shape, output_shapes=output_shape, arguments=user_kwargs) # Apply activity regularizer if any: if (hasattr(self, 'activity_regularizer') and self.activity_regularizer is not None): with K.name_scope('activity_regularizer'): regularization_losses = [ self.activity_regularizer(x) for x in to_list(output) ] self.add_loss(regularization_losses, inputs=to_list(inputs)) return output
def _sup_rnn_call(self, inputs, initial_state=None, constants=None, **kwargs): # reshaped_input = Reshape((inputs._shape[1],inputs._keras_shape[2]*inputs._keras_shape[3]))(inputs) # super(RNNStackedAttention,self).__call__(reshaped_input,**kwargs) if isinstance(inputs, list): inputs = inputs[:] with K.name_scope(self.name): # Handle laying building (weight creating, input spec locking). if not self.built: input_shapes = [] input_shapes.append(self.shape_lstm) # Collect input shapes to build layer. #input_shapes.append(self.shape_lstm) for x_elem in to_list(inputs): if hasattr(x_elem, '_keras_shape'): input_shapes.append(x_elem._keras_shape) elif hasattr(K, 'int_shape'): input_shapes.append(K.int_shape(x_elem)) else: raise ValueError('You tried to call layer "' + self.name + '". This layer has no information' ' about its expected input shape, ' 'and thus cannot be built. ' 'You can build it manually via: ' '`layer.build(batch_input_shape)`') self.build(unpack_singleton(input_shapes)) self.built = True # Load weights that were specified at layer instantiation. if self._initial_weights is not None: self.set_weights(self._initial_weights) # Raise exceptions in case the input is not compatible # with the input_spec set at build time. # self.assert_input_compatibility(inputs) # Handle mask propagation. # previous_mask = _collect_previous_mask(inputs) user_kwargs = kwargs.copy() # if not is_all_none(previous_mask): # # The previous layer generated a mask. # if has_arg(self.call, 'mask'): # if 'mask' not in kwargs: # # If mask is explicitly passed to __call__, # # we should override the default mask. # kwargs['mask'] = previous_mask # # Handle automatic shape inference (only useful for Theano). # input_shape = _collect_input_shape(inputs) # Actually call the layer, # collecting output(s), mask(s), and shape(s). output = self.call(inputs, **kwargs) # output_mask = self.compute_mask(inputs, previous_mask) # If the layer returns tensors from its inputs, unmodified, # we copy them to avoid loss of tensor metadata. output_ls = to_list(output) inputs_ls = to_list(inputs) output_ls_copy = [] for x in output_ls: if id(x) in [id(i) for i in inputs_ls]: x = K.identity(x) output_ls_copy.append(x) output = unpack_singleton(output_ls_copy) input_shape = _collect_input_shape(inputs) input_shape[0] = self.shape_lstm # Inferring the output shape is only relevant for Theano. if all([s is not None for s in to_list(input_shape)]): output_shape = self.compute_output_shape(input_shape) else: if isinstance(input_shape, list): output_shape = [None for _ in input_shape] else: output_shape = None # # if (not isinstance(output_mask, (list, tuple)) and # len(output_ls) > 1): # # Augment the mask to match the length of the output. # output_mask = [output_mask] * len(output_ls) # Add an inbound node to the layer, so that it keeps track # of the call and of all new variables created during the call. # This also updates the layer history of the output tensor(s). # If the input tensor(s) had not previous Keras history, # this does nothing. previous_mask = None output_mask = None self._add_inbound_node(input_tensors=inputs, output_tensors=output, input_masks=previous_mask, output_masks=output_mask, input_shapes=input_shape, output_shapes=output_shape, arguments=user_kwargs) # Apply activity regularizer if any: if (hasattr(self, 'activity_regularizer') and self.activity_regularizer is not None): with K.name_scope('activity_regularizer'): regularization_losses = [ self.activity_regularizer(x) for x in to_list(output) ] self.add_loss(regularization_losses, inputs=to_list(inputs)) return output