|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""Tests for mobilenet_v2.""" |
|
import itertools |
|
import numpy as np |
|
import tensorflow as tf |
|
|
|
from google.protobuf import text_format |
|
|
|
from object_detection.builders import hyperparams_builder |
|
from object_detection.models.keras_models import mobilenet_v2 |
|
from object_detection.models.keras_models import test_utils |
|
from object_detection.protos import hyperparams_pb2 |
|
from object_detection.utils import test_case |
|
|
|
_layers_to_check = [ |
|
'Conv1_relu', |
|
'block_1_expand_relu', 'block_1_depthwise_relu', 'block_1_project_BN', |
|
'block_2_expand_relu', 'block_2_depthwise_relu', 'block_2_project_BN', |
|
'block_3_expand_relu', 'block_3_depthwise_relu', 'block_3_project_BN', |
|
'block_4_expand_relu', 'block_4_depthwise_relu', 'block_4_project_BN', |
|
'block_5_expand_relu', 'block_5_depthwise_relu', 'block_5_project_BN', |
|
'block_6_expand_relu', 'block_6_depthwise_relu', 'block_6_project_BN', |
|
'block_7_expand_relu', 'block_7_depthwise_relu', 'block_7_project_BN', |
|
'block_8_expand_relu', 'block_8_depthwise_relu', 'block_8_project_BN', |
|
'block_9_expand_relu', 'block_9_depthwise_relu', 'block_9_project_BN', |
|
'block_10_expand_relu', 'block_10_depthwise_relu', 'block_10_project_BN', |
|
'block_11_expand_relu', 'block_11_depthwise_relu', 'block_11_project_BN', |
|
'block_12_expand_relu', 'block_12_depthwise_relu', 'block_12_project_BN', |
|
'block_13_expand_relu', 'block_13_depthwise_relu', 'block_13_project_BN', |
|
'block_14_expand_relu', 'block_14_depthwise_relu', 'block_14_project_BN', |
|
'block_15_expand_relu', 'block_15_depthwise_relu', 'block_15_project_BN', |
|
'block_16_expand_relu', 'block_16_depthwise_relu', 'block_16_project_BN', |
|
'out_relu'] |
|
|
|
|
|
class MobilenetV2Test(test_case.TestCase): |
|
|
|
def _build_conv_hyperparams(self): |
|
conv_hyperparams = hyperparams_pb2.Hyperparams() |
|
conv_hyperparams_text_proto = """ |
|
activation: RELU_6 |
|
regularizer { |
|
l2_regularizer { |
|
} |
|
} |
|
initializer { |
|
truncated_normal_initializer { |
|
} |
|
} |
|
batch_norm { |
|
train: true, |
|
scale: false, |
|
center: true, |
|
decay: 0.2, |
|
epsilon: 0.1, |
|
} |
|
""" |
|
text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams) |
|
return hyperparams_builder.KerasLayerHyperparams(conv_hyperparams) |
|
|
|
def _create_application_with_layer_outputs( |
|
self, layer_names, batchnorm_training, |
|
conv_hyperparams=None, |
|
use_explicit_padding=False, |
|
alpha=1.0, |
|
min_depth=None): |
|
"""Constructs Keras mobilenetv2 that extracts intermediate layer outputs.""" |
|
if not layer_names: |
|
layer_names = _layers_to_check |
|
full_model = mobilenet_v2.mobilenet_v2( |
|
batchnorm_training=batchnorm_training, |
|
conv_hyperparams=conv_hyperparams, |
|
weights=None, |
|
use_explicit_padding=use_explicit_padding, |
|
alpha=alpha, |
|
min_depth=min_depth, |
|
include_top=False) |
|
layer_outputs = [full_model.get_layer(name=layer).output |
|
for layer in layer_names] |
|
return tf.keras.Model( |
|
inputs=full_model.inputs, |
|
outputs=layer_outputs) |
|
|
|
def _check_returns_correct_shape( |
|
self, batch_size, image_height, image_width, depth_multiplier, |
|
expected_feature_map_shapes, use_explicit_padding=False, min_depth=None, |
|
layer_names=None): |
|
def graph_fn(image_tensor): |
|
model = self._create_application_with_layer_outputs( |
|
layer_names=layer_names, |
|
batchnorm_training=False, use_explicit_padding=use_explicit_padding, |
|
min_depth=min_depth, |
|
alpha=depth_multiplier) |
|
return model(image_tensor) |
|
|
|
image_tensor = np.random.rand(batch_size, image_height, image_width, |
|
3).astype(np.float32) |
|
feature_maps = self.execute(graph_fn, [image_tensor]) |
|
|
|
for feature_map, expected_shape in itertools.izip( |
|
feature_maps, expected_feature_map_shapes): |
|
self.assertAllEqual(feature_map.shape, expected_shape) |
|
|
|
def _check_returns_correct_shapes_with_dynamic_inputs( |
|
self, batch_size, image_height, image_width, depth_multiplier, |
|
expected_feature_map_shapes, use_explicit_padding=False, |
|
layer_names=None): |
|
def graph_fn(image_height, image_width): |
|
image_tensor = tf.random_uniform([batch_size, image_height, image_width, |
|
3], dtype=tf.float32) |
|
model = self._create_application_with_layer_outputs( |
|
layer_names=layer_names, |
|
batchnorm_training=False, use_explicit_padding=use_explicit_padding, |
|
alpha=depth_multiplier) |
|
return model(image_tensor) |
|
|
|
feature_maps = self.execute_cpu(graph_fn, [ |
|
np.array(image_height, dtype=np.int32), |
|
np.array(image_width, dtype=np.int32) |
|
]) |
|
|
|
for feature_map, expected_shape in itertools.izip( |
|
feature_maps, expected_feature_map_shapes): |
|
self.assertAllEqual(feature_map.shape, expected_shape) |
|
|
|
def _get_variables(self, depth_multiplier, layer_names=None): |
|
g = tf.Graph() |
|
with g.as_default(): |
|
preprocessed_inputs = tf.placeholder(tf.float32, (4, None, None, 3)) |
|
model = self._create_application_with_layer_outputs( |
|
layer_names=layer_names, |
|
batchnorm_training=False, use_explicit_padding=False, |
|
alpha=depth_multiplier) |
|
model(preprocessed_inputs) |
|
return g.get_collection(tf.GraphKeys.GLOBAL_VARIABLES) |
|
|
|
def test_returns_correct_shapes_128(self): |
|
image_height = 128 |
|
image_width = 128 |
|
depth_multiplier = 1.0 |
|
expected_feature_map_shape = ( |
|
test_utils.moblenet_v2_expected_feature_map_shape_128) |
|
|
|
self._check_returns_correct_shape( |
|
2, image_height, image_width, depth_multiplier, |
|
expected_feature_map_shape) |
|
|
|
def test_returns_correct_shapes_128_explicit_padding( |
|
self): |
|
image_height = 128 |
|
image_width = 128 |
|
depth_multiplier = 1.0 |
|
expected_feature_map_shape = ( |
|
test_utils.moblenet_v2_expected_feature_map_shape_128_explicit_padding) |
|
self._check_returns_correct_shape( |
|
2, image_height, image_width, depth_multiplier, |
|
expected_feature_map_shape, use_explicit_padding=True) |
|
|
|
def test_returns_correct_shapes_with_dynamic_inputs( |
|
self): |
|
image_height = 128 |
|
image_width = 128 |
|
depth_multiplier = 1.0 |
|
expected_feature_map_shape = ( |
|
test_utils.mobilenet_v2_expected_feature_map_shape_with_dynamic_inputs) |
|
self._check_returns_correct_shapes_with_dynamic_inputs( |
|
2, image_height, image_width, depth_multiplier, |
|
expected_feature_map_shape) |
|
|
|
def test_returns_correct_shapes_299(self): |
|
image_height = 299 |
|
image_width = 299 |
|
depth_multiplier = 1.0 |
|
expected_feature_map_shape = ( |
|
test_utils.moblenet_v2_expected_feature_map_shape_299) |
|
self._check_returns_correct_shape( |
|
2, image_height, image_width, depth_multiplier, |
|
expected_feature_map_shape) |
|
|
|
def test_returns_correct_shapes_enforcing_min_depth( |
|
self): |
|
image_height = 299 |
|
image_width = 299 |
|
depth_multiplier = 0.5**12 |
|
expected_feature_map_shape = ( |
|
test_utils.moblenet_v2_expected_feature_map_shape_enforcing_min_depth) |
|
self._check_returns_correct_shape( |
|
2, image_height, image_width, depth_multiplier, |
|
expected_feature_map_shape, min_depth=32) |
|
|
|
def test_hyperparam_override(self): |
|
hyperparams = self._build_conv_hyperparams() |
|
model = mobilenet_v2.mobilenet_v2( |
|
batchnorm_training=True, |
|
conv_hyperparams=hyperparams, |
|
weights=None, |
|
use_explicit_padding=False, |
|
alpha=1.0, |
|
min_depth=32, |
|
include_top=False) |
|
hyperparams.params() |
|
bn_layer = model.get_layer(name='block_5_project_BN') |
|
self.assertAllClose(bn_layer.momentum, 0.2) |
|
self.assertAllClose(bn_layer.epsilon, 0.1) |
|
|
|
def test_variable_count(self): |
|
depth_multiplier = 1 |
|
variables = self._get_variables(depth_multiplier) |
|
self.assertEqual(len(variables), 260) |
|
|
|
|
|
if __name__ == '__main__': |
|
tf.test.main() |
|
|