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

280 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# pylint: disable=protected-access 

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

17 

18import copy 

19import warnings 

20 

21from tensorflow.python import tf2 

22from tensorflow.python.framework import ops 

23from tensorflow.python.framework import tensor_util 

24from tensorflow.python.keras import layers as layer_module 

25from tensorflow.python.keras.engine import base_layer 

26from tensorflow.python.keras.engine import functional 

27from tensorflow.python.keras.engine import input_layer 

28from tensorflow.python.keras.engine import training_utils 

29from tensorflow.python.keras.saving.saved_model import model_serialization 

30from tensorflow.python.keras.utils import generic_utils 

31from tensorflow.python.keras.utils import layer_utils 

32from tensorflow.python.keras.utils import tf_inspect 

33from tensorflow.python.keras.utils import tf_utils 

34from tensorflow.python.module import module 

35from tensorflow.python.ops.numpy_ops import np_arrays 

36from tensorflow.python.platform import tf_logging as logging 

37from tensorflow.python.trackable import base as trackable 

38from tensorflow.python.util import nest 

39from tensorflow.python.util.tf_export import keras_export 

40 

41 

42SINGLE_LAYER_OUTPUT_ERROR_MSG = ('All layers in a Sequential model should have ' 

43 'a single output tensor. For multi-output ' 

44 'layers, use the functional API.') 

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 >>> # Optionally, the first layer can receive an `input_shape` argument: 

56 >>> model = tf.keras.Sequential() 

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

58 >>> # Afterwards, we do automatic shape inference: 

59 >>> model.add(tf.keras.layers.Dense(4)) 

60 

61 >>> # This is identical to the following: 

62 >>> model = tf.keras.Sequential() 

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

64 >>> model.add(tf.keras.layers.Dense(8)) 

65 

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

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

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

69 >>> model = tf.keras.Sequential() 

70 >>> model.add(tf.keras.layers.Dense(8)) 

71 >>> model.add(tf.keras.layers.Dense(4)) 

72 >>> # model.weights not created yet 

73 

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

75 >>> # continuously as you are adding layers: 

76 >>> model = tf.keras.Sequential() 

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

78 >>> model.add(tf.keras.layers.Dense(4)) 

79 >>> len(model.weights) 

80 4 

81 

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

83 >>> # choose to manually build your model by calling 

84 >>> # `build(batch_input_shape)`: 

85 >>> model = tf.keras.Sequential() 

86 >>> model.add(tf.keras.layers.Dense(8)) 

87 >>> model.add(tf.keras.layers.Dense(4)) 

88 >>> model.build((None, 16)) 

89 >>> len(model.weights) 

90 4 

91 

92 ```python 

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 @trackable.no_automatic_dependency_tracking 

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

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

108 

109 Args: 

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

111 name: Optional name for the model. 

112 """ 

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

114 super(functional.Functional, self).__init__( # pylint: disable=bad-super-call 

115 name=name, autocast=False) 

116 self.supports_masking = True 

117 self._compute_output_and_mask_jointly = True 

118 self._auto_track_sub_layers = False 

119 self._inferred_input_shape = None 

120 self._has_explicit_input_shape = False 

121 self._input_dtype = None 

122 self._layer_call_argspecs = {} 

123 self._created_nodes = set() 

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

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

126 # have input shape. 

127 self._graph_initialized = False 

128 

129 # Unfortunately some Sequential models using custom layers or FeatureColumn 

130 # layers have multiple inputs. This is fundamentally incompatible with 

131 # most of the Sequential API, and we have to disable a number of features 

132 # for such models. 

133 self._use_legacy_deferred_behavior = False 

134 

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

136 if layers: 

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

138 layers = [layers] 

139 for layer in layers: 

140 self.add(layer) 

141 

142 @property 

143 def layers(self): 

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

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

146 # bottom of the stack. 

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

148 # over it. 

149 layers = super(Sequential, self).layers 

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

151 return layers[1:] 

152 return layers[:] 

153 

154 @trackable.no_automatic_dependency_tracking 

155 def add(self, layer): 

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

157 

158 Args: 

159 layer: layer instance. 

160 

161 Raises: 

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

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

164 know its input shape. 

165 ValueError: In case the `layer` argument has 

166 multiple output tensors, or is already connected 

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

168 """ 

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

170 # the input layer from its keras history and use that without any loss of 

171 # generality. 

172 if hasattr(layer, '_keras_history'): 

173 origin_layer = layer._keras_history[0] 

174 if isinstance(origin_layer, input_layer.InputLayer): 

175 layer = origin_layer 

176 logging.warning( 

177 'Please add `keras.layers.InputLayer` instead of `keras.Input` to ' 

178 'Sequential model. `keras.Input` is intended to be used by ' 

179 'Functional model.') 

180 

181 if isinstance(layer, module.Module): 

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

183 layer = functional.ModuleWrapper(layer) 

184 else: 

185 raise TypeError('The added layer must be ' 

186 'an instance of class Layer. ' 

187 'Found: ' + str(layer)) 

188 

189 tf_utils.assert_no_legacy_layers([layer]) 

190 if not self._is_layer_name_unique(layer): 

191 raise ValueError('All layers added to a Sequential model ' 

192 'should have unique names. Name "%s" is already the name' 

193 ' of a layer in this model. Update the `name` argument ' 

194 'to pass a unique name.' % (layer.name,)) 

195 

196 self.built = False 

197 set_inputs = False 

198 self._maybe_create_attribute('_self_tracked_trackables', []) 

199 if not self._self_tracked_trackables: 

200 if isinstance(layer, input_layer.InputLayer): 

201 # Case where the user passes an Input or InputLayer layer via `add`. 

202 set_inputs = True 

203 else: 

204 batch_shape, dtype = training_utils.get_input_shape_and_dtype(layer) 

205 if batch_shape: 

206 # Instantiate an input layer. 

207 x = input_layer.Input( 

208 batch_shape=batch_shape, dtype=dtype, name=layer.name + '_input') 

209 # This will build the current layer 

210 # and create the node connecting the current layer 

211 # to the input layer we just created. 

212 layer(x) 

213 set_inputs = True 

214 

215 if set_inputs: 

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

217 if len(outputs) != 1: 

218 raise ValueError(SINGLE_LAYER_OUTPUT_ERROR_MSG) 

219 self.outputs = outputs 

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

221 self.built = True 

222 self._has_explicit_input_shape = True 

223 

224 elif self.outputs: 

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

226 # refresh its output. 

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

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

229 raise ValueError(SINGLE_LAYER_OUTPUT_ERROR_MSG) 

230 self.outputs = [output_tensor] 

231 self.built = True 

232 

233 if set_inputs or self._graph_initialized: 

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

235 self._graph_initialized = True 

236 else: 

237 self._self_tracked_trackables.append(layer) 

238 self._handle_deferred_layer_dependencies([layer]) 

239 

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

241 

242 @trackable.no_automatic_dependency_tracking 

243 def pop(self): 

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

245 

246 Raises: 

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

248 """ 

249 if not self.layers: 

250 raise TypeError('There are no layers in the model.') 

251 

252 layer = self._self_tracked_trackables.pop() 

253 self._layer_call_argspecs.pop(layer) 

254 if not self.layers: 

255 self.outputs = None 

256 self.inputs = None 

257 self.built = False 

258 self._inferred_input_shape = None 

259 self._has_explicit_input_shape = False 

260 self._graph_initialized = False 

261 elif self._graph_initialized: 

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

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

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

265 self.built = True 

266 

267 @trackable.no_automatic_dependency_tracking 

268 def _build_graph_network_for_inferred_shape(self, 

269 input_shape, 

270 input_dtype=None): 

271 if input_shape is None or not self.layers: 

272 return 

273 if not tf2.enabled() or not ops.executing_eagerly_outside_functions(): 

274 # This behavior is disabled in V1 or when eager execution is disabled. 

275 return 

276 if (not self._has_explicit_input_shape and 

277 not self._use_legacy_deferred_behavior): 

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

279 # should be rebuilt. 

280 input_shape = tuple(input_shape) 

281 if self._inferred_input_shape is None: 

282 new_shape = input_shape 

283 else: 

284 new_shape = relax_input_shape(self._inferred_input_shape, input_shape) 

285 if (new_shape is not None and new_shape != self._inferred_input_shape): 

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

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

288 with ops.init_scope(): 

289 inputs = input_layer.Input( 

290 batch_shape=new_shape, 

291 dtype=input_dtype, 

292 name=self.layers[0].name + '_input') 

293 layer_input = inputs 

294 created_nodes = set() 

295 for layer in self.layers: 

296 # Clear nodes previously created via this method. This prevents 

297 # node accumulation and ensures that e.g. `layer.output` is 

298 # always connected to `model.inputs` 

299 # (this is important e.g. for the feature extraction use case). 

300 # We don't just do `layer._inbound_nodes = []` in order 

301 # not to break shared layers added to Sequential models (which is 

302 # technically illegal as per the `add()` docstring, 

303 # but wasn't previously disabled). 

304 clear_previously_created_nodes(layer, self._created_nodes) 

305 try: 

306 # Create Functional API connection by calling the current layer 

307 layer_output = layer(layer_input) 

308 except: # pylint:disable=bare-except 

309 # Functional API calls may fail for a number of reasons: 

310 # 1) The layer may be buggy. In this case it will be easier for 

311 # the user to debug if we fail on the first call on concrete data, 

312 # instead of our own call on a symbolic input. 

313 # 2) The layer is dynamic (graph-incompatible) and hasn't 

314 # overridden `compute_output_shape`. In this case, it is 

315 # impossible to build a graph network. 

316 # 3) The layer is otherwise incompatible with the Functional API 

317 # (e.g. this is the case for some probabilistic layers that rely 

318 # on hacks and that do not return tensors). 

319 # In all these cases, we should avoid creating a graph network 

320 # (or we simply can't). 

321 self._use_legacy_deferred_behavior = True 

322 return 

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

324 raise ValueError(SINGLE_LAYER_OUTPUT_ERROR_MSG) 

325 # Keep track of nodes just created above 

326 track_nodes_created_by_last_call(layer, created_nodes) 

327 layer_input = layer_output 

328 outputs = layer_output 

329 self._created_nodes = created_nodes 

330 try: 

331 # Initialize a graph Network. This call will never fail for 

332 # a stack of valid Keras layers. 

333 # However some users have layers that are fundamentally incompatible 

334 # with the Functional API, which do not return tensors. In this 

335 # case, we fall back to the legacy deferred behavior. 

336 # TODO(fchollet): consider raising here, as we should not be 

337 # supporting such layers. 

338 self._init_graph_network(inputs, outputs) 

339 self._graph_initialized = True 

340 except: # pylint:disable=bare-except 

341 self._use_legacy_deferred_behavior = True 

342 self._inferred_input_shape = new_shape 

343 

344 @generic_utils.default 

345 def build(self, input_shape=None): 

346 if self._graph_initialized: 

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

348 else: 

349 if input_shape is None: 

350 raise ValueError('You must provide an `input_shape` argument.') 

351 self._build_graph_network_for_inferred_shape(input_shape) 

352 if not self.built: 

353 input_shape = tuple(input_shape) 

354 self._build_input_shape = input_shape 

355 super(Sequential, self).build(input_shape) 

356 self.built = True 

357 

358 def call(self, inputs, training=None, mask=None): # pylint: disable=redefined-outer-name 

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

360 if not self._has_explicit_input_shape: 

361 if not tensor_util.is_tf_type(inputs) and not isinstance( 

362 inputs, np_arrays.ndarray): 

363 # This is a Sequential with mutiple inputs. This is technically an 

364 # invalid use case of Sequential, but we tolerate it for backwards 

365 # compatibility. 

366 self._use_legacy_deferred_behavior = True 

367 self._build_input_shape = nest.map_structure(_get_shape_tuple, inputs) 

368 if tf2.enabled(): 

369 logging.warning('Layers in a Sequential model should only have a ' 

370 'single input tensor, but we receive a %s input: %s' 

371 '\nConsider rewriting this model with the Functional ' 

372 'API.' % (type(inputs), inputs)) 

373 else: 

374 self._build_graph_network_for_inferred_shape(inputs.shape, inputs.dtype) 

375 

376 if self._graph_initialized: 

377 if not self.built: 

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

379 return super(Sequential, self).call(inputs, training=training, mask=mask) 

380 

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

382 for layer in self.layers: 

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

384 # are the outputs of `layer` applied to `inputs`. At the end of each 

385 # iteration `inputs` is set to `outputs` to prepare for the next layer. 

386 kwargs = {} 

387 argspec = self._layer_call_argspecs[layer].args 

388 if 'mask' in argspec: 

389 kwargs['mask'] = mask 

390 if 'training' in argspec: 

391 kwargs['training'] = training 

392 

393 outputs = layer(inputs, **kwargs) 

394 

395 if len(nest.flatten(outputs)) != 1: 

396 raise ValueError(SINGLE_LAYER_OUTPUT_ERROR_MSG) 

397 # `outputs` will be the inputs to the next layer. 

398 inputs = outputs 

399 mask = getattr(outputs, '_keras_mask', None) 

400 return outputs 

401 

402 def compute_output_shape(self, input_shape): 

403 shape = input_shape 

404 for layer in self.layers: 

405 shape = layer.compute_output_shape(shape) 

406 return shape 

407 

408 def compute_mask(self, inputs, mask): 

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

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

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

412 outputs = self.call(inputs, mask=mask) # pylint: disable=unexpected-keyword-arg 

413 return getattr(outputs, '_keras_mask', None) 

414 

415 def predict_proba(self, x, batch_size=32, verbose=0): 

416 """Generates class probability predictions for the input samples. 

417 

418 The input samples are processed batch by batch. 

419 

420 Args: 

421 x: input data, as a Numpy array or list of Numpy arrays 

422 (if the model has multiple inputs). 

423 batch_size: integer. 

424 verbose: verbosity mode, 0 or 1. 

425 

426 Returns: 

427 A Numpy array of probability predictions. 

428 """ 

429 warnings.warn('`model.predict_proba()` is deprecated and ' 

430 'will be removed after 2021-01-01. ' 

431 'Please use `model.predict()` instead.') 

432 preds = self.predict(x, batch_size, verbose) 

433 if preds.min() < 0. or preds.max() > 1.: 

434 logging.warning('Network returning invalid probability values. ' 

435 'The last layer might not normalize predictions ' 

436 'into probabilities ' 

437 '(like softmax or sigmoid would).') 

438 return preds 

439 

440 def predict_classes(self, x, batch_size=32, verbose=0): 

441 """Generate class predictions for the input samples. 

442 

443 The input samples are processed batch by batch. 

444 

445 Args: 

446 x: input data, as a Numpy array or list of Numpy arrays 

447 (if the model has multiple inputs). 

448 batch_size: integer. 

449 verbose: verbosity mode, 0 or 1. 

450 

451 Returns: 

452 A numpy array of class predictions. 

453 """ 

454 warnings.warn('`model.predict_classes()` is deprecated and ' 

455 'will be removed after 2021-01-01. ' 

456 'Please use instead:' 

457 '* `np.argmax(model.predict(x), axis=-1)`, ' 

458 ' if your model does multi-class classification ' 

459 ' (e.g. if it uses a `softmax` last-layer activation).' 

460 '* `(model.predict(x) > 0.5).astype("int32")`, ' 

461 ' if your model does binary classification ' 

462 ' (e.g. if it uses a `sigmoid` last-layer activation).') 

463 proba = self.predict(x, batch_size=batch_size, verbose=verbose) 

464 if proba.shape[-1] > 1: 

465 return proba.argmax(axis=-1) 

466 else: 

467 return (proba > 0.5).astype('int32') 

468 

469 def get_config(self): 

470 layer_configs = [] 

471 for layer in super(Sequential, self).layers: 

472 # `super().layers` include the InputLayer if available (it is filtered out 

473 # of `self.layers`). Note that `self._self_tracked_trackables` is managed 

474 # by the tracking infrastructure and should not be used. 

475 layer_configs.append(generic_utils.serialize_keras_object(layer)) 

476 config = { 

477 'name': self.name, 

478 'layers': copy.deepcopy(layer_configs) 

479 } 

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

481 config['build_input_shape'] = self._build_input_shape 

482 return config 

483 

484 @classmethod 

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

486 if 'name' in config: 

487 name = config['name'] 

488 build_input_shape = config.get('build_input_shape') 

489 layer_configs = config['layers'] 

490 else: 

491 name = None 

492 build_input_shape = None 

493 layer_configs = config 

494 model = cls(name=name) 

495 for layer_config in layer_configs: 

496 layer = layer_module.deserialize(layer_config, 

497 custom_objects=custom_objects) 

498 model.add(layer) 

499 if (not model.inputs and build_input_shape and 

500 isinstance(build_input_shape, (tuple, list))): 

501 model.build(build_input_shape) 

502 return model 

503 

504 @property 

505 def input_spec(self): 

506 if hasattr(self, '_manual_input_spec'): 

507 return self._manual_input_spec 

508 if self.layers and hasattr(self.layers[0], 'input_spec'): 

509 return self.layers[0].input_spec 

510 return None 

511 

512 @input_spec.setter 

513 def input_spec(self, value): 

514 self._manual_input_spec = value 

515 

516 @property 

517 def _trackable_saved_model_saver(self): 

518 return model_serialization.SequentialSavedModelSaver(self) 

519 

520 def _is_layer_name_unique(self, layer): 

521 for ref_layer in self.layers: 

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

523 return False 

524 return True 

525 

526 def _assert_weights_created(self): 

527 if self._graph_initialized: 

528 return 

529 # When the graph has not been initialized, use the Model's implementation to 

530 # to check if the weights has been created. 

531 super(functional.Functional, self)._assert_weights_created() # pylint: disable=bad-super-call 

532 

533 

534def _get_shape_tuple(t): 

535 if hasattr(t, 'shape'): 

536 shape = t.shape 

537 if isinstance(shape, tuple): 

538 return shape 

539 if shape.rank is not None: 

540 return tuple(shape.as_list()) 

541 return None 

542 return None 

543 

544 

545def relax_input_shape(shape_1, shape_2): 

546 if shape_1 is None or shape_2 is None: 

547 return None 

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

549 return None 

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

551 

552 

553def clear_previously_created_nodes(layer, created_nodes): 

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

555 for node in layer._inbound_nodes: 

556 prev_layers = node.inbound_layers 

557 for prev_layer in nest.flatten(prev_layers): 

558 prev_layer._outbound_nodes = [ 

559 n for n in prev_layer._outbound_nodes 

560 if n not in created_nodes] 

561 layer._inbound_nodes = [ 

562 n for n in layer._inbound_nodes if n not in created_nodes] 

563 

564 

565def track_nodes_created_by_last_call(layer, created_nodes): 

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

567 if not layer._inbound_nodes: 

568 return 

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

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

571 for prev_layer in nest.flatten(prev_layers): 

572 if prev_layer._outbound_nodes: 

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