1# Copyright 2018 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=invalid-name 16"""NASNet-A models for Keras. 17 18NASNet refers to Neural Architecture Search Network, a family of models 19that were designed automatically by learning the model architectures 20directly on the dataset of interest. 21 22Here we consider NASNet-A, the highest performance model that was found 23for the CIFAR-10 dataset, and then extended to ImageNet 2012 dataset, 24obtaining state of the art performance on CIFAR-10 and ImageNet 2012. 25Only the NASNet-A models, and their respective weights, which are suited 26for ImageNet 2012 are provided. 27 28The below table describes the performance on ImageNet 2012: 29-------------------------------------------------------------------------------- 30 Architecture | Top-1 Acc | Top-5 Acc | Multiply-Adds | Params (M) 31-------------------------------------------------------------------------------- 32| NASNet-A (4 @ 1056) | 74.0 % | 91.6 % | 564 M | 5.3 | 33| NASNet-A (6 @ 4032) | 82.7 % | 96.2 % | 23.8 B | 88.9 | 34-------------------------------------------------------------------------------- 35 36Reference: 37 - [Learning Transferable Architectures for Scalable Image Recognition]( 38 https://arxiv.org/abs/1707.07012) (CVPR 2018) 39""" 40from __future__ import absolute_import 41from __future__ import division 42from __future__ import print_function 43 44from tensorflow.python.keras import backend 45from tensorflow.python.keras.applications import imagenet_utils 46from tensorflow.python.keras.engine import training 47from tensorflow.python.keras.layers import VersionAwareLayers 48from tensorflow.python.keras.utils import data_utils 49from tensorflow.python.keras.utils import layer_utils 50from tensorflow.python.lib.io import file_io 51from tensorflow.python.platform import tf_logging as logging 52from tensorflow.python.util.tf_export import keras_export 53 54 55BASE_WEIGHTS_PATH = ('https://storage.googleapis.com/tensorflow/' 56 'keras-applications/nasnet/') 57NASNET_MOBILE_WEIGHT_PATH = BASE_WEIGHTS_PATH + 'NASNet-mobile.h5' 58NASNET_MOBILE_WEIGHT_PATH_NO_TOP = BASE_WEIGHTS_PATH + 'NASNet-mobile-no-top.h5' 59NASNET_LARGE_WEIGHT_PATH = BASE_WEIGHTS_PATH + 'NASNet-large.h5' 60NASNET_LARGE_WEIGHT_PATH_NO_TOP = BASE_WEIGHTS_PATH + 'NASNet-large-no-top.h5' 61 62layers = VersionAwareLayers() 63 64 65def NASNet(input_shape=None, 66 penultimate_filters=4032, 67 num_blocks=6, 68 stem_block_filters=96, 69 skip_reduction=True, 70 filter_multiplier=2, 71 include_top=True, 72 weights='imagenet', 73 input_tensor=None, 74 pooling=None, 75 classes=1000, 76 default_size=None, 77 classifier_activation='softmax'): 78 """Instantiates a NASNet model. 79 80 Reference: 81 - [Learning Transferable Architectures for Scalable Image Recognition]( 82 https://arxiv.org/abs/1707.07012) (CVPR 2018) 83 84 Optionally loads weights pre-trained on ImageNet. 85 Note that the data format convention used by the model is 86 the one specified in your Keras config at `~/.keras/keras.json`. 87 88 Args: 89 input_shape: Optional shape tuple, the input shape 90 is by default `(331, 331, 3)` for NASNetLarge and 91 `(224, 224, 3)` for NASNetMobile. 92 It should have exactly 3 input channels, 93 and width and height should be no smaller than 32. 94 E.g. `(224, 224, 3)` would be one valid value. 95 penultimate_filters: Number of filters in the penultimate layer. 96 NASNet models use the notation `NASNet (N @ P)`, where: 97 - N is the number of blocks 98 - P is the number of penultimate filters 99 num_blocks: Number of repeated blocks of the NASNet model. 100 NASNet models use the notation `NASNet (N @ P)`, where: 101 - N is the number of blocks 102 - P is the number of penultimate filters 103 stem_block_filters: Number of filters in the initial stem block 104 skip_reduction: Whether to skip the reduction step at the tail 105 end of the network. 106 filter_multiplier: Controls the width of the network. 107 - If `filter_multiplier` < 1.0, proportionally decreases the number 108 of filters in each layer. 109 - If `filter_multiplier` > 1.0, proportionally increases the number 110 of filters in each layer. 111 - If `filter_multiplier` = 1, default number of filters from the 112 paper are used at each layer. 113 include_top: Whether to include the fully-connected 114 layer at the top of the network. 115 weights: `None` (random initialization) or 116 `imagenet` (ImageNet weights) 117 input_tensor: Optional Keras tensor (i.e. output of 118 `layers.Input()`) 119 to use as image input for the model. 120 pooling: Optional pooling mode for feature extraction 121 when `include_top` is `False`. 122 - `None` means that the output of the model 123 will be the 4D tensor output of the 124 last convolutional block. 125 - `avg` means that global average pooling 126 will be applied to the output of the 127 last convolutional block, and thus 128 the output of the model will be a 129 2D tensor. 130 - `max` means that global max pooling will 131 be applied. 132 classes: Optional number of classes to classify images 133 into, only to be specified if `include_top` is True, and 134 if no `weights` argument is specified. 135 default_size: Specifies the default image size of the model 136 classifier_activation: A `str` or callable. The activation function to use 137 on the "top" layer. Ignored unless `include_top=True`. Set 138 `classifier_activation=None` to return the logits of the "top" layer. 139 140 Returns: 141 A `keras.Model` instance. 142 143 Raises: 144 ValueError: In case of invalid argument for `weights`, 145 invalid input shape or invalid `penultimate_filters` value. 146 ValueError: if `classifier_activation` is not `softmax` or `None` when 147 using a pretrained top layer. 148 """ 149 if not (weights in {'imagenet', None} or file_io.file_exists_v2(weights)): 150 raise ValueError('The `weights` argument should be either ' 151 '`None` (random initialization), `imagenet` ' 152 '(pre-training on ImageNet), ' 153 'or the path to the weights file to be loaded.') 154 155 if weights == 'imagenet' and include_top and classes != 1000: 156 raise ValueError('If using `weights` as `"imagenet"` with `include_top` ' 157 'as true, `classes` should be 1000') 158 159 if (isinstance(input_shape, tuple) and None in input_shape and 160 weights == 'imagenet'): 161 raise ValueError('When specifying the input shape of a NASNet' 162 ' and loading `ImageNet` weights, ' 163 'the input_shape argument must be static ' 164 '(no None entries). Got: `input_shape=' + 165 str(input_shape) + '`.') 166 167 if default_size is None: 168 default_size = 331 169 170 # Determine proper input shape and default size. 171 input_shape = imagenet_utils.obtain_input_shape( 172 input_shape, 173 default_size=default_size, 174 min_size=32, 175 data_format=backend.image_data_format(), 176 require_flatten=True, 177 weights=weights) 178 179 if backend.image_data_format() != 'channels_last': 180 logging.warning('The NASNet family of models is only available ' 181 'for the input data format "channels_last" ' 182 '(width, height, channels). ' 183 'However your settings specify the default ' 184 'data format "channels_first" (channels, width, height).' 185 ' You should set `image_data_format="channels_last"` ' 186 'in your Keras config located at ~/.keras/keras.json. ' 187 'The model being returned right now will expect inputs ' 188 'to follow the "channels_last" data format.') 189 backend.set_image_data_format('channels_last') 190 old_data_format = 'channels_first' 191 else: 192 old_data_format = None 193 194 if input_tensor is None: 195 img_input = layers.Input(shape=input_shape) 196 else: 197 if not backend.is_keras_tensor(input_tensor): 198 img_input = layers.Input(tensor=input_tensor, shape=input_shape) 199 else: 200 img_input = input_tensor 201 202 if penultimate_filters % (24 * (filter_multiplier**2)) != 0: 203 raise ValueError( 204 'For NASNet-A models, the `penultimate_filters` must be a multiple ' 205 'of 24 * (`filter_multiplier` ** 2). Current value: %d' % 206 penultimate_filters) 207 208 channel_dim = 1 if backend.image_data_format() == 'channels_first' else -1 209 filters = penultimate_filters // 24 210 211 x = layers.Conv2D( 212 stem_block_filters, (3, 3), 213 strides=(2, 2), 214 padding='valid', 215 use_bias=False, 216 name='stem_conv1', 217 kernel_initializer='he_normal')( 218 img_input) 219 220 x = layers.BatchNormalization( 221 axis=channel_dim, momentum=0.9997, epsilon=1e-3, name='stem_bn1')( 222 x) 223 224 p = None 225 x, p = _reduction_a_cell( 226 x, p, filters // (filter_multiplier**2), block_id='stem_1') 227 x, p = _reduction_a_cell( 228 x, p, filters // filter_multiplier, block_id='stem_2') 229 230 for i in range(num_blocks): 231 x, p = _normal_a_cell(x, p, filters, block_id='%d' % (i)) 232 233 x, p0 = _reduction_a_cell( 234 x, p, filters * filter_multiplier, block_id='reduce_%d' % (num_blocks)) 235 236 p = p0 if not skip_reduction else p 237 238 for i in range(num_blocks): 239 x, p = _normal_a_cell( 240 x, p, filters * filter_multiplier, block_id='%d' % (num_blocks + i + 1)) 241 242 x, p0 = _reduction_a_cell( 243 x, 244 p, 245 filters * filter_multiplier**2, 246 block_id='reduce_%d' % (2 * num_blocks)) 247 248 p = p0 if not skip_reduction else p 249 250 for i in range(num_blocks): 251 x, p = _normal_a_cell( 252 x, 253 p, 254 filters * filter_multiplier**2, 255 block_id='%d' % (2 * num_blocks + i + 1)) 256 257 x = layers.Activation('relu')(x) 258 259 if include_top: 260 x = layers.GlobalAveragePooling2D()(x) 261 imagenet_utils.validate_activation(classifier_activation, weights) 262 x = layers.Dense(classes, activation=classifier_activation, 263 name='predictions')(x) 264 else: 265 if pooling == 'avg': 266 x = layers.GlobalAveragePooling2D()(x) 267 elif pooling == 'max': 268 x = layers.GlobalMaxPooling2D()(x) 269 270 # Ensure that the model takes into account 271 # any potential predecessors of `input_tensor`. 272 if input_tensor is not None: 273 inputs = layer_utils.get_source_inputs(input_tensor) 274 else: 275 inputs = img_input 276 277 model = training.Model(inputs, x, name='NASNet') 278 279 # Load weights. 280 if weights == 'imagenet': 281 if default_size == 224: # mobile version 282 if include_top: 283 weights_path = data_utils.get_file( 284 'nasnet_mobile.h5', 285 NASNET_MOBILE_WEIGHT_PATH, 286 cache_subdir='models', 287 file_hash='020fb642bf7360b370c678b08e0adf61') 288 else: 289 weights_path = data_utils.get_file( 290 'nasnet_mobile_no_top.h5', 291 NASNET_MOBILE_WEIGHT_PATH_NO_TOP, 292 cache_subdir='models', 293 file_hash='1ed92395b5b598bdda52abe5c0dbfd63') 294 model.load_weights(weights_path) 295 elif default_size == 331: # large version 296 if include_top: 297 weights_path = data_utils.get_file( 298 'nasnet_large.h5', 299 NASNET_LARGE_WEIGHT_PATH, 300 cache_subdir='models', 301 file_hash='11577c9a518f0070763c2b964a382f17') 302 else: 303 weights_path = data_utils.get_file( 304 'nasnet_large_no_top.h5', 305 NASNET_LARGE_WEIGHT_PATH_NO_TOP, 306 cache_subdir='models', 307 file_hash='d81d89dc07e6e56530c4e77faddd61b5') 308 model.load_weights(weights_path) 309 else: 310 raise ValueError('ImageNet weights can only be loaded with NASNetLarge' 311 ' or NASNetMobile') 312 elif weights is not None: 313 model.load_weights(weights) 314 315 if old_data_format: 316 backend.set_image_data_format(old_data_format) 317 318 return model 319 320 321@keras_export('keras.applications.nasnet.NASNetMobile', 322 'keras.applications.NASNetMobile') 323def NASNetMobile(input_shape=None, 324 include_top=True, 325 weights='imagenet', 326 input_tensor=None, 327 pooling=None, 328 classes=1000): 329 """Instantiates a Mobile NASNet model in ImageNet mode. 330 331 Reference: 332 - [Learning Transferable Architectures for Scalable Image Recognition]( 333 https://arxiv.org/abs/1707.07012) (CVPR 2018) 334 335 Optionally loads weights pre-trained on ImageNet. 336 Note that the data format convention used by the model is 337 the one specified in your Keras config at `~/.keras/keras.json`. 338 339 Note: each Keras Application expects a specific kind of input preprocessing. 340 For NASNet, call `tf.keras.applications.nasnet.preprocess_input` on your 341 inputs before passing them to the model. 342 343 Args: 344 input_shape: Optional shape tuple, only to be specified 345 if `include_top` is False (otherwise the input shape 346 has to be `(224, 224, 3)` for NASNetMobile 347 It should have exactly 3 inputs channels, 348 and width and height should be no smaller than 32. 349 E.g. `(224, 224, 3)` would be one valid value. 350 include_top: Whether to include the fully-connected 351 layer at the top of the network. 352 weights: `None` (random initialization) or 353 `imagenet` (ImageNet weights) 354 For loading `imagenet` weights, `input_shape` should be (224, 224, 3) 355 input_tensor: Optional Keras tensor (i.e. output of 356 `layers.Input()`) 357 to use as image input for the model. 358 pooling: Optional pooling mode for feature extraction 359 when `include_top` is `False`. 360 - `None` means that the output of the model 361 will be the 4D tensor output of the 362 last convolutional layer. 363 - `avg` means that global average pooling 364 will be applied to the output of the 365 last convolutional layer, and thus 366 the output of the model will be a 367 2D tensor. 368 - `max` means that global max pooling will 369 be applied. 370 classes: Optional number of classes to classify images 371 into, only to be specified if `include_top` is True, and 372 if no `weights` argument is specified. 373 374 Returns: 375 A Keras model instance. 376 377 Raises: 378 ValueError: In case of invalid argument for `weights`, 379 or invalid input shape. 380 RuntimeError: If attempting to run this model with a 381 backend that does not support separable convolutions. 382 """ 383 return NASNet( 384 input_shape, 385 penultimate_filters=1056, 386 num_blocks=4, 387 stem_block_filters=32, 388 skip_reduction=False, 389 filter_multiplier=2, 390 include_top=include_top, 391 weights=weights, 392 input_tensor=input_tensor, 393 pooling=pooling, 394 classes=classes, 395 default_size=224) 396 397 398@keras_export('keras.applications.nasnet.NASNetLarge', 399 'keras.applications.NASNetLarge') 400def NASNetLarge(input_shape=None, 401 include_top=True, 402 weights='imagenet', 403 input_tensor=None, 404 pooling=None, 405 classes=1000): 406 """Instantiates a NASNet model in ImageNet mode. 407 408 Reference: 409 - [Learning Transferable Architectures for Scalable Image Recognition]( 410 https://arxiv.org/abs/1707.07012) (CVPR 2018) 411 412 Optionally loads weights pre-trained on ImageNet. 413 Note that the data format convention used by the model is 414 the one specified in your Keras config at `~/.keras/keras.json`. 415 416 Note: each Keras Application expects a specific kind of input preprocessing. 417 For NASNet, call `tf.keras.applications.nasnet.preprocess_input` on your 418 inputs before passing them to the model. 419 420 Args: 421 input_shape: Optional shape tuple, only to be specified 422 if `include_top` is False (otherwise the input shape 423 has to be `(331, 331, 3)` for NASNetLarge. 424 It should have exactly 3 inputs channels, 425 and width and height should be no smaller than 32. 426 E.g. `(224, 224, 3)` would be one valid value. 427 include_top: Whether to include the fully-connected 428 layer at the top of the network. 429 weights: `None` (random initialization) or 430 `imagenet` (ImageNet weights) 431 For loading `imagenet` weights, `input_shape` should be (331, 331, 3) 432 input_tensor: Optional Keras tensor (i.e. output of 433 `layers.Input()`) 434 to use as image input for the model. 435 pooling: Optional pooling mode for feature extraction 436 when `include_top` is `False`. 437 - `None` means that the output of the model 438 will be the 4D tensor output of the 439 last convolutional layer. 440 - `avg` means that global average pooling 441 will be applied to the output of the 442 last convolutional layer, and thus 443 the output of the model will be a 444 2D tensor. 445 - `max` means that global max pooling will 446 be applied. 447 classes: Optional number of classes to classify images 448 into, only to be specified if `include_top` is True, and 449 if no `weights` argument is specified. 450 451 Returns: 452 A Keras model instance. 453 454 Raises: 455 ValueError: in case of invalid argument for `weights`, 456 or invalid input shape. 457 RuntimeError: If attempting to run this model with a 458 backend that does not support separable convolutions. 459 """ 460 return NASNet( 461 input_shape, 462 penultimate_filters=4032, 463 num_blocks=6, 464 stem_block_filters=96, 465 skip_reduction=True, 466 filter_multiplier=2, 467 include_top=include_top, 468 weights=weights, 469 input_tensor=input_tensor, 470 pooling=pooling, 471 classes=classes, 472 default_size=331) 473 474 475def _separable_conv_block(ip, 476 filters, 477 kernel_size=(3, 3), 478 strides=(1, 1), 479 block_id=None): 480 """Adds 2 blocks of [relu-separable conv-batchnorm]. 481 482 Args: 483 ip: Input tensor 484 filters: Number of output filters per layer 485 kernel_size: Kernel size of separable convolutions 486 strides: Strided convolution for downsampling 487 block_id: String block_id 488 489 Returns: 490 A Keras tensor 491 """ 492 channel_dim = 1 if backend.image_data_format() == 'channels_first' else -1 493 494 with backend.name_scope('separable_conv_block_%s' % block_id): 495 x = layers.Activation('relu')(ip) 496 if strides == (2, 2): 497 x = layers.ZeroPadding2D( 498 padding=imagenet_utils.correct_pad(x, kernel_size), 499 name='separable_conv_1_pad_%s' % block_id)(x) 500 conv_pad = 'valid' 501 else: 502 conv_pad = 'same' 503 x = layers.SeparableConv2D( 504 filters, 505 kernel_size, 506 strides=strides, 507 name='separable_conv_1_%s' % block_id, 508 padding=conv_pad, 509 use_bias=False, 510 kernel_initializer='he_normal')( 511 x) 512 x = layers.BatchNormalization( 513 axis=channel_dim, 514 momentum=0.9997, 515 epsilon=1e-3, 516 name='separable_conv_1_bn_%s' % (block_id))( 517 x) 518 x = layers.Activation('relu')(x) 519 x = layers.SeparableConv2D( 520 filters, 521 kernel_size, 522 name='separable_conv_2_%s' % block_id, 523 padding='same', 524 use_bias=False, 525 kernel_initializer='he_normal')( 526 x) 527 x = layers.BatchNormalization( 528 axis=channel_dim, 529 momentum=0.9997, 530 epsilon=1e-3, 531 name='separable_conv_2_bn_%s' % (block_id))( 532 x) 533 return x 534 535 536def _adjust_block(p, ip, filters, block_id=None): 537 """Adjusts the input `previous path` to match the shape of the `input`. 538 539 Used in situations where the output number of filters needs to be changed. 540 541 Args: 542 p: Input tensor which needs to be modified 543 ip: Input tensor whose shape needs to be matched 544 filters: Number of output filters to be matched 545 block_id: String block_id 546 547 Returns: 548 Adjusted Keras tensor 549 """ 550 channel_dim = 1 if backend.image_data_format() == 'channels_first' else -1 551 img_dim = 2 if backend.image_data_format() == 'channels_first' else -2 552 553 ip_shape = backend.int_shape(ip) 554 555 if p is not None: 556 p_shape = backend.int_shape(p) 557 558 with backend.name_scope('adjust_block'): 559 if p is None: 560 p = ip 561 562 elif p_shape[img_dim] != ip_shape[img_dim]: 563 with backend.name_scope('adjust_reduction_block_%s' % block_id): 564 p = layers.Activation('relu', name='adjust_relu_1_%s' % block_id)(p) 565 p1 = layers.AveragePooling2D((1, 1), 566 strides=(2, 2), 567 padding='valid', 568 name='adjust_avg_pool_1_%s' % block_id)( 569 p) 570 p1 = layers.Conv2D( 571 filters // 2, (1, 1), 572 padding='same', 573 use_bias=False, 574 name='adjust_conv_1_%s' % block_id, 575 kernel_initializer='he_normal')( 576 p1) 577 578 p2 = layers.ZeroPadding2D(padding=((0, 1), (0, 1)))(p) 579 p2 = layers.Cropping2D(cropping=((1, 0), (1, 0)))(p2) 580 p2 = layers.AveragePooling2D((1, 1), 581 strides=(2, 2), 582 padding='valid', 583 name='adjust_avg_pool_2_%s' % block_id)( 584 p2) 585 p2 = layers.Conv2D( 586 filters // 2, (1, 1), 587 padding='same', 588 use_bias=False, 589 name='adjust_conv_2_%s' % block_id, 590 kernel_initializer='he_normal')( 591 p2) 592 593 p = layers.concatenate([p1, p2], axis=channel_dim) 594 p = layers.BatchNormalization( 595 axis=channel_dim, 596 momentum=0.9997, 597 epsilon=1e-3, 598 name='adjust_bn_%s' % block_id)( 599 p) 600 601 elif p_shape[channel_dim] != filters: 602 with backend.name_scope('adjust_projection_block_%s' % block_id): 603 p = layers.Activation('relu')(p) 604 p = layers.Conv2D( 605 filters, (1, 1), 606 strides=(1, 1), 607 padding='same', 608 name='adjust_conv_projection_%s' % block_id, 609 use_bias=False, 610 kernel_initializer='he_normal')( 611 p) 612 p = layers.BatchNormalization( 613 axis=channel_dim, 614 momentum=0.9997, 615 epsilon=1e-3, 616 name='adjust_bn_%s' % block_id)( 617 p) 618 return p 619 620 621def _normal_a_cell(ip, p, filters, block_id=None): 622 """Adds a Normal cell for NASNet-A (Fig. 4 in the paper). 623 624 Args: 625 ip: Input tensor `x` 626 p: Input tensor `p` 627 filters: Number of output filters 628 block_id: String block_id 629 630 Returns: 631 A Keras tensor 632 """ 633 channel_dim = 1 if backend.image_data_format() == 'channels_first' else -1 634 635 with backend.name_scope('normal_A_block_%s' % block_id): 636 p = _adjust_block(p, ip, filters, block_id) 637 638 h = layers.Activation('relu')(ip) 639 h = layers.Conv2D( 640 filters, (1, 1), 641 strides=(1, 1), 642 padding='same', 643 name='normal_conv_1_%s' % block_id, 644 use_bias=False, 645 kernel_initializer='he_normal')( 646 h) 647 h = layers.BatchNormalization( 648 axis=channel_dim, 649 momentum=0.9997, 650 epsilon=1e-3, 651 name='normal_bn_1_%s' % block_id)( 652 h) 653 654 with backend.name_scope('block_1'): 655 x1_1 = _separable_conv_block( 656 h, filters, kernel_size=(5, 5), block_id='normal_left1_%s' % block_id) 657 x1_2 = _separable_conv_block( 658 p, filters, block_id='normal_right1_%s' % block_id) 659 x1 = layers.add([x1_1, x1_2], name='normal_add_1_%s' % block_id) 660 661 with backend.name_scope('block_2'): 662 x2_1 = _separable_conv_block( 663 p, filters, (5, 5), block_id='normal_left2_%s' % block_id) 664 x2_2 = _separable_conv_block( 665 p, filters, (3, 3), block_id='normal_right2_%s' % block_id) 666 x2 = layers.add([x2_1, x2_2], name='normal_add_2_%s' % block_id) 667 668 with backend.name_scope('block_3'): 669 x3 = layers.AveragePooling2D((3, 3), 670 strides=(1, 1), 671 padding='same', 672 name='normal_left3_%s' % (block_id))( 673 h) 674 x3 = layers.add([x3, p], name='normal_add_3_%s' % block_id) 675 676 with backend.name_scope('block_4'): 677 x4_1 = layers.AveragePooling2D((3, 3), 678 strides=(1, 1), 679 padding='same', 680 name='normal_left4_%s' % (block_id))( 681 p) 682 x4_2 = layers.AveragePooling2D((3, 3), 683 strides=(1, 1), 684 padding='same', 685 name='normal_right4_%s' % (block_id))( 686 p) 687 x4 = layers.add([x4_1, x4_2], name='normal_add_4_%s' % block_id) 688 689 with backend.name_scope('block_5'): 690 x5 = _separable_conv_block( 691 h, filters, block_id='normal_left5_%s' % block_id) 692 x5 = layers.add([x5, h], name='normal_add_5_%s' % block_id) 693 694 x = layers.concatenate([p, x1, x2, x3, x4, x5], 695 axis=channel_dim, 696 name='normal_concat_%s' % block_id) 697 return x, ip 698 699 700def _reduction_a_cell(ip, p, filters, block_id=None): 701 """Adds a Reduction cell for NASNet-A (Fig. 4 in the paper). 702 703 Args: 704 ip: Input tensor `x` 705 p: Input tensor `p` 706 filters: Number of output filters 707 block_id: String block_id 708 709 Returns: 710 A Keras tensor 711 """ 712 channel_dim = 1 if backend.image_data_format() == 'channels_first' else -1 713 714 with backend.name_scope('reduction_A_block_%s' % block_id): 715 p = _adjust_block(p, ip, filters, block_id) 716 717 h = layers.Activation('relu')(ip) 718 h = layers.Conv2D( 719 filters, (1, 1), 720 strides=(1, 1), 721 padding='same', 722 name='reduction_conv_1_%s' % block_id, 723 use_bias=False, 724 kernel_initializer='he_normal')( 725 h) 726 h = layers.BatchNormalization( 727 axis=channel_dim, 728 momentum=0.9997, 729 epsilon=1e-3, 730 name='reduction_bn_1_%s' % block_id)( 731 h) 732 h3 = layers.ZeroPadding2D( 733 padding=imagenet_utils.correct_pad(h, 3), 734 name='reduction_pad_1_%s' % block_id)( 735 h) 736 737 with backend.name_scope('block_1'): 738 x1_1 = _separable_conv_block( 739 h, 740 filters, (5, 5), 741 strides=(2, 2), 742 block_id='reduction_left1_%s' % block_id) 743 x1_2 = _separable_conv_block( 744 p, 745 filters, (7, 7), 746 strides=(2, 2), 747 block_id='reduction_right1_%s' % block_id) 748 x1 = layers.add([x1_1, x1_2], name='reduction_add_1_%s' % block_id) 749 750 with backend.name_scope('block_2'): 751 x2_1 = layers.MaxPooling2D((3, 3), 752 strides=(2, 2), 753 padding='valid', 754 name='reduction_left2_%s' % block_id)( 755 h3) 756 x2_2 = _separable_conv_block( 757 p, 758 filters, (7, 7), 759 strides=(2, 2), 760 block_id='reduction_right2_%s' % block_id) 761 x2 = layers.add([x2_1, x2_2], name='reduction_add_2_%s' % block_id) 762 763 with backend.name_scope('block_3'): 764 x3_1 = layers.AveragePooling2D((3, 3), 765 strides=(2, 2), 766 padding='valid', 767 name='reduction_left3_%s' % block_id)( 768 h3) 769 x3_2 = _separable_conv_block( 770 p, 771 filters, (5, 5), 772 strides=(2, 2), 773 block_id='reduction_right3_%s' % block_id) 774 x3 = layers.add([x3_1, x3_2], name='reduction_add3_%s' % block_id) 775 776 with backend.name_scope('block_4'): 777 x4 = layers.AveragePooling2D((3, 3), 778 strides=(1, 1), 779 padding='same', 780 name='reduction_left4_%s' % block_id)( 781 x1) 782 x4 = layers.add([x2, x4]) 783 784 with backend.name_scope('block_5'): 785 x5_1 = _separable_conv_block( 786 x1, filters, (3, 3), block_id='reduction_left4_%s' % block_id) 787 x5_2 = layers.MaxPooling2D((3, 3), 788 strides=(2, 2), 789 padding='valid', 790 name='reduction_right5_%s' % block_id)( 791 h3) 792 x5 = layers.add([x5_1, x5_2], name='reduction_add4_%s' % block_id) 793 794 x = layers.concatenate([x2, x3, x4, x5], 795 axis=channel_dim, 796 name='reduction_concat_%s' % block_id) 797 return x, ip 798 799 800@keras_export('keras.applications.nasnet.preprocess_input') 801def preprocess_input(x, data_format=None): 802 return imagenet_utils.preprocess_input(x, data_format=data_format, mode='tf') 803 804 805@keras_export('keras.applications.nasnet.decode_predictions') 806def decode_predictions(preds, top=5): 807 return imagenet_utils.decode_predictions(preds, top=top) 808 809 810preprocess_input.__doc__ = imagenet_utils.PREPROCESS_INPUT_DOC.format( 811 mode='', 812 ret=imagenet_utils.PREPROCESS_INPUT_RET_DOC_TF, 813 error=imagenet_utils.PREPROCESS_INPUT_ERROR_DOC) 814decode_predictions.__doc__ = imagenet_utils.decode_predictions.__doc__ 815