Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/keras/src/engine/sequential.py: 20%

270 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-03 07:57 +0000

1# Copyright 2015 The TensorFlow Authors. All Rights Reserved. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================== 

15 

16"""Home of the `Sequential` model.""" 

17 

18import copy 

19 

20import tensorflow.compat.v2 as tf 

21 

22from keras.src import layers as layer_module 

23from keras.src.engine import base_layer 

24from keras.src.engine import functional 

25from keras.src.engine import input_layer 

26from keras.src.engine import training 

27from keras.src.engine import training_utils 

28from keras.src.saving import serialization_lib 

29from keras.src.saving.legacy import serialization as legacy_serialization 

30from keras.src.saving.legacy.saved_model import model_serialization 

31from keras.src.utils import generic_utils 

32from keras.src.utils import layer_utils 

33from keras.src.utils import tf_inspect 

34from keras.src.utils import tf_utils 

35from keras.src.utils import traceback_utils 

36 

37# isort: off 

38from tensorflow.python.util.tf_export import keras_export 

39 

40SINGLE_LAYER_OUTPUT_ERROR_MSG = ( 

41 "All layers in a Sequential model should have " 

42 "a single output tensor. For multi-output " 

43 "layers, use the functional API." 

44) 

45 

46 

47@keras_export("keras.Sequential", "keras.models.Sequential") 

48class Sequential(functional.Functional): 

49 """`Sequential` groups a linear stack of layers into a `tf.keras.Model`. 

50 

51 `Sequential` provides training and inference features on this model. 

52 

53 Examples: 

54 

55 ```python 

56 # Optionally, the first layer can receive an `input_shape` argument: 

57 model = tf.keras.Sequential() 

58 model.add(tf.keras.layers.Dense(8, input_shape=(16,))) 

59 # Afterwards, we do automatic shape inference: 

60 model.add(tf.keras.layers.Dense(4)) 

61 

62 # This is identical to the following: 

63 model = tf.keras.Sequential() 

64 model.add(tf.keras.Input(shape=(16,))) 

65 model.add(tf.keras.layers.Dense(8)) 

66 

67 # Note that you can also omit the `input_shape` argument. 

68 # In that case the model doesn't have any weights until the first call 

69 # to a training/evaluation method (since it isn't yet built): 

70 model = tf.keras.Sequential() 

71 model.add(tf.keras.layers.Dense(8)) 

72 model.add(tf.keras.layers.Dense(4)) 

73 # model.weights not created yet 

74 

75 # Whereas if you specify the input shape, the model gets built 

76 # continuously as you are adding layers: 

77 model = tf.keras.Sequential() 

78 model.add(tf.keras.layers.Dense(8, input_shape=(16,))) 

79 model.add(tf.keras.layers.Dense(4)) 

80 len(model.weights) 

81 # Returns "4" 

82 

83 # When using the delayed-build pattern (no input shape specified), you can 

84 # choose to manually build your model by calling 

85 # `build(batch_input_shape)`: 

86 model = tf.keras.Sequential() 

87 model.add(tf.keras.layers.Dense(8)) 

88 model.add(tf.keras.layers.Dense(4)) 

89 model.build((None, 16)) 

90 len(model.weights) 

91 # Returns "4" 

92 

93 # Note that when using the delayed-build pattern (no input shape specified), 

94 # the model gets built the first time you call `fit`, `eval`, or `predict`, 

95 # or the first time you call the model on some input data. 

96 model = tf.keras.Sequential() 

97 model.add(tf.keras.layers.Dense(8)) 

98 model.add(tf.keras.layers.Dense(1)) 

99 model.compile(optimizer='sgd', loss='mse') 

100 # This builds the model for the first time: 

101 model.fit(x, y, batch_size=32, epochs=10) 

102 ``` 

103 """ 

104 

105 @tf.__internal__.tracking.no_automatic_dependency_tracking 

106 @traceback_utils.filter_traceback 

107 def __init__(self, layers=None, name=None): 

108 """Creates a `Sequential` model instance. 

109 

110 Args: 

111 layers: Optional list of layers to add to the model. 

112 name: Optional name for the model. 

113 """ 

114 # Skip the init in FunctionalModel since model doesn't have input/output 

115 # yet 

116 super(functional.Functional, self).__init__(name=name, autocast=False) 

117 base_layer.keras_api_gauge.get_cell("Sequential").set(True) 

118 self.supports_masking = True 

119 self._compute_output_and_mask_jointly = True 

120 self._auto_track_sub_layers = False 

121 self._inferred_input_shape = None 

122 self._has_explicit_input_shape = False 

123 self._input_dtype = None 

124 self._layer_call_argspecs = {} 

125 self._created_nodes = set() 

126 # Flag that indicate whether the sequential network topology has been 

127 # created. It is false when there isn't any layer, or the layers don't 

128 # have an input shape. 

129 self._graph_initialized = False 

130 

131 # Unfortunately some Sequential models using custom layers or 

132 # FeatureColumn layers have multiple inputs. This is fundamentally 

133 # incompatible with most of the Sequential API, and we have to disable a 

134 # number of features for such models. 

135 self._use_legacy_deferred_behavior = False 

136 

137 # Add to the model any layers passed to the constructor. 

138 if layers: 

139 if not isinstance(layers, (list, tuple)): 

140 layers = [layers] 

141 for layer in layers: 

142 self.add(layer) 

143 

144 @property 

145 def layers(self): 

146 # Historically, `sequential.layers` only returns layers that were added 

147 # via `add`, and omits the auto-generated `InputLayer` that comes at the 

148 # bottom of the stack. 

149 # `Trackable` manages the `_layers` attributes and does filtering 

150 # over it. 

151 layers = super().layers 

152 if layers and isinstance(layers[0], input_layer.InputLayer): 

153 return layers[1:] 

154 return layers[:] 

155 

156 @tf.__internal__.tracking.no_automatic_dependency_tracking 

157 @traceback_utils.filter_traceback 

158 def add(self, layer): 

159 """Adds a layer instance on top of the layer stack. 

160 

161 Args: 

162 layer: layer instance. 

163 

164 Raises: 

165 TypeError: If `layer` is not a layer instance. 

166 ValueError: In case the `layer` argument does not 

167 know its input shape. 

168 ValueError: In case the `layer` argument has 

169 multiple output tensors, or is already connected 

170 somewhere else (forbidden in `Sequential` models). 

171 """ 

172 # If we are passed a Keras tensor created by keras.Input(), we can 

173 # extract the input layer from its keras history and use that without 

174 # any loss of 

175 # generality. 

176 if hasattr(layer, "_keras_history"): 

177 origin_layer = layer._keras_history[0] 

178 if isinstance(origin_layer, input_layer.InputLayer): 

179 layer = origin_layer 

180 

181 if isinstance(layer, tf.Module): 

182 if not isinstance(layer, base_layer.Layer): 

183 layer = functional.ModuleWrapper(layer) 

184 else: 

185 raise TypeError( 

186 "The added layer must be an instance of class Layer. " 

187 f"Received: layer={layer} of type {type(layer)}." 

188 ) 

189 

190 tf_utils.assert_no_legacy_layers([layer]) 

191 if not self._is_layer_name_unique(layer): 

192 raise ValueError( 

193 "All layers added to a Sequential model " 

194 f'should have unique names. Name "{layer.name}" is already ' 

195 "the name of a layer in this model. Update the `name` argument " 

196 "to pass a unique name." 

197 ) 

198 

199 self.built = False 

200 set_inputs = False 

201 self._maybe_create_attribute("_self_tracked_trackables", []) 

202 if not self._self_tracked_trackables: 

203 if isinstance(layer, input_layer.InputLayer): 

204 # Case where the user passes an Input or InputLayer layer via 

205 # `add`. 

206 set_inputs = True 

207 else: 

208 batch_shape, dtype = training_utils.get_input_shape_and_dtype( 

209 layer 

210 ) 

211 if batch_shape: 

212 # Instantiate an input layer. 

213 x = input_layer.Input( 

214 batch_shape=batch_shape, 

215 dtype=dtype, 

216 name=layer.name + "_input", 

217 ) 

218 # This will build the current layer 

219 # and create the node connecting the current layer 

220 # to the input layer we just created. 

221 layer(x) 

222 set_inputs = True 

223 

224 if set_inputs: 

225 outputs = tf.nest.flatten(layer._inbound_nodes[-1].outputs) 

226 if len(outputs) != 1: 

227 raise ValueError(SINGLE_LAYER_OUTPUT_ERROR_MSG) 

228 self.outputs = outputs 

229 self.inputs = layer_utils.get_source_inputs(self.outputs[0]) 

230 self.built = True 

231 self._has_explicit_input_shape = True 

232 

233 elif self.outputs: 

234 # If the model is being built continuously on top of an input layer: 

235 # refresh its output. 

236 output_tensor = layer(self.outputs[0]) 

237 if len(tf.nest.flatten(output_tensor)) != 1: 

238 raise ValueError(SINGLE_LAYER_OUTPUT_ERROR_MSG) 

239 self.outputs = [output_tensor] 

240 self.built = True 

241 

242 if set_inputs or self._graph_initialized: 

243 self._init_graph_network(self.inputs, self.outputs) 

244 self._graph_initialized = True 

245 else: 

246 self._self_tracked_trackables.append(layer) 

247 self._handle_deferred_layer_dependencies([layer]) 

248 

249 self._layer_call_argspecs[layer] = tf_inspect.getfullargspec(layer.call) 

250 

251 @tf.__internal__.tracking.no_automatic_dependency_tracking 

252 @traceback_utils.filter_traceback 

253 def pop(self): 

254 """Removes the last layer in the model. 

255 

256 Raises: 

257 TypeError: if there are no layers in the model. 

258 """ 

259 if not self.layers: 

260 raise TypeError("There are no layers in the model.") 

261 

262 layer = self._self_tracked_trackables.pop() 

263 self._layer_call_argspecs.pop(layer) 

264 if not self.layers: 

265 self.outputs = None 

266 self.inputs = None 

267 self.built = False 

268 self._inferred_input_shape = None 

269 self._has_explicit_input_shape = False 

270 self._graph_initialized = False 

271 elif self._graph_initialized: 

272 self.layers[-1]._outbound_nodes = [] 

273 self.outputs = [self.layers[-1].output] 

274 self._init_graph_network(self.inputs, self.outputs) 

275 self.built = True 

276 

277 @tf.__internal__.tracking.no_automatic_dependency_tracking 

278 def _build_graph_network_for_inferred_shape( 

279 self, input_shape, input_dtype=None 

280 ): 

281 if input_shape is None or not self.layers: 

282 return 

283 if ( 

284 not tf.__internal__.tf2.enabled() 

285 or not tf.compat.v1.executing_eagerly_outside_functions() 

286 ): 

287 # This behavior is disabled in V1 or when eager execution is 

288 # disabled. 

289 return 

290 if ( 

291 not self._has_explicit_input_shape 

292 and not self._use_legacy_deferred_behavior 

293 ): 

294 # Determine whether the input shape is novel, i.e. whether the model 

295 # should be rebuilt. 

296 input_shape = tuple(input_shape) 

297 if self._inferred_input_shape is None: 

298 new_shape = input_shape 

299 else: 

300 new_shape = relax_input_shape( 

301 self._inferred_input_shape, input_shape 

302 ) 

303 if ( 

304 new_shape is not None 

305 and new_shape != self._inferred_input_shape 

306 ): 

307 # A novel shape has been received: we need to rebuild the model. 

308 # In case we are inside a graph function, we step out of it. 

309 with tf.init_scope(): 

310 inputs = input_layer.Input( 

311 batch_shape=new_shape, 

312 dtype=input_dtype, 

313 name=self.layers[0].name + "_input", 

314 ) 

315 layer_input = inputs 

316 created_nodes = set() 

317 for layer in self.layers: 

318 # Clear nodes previously created via this method. This 

319 # prevents node accumulation and ensures that e.g. 

320 # `layer.output` is always connected to `model.inputs` 

321 # (this is important e.g. for the feature extraction use 

322 # case). We don't just do `layer._inbound_nodes = []` 

323 # in order not to break shared layers added to 

324 # Sequential models (which is technically illegal as per 

325 # the `add()` docstring, but wasn't previously 

326 # disabled). 

327 clear_previously_created_nodes( 

328 layer, self._created_nodes 

329 ) 

330 try: 

331 # Create Functional API connection by calling the 

332 # current layer 

333 layer_output = layer(layer_input) 

334 except: # noqa: E722 

335 # Functional API calls may fail for a number of 

336 # reasons: 1) The layer may be buggy. In this case 

337 # it will be easier for the user to debug if we fail 

338 # on the first call on concrete data, instead of our 

339 # own call on a symbolic input. 2) The layer is 

340 # dynamic (graph-incompatible) and hasn't overridden 

341 # `compute_output_shape`. In this case, it is 

342 # impossible to build a graph network. 3) The layer 

343 # is otherwise incompatible with the Functional API 

344 # (e.g. this is the case for some probabilistic 

345 # layers that rely on hacks and that do not return 

346 # tensors). In all these cases, we should avoid 

347 # creating a graph network (or we simply can't). 

348 self._use_legacy_deferred_behavior = True 

349 return 

350 if len(tf.nest.flatten(layer_output)) != 1: 

351 raise ValueError(SINGLE_LAYER_OUTPUT_ERROR_MSG) 

352 # Keep track of nodes just created above 

353 track_nodes_created_by_last_call(layer, created_nodes) 

354 layer_input = layer_output 

355 outputs = layer_output 

356 self._created_nodes = created_nodes 

357 try: 

358 # Initialize a graph Network. This call will never fail 

359 # for a stack of valid Keras layers. However some users 

360 # have layers that are fundamentally incompatible with 

361 # the Functional API, which do not return tensors. In 

362 # this case, we fall back to the legacy deferred 

363 # behavior. 

364 # TODO(fchollet): consider raising here, as we should 

365 # not be supporting such layers. 

366 self._init_graph_network(inputs, outputs) 

367 self._graph_initialized = True 

368 except: # noqa: E722 

369 self._use_legacy_deferred_behavior = True 

370 self._inferred_input_shape = new_shape 

371 

372 @generic_utils.default 

373 def build(self, input_shape=None): 

374 if self._graph_initialized: 

375 self._init_graph_network(self.inputs, self.outputs) 

376 else: 

377 if input_shape is None: 

378 raise ValueError("You must provide an `input_shape` argument.") 

379 self._build_graph_network_for_inferred_shape(input_shape) 

380 if not self.built: 

381 input_shape = tuple(input_shape) 

382 self._build_input_shape = input_shape 

383 super().build(input_shape) 

384 self.built = True 

385 

386 def call(self, inputs, training=None, mask=None): 

387 # If applicable, update the static input shape of the model. 

388 if not self._has_explicit_input_shape: 

389 if not tf.is_tensor(inputs) and not isinstance(inputs, tf.Tensor): 

390 # This is a Sequential with multiple inputs. This is technically 

391 # an invalid use case of Sequential, but we tolerate it for 

392 # backwards compatibility. 

393 self._use_legacy_deferred_behavior = True 

394 self._build_input_shape = tf.nest.map_structure( 

395 _get_shape_tuple, inputs 

396 ) 

397 else: 

398 self._build_graph_network_for_inferred_shape( 

399 inputs.shape, inputs.dtype 

400 ) 

401 

402 if self._graph_initialized: 

403 if not self.built: 

404 self._init_graph_network(self.inputs, self.outputs) 

405 return super().call(inputs, training=training, mask=mask) 

406 

407 outputs = inputs # handle the corner case where self.layers is empty 

408 for layer in self.layers: 

409 # During each iteration, `inputs` are the inputs to `layer`, and 

410 # `outputs` are the outputs of `layer` applied to `inputs`. At the 

411 # end of each iteration `inputs` is set to `outputs` to prepare for 

412 # the next layer. 

413 kwargs = {} 

414 argspec = self._layer_call_argspecs[layer].args 

415 if "mask" in argspec: 

416 kwargs["mask"] = mask 

417 if "training" in argspec: 

418 kwargs["training"] = training 

419 

420 outputs = layer(inputs, **kwargs) 

421 

422 inputs = outputs 

423 

424 def _get_mask_from_keras_tensor(kt): 

425 return getattr(kt, "_keras_mask", None) 

426 

427 mask = tf.nest.map_structure(_get_mask_from_keras_tensor, outputs) 

428 return outputs 

429 

430 def compute_output_shape(self, input_shape): 

431 shape = input_shape 

432 for layer in self.layers: 

433 shape = layer.compute_output_shape(shape) 

434 return shape 

435 

436 def compute_mask(self, inputs, mask): 

437 # TODO(omalleyt): b/123540974 This function is not really safe to call 

438 # by itself because it will duplicate any updates and losses in graph 

439 # mode by `call`ing the Layers again. 

440 outputs = self.call(inputs, mask=mask) 

441 return getattr(outputs, "_keras_mask", None) 

442 

443 def get_config(self): 

444 layer_configs = [] 

445 serialize_obj_fn = serialization_lib.serialize_keras_object 

446 if getattr(self, "use_legacy_config", None): 

447 serialize_obj_fn = legacy_serialization.serialize_keras_object 

448 for layer in super().layers: 

449 # `super().layers` include the InputLayer if available (it is 

450 # filtered out of `self.layers`). Note that 

451 # `self._self_tracked_trackables` is managed by the tracking 

452 # infrastructure and should not be used. 

453 layer_configs.append(serialize_obj_fn(layer)) 

454 config = training.Model.get_config(self) 

455 config["name"] = self.name 

456 config["layers"] = copy.deepcopy(layer_configs) 

457 if not self._is_graph_network and self._build_input_shape is not None: 

458 config["build_input_shape"] = self._build_input_shape 

459 return config 

460 

461 @classmethod 

462 def from_config(cls, config, custom_objects=None): 

463 if "name" in config: 

464 name = config["name"] 

465 build_input_shape = config.get("build_input_shape") 

466 layer_configs = config["layers"] 

467 else: 

468 name = None 

469 layer_configs = config 

470 model = cls(name=name) 

471 for layer_config in layer_configs: 

472 use_legacy_format = "module" not in layer_config 

473 layer = layer_module.deserialize( 

474 layer_config, 

475 custom_objects=custom_objects, 

476 use_legacy_format=use_legacy_format, 

477 ) 

478 model.add(layer) 

479 

480 if ( 

481 not model.inputs 

482 and build_input_shape 

483 and isinstance(build_input_shape, (tuple, list)) 

484 ): 

485 model.build(build_input_shape) 

486 

487 return model 

488 

489 @property 

490 def input_spec(self): 

491 if hasattr(self, "_manual_input_spec"): 

492 return self._manual_input_spec 

493 if self._has_explicit_input_shape: 

494 return super().input_spec 

495 return None 

496 

497 @input_spec.setter 

498 def input_spec(self, value): 

499 self._manual_input_spec = value 

500 

501 @property 

502 def _trackable_saved_model_saver(self): 

503 return model_serialization.SequentialSavedModelSaver(self) 

504 

505 def _is_layer_name_unique(self, layer): 

506 for ref_layer in self.layers: 

507 if layer.name == ref_layer.name and ref_layer is not layer: 

508 return False 

509 return True 

510 

511 def _assert_weights_created(self): 

512 if self._graph_initialized: 

513 return 

514 # When the graph has not been initialized, use the Model's 

515 # implementation to to check if the weights has been created. 

516 super(functional.Functional, self)._assert_weights_created() 

517 

518 

519def _get_shape_tuple(t): 

520 if hasattr(t, "shape"): 

521 shape = t.shape 

522 if isinstance(shape, tuple): 

523 return shape 

524 if shape.rank is not None: 

525 return tuple(shape.as_list()) 

526 return None 

527 return None 

528 

529 

530def relax_input_shape(shape_1, shape_2): 

531 if shape_1 is None or shape_2 is None: 

532 return None 

533 if len(shape_1) != len(shape_2): 

534 return None 

535 return tuple(None if d1 != d2 else d1 for d1, d2 in zip(shape_1, shape_2)) 

536 

537 

538def clear_previously_created_nodes(layer, created_nodes): 

539 """Remove nodes from `created_nodes` from the layer's inbound_nodes.""" 

540 for node in layer._inbound_nodes: 

541 prev_layers = node.inbound_layers 

542 for prev_layer in tf.nest.flatten(prev_layers): 

543 prev_layer._outbound_nodes = [ 

544 n for n in prev_layer._outbound_nodes if n not in created_nodes 

545 ] 

546 layer._inbound_nodes = [ 

547 n for n in layer._inbound_nodes if n not in created_nodes 

548 ] 

549 

550 

551def track_nodes_created_by_last_call(layer, created_nodes): 

552 """Adds to `created_nodes` the nodes created by the last call to `layer`.""" 

553 if not layer._inbound_nodes: 

554 return 

555 created_nodes.add(layer._inbound_nodes[-1]) 

556 prev_layers = layer._inbound_nodes[-1].inbound_layers 

557 for prev_layer in tf.nest.flatten(prev_layers): 

558 if prev_layer._outbound_nodes: 

559 created_nodes.add(prev_layer._outbound_nodes[-1]) 

560