You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
12 KiB

6 years ago
  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. """A wrapper around the MobileNet v2 models for Keras, for object detection."""
  16. from __future__ import absolute_import
  17. from __future__ import division
  18. from __future__ import print_function
  19. import tensorflow as tf
  20. from object_detection.core import freezable_batch_norm
  21. from object_detection.utils import ops
  22. # pylint: disable=invalid-name
  23. # This method copied from the slim mobilenet base network code (same license)
  24. def _make_divisible(v, divisor, min_value=None):
  25. if min_value is None:
  26. min_value = divisor
  27. new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
  28. # Make sure that round down does not go down by more than 10%.
  29. if new_v < 0.9 * v:
  30. new_v += divisor
  31. return new_v
  32. class _LayersOverride(object):
  33. """Alternative Keras layers interface for the Keras MobileNetV2."""
  34. def __init__(self,
  35. batchnorm_training,
  36. default_batchnorm_momentum=0.999,
  37. conv_hyperparams=None,
  38. use_explicit_padding=False,
  39. alpha=1.0,
  40. min_depth=None):
  41. """Alternative tf.keras.layers interface, for use by the Keras MobileNetV2.
  42. It is used by the Keras applications kwargs injection API to
  43. modify the Mobilenet v2 Keras application with changes required by
  44. the Object Detection API.
  45. These injected interfaces make the following changes to the network:
  46. - Applies the Object Detection hyperparameter configuration
  47. - Supports FreezableBatchNorms
  48. - Adds support for a min number of filters for each layer
  49. - Makes the `alpha` parameter affect the final convolution block even if it
  50. is less than 1.0
  51. - Adds support for explicit padding of convolutions
  52. Args:
  53. batchnorm_training: Bool. Assigned to Batch norm layer `training` param
  54. when constructing `freezable_batch_norm.FreezableBatchNorm` layers.
  55. default_batchnorm_momentum: Float. When 'conv_hyperparams' is None,
  56. batch norm layers will be constructed using this value as the momentum.
  57. conv_hyperparams: A `hyperparams_builder.KerasLayerHyperparams` object
  58. containing hyperparameters for convolution ops. Optionally set to `None`
  59. to use default mobilenet_v2 layer builders.
  60. use_explicit_padding: If True, use 'valid' padding for convolutions,
  61. but explicitly pre-pads inputs so that the output dimensions are the
  62. same as if 'same' padding were used. Off by default.
  63. alpha: The width multiplier referenced in the MobileNetV2 paper. It
  64. modifies the number of filters in each convolutional layer.
  65. min_depth: Minimum number of filters in the convolutional layers.
  66. """
  67. self._alpha = alpha
  68. self._batchnorm_training = batchnorm_training
  69. self._default_batchnorm_momentum = default_batchnorm_momentum
  70. self._conv_hyperparams = conv_hyperparams
  71. self._use_explicit_padding = use_explicit_padding
  72. self._min_depth = min_depth
  73. self.regularizer = tf.keras.regularizers.l2(0.00004 * 0.5)
  74. self.initializer = tf.truncated_normal_initializer(stddev=0.09)
  75. def _FixedPaddingLayer(self, kernel_size):
  76. return tf.keras.layers.Lambda(lambda x: ops.fixed_padding(x, kernel_size))
  77. def Conv2D(self, filters, **kwargs):
  78. """Builds a Conv2D layer according to the current Object Detection config.
  79. Overrides the Keras MobileNetV2 application's convolutions with ones that
  80. follow the spec specified by the Object Detection hyperparameters.
  81. Args:
  82. filters: The number of filters to use for the convolution.
  83. **kwargs: Keyword args specified by the Keras application for
  84. constructing the convolution.
  85. Returns:
  86. A one-arg callable that will either directly apply a Keras Conv2D layer to
  87. the input argument, or that will first pad the input then apply a Conv2D
  88. layer.
  89. """
  90. # Make sure 'alpha' is always applied to the last convolution block's size
  91. # (This overrides the Keras application's functionality)
  92. if kwargs.get('name') == 'Conv_1' and self._alpha < 1.0:
  93. filters = _make_divisible(1280 * self._alpha, 8)
  94. # Apply the minimum depth to the convolution layers
  95. if (self._min_depth and (filters < self._min_depth)
  96. and not kwargs.get('name').endswith('expand')):
  97. filters = self._min_depth
  98. if self._conv_hyperparams:
  99. kwargs = self._conv_hyperparams.params(**kwargs)
  100. else:
  101. kwargs['kernel_regularizer'] = self.regularizer
  102. kwargs['kernel_initializer'] = self.initializer
  103. kwargs['padding'] = 'same'
  104. kernel_size = kwargs.get('kernel_size')
  105. if self._use_explicit_padding and kernel_size > 1:
  106. kwargs['padding'] = 'valid'
  107. def padded_conv(features):
  108. padded_features = self._FixedPaddingLayer(kernel_size)(features)
  109. return tf.keras.layers.Conv2D(filters, **kwargs)(padded_features)
  110. return padded_conv
  111. else:
  112. return tf.keras.layers.Conv2D(filters, **kwargs)
  113. def DepthwiseConv2D(self, **kwargs):
  114. """Builds a DepthwiseConv2D according to the Object Detection config.
  115. Overrides the Keras MobileNetV2 application's convolutions with ones that
  116. follow the spec specified by the Object Detection hyperparameters.
  117. Args:
  118. **kwargs: Keyword args specified by the Keras application for
  119. constructing the convolution.
  120. Returns:
  121. A one-arg callable that will either directly apply a Keras DepthwiseConv2D
  122. layer to the input argument, or that will first pad the input then apply
  123. the depthwise convolution.
  124. """
  125. if self._conv_hyperparams:
  126. kwargs = self._conv_hyperparams.params(**kwargs)
  127. else:
  128. kwargs['depthwise_initializer'] = self.initializer
  129. kwargs['padding'] = 'same'
  130. kernel_size = kwargs.get('kernel_size')
  131. if self._use_explicit_padding and kernel_size > 1:
  132. kwargs['padding'] = 'valid'
  133. def padded_depthwise_conv(features):
  134. padded_features = self._FixedPaddingLayer(kernel_size)(features)
  135. return tf.keras.layers.DepthwiseConv2D(**kwargs)(padded_features)
  136. return padded_depthwise_conv
  137. else:
  138. return tf.keras.layers.DepthwiseConv2D(**kwargs)
  139. def BatchNormalization(self, **kwargs):
  140. """Builds a normalization layer.
  141. Overrides the Keras application batch norm with the norm specified by the
  142. Object Detection configuration.
  143. Args:
  144. **kwargs: Only the name is used, all other params ignored.
  145. Required for matching `layers.BatchNormalization` calls in the Keras
  146. application.
  147. Returns:
  148. A normalization layer specified by the Object Detection hyperparameter
  149. configurations.
  150. """
  151. name = kwargs.get('name')
  152. if self._conv_hyperparams:
  153. return self._conv_hyperparams.build_batch_norm(
  154. training=self._batchnorm_training,
  155. name=name)
  156. else:
  157. return freezable_batch_norm.FreezableBatchNorm(
  158. training=self._batchnorm_training,
  159. epsilon=1e-3,
  160. momentum=self._default_batchnorm_momentum,
  161. name=name)
  162. def Input(self, shape):
  163. """Builds an Input layer.
  164. Overrides the Keras application Input layer with one that uses a
  165. tf.placeholder_with_default instead of a tf.placeholder. This is necessary
  166. to ensure the application works when run on a TPU.
  167. Args:
  168. shape: The shape for the input layer to use. (Does not include a dimension
  169. for the batch size).
  170. Returns:
  171. An input layer for the specified shape that internally uses a
  172. placeholder_with_default.
  173. """
  174. default_size = 224
  175. default_batch_size = 1
  176. shape = list(shape)
  177. default_shape = [default_size if dim is None else dim for dim in shape]
  178. input_tensor = tf.constant(0.0, shape=[default_batch_size] + default_shape)
  179. placeholder_with_default = tf.placeholder_with_default(
  180. input=input_tensor, shape=[None] + shape)
  181. return tf.keras.layers.Input(tensor=placeholder_with_default)
  182. # pylint: disable=unused-argument
  183. def ReLU(self, *args, **kwargs):
  184. """Builds an activation layer.
  185. Overrides the Keras application ReLU with the activation specified by the
  186. Object Detection configuration.
  187. Args:
  188. *args: Ignored, required to match the `tf.keras.ReLU` interface
  189. **kwargs: Only the name is used,
  190. required to match `tf.keras.ReLU` interface
  191. Returns:
  192. An activation layer specified by the Object Detection hyperparameter
  193. configurations.
  194. """
  195. name = kwargs.get('name')
  196. if self._conv_hyperparams:
  197. return self._conv_hyperparams.build_activation_layer(name=name)
  198. else:
  199. return tf.keras.layers.Lambda(tf.nn.relu6, name=name)
  200. # pylint: enable=unused-argument
  201. # pylint: disable=unused-argument
  202. def ZeroPadding2D(self, **kwargs):
  203. """Replaces explicit padding in the Keras application with a no-op.
  204. Args:
  205. **kwargs: Ignored, required to match the Keras applications usage.
  206. Returns:
  207. A no-op identity lambda.
  208. """
  209. return lambda x: x
  210. # pylint: enable=unused-argument
  211. # Forward all non-overridden methods to the keras layers
  212. def __getattr__(self, item):
  213. return getattr(tf.keras.layers, item)
  214. def mobilenet_v2(batchnorm_training,
  215. default_batchnorm_momentum=0.9997,
  216. conv_hyperparams=None,
  217. use_explicit_padding=False,
  218. alpha=1.0,
  219. min_depth=None,
  220. **kwargs):
  221. """Instantiates the MobileNetV2 architecture, modified for object detection.
  222. This wraps the MobileNetV2 tensorflow Keras application, but uses the
  223. Keras application's kwargs-based monkey-patching API to override the Keras
  224. architecture with the following changes:
  225. - Changes the default batchnorm momentum to 0.9997
  226. - Applies the Object Detection hyperparameter configuration
  227. - Supports FreezableBatchNorms
  228. - Adds support for a min number of filters for each layer
  229. - Makes the `alpha` parameter affect the final convolution block even if it
  230. is less than 1.0
  231. - Adds support for explicit padding of convolutions
  232. - Makes the Input layer use a tf.placeholder_with_default instead of a
  233. tf.placeholder, to work on TPUs.
  234. Args:
  235. batchnorm_training: Bool. Assigned to Batch norm layer `training` param
  236. when constructing `freezable_batch_norm.FreezableBatchNorm` layers.
  237. default_batchnorm_momentum: Float. When 'conv_hyperparams' is None,
  238. batch norm layers will be constructed using this value as the momentum.
  239. conv_hyperparams: A `hyperparams_builder.KerasLayerHyperparams` object
  240. containing hyperparameters for convolution ops. Optionally set to `None`
  241. to use default mobilenet_v2 layer builders.
  242. use_explicit_padding: If True, use 'valid' padding for convolutions,
  243. but explicitly pre-pads inputs so that the output dimensions are the
  244. same as if 'same' padding were used. Off by default.
  245. alpha: The width multiplier referenced in the MobileNetV2 paper. It
  246. modifies the number of filters in each convolutional layer.
  247. min_depth: Minimum number of filters in the convolutional layers.
  248. **kwargs: Keyword arguments forwarded directly to the
  249. `tf.keras.applications.MobilenetV2` method that constructs the Keras
  250. model.
  251. Returns:
  252. A Keras model instance.
  253. """
  254. layers_override = _LayersOverride(
  255. batchnorm_training,
  256. default_batchnorm_momentum=default_batchnorm_momentum,
  257. conv_hyperparams=conv_hyperparams,
  258. use_explicit_padding=use_explicit_padding,
  259. min_depth=min_depth,
  260. alpha=alpha)
  261. return tf.keras.applications.MobileNetV2(alpha=alpha,
  262. layers=layers_override,
  263. **kwargs)
  264. # pylint: enable=invalid-name