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