def _set_inputs_and_outputs(self, input_shape=None, tensor=None): """Set model's input and output specs based on the input received. If `tensor` is provided, `input_shape` is not required. Args: input_shape: Optional shape of input. tensor: Optional existing tensor to wrap into the `Input` layer. """ if not self.inputs: dtype = K.floatx() if tensor is not None: batch_shape = (None,) + tuple(tensor.get_shape().as_list()[1:]) x = Input(dtype=dtype, name=self.name + '_input', tensor=tensor) elif input_shape is not None: batch_shape = tuple(input_shape) x = Input( batch_shape=batch_shape, dtype=dtype, name=self.name + '_input') self.inputs = [x] for layer in self._layers: x = layer(x) self.outputs = [x] # Make sure that the model's input shape will be preserved during # serialization. if self._layers: self._layers[0]._batch_input_shape = batch_shape if self.inputs: self._init_graph_network(self.inputs, self.outputs, name=self.name) self.built = True if self._layers: self._track_layers(self._layers)
def __init__(self): #self.inception = InceptionResNetV2(weights=None, include_top=True) #self.inception.load_weights('/data/inception_resnet_v2_weights_tf_dim_ordering_tf_kernels.h5') #inception.graph = tf.get_default_graph() embed_input = Input(shape=(1000,)) encoder_input = Input(shape=(256, 256, 1,)) #encoder_output=GaussianNoise(0.1)(encoder_input) encoder_output = Conv2D(64, (3, 3), activation='relu', padding='same', strides=2)(encoder_input) encoder_output = Conv2D(128, (3, 3), activation='relu', padding='same')(encoder_output) encoder_output = Conv2D(128, (3, 3), activation='relu', padding='same', strides=2)(encoder_output) encoder_output = Conv2D(256, (3, 3), activation='relu', padding='same')(encoder_output) encoder_output = Conv2D(256, (3, 3), activation='relu', padding='same', strides=2)(encoder_output) encoder_output = Conv2D(512, (3, 3), activation='relu', padding='same')(encoder_output) encoder_output = Conv2D(512, (3, 3), activation='relu', padding='same')(encoder_output) encoder_output = Conv2D(256, (3, 3), activation='relu', padding='same')(encoder_output) # Fusion fusion_output = RepeatVector(32 * 32)(embed_input) fusion_output = Reshape(([32, 32, 1000]))(fusion_output) fusion_output = concatenate([encoder_output, fusion_output], axis=3) fusion_output = Conv2D(256, (1, 1), activation='relu', padding='same')(fusion_output) # Decoder decoder_output = Conv2D(128, (3, 3), activation='relu', padding='same')(fusion_output) decoder_output = UpSampling2D((2, 2))(decoder_output) decoder_output = Conv2D(64, (3, 3), activation='relu', padding='same')(decoder_output) decoder_output = UpSampling2D((2, 2))(decoder_output) decoder_output = Conv2D(32, (3, 3), activation='relu', padding='same')(decoder_output) decoder_output = Conv2D(16, (3, 3), activation='relu', padding='same')(decoder_output) decoder_output = Conv2D(2, (3, 3), activation='tanh', padding='same')(decoder_output) decoder_output = UpSampling2D((2, 2))(decoder_output) model = Model(inputs=[encoder_input, embed_input], outputs=decoder_output) model.compile(optimizer="adagrad", loss='mse') self.model = model
def test_include_layers_with_dict_input(self): class PLMergeDict(PLMerge): def call(self, inputs): return inputs['a'] + inputs['b'] x0 = Input(shape=(3,)) x1 = Input(shape=(3,)) l0 = PLMergeDict() y = l0({'a': x0, 'b': x1}) l1 = PLSplit() z, y = l1(y) stage = preprocessing_stage.FunctionalPreprocessingStage([x0, x1], [y, z]) stage.compile() one_array = np.ones((4, 3), dtype='float32') adapt_data = dataset_ops.Dataset.from_tensor_slices((one_array, one_array)) stage.adapt(adapt_data) self.assertEqual(l0.adapt_count, 1) self.assertEqual(l1.adapt_count, 1) self.assertLessEqual(l0.adapt_time, l1.adapt_time) # Check call y, z = stage([ array_ops.ones((4, 3), dtype='float32'), array_ops.ones((4, 3), dtype='float32') ]) self.assertAllClose(y, np.ones((4, 3), dtype='float32')) self.assertAllClose(z, np.ones((4, 3), dtype='float32') + 2.)
def test_include_layers_with_nested_input(self): class PLMergeNest(PLMerge): def call(self, inputs): a = inputs[0] b = inputs[1][0] c = inputs[1][1] return a + b + c x0 = Input(shape=(3,)) x1 = Input(shape=(3,)) x2 = Input(shape=(3,)) l0 = PLMergeNest() y = l0([x0, [x1, x2]]) stage = preprocessing_stage.FunctionalPreprocessingStage([x0, x1, x2], y) stage.compile() one_array = np.ones((4, 3), dtype='float32') adapt_data = dataset_ops.Dataset.from_tensor_slices((one_array,) * 3) stage.adapt(adapt_data) self.assertEqual(l0.adapt_count, 1) # Check call y = stage([ array_ops.ones((4, 3), dtype='float32'), array_ops.ones((4, 3), dtype='float32'), array_ops.ones((4, 3), dtype='float32') ]) self.assertAllClose(y, np.ones((4, 3), dtype='float32') + 2.)
def compute_word_embeddings(model_dir, epoch): path_to_model = to_model_path(model_dir, epoch) """ lookup model vocabulary """ vocabulary = Vocabulary.create_vocabulary_from_vocabulary_json( model_dir, "", use_nltk=False) """ prepare and load model """ vinput = Input((196, 512)) model = create_shatt_model_v2(image_features_graph=(vinput, vinput), caption_max_length=16, vocabulary_size=len(vocabulary), dropout_rate=0., start_encoding=vocabulary.get_start_symbol(), image_features_dimensions=196, embedding_size=512, hidden_size=1024, inference_mode=True, attention_graph=None, return_attention=True, use_max_sampler=True) model.load_weights(path_to_model, by_name=True) """ establish embedding model """ layer_name = "shatt_word_embeddings" layer = model.get_layer(layer_name) if layer == None: raise Exception("Cannot find layer with name " + layer_name) input_words = Input(shape=(1, ), name="embedding_callback_input_words") layer_output = layer(input_words) layer_output = Flatten(name="embedding_callback_flatten")(layer_output) embedding_model = Model(inputs=input_words, outputs=layer_output) """ write metadata.tsv """ word_sequence = vocabulary.get_word_sequence(padding_symbol="<PAD>") store_json_to(word_sequence, model_dir, lookup_filename="word_sequence.json") """ encode sequence""" encoded_word_sequence = vocabulary.get_encoded_word_sequence( include_padding=True) sequence = WordSequence(encoded_word_sequence, 64) processed_count = 0 expected_num_batches = sequence.get_num_batches() results = [] try: for words in sequence.one_shot_iterator(): words = np.expand_dims(words, axis=-1) word_embeddings = embedding_model.predict_on_batch(words) results.extend(word_embeddings) processed_count = processed_count + 1 print(">> Computing word embeddings {:d}/{:d} ({:3.0f}%)".format( processed_count, expected_num_batches, processed_count / expected_num_batches * 100), end="\r") except Exception as e: print("Exception: ", e) results = np.array(results) store_numpy_to(results, model_dir, lookup_file_name="word_embeddings.npy")
def test_raise_dimension_specified(self): with self.assertRaises(ValueError): inputs = Input(shape=(32, 32, None)) outputs = OctaveConv2D(13, kernel_size=3, ratio_out=0.0)(inputs) model = Model(inputs=inputs, outputs=outputs) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy') with self.assertRaises(ValueError): inputs_high = Input(shape=(32, 32, 3)) inputs_low = Input(shape=(32, 32, None)) outputs = OctaveConv2D(13, kernel_size=3, ratio_out=0.0)([inputs_high, inputs_low]) model = Model(inputs=[inputs_high, inputs_low], outputs=outputs) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy')
def test_preprocessing_stage_with_nested_input(self): # Test with NumPy array x0 = Input(shape=(3,)) x1 = Input(shape=(3,)) x2 = Input(shape=(3,)) l0 = PLMerge() y = l0([x0, x1]) l1 = PLMerge() y = l1([y, x2]) l2 = PLSplit() z, y = l2(y) stage = preprocessing_stage.FunctionalPreprocessingStage([x0, [x1, x2]], [y, z]) stage.compile() one_array = np.ones((4, 3), dtype='float32') stage.adapt([one_array, [one_array, one_array]]) self.assertEqual(l0.adapt_count, 1) self.assertEqual(l1.adapt_count, 1) self.assertEqual(l2.adapt_count, 1) self.assertLessEqual(l0.adapt_time, l1.adapt_time) self.assertLessEqual(l1.adapt_time, l2.adapt_time) # Check call y, z = stage([ array_ops.ones((4, 3), dtype='float32'), [ array_ops.ones((4, 3), dtype='float32'), array_ops.ones((4, 3), dtype='float32') ] ]) self.assertAllClose(y, np.ones((4, 3), dtype='float32') + 1.) self.assertAllClose(z, np.ones((4, 3), dtype='float32') + 3.) # Test with dataset adapt_data = dataset_ops.Dataset.from_tensor_slices( (one_array, (one_array, one_array))) adapt_data = adapt_data.batch(2) # 5 batches of 2 samples stage.adapt(adapt_data) self.assertEqual(l0.adapt_count, 2) self.assertEqual(l1.adapt_count, 2) self.assertEqual(l2.adapt_count, 2) self.assertLessEqual(l0.adapt_time, l1.adapt_time) self.assertLessEqual(l1.adapt_time, l2.adapt_time) # Test error with bad data with self.assertRaisesRegex(ValueError, 'requires a '): stage.adapt(None)
def init_discriminator(self): layers = [ Input(shape=( self.image_height, self.image_width, 1, )), Conv2D(16, (4, 4), kernel_initializer=self.kernel_init, use_bias=False), LeakyReLU(), BatchNormalization(), Flatten(), Dense(self.image_height * self.image_width, activation='relu', kernel_initializer=self.kernel_init), Dropout(0.5), Dense(1, activation='sigmoid') ] self.discriminator = self.make_model(layers) self.discriminator = self.compile_model(self.discriminator) self.discriminator_first_layer_to_unlock = 0 self.discriminator_last_layer_to_unlock = len(layers) #-1 ?
def __init__(self, config): super().__init__() self.num_class = config.num_cls self.num_anchor = config.num_anchors self.pooling_region = config.pooling_region self.num_rois = config.num_roi self.feature_shape = config.feature_shape self.img_input = Input(config.img_shape) self.roi_input = Input(shape=(None, 4)) self.feature_input = Input(self.feature_shape) self.base_fun = config.base_net self.num_anchors = config.num_anchors self.num_cls = config.num_cls
def test_adapt_preprocessing_stage_with_dict_output(self): x = Input(shape=(3,), name='x') l0 = PLSplit() y0, y1 = l0(x) l1 = PLSplit() z0, z1 = l1(y0) stage = preprocessing_stage.FunctionalPreprocessingStage({'x': x}, { 'y1': y1, 'z1': z1, 'y0': y0, 'z0': z0 }) stage.compile() # Test with NumPy array one_array = np.ones((4, 3), dtype='float32') adapt_data = {'x': one_array} stage.adapt(adapt_data) self.assertEqual(l0.adapt_count, 1) self.assertEqual(l1.adapt_count, 1) self.assertLessEqual(l0.adapt_time, l1.adapt_time) # Check call outputs = stage({'x': array_ops.constant(one_array)}) self.assertEqual(set(outputs.keys()), {'y0', 'y1', 'z0', 'z1'}) self.assertAllClose(outputs['y0'], np.ones((4, 3), dtype='float32') + 1.) self.assertAllClose(outputs['y1'], np.ones((4, 3), dtype='float32') - 1.) self.assertAllClose(outputs['z0'], np.ones((4, 3), dtype='float32') + 2.) self.assertAllClose(outputs['z1'], np.ones((4, 3), dtype='float32'))
def model(self) -> Model: if self._lazy_model is None: input = Input(shape=self.input_size) layer = input kernel_size = (3, 3) depth = int(self.input_size[1] / 8) for i in range(depth): filters = (2**i) * self.filter_root params = {'kernel_size': kernel_size, 'padding': 'same'} layer = Conv2D(filters=filters, name=f'CV_{i}', **params)(layer) layer = Activation(activation=self.activation, name=f'Act_{i}')(layer) layer = Flatten()(layer) lung_pixel = Input(shape=(1, )) layer = Concatenate(axis=1)([layer, lung_pixel]) layer = Dense(filters=self.filter_root, activation='relu')(layer) self._lazy_model = Model(input, layer, name="Volume") return self._lazy_model
def _clone_sequential_model(model, input_tensors=None): """Clone a `Sequential` model instance. Model cloning is similar to calling a model on new inputs, except that it creates new layers (and thus new weights) instead of sharing the weights of the existing layers. Arguments: model: Instance of `Sequential`. input_tensors: optional list of input tensors to build the model upon. If not provided, placeholders will be created. Returns: An instance of `Sequential` reproducing the behavior of the original model, on top of new inputs tensors, using newly instantiated weights. Raises: ValueError: in case of invalid `model` argument value. """ if not isinstance(model, Sequential): raise ValueError( 'Expected `model` argument ' 'to be a `Sequential` model instance, ' 'but got:', model) def clone(layer): return layer.__class__.from_config(layer.get_config()) layers = [clone(layer) for layer in model.layers] if input_tensors is None: return Sequential(layers=layers, name=model.name) else: if len(generic_utils.to_list(input_tensors)) != 1: raise ValueError('To clone a `Sequential` model, we expect ' ' at most one tensor ' 'as part of `input_tensors`.') if isinstance(input_tensors, tuple): input_tensors = list(input_tensors) x = generic_utils.to_list(input_tensors)[0] if K.is_keras_tensor(x): origin_layer = x._keras_history[0] if isinstance(origin_layer, InputLayer): return Sequential(layers=[origin_layer] + layers, name=model.name) else: raise ValueError('Cannot clone a `Sequential` model on top ' 'of a tensor that comes from a Keras layer ' 'other than an `InputLayer`. ' 'Use the functional API instead.') input_tensor = Input(tensor=x, name='input_wrapper_for_' + str(x.name)) input_layer = input_tensor._keras_history[0] return Sequential(layers=[input_layer] + layers, name=model.name)
def test_fit_octave(self): inputs = Input(shape=(32, 3)) high, low = OctaveConv1D(13, kernel_size=3, octave=4)(inputs) high, low = MaxPool1D()(high), MaxPool1D()(low) conv = OctaveConv1D(5, kernel_size=3, octave=4, ratio_out=0.0)([high, low]) flatten = Flatten()(conv) outputs = Dense(units=2, activation='softmax')(flatten) model = Model(inputs=inputs, outputs=outputs) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy') model.summary(line_length=200) self._test_fit(model)
def L_model(input_shape, nb_actions): print('Creating L_Model...') action_input = Input(shape=(nb_actions, ), name='action_input') observation_input = Input(input_shape, name='observation_input') x = Concatenate()([action_input, Flatten()(observation_input)]) # x = concatenate([Flatten()(observation_input),action_input]) # x =Reshape((time_frame,nb_observation+nb_actions))(x) # x =LSTM(nb_observation*5,activation='sigmoid',return_sequences=True,stateful=False)(x) x = Dense(nb_observation, activation='tanh')(x) # x =LSTM(nb_observation*5,activation='sigmoid',return_sequences=True,stateful=False)(x) x = Dense(nb_observation, activation='sigmoid')(x) x = Dense(nb_observation, activation='sigmoid')(x) x = Dense(nb_observation, activation='sigmoid')(x) x = Dense(nb_observation, activation='sigmoid')(x) # x =LSTM(nb_observation*5,activation='sigmoid',return_sequences=False,stateful=False)(x) x = Dense((nb_actions * nb_actions + nb_actions) // 2, activation='linear')( x) # ((nb_actions * nb_actions + nb_actions) / 2)) L_model = Model(inputs=[action_input, observation_input], outputs=x) # print(L_model.summary()) return L_model
def test_fit_channels_first(self): inputs = Input(shape=(3, 32, 32)) high, low = OctaveConv2D(13, kernel_size=3, data_format='channels_first')(inputs) high, low = MaxPool2D(data_format='channels_first')(high), MaxPool2D(data_format='channels_first')(low) high, low = OctaveConv2D(7, kernel_size=3, data_format='channels_first')([high, low]) high, low = MaxPool2D(data_format='channels_first')(high), MaxPool2D(data_format='channels_first')(low) conv = OctaveConv2D(5, kernel_size=3, ratio_out=0.0, data_format='channels_first')([high, low]) flatten = Flatten()(conv) outputs = Dense(units=2, activation='softmax')(flatten) model = Model(inputs=inputs, outputs=outputs) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy') model.summary(line_length=200) self._test_fit(model, data_format='channels_first')
def test_make_dual_lambda(self): inputs = Input(shape=(32, 32, 3)) conv = OctaveConv2D(13, kernel_size=3)(inputs) pool = OctaveConvDual()(conv, lambda: MaxPool2D()) conv = OctaveConv2D(7, kernel_size=3)(pool) pool = OctaveConvDual()(conv, lambda: MaxPool2D()) conv = OctaveConv2D(5, kernel_size=3, ratio_out=0.0)(pool) flatten = Flatten()(conv) outputs = Dense(units=2, activation='softmax')(flatten) model = Model(inputs=inputs, outputs=outputs) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy') model.summary(line_length=200) self._test_fit(model)
def test_mixing_preprocessing_and_regular_layers(self): x0 = Input(shape=(10, 10, 3)) x1 = Input(shape=(10, 10, 3)) x2 = Input(shape=(10, 10, 3)) y0 = merge.Add()([x0, x1]) y1 = image_preprocessing.CenterCrop(8, 8)(x2) y1 = convolutional.ZeroPadding2D(padding=1)(y1) z = merge.Add()([y0, y1]) z = normalization.Normalization()(z) z = convolutional.Conv2D(4, 3)(z) stage = preprocessing_stage.FunctionalPreprocessingStage([x0, x1, x2], z) data = [ np.ones((12, 10, 10, 3), dtype='float32'), np.ones((12, 10, 10, 3), dtype='float32'), np.ones((12, 10, 10, 3), dtype='float32') ] stage.adapt(data) _ = stage(data) stage.compile('rmsprop', 'mse') with self.assertRaisesRegex(ValueError, 'Preprocessing stage'): stage.fit(data, np.ones((12, 8, 8, 4))) ds_x0 = dataset_ops.Dataset.from_tensor_slices(np.ones((12, 10, 10, 3))) ds_x1 = dataset_ops.Dataset.from_tensor_slices(np.ones((12, 10, 10, 3))) ds_x2 = dataset_ops.Dataset.from_tensor_slices(np.ones((12, 10, 10, 3))) ds_x = dataset_ops.Dataset.zip((ds_x0, ds_x1, ds_x2)) ds_y = dataset_ops.Dataset.from_tensor_slices(np.ones((12, 8, 8, 4))) dataset = dataset_ops.Dataset.zip((ds_x, ds_y)).batch(4) with self.assertRaisesRegex(ValueError, 'Preprocessing stage'): stage.fit(dataset) _ = stage.evaluate(data, np.ones((12, 8, 8, 4))) _ = stage.predict(data)
def build(self, input_shape=None): if input_shape and not self.inputs: batch_shape = tuple(input_shape) dtype = K.floatx() x = Input(batch_shape=batch_shape, dtype=dtype, name=self.name + '_input') self.inputs = [x] for layer in self._layers: x = layer(x) self.outputs = [x] if self.inputs: self._init_graph_network(self.inputs, self.outputs, name=self.name) self.built = True self._track_layers(self._layers)
def build(self, input_shape=None): if input_shape and not self.inputs: batch_shape = tuple(input_shape) dtype = K.floatx() x = Input( batch_shape=batch_shape, dtype=dtype, name=self.name + '_input') self.inputs = [x] for layer in self._layers: x = layer(x) self.outputs = [x] # Make sure that the model's input shape will be preserved during # serialization. if self._layers: self._layers[0]._batch_input_shape = batch_shape if self.inputs: self._init_graph_network(self.inputs, self.outputs, name=self.name) self.built = True if self._layers: self._track_layers(self._layers)
def test_adapt_preprocessing_stage_with_single_input_output(self): x = Input(shape=(3,)) l0 = PL() y = l0(x) l1 = PL() z = l1(y) stage = preprocessing_stage.FunctionalPreprocessingStage(x, z) stage.compile() # Test with NumPy array one_array = np.ones((4, 3), dtype='float32') stage.adapt(one_array) self.assertEqual(l0.adapt_count, 1) self.assertEqual(l1.adapt_count, 1) self.assertLessEqual(l0.adapt_time, l1.adapt_time) # Check call z = stage(array_ops.ones((4, 3), dtype='float32')) self.assertAllClose(z, np.ones((4, 3), dtype='float32') + 2.) # Test with dataset adapt_data = dataset_ops.Dataset.from_tensor_slices(one_array) adapt_data = adapt_data.batch(2) # 5 batches of 2 samples stage.adapt(adapt_data) self.assertEqual(l0.adapt_count, 2) self.assertEqual(l1.adapt_count, 2) self.assertLessEqual(l0.adapt_time, l1.adapt_time) # Test error with bad data with self.assertRaisesRegex(ValueError, 'requires a '): stage.adapt(None) # Disallow calling fit with self.assertRaisesRegex(ValueError, 'Preprocessing stage'): stage.fit(None)
def get_model(data, params): input = Input(shape=data.x_train.shape[1:]) x = input for element in params["network"]: if element[0] == "C2D": x = Conv2D(filters=element[1], kernel_size=element[2], padding='same')(x) if element[3]: x = BatchNormalization()(x) elif element[0] == "Dense": x = Dense(units=element[1])(x) elif element[0] == "A": x = Activation(element[1])(x) elif element[0] == "MaxPool2D": x = MaxPool2D()(x) elif element[0] == "Flatten": x = Flatten()(x) else: print("Invalid element: " + element[0]) # There has to be a Dense layer at the end x = Dense(units=data.num_classes)(x) y_pred = Activation("softmax")(x) # Build the model model = Model(inputs=[input], outputs=[y_pred]) model.compile( loss="categorical_crossentropy", optimizer=params["optimizer"](learning_rate=params["learning_rate"]), metrics=["accuracy"]) return model
def _clone_sequential_model(model, input_tensors=None, layer_fn=_clone_layer): """Clone a `Sequential` model instance. Model cloning is similar to calling a model on new inputs, except that it creates new layers (and thus new weights) instead of sharing the weights of the existing layers. Arguments: model: Instance of `Sequential`. input_tensors: optional list of input tensors to build the model upon. If not provided, placeholders will be created. layer_fn: callable to be applied on non-input layers in the model. By default it clones the layer. Another example is to preserve the layer to share the weights. This is required when we create a per-replica copy of the model with distribution strategy; we want the weights to be shared but still feed inputs separately so we create new input layers. Returns: An instance of `Sequential` reproducing the behavior of the original model, on top of new inputs tensors, using newly instantiated weights. Raises: ValueError: in case of invalid `model` argument value or `layer_fn` argument value. """ if not isinstance(model, Sequential): raise ValueError( 'Expected `model` argument ' 'to be a `Sequential` model instance, ' 'but got:', model) if not callable(layer_fn): raise ValueError('Expected `layer_fn` argument to be a callable.') layers = [] # Layers needed to compute the model's outputs. layer_map = {} # Use model._layers to ensure that all layers are cloned. The model's layers # property will exclude the initial InputLayer (if it exists) in the model, # resulting in a different Sequential model structure. for layer in model._layers: if isinstance(layer, InputLayer) and input_tensors is not None: # If input tensors are provided, the original model's InputLayer is # overwritten with a different InputLayer. continue cloned_layer = (_clone_layer(layer) if isinstance(layer, InputLayer) else layer_fn(layer)) layers.append(cloned_layer) layer_map[layer] = cloned_layer layers, ancillary_layers = _remove_ancillary_layers( model, layer_map, layers) if input_tensors is None: cloned_model = Sequential(layers=layers, name=model.name) elif len(generic_utils.to_list(input_tensors)) != 1: raise ValueError('To clone a `Sequential` model, we expect ' ' at most one tensor ' 'as part of `input_tensors`.') else: # Overwrite the original model's input layer. if isinstance(input_tensors, tuple): input_tensors = list(input_tensors) x = generic_utils.to_list(input_tensors)[0] if K.is_keras_tensor(x): origin_layer = x._keras_history.layer if isinstance(origin_layer, InputLayer): cloned_model = Sequential(layers=[origin_layer] + layers, name=model.name) else: raise ValueError('Cannot clone a `Sequential` model on top ' 'of a tensor that comes from a Keras layer ' 'other than an `InputLayer`. ' 'Use the functional API instead.') else: input_tensor = Input(tensor=x, name='input_wrapper_for_' + str(x.name)) input_layer = input_tensor._keras_history.layer cloned_model = Sequential(layers=[input_layer] + layers, name=model.name) if not ancillary_layers: return cloned_model tensor_map = {} # Maps tensors from `model` to those in `cloned_model`. for depth, cloned_nodes in cloned_model._nodes_by_depth.items(): nodes = model._nodes_by_depth[depth] # This should be safe in a Sequential model. In an arbitrary network, you # need to sort using the outbound layer of the node as a key. for cloned_node, node in zip(cloned_nodes, nodes): if isinstance(cloned_node.output_tensors, list): for j, output_tensor in enumerate(cloned_node.output_tensors): tensor_map[node.output_tensors[j]] = output_tensor else: tensor_map[node.output_tensors] = cloned_node.output_tensors # Ancillary nodes have negative depth. new_nodes = _make_new_nodes( { depth: nodes for depth, nodes in model._nodes_by_depth.items() if depth < 0 }, layer_fn, layer_map, tensor_map) _insert_ancillary_layers(cloned_model, ancillary_layers, model.metrics_names, new_nodes) return cloned_model
def _clone_functional_model(model, input_tensors=None, layer_fn=_clone_layer): """Clone a functional `Model` instance. Model cloning is similar to calling a model on new inputs, except that it creates new layers (and thus new weights) instead of sharing the weights of the existing layers. Input layers are always cloned. Arguments: model: Instance of `Model`. input_tensors: optional list of input tensors to build the model upon. If not provided, placeholders will be created. layer_fn: callable to be applied on non-input layers in the model. By default it clones the layer. Another example is to preserve the layer to share the weights. This is required when we create a per-replica copy of the model with distribution strategy; we want the weights to be shared but still feed inputs separately so we create new input layers. Returns: An instance of `Model` reproducing the behavior of the original model, on top of new inputs tensors, using newly instantiated weights. Raises: ValueError: in case of invalid `model` argument value or `layer_fn` argument value. """ if not isinstance(model, Model): raise ValueError('Expected `model` argument ' 'to be a `Model` instance, got ', model) if isinstance(model, Sequential): raise ValueError('Expected `model` argument ' 'to be a functional `Model` instance, ' 'got a `Sequential` instance instead:', model) if not model._is_graph_network: raise ValueError('Expected `model` argument ' 'to be a functional `Model` instance, ' 'but got a subclass model instead.') layer_map = {} # Cache for created layers. tensor_map = {} # Map {reference_tensor: corresponding_tensor} if input_tensors is None: # Create placeholders to build the model on top of. input_tensors = [] for layer in model._input_layers: input_tensor = Input( batch_shape=layer._batch_input_shape, dtype=layer.dtype, sparse=layer.sparse, name=layer.name) input_tensors.append(input_tensor) # Cache newly created input layer. newly_created_input_layer = input_tensor._keras_history[0] layer_map[layer] = newly_created_input_layer else: # Make sure that all input tensors come from a Keras layer. # If tensor comes from an input layer: cache the input layer. input_tensors = nest.flatten(input_tensors) input_tensors_ = [] for i in range(len(input_tensors)): input_tensor = input_tensors[i] if not K.is_keras_tensor(input_tensor): original_input_layer = model._input_layers[i] name = original_input_layer.name input_tensor = Input(tensor=input_tensor, name='input_wrapper_for_' + name) input_tensors_.append(input_tensor) # Cache newly created input layer. newly_created_input_layer = input_tensor._keras_history[0] layer_map[original_input_layer] = newly_created_input_layer else: input_tensors_.append(input_tensor) input_tensors = input_tensors_ for x, y in zip(model.inputs, input_tensors): tensor_map[x] = y if not callable(layer_fn): raise ValueError('Expected `layer_fn` argument to be a callable.') new_nodes = set() # Iterated over every node in the reference model, in depth order. 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: # Recover the corresponding layer. layer = node.outbound_layer # Get or create layer. if layer not in layer_map: new_layer = layer_fn(layer) layer_map[layer] = new_layer layer = new_layer else: # Reuse previously cloned layer. layer = layer_map[layer] # Don't call InputLayer multiple times. if isinstance(layer, InputLayer): continue # If all previous input tensors are available in tensor_map, # then call node.inbound_layer on them. if all( tensor in tensor_map for tensor in nest.flatten(node.input_tensors)): computed_tensors = nest.map_structure(lambda t: tensor_map[t], node.input_tensors) # Call layer. kwargs = node.arguments or {} output_tensors = layer(computed_tensors, **kwargs) # Thread-safe way to keep track of what node was created. first_output_tensor = nest.flatten(output_tensors)[0] new_nodes.add( layer._inbound_nodes[first_output_tensor._keras_history[1]]) for x, y in zip( nest.flatten(node.output_tensors), nest.flatten(output_tensors)): tensor_map[x] = y # Check that we did compute the model outputs, # then instantiate a new model from inputs and outputs. output_tensors = [] for x in model.outputs: assert x in tensor_map, 'Could not compute output ' + str(x) output_tensors.append(tensor_map[x]) input_tensors = nest.pack_sequence_as(model._nested_inputs, input_tensors) output_tensors = nest.pack_sequence_as(model._nested_outputs, output_tensors) model = Model(input_tensors, output_tensors, name=model.name) # Layers not directly tied to outputs of the Model, such as loss layers # created in `add_loss`. ancillary_layers = [ layer for layer in layer_map.values() if layer not in model.layers ] if ancillary_layers: nodes = set( nest.flatten([layer._inbound_nodes for layer in ancillary_layers])) relevant_nodes = list(nodes.intersection(new_nodes)) model._insert_layers(ancillary_layers, relevant_nodes=relevant_nodes) return model
def test_adapt_preprocessing_stage_with_dict_input(self): x0 = Input(shape=(3,), name='x0') x1 = Input(shape=(4,), name='x1') x2 = Input(shape=(3, 5), name='x2') # dimension will mismatch if x1 incorrectly placed. x1_sum = core.Lambda( lambda x: math_ops.reduce_sum(x, axis=-1, keepdims=True))( x1) x2_sum = core.Lambda(lambda x: math_ops.reduce_sum(x, axis=-1))(x2) l0 = PLMerge() y = l0([x0, x1_sum]) l1 = PLMerge() y = l1([y, x2_sum]) l2 = PLSplit() z, y = l2(y) stage = preprocessing_stage.FunctionalPreprocessingStage( { 'x2': x2, 'x0': x0, 'x1': x1 }, [y, z]) stage.compile() # Test with dict of NumPy array one_array0 = np.ones((4, 3), dtype='float32') one_array1 = np.ones((4, 4), dtype='float32') one_array2 = np.ones((4, 3, 5), dtype='float32') adapt_data = {'x1': one_array1, 'x0': one_array0, 'x2': one_array2} stage.adapt(adapt_data) self.assertEqual(l0.adapt_count, 1) self.assertEqual(l1.adapt_count, 1) self.assertEqual(l2.adapt_count, 1) self.assertLessEqual(l0.adapt_time, l1.adapt_time) self.assertLessEqual(l1.adapt_time, l2.adapt_time) # Check call y, z = stage({ 'x1': array_ops.constant(one_array1), 'x2': array_ops.constant(one_array2), 'x0': array_ops.constant(one_array0) }) self.assertAllClose(y, np.zeros((4, 3), dtype='float32') + 9.) self.assertAllClose(z, np.zeros((4, 3), dtype='float32') + 11.) # Test with list of NumPy array adapt_data = [one_array0, one_array1, one_array2] stage.adapt(adapt_data) self.assertEqual(l0.adapt_count, 2) self.assertEqual(l1.adapt_count, 2) self.assertEqual(l2.adapt_count, 2) self.assertLessEqual(l0.adapt_time, l1.adapt_time) self.assertLessEqual(l1.adapt_time, l2.adapt_time) # Test with flattened dataset adapt_data = dataset_ops.Dataset.from_tensor_slices( (one_array0, one_array1, one_array2)) adapt_data = adapt_data.batch(2) # 5 batches of 2 samples stage.adapt(adapt_data) self.assertEqual(l0.adapt_count, 3) self.assertEqual(l1.adapt_count, 3) self.assertEqual(l2.adapt_count, 3) self.assertLessEqual(l0.adapt_time, l1.adapt_time) self.assertLessEqual(l1.adapt_time, l2.adapt_time) # Test with dataset in dict shape adapt_data = dataset_ops.Dataset.from_tensor_slices({ 'x0': one_array0, 'x2': one_array2, 'x1': one_array1 }) adapt_data = adapt_data.batch(2) # 5 batches of 2 samples stage.adapt(adapt_data) self.assertEqual(l0.adapt_count, 4) self.assertEqual(l1.adapt_count, 4) self.assertEqual(l2.adapt_count, 4) self.assertLessEqual(l0.adapt_time, l1.adapt_time) self.assertLessEqual(l1.adapt_time, l2.adapt_time) # Test error with bad data with self.assertRaisesRegex(ValueError, 'requires a '): stage.adapt(None)
def _clone_functional_model(model, input_tensors=None, layer_fn=_clone_layer): """Clone a functional `Model` instance. Model cloning is similar to calling a model on new inputs, except that it creates new layers (and thus new weights) instead of sharing the weights of the existing layers. Input layers are always cloned. Arguments: model: Instance of `Model`. input_tensors: optional list of input tensors to build the model upon. If not provided, placeholders will be created. layer_fn: callable to be applied on non-input layers in the model. By default it clones the layer. Another example is to preserve the layer to share the weights. This is required when we create a per-replica copy of the model with distribution strategy; we want the weights to be shared but still feed inputs separately so we create new input layers. Returns: An instance of `Model` reproducing the behavior of the original model, on top of new inputs tensors, using newly instantiated weights. Raises: ValueError: in case of invalid `model` argument value or `layer_fn` argument value. """ if not isinstance(model, Model): raise ValueError( 'Expected `model` argument ' 'to be a `Model` instance, got ', model) if isinstance(model, Sequential): raise ValueError( 'Expected `model` argument ' 'to be a functional `Model` instance, ' 'got a `Sequential` instance instead:', model) if not model._is_graph_network: raise ValueError('Expected `model` argument ' 'to be a functional `Model` instance, ' 'but got a subclass model instead.') new_input_layers = {} # Cache for created layers. if input_tensors is not None: # Make sure that all input tensors come from a Keras layer. input_tensors = nest.flatten(input_tensors) for i, input_tensor in enumerate(input_tensors): original_input_layer = model._input_layers[i] # Cache input layer. Create a new layer if the tensor is originally not # from a Keras layer. if not K.is_keras_tensor(input_tensor): name = original_input_layer.name input_tensor = Input(tensor=input_tensor, name='input_wrapper_for_' + name) newly_created_input_layer = input_tensor._keras_history.layer new_input_layers[ original_input_layer] = newly_created_input_layer else: new_input_layers[original_input_layer] = original_input_layer if not callable(layer_fn): raise ValueError('Expected `layer_fn` argument to be a callable.') model_config, created_layers = _clone_layers_and_model_config( model, new_input_layers, layer_fn) # Reconstruct model from the config, using the cloned layers. input_tensors, output_tensors, created_layers = ( network.reconstruct_from_config(model_config, created_layers=created_layers)) metrics_names = model.metrics_names model = Model(input_tensors, output_tensors, name=model.name) # Layers not directly tied to outputs of the Model, such as loss layers # created in `add_loss` and `add_metric`. ancillary_layers = [ layer for layer in created_layers.values() if layer not in model.layers ] if ancillary_layers: new_nodes = nest.flatten([ layer.inbound_nodes[1:] if network._should_skip_first_node(layer) else layer.inbound_nodes for layer in created_layers.values() ]) _insert_ancillary_layers(model, ancillary_layers, metrics_names, new_nodes) return model
def _clone_sequential_model(model, input_tensors=None, share_weights=False): """Clone a `Sequential` model instance. Model cloning is similar to calling a model on new inputs, except that it creates new layers (and thus new weights) instead of sharing the weights of the existing layers. Arguments: model: Instance of `Sequential`. input_tensors: optional list of input tensors to build the model upon. If not provided, placeholders will be created. share_weights: flag to enable sharing of non-input layers between the cloned and original model. Note this still clones the input layers. This is required when we create a per-replica copy of the model with distribution strategy; we want the weights to be shared but still feed inputs separately so we create new input layers. Returns: An instance of `Sequential` reproducing the behavior of the original model, on top of new inputs tensors, using newly instantiated weights. Raises: ValueError: in case of invalid `model` argument value. """ if not isinstance(model, Sequential): raise ValueError( 'Expected `model` argument ' 'to be a `Sequential` model instance, ' 'but got:', model) # Use model._layers to ensure that all layers are cloned. The model's layers # property will exclude the initial InputLayer (if it exists) in the model, # resulting in a different Sequential model structure. if input_tensors is None: if share_weights: # In preserve weights case we still want the input layers to be cloned. layers = [] for layer in model._layers: if isinstance(layer, InputLayer): layers.append(_clone_layer(layer)) else: layers.append(layer) else: layers = [_clone_layer(layer) for layer in model._layers] return Sequential(layers=layers, name=model.name) else: # If input tensors are provided, the original model's InputLayer is # overwritten with a different InputLayer. layers = [ layer for layer in model._layers if not isinstance(layer, InputLayer) ] if not share_weights: layers = [_clone_layer(layer) for layer in layers] if len(generic_utils.to_list(input_tensors)) != 1: raise ValueError('To clone a `Sequential` model, we expect ' ' at most one tensor ' 'as part of `input_tensors`.') if isinstance(input_tensors, tuple): input_tensors = list(input_tensors) x = generic_utils.to_list(input_tensors)[0] if K.is_keras_tensor(x): origin_layer = x._keras_history[0] if isinstance(origin_layer, InputLayer): return Sequential(layers=[origin_layer] + layers, name=model.name) else: raise ValueError('Cannot clone a `Sequential` model on top ' 'of a tensor that comes from a Keras layer ' 'other than an `InputLayer`. ' 'Use the functional API instead.') input_tensor = Input(tensor=x, name='input_wrapper_for_' + str(x.name)) input_layer = input_tensor._keras_history[0] return Sequential(layers=[input_layer] + layers, name=model.name)
def _clone_functional_model(model, input_tensors=None): """Clone a functional `Model` instance. Model cloning is similar to calling a model on new inputs, except that it creates new layers (and thus new weights) instead of sharing the weights of the existing layers. Arguments: model: Instance of `Model`. input_tensors: optional list of input tensors to build the model upon. If not provided, placeholders will be created. Returns: An instance of `Model` reproducing the behavior of the original model, on top of new inputs tensors, using newly instantiated weights. Raises: ValueError: in case of invalid `model` argument value. """ if not isinstance(model, Model): raise ValueError( 'Expected `model` argument ' 'to be a `Model` instance, got ', model) if isinstance(model, Sequential): raise ValueError( 'Expected `model` argument ' 'to be a functional `Model` instance, ' 'got a `Sequential` instance instead:', model) layer_map = {} # Cache for created layers. tensor_map = {} # Map {reference_tensor: corresponding_tensor} if input_tensors is None: # Create placeholders to build the model on top of. input_layers = [] input_tensors = [] for layer in model._input_layers: input_tensor = Input(batch_shape=layer._batch_input_shape, dtype=layer.dtype, sparse=layer.sparse, name=layer.name) input_tensors.append(input_tensor) # Cache newly created input layer. newly_created_input_layer = input_tensor._keras_history[0] layer_map[layer] = newly_created_input_layer for original_input_layer, cloned_input_layer in zip( model._input_layers, input_layers): layer_map[original_input_layer] = cloned_input_layer else: # Make sure that all input tensors come from a Keras layer. # If tensor comes from an input layer: cache the input layer. if isinstance(input_tensors, tuple): input_tensors = list(input_tensors) input_tensors = generic_utils.to_list(input_tensors) input_tensors_ = [] for i in range(len(input_tensors)): input_tensor = input_tensors[i] if not K.is_keras_tensor(input_tensor): original_input_layer = model._input_layers[i] name = original_input_layer.name input_tensor = Input(tensor=input_tensor, name='input_wrapper_for_' + name) input_tensors_.append(input_tensor) # Cache newly created input layer. newly_created_input_layer = input_tensor._keras_history[0] layer_map[original_input_layer] = newly_created_input_layer else: input_tensors_.append(input_tensor) input_tensors = input_tensors_ for x, y in zip(model.inputs, input_tensors): tensor_map[x] = y # Iterated over every node in the reference model, in depth order. 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: # Recover the corresponding layer. layer = node.outbound_layer # Get or create layer. if layer not in layer_map: # Clone layer. new_layer = layer.__class__.from_config(layer.get_config()) layer_map[layer] = new_layer layer = new_layer else: # Reuse previously cloned layer. layer = layer_map[layer] # Don't call InputLayer multiple times. if isinstance(layer, InputLayer): continue # Gather inputs to call the new layer. reference_input_tensors = node.input_tensors reference_output_tensors = node.output_tensors # If all previous input tensors are available in tensor_map, # then call node.inbound_layer on them. computed_tensors = [] for x in reference_input_tensors: if x in tensor_map: computed_tensors.append(tensor_map[x]) if len(computed_tensors) == len(reference_input_tensors): # Call layer. if node.arguments: kwargs = node.arguments else: kwargs = {} if len(computed_tensors) == 1: computed_tensor = computed_tensors[0] output_tensors = generic_utils.to_list( layer(computed_tensor, **kwargs)) computed_tensors = [computed_tensor] else: computed_tensors = computed_tensors output_tensors = generic_utils.to_list( layer(computed_tensors, **kwargs)) for x, y in zip(reference_output_tensors, output_tensors): tensor_map[x] = y # Check that we did compute the model outputs, # then instantiate a new model from inputs and outputs. output_tensors = [] for x in model.outputs: assert x in tensor_map, 'Could not compute output ' + str(x) output_tensors.append(tensor_map[x]) return Model(input_tensors, output_tensors, name=model.name)
def create_model(noise=True, first_kernel_size=(7, 7), n_filters=64, n_covul_layers=5, activation='swish', dense_neurons=1024, dropout=0.5, lr=0.0001): kernel = (3, 3) n_classes = len(classes) input_layer = Input(shape=(300, 300, 3)) if noise: input_layer = GaussianNoise(0.1)(input_layer) model = BatchNormalization(axis=[1, 2])(input_layer) model = Conv2D(filters=n_filters, kernel_size=first_kernel_size, activation=activation)(model) model = BatchNormalization(axis=[1, 2])(model) model = MaxPooling2D((2, 2))(model) for i in range(2, n_covul_layers): model = Conv2D(filters=n_filters * i, kernel_size=kernel, activation=activation)(model) model = Conv2D(filters=n_filters * i, kernel_size=kernel, activation=activation, padding='same')(model) model = BatchNormalization(axis=[1, 2])(model) model = MaxPooling2D((2, 2))(model) model = Conv2D(filters=n_filters * (n_covul_layers + 1), kernel_size=kernel, activation=activation, padding='same')(model) model = Conv2D(filters=n_filters * (n_covul_layers + 1), kernel_size=kernel, activation=activation, padding='same')(model) model = BatchNormalization(axis=[1, 2])(model) model = MaxPooling2D((2, 2))(model) model = Flatten()(model) model = Dense(dense_neurons, activation=activation)(model) model = BatchNormalization()(model) model = Dropout(dropout)(model) model = Dense(dense_neurons / 2, activation=activation)(model) model = BatchNormalization()(model) model = Dropout(dropout)(model) output = Dense(n_classes, activation="softmax")(model) model = Model(input_layer, output) model.compile(loss="sparse_categorical_crossentropy", optimizer=keras.optimizers.Adam(lr=lr), metrics=["accuracy"]) return model
def _clone_functional_model(model, input_tensors=None, share_weights=False): """Clone a functional `Model` instance. Model cloning is similar to calling a model on new inputs, except that it creates new layers (and thus new weights) instead of sharing the weights of the existing layers. Arguments: model: Instance of `Model`. input_tensors: optional list of input tensors to build the model upon. If not provided, placeholders will be created. share_weights: flag to enable sharing of non-input layers between the cloned and original model. Note this still clones the input layers. This is required when we create a per-replica copy of the model with distribution strategy; we want the weights to be shared but still feed inputs separately so we create new input layers. Returns: An instance of `Model` reproducing the behavior of the original model, on top of new inputs tensors, using newly instantiated weights. Raises: ValueError: in case of invalid `model` argument value. """ if not isinstance(model, Model): raise ValueError( 'Expected `model` argument ' 'to be a `Model` instance, got ', model) if isinstance(model, Sequential): raise ValueError( 'Expected `model` argument ' 'to be a functional `Model` instance, ' 'got a `Sequential` instance instead:', model) layer_map = {} # Cache for created layers. tensor_map = {} # Map {reference_tensor: corresponding_tensor} if input_tensors is None: # Create placeholders to build the model on top of. input_layers = [] input_tensors = [] for layer in model._input_layers: input_tensor = Input(batch_shape=layer._batch_input_shape, dtype=layer.dtype, sparse=layer.sparse, name=layer.name) input_tensors.append(input_tensor) # Cache newly created input layer. newly_created_input_layer = input_tensor._keras_history[0] layer_map[layer] = newly_created_input_layer for original_input_layer, cloned_input_layer in zip( model._input_layers, input_layers): layer_map[original_input_layer] = cloned_input_layer else: # Make sure that all input tensors come from a Keras layer. # If tensor comes from an input layer: cache the input layer. input_tensors = nest.flatten(input_tensors) input_tensors_ = [] for i in range(len(input_tensors)): input_tensor = input_tensors[i] if not K.is_keras_tensor(input_tensor): original_input_layer = model._input_layers[i] name = original_input_layer.name input_tensor = Input(tensor=input_tensor, name='input_wrapper_for_' + name) input_tensors_.append(input_tensor) # Cache newly created input layer. newly_created_input_layer = input_tensor._keras_history[0] layer_map[original_input_layer] = newly_created_input_layer else: input_tensors_.append(input_tensor) input_tensors = input_tensors_ for x, y in zip(model.inputs, input_tensors): tensor_map[x] = y # Iterated over every node in the reference model, in depth order. 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: # Recover the corresponding layer. layer = node.outbound_layer # Get or create layer. if layer not in layer_map: if not share_weights: # Clone layer. new_layer = _clone_layer(layer) layer_map[layer] = new_layer layer = new_layer else: # Reuse previously cloned layer. layer = layer_map[layer] # Don't call InputLayer multiple times. if isinstance(layer, InputLayer): continue # If all previous input tensors are available in tensor_map, # then call node.inbound_layer on them. if all(tensor in tensor_map for tensor in nest.flatten(node.input_tensors)): computed_tensors = nest.map_structure(lambda t: tensor_map[t], node.input_tensors) # Call layer. kwargs = node.arguments or {} output_tensors = layer(computed_tensors, **kwargs) for x, y in zip(nest.flatten(node.output_tensors), nest.flatten(output_tensors)): tensor_map[x] = y # Check that we did compute the model outputs, # then instantiate a new model from inputs and outputs. output_tensors = [] for x in model.outputs: assert x in tensor_map, 'Could not compute output ' + str(x) output_tensors.append(tensor_map[x]) input_tensors = nest.pack_sequence_as(model._nested_inputs, input_tensors) output_tensors = nest.pack_sequence_as(model._nested_outputs, output_tensors) return Model(input_tensors, output_tensors, name=model.name)
def _clone_functional_model(model, input_tensors=None, layer_fn=_clone_layer): """Clone a functional `Model` instance. Model cloning is similar to calling a model on new inputs, except that it creates new layers (and thus new weights) instead of sharing the weights of the existing layers. Input layers are always cloned. Arguments: model: Instance of `Model`. input_tensors: optional list of input tensors to build the model upon. If not provided, placeholders will be created. layer_fn: callable to be applied on non-input layers in the model. By default it clones the layer. Another example is to preserve the layer to share the weights. This is required when we create a per-replica copy of the model with distribution strategy; we want the weights to be shared but still feed inputs separately so we create new input layers. Returns: An instance of `Model` reproducing the behavior of the original model, on top of new inputs tensors, using newly instantiated weights. Raises: ValueError: in case of invalid `model` argument value or `layer_fn` argument value. """ if not isinstance(model, Model): raise ValueError('Expected `model` argument ' 'to be a `Model` instance, got ', model) if isinstance(model, Sequential): raise ValueError('Expected `model` argument ' 'to be a functional `Model` instance, ' 'got a `Sequential` instance instead:', model) if not model._is_graph_network: raise ValueError('Expected `model` argument ' 'to be a functional `Model` instance, ' 'but got a subclass model instead.') layer_map = {} # Cache for created layers. tensor_map = object_identity.ObjectIdentityDictionary( ) # Map {reference_tensor: corresponding_tensor} if input_tensors is None: # Create placeholders to build the model on top of. input_tensors = [] for layer in model._input_layers: input_tensor = Input( batch_shape=layer._batch_input_shape, dtype=layer.dtype, sparse=layer.sparse, name=layer.name) input_tensors.append(input_tensor) # Cache newly created input layer. newly_created_input_layer = input_tensor._keras_history.layer layer_map[layer] = newly_created_input_layer else: # Make sure that all input tensors come from a Keras layer. # If tensor comes from an input layer: cache the input layer. input_tensors = nest.flatten(input_tensors) input_tensors_ = [] for i, input_tensor in enumerate(input_tensors): if not K.is_keras_tensor(input_tensor): original_input_layer = model._input_layers[i] name = original_input_layer.name input_tensor = Input(tensor=input_tensor, name='input_wrapper_for_' + name) input_tensors_.append(input_tensor) # Cache newly created input layer. newly_created_input_layer = input_tensor._keras_history.layer layer_map[original_input_layer] = newly_created_input_layer else: input_tensors_.append(input_tensor) input_tensors = input_tensors_ for x, y in zip(model.inputs, input_tensors): tensor_map[x] = y if not callable(layer_fn): raise ValueError('Expected `layer_fn` argument to be a callable.') # Has the side effect of filling out `layer_map` and `tensor_map`. new_nodes = _make_new_nodes(model._nodes_by_depth, layer_fn, layer_map, tensor_map) # Check that we did compute the model outputs, # then instantiate a new model from inputs and outputs. output_tensors = [] for x in model.outputs: assert x in tensor_map, 'Could not compute output ' + str(x) output_tensors.append(tensor_map[x]) input_tensors = nest.pack_sequence_as(model._nested_inputs, input_tensors) output_tensors = nest.pack_sequence_as(model._nested_outputs, output_tensors) metrics_names = model.metrics_names model = Model(input_tensors, output_tensors, name=model.name) # Layers not directly tied to outputs of the Model, such as loss layers # created in `add_loss` and `add_metric`. ancillary_layers = [ layer for layer in layer_map.values() if layer not in model.layers ] if ancillary_layers: _insert_ancillary_layers(model, ancillary_layers, metrics_names, new_nodes) return model