• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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