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