|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""Tests for object_detection.models.model_builder.""" |
|
|
|
from absl.testing import parameterized |
|
|
|
from google.protobuf import text_format |
|
from object_detection.builders import model_builder |
|
from object_detection.meta_architectures import faster_rcnn_meta_arch |
|
from object_detection.meta_architectures import rfcn_meta_arch |
|
from object_detection.meta_architectures import ssd_meta_arch |
|
from object_detection.protos import hyperparams_pb2 |
|
from object_detection.protos import losses_pb2 |
|
from object_detection.protos import model_pb2 |
|
from object_detection.utils import test_case |
|
|
|
|
|
class ModelBuilderTest(test_case.TestCase, parameterized.TestCase): |
|
|
|
def default_ssd_feature_extractor(self): |
|
raise NotImplementedError |
|
|
|
def default_faster_rcnn_feature_extractor(self): |
|
raise NotImplementedError |
|
|
|
def ssd_feature_extractors(self): |
|
raise NotImplementedError |
|
|
|
def faster_rcnn_feature_extractors(self): |
|
raise NotImplementedError |
|
|
|
def create_model(self, model_config, is_training=True): |
|
"""Builds a DetectionModel based on the model config. |
|
|
|
Args: |
|
model_config: A model.proto object containing the config for the desired |
|
DetectionModel. |
|
is_training: True if this model is being built for training purposes. |
|
|
|
Returns: |
|
DetectionModel based on the config. |
|
""" |
|
return model_builder.build(model_config, is_training=is_training) |
|
|
|
def create_default_ssd_model_proto(self): |
|
"""Creates a DetectionModel proto with ssd model fields populated.""" |
|
model_text_proto = """ |
|
ssd { |
|
feature_extractor { |
|
conv_hyperparams { |
|
regularizer { |
|
l2_regularizer { |
|
} |
|
} |
|
initializer { |
|
truncated_normal_initializer { |
|
} |
|
} |
|
} |
|
override_base_feature_extractor_hyperparams: true |
|
} |
|
box_coder { |
|
faster_rcnn_box_coder { |
|
} |
|
} |
|
matcher { |
|
argmax_matcher { |
|
} |
|
} |
|
similarity_calculator { |
|
iou_similarity { |
|
} |
|
} |
|
anchor_generator { |
|
ssd_anchor_generator { |
|
aspect_ratios: 1.0 |
|
} |
|
} |
|
image_resizer { |
|
fixed_shape_resizer { |
|
height: 320 |
|
width: 320 |
|
} |
|
} |
|
box_predictor { |
|
convolutional_box_predictor { |
|
conv_hyperparams { |
|
regularizer { |
|
l2_regularizer { |
|
} |
|
} |
|
initializer { |
|
truncated_normal_initializer { |
|
} |
|
} |
|
} |
|
} |
|
} |
|
loss { |
|
classification_loss { |
|
weighted_softmax { |
|
} |
|
} |
|
localization_loss { |
|
weighted_smooth_l1 { |
|
} |
|
} |
|
} |
|
}""" |
|
model_proto = model_pb2.DetectionModel() |
|
text_format.Merge(model_text_proto, model_proto) |
|
model_proto.ssd.feature_extractor.type = (self. |
|
default_ssd_feature_extractor()) |
|
return model_proto |
|
|
|
def create_default_faster_rcnn_model_proto(self): |
|
"""Creates a DetectionModel proto with FasterRCNN model fields populated.""" |
|
model_text_proto = """ |
|
faster_rcnn { |
|
inplace_batchnorm_update: false |
|
num_classes: 3 |
|
image_resizer { |
|
keep_aspect_ratio_resizer { |
|
min_dimension: 600 |
|
max_dimension: 1024 |
|
} |
|
} |
|
first_stage_anchor_generator { |
|
grid_anchor_generator { |
|
scales: [0.25, 0.5, 1.0, 2.0] |
|
aspect_ratios: [0.5, 1.0, 2.0] |
|
height_stride: 16 |
|
width_stride: 16 |
|
} |
|
} |
|
first_stage_box_predictor_conv_hyperparams { |
|
regularizer { |
|
l2_regularizer { |
|
} |
|
} |
|
initializer { |
|
truncated_normal_initializer { |
|
} |
|
} |
|
} |
|
initial_crop_size: 14 |
|
maxpool_kernel_size: 2 |
|
maxpool_stride: 2 |
|
second_stage_box_predictor { |
|
mask_rcnn_box_predictor { |
|
conv_hyperparams { |
|
regularizer { |
|
l2_regularizer { |
|
} |
|
} |
|
initializer { |
|
truncated_normal_initializer { |
|
} |
|
} |
|
} |
|
fc_hyperparams { |
|
op: FC |
|
regularizer { |
|
l2_regularizer { |
|
} |
|
} |
|
initializer { |
|
truncated_normal_initializer { |
|
} |
|
} |
|
} |
|
} |
|
} |
|
second_stage_post_processing { |
|
batch_non_max_suppression { |
|
score_threshold: 0.01 |
|
iou_threshold: 0.6 |
|
max_detections_per_class: 100 |
|
max_total_detections: 300 |
|
} |
|
score_converter: SOFTMAX |
|
} |
|
}""" |
|
model_proto = model_pb2.DetectionModel() |
|
text_format.Merge(model_text_proto, model_proto) |
|
(model_proto.faster_rcnn.feature_extractor.type |
|
) = self.default_faster_rcnn_feature_extractor() |
|
return model_proto |
|
|
|
def test_create_ssd_models_from_config(self): |
|
model_proto = self.create_default_ssd_model_proto() |
|
for extractor_type, extractor_class in self.ssd_feature_extractors().items( |
|
): |
|
model_proto.ssd.feature_extractor.type = extractor_type |
|
model = model_builder.build(model_proto, is_training=True) |
|
self.assertIsInstance(model, ssd_meta_arch.SSDMetaArch) |
|
self.assertIsInstance(model._feature_extractor, extractor_class) |
|
|
|
def test_create_ssd_fpn_model_from_config(self): |
|
model_proto = self.create_default_ssd_model_proto() |
|
model_proto.ssd.feature_extractor.fpn.min_level = 3 |
|
model_proto.ssd.feature_extractor.fpn.max_level = 7 |
|
model = model_builder.build(model_proto, is_training=True) |
|
self.assertEqual(model._feature_extractor._fpn_min_level, 3) |
|
self.assertEqual(model._feature_extractor._fpn_max_level, 7) |
|
|
|
|
|
@parameterized.named_parameters( |
|
{ |
|
'testcase_name': 'mask_rcnn_with_matmul', |
|
'use_matmul_crop_and_resize': False, |
|
'enable_mask_prediction': True |
|
}, |
|
{ |
|
'testcase_name': 'mask_rcnn_without_matmul', |
|
'use_matmul_crop_and_resize': True, |
|
'enable_mask_prediction': True |
|
}, |
|
{ |
|
'testcase_name': 'faster_rcnn_with_matmul', |
|
'use_matmul_crop_and_resize': False, |
|
'enable_mask_prediction': False |
|
}, |
|
{ |
|
'testcase_name': 'faster_rcnn_without_matmul', |
|
'use_matmul_crop_and_resize': True, |
|
'enable_mask_prediction': False |
|
}, |
|
) |
|
def test_create_faster_rcnn_models_from_config(self, |
|
use_matmul_crop_and_resize, |
|
enable_mask_prediction): |
|
model_proto = self.create_default_faster_rcnn_model_proto() |
|
faster_rcnn_config = model_proto.faster_rcnn |
|
faster_rcnn_config.use_matmul_crop_and_resize = use_matmul_crop_and_resize |
|
if enable_mask_prediction: |
|
faster_rcnn_config.second_stage_mask_prediction_loss_weight = 3.0 |
|
mask_predictor_config = ( |
|
faster_rcnn_config.second_stage_box_predictor.mask_rcnn_box_predictor) |
|
mask_predictor_config.predict_instance_masks = True |
|
|
|
for extractor_type, extractor_class in ( |
|
self.faster_rcnn_feature_extractors().items()): |
|
faster_rcnn_config.feature_extractor.type = extractor_type |
|
model = model_builder.build(model_proto, is_training=True) |
|
self.assertIsInstance(model, faster_rcnn_meta_arch.FasterRCNNMetaArch) |
|
self.assertIsInstance(model._feature_extractor, extractor_class) |
|
if enable_mask_prediction: |
|
self.assertAlmostEqual(model._second_stage_mask_loss_weight, 3.0) |
|
|
|
def test_create_faster_rcnn_model_from_config_with_example_miner(self): |
|
model_proto = self.create_default_faster_rcnn_model_proto() |
|
model_proto.faster_rcnn.hard_example_miner.num_hard_examples = 64 |
|
model = model_builder.build(model_proto, is_training=True) |
|
self.assertIsNotNone(model._hard_example_miner) |
|
|
|
def test_create_rfcn_model_from_config(self): |
|
model_proto = self.create_default_faster_rcnn_model_proto() |
|
rfcn_predictor_config = ( |
|
model_proto.faster_rcnn.second_stage_box_predictor.rfcn_box_predictor) |
|
rfcn_predictor_config.conv_hyperparams.op = hyperparams_pb2.Hyperparams.CONV |
|
for extractor_type, extractor_class in ( |
|
self.faster_rcnn_feature_extractors().items()): |
|
model_proto.faster_rcnn.feature_extractor.type = extractor_type |
|
model = model_builder.build(model_proto, is_training=True) |
|
self.assertIsInstance(model, rfcn_meta_arch.RFCNMetaArch) |
|
self.assertIsInstance(model._feature_extractor, extractor_class) |
|
|
|
@parameterized.parameters(True, False) |
|
def test_create_faster_rcnn_from_config_with_crop_feature( |
|
self, output_final_box_features): |
|
model_proto = self.create_default_faster_rcnn_model_proto() |
|
model_proto.faster_rcnn.output_final_box_features = ( |
|
output_final_box_features) |
|
_ = model_builder.build(model_proto, is_training=True) |
|
|
|
def test_invalid_model_config_proto(self): |
|
model_proto = '' |
|
with self.assertRaisesRegex( |
|
ValueError, 'model_config not of type model_pb2.DetectionModel.'): |
|
model_builder.build(model_proto, is_training=True) |
|
|
|
def test_unknown_meta_architecture(self): |
|
model_proto = model_pb2.DetectionModel() |
|
with self.assertRaisesRegex(ValueError, 'Unknown meta architecture'): |
|
model_builder.build(model_proto, is_training=True) |
|
|
|
def test_unknown_ssd_feature_extractor(self): |
|
model_proto = self.create_default_ssd_model_proto() |
|
model_proto.ssd.feature_extractor.type = 'unknown_feature_extractor' |
|
with self.assertRaises(ValueError): |
|
model_builder.build(model_proto, is_training=True) |
|
|
|
def test_unknown_faster_rcnn_feature_extractor(self): |
|
model_proto = self.create_default_faster_rcnn_model_proto() |
|
model_proto.faster_rcnn.feature_extractor.type = 'unknown_feature_extractor' |
|
with self.assertRaises(ValueError): |
|
model_builder.build(model_proto, is_training=True) |
|
|
|
def test_invalid_first_stage_nms_iou_threshold(self): |
|
model_proto = self.create_default_faster_rcnn_model_proto() |
|
model_proto.faster_rcnn.first_stage_nms_iou_threshold = 1.1 |
|
with self.assertRaisesRegex(ValueError, |
|
r'iou_threshold not in \[0, 1\.0\]'): |
|
model_builder.build(model_proto, is_training=True) |
|
model_proto.faster_rcnn.first_stage_nms_iou_threshold = -0.1 |
|
with self.assertRaisesRegex(ValueError, |
|
r'iou_threshold not in \[0, 1\.0\]'): |
|
model_builder.build(model_proto, is_training=True) |
|
|
|
def test_invalid_second_stage_batch_size(self): |
|
model_proto = self.create_default_faster_rcnn_model_proto() |
|
model_proto.faster_rcnn.first_stage_max_proposals = 1 |
|
model_proto.faster_rcnn.second_stage_batch_size = 2 |
|
with self.assertRaisesRegex( |
|
ValueError, 'second_stage_batch_size should be no greater ' |
|
'than first_stage_max_proposals.'): |
|
model_builder.build(model_proto, is_training=True) |
|
|
|
def test_invalid_faster_rcnn_batchnorm_update(self): |
|
model_proto = self.create_default_faster_rcnn_model_proto() |
|
model_proto.faster_rcnn.inplace_batchnorm_update = True |
|
with self.assertRaisesRegex(ValueError, |
|
'inplace batchnorm updates not supported'): |
|
model_builder.build(model_proto, is_training=True) |
|
|
|
def test_create_experimental_model(self): |
|
|
|
model_text_proto = """ |
|
experimental_model { |
|
name: 'model42' |
|
}""" |
|
|
|
build_func = lambda *args: 42 |
|
model_builder.EXPERIMENTAL_META_ARCH_BUILDER_MAP['model42'] = build_func |
|
model_proto = model_pb2.DetectionModel() |
|
text_format.Merge(model_text_proto, model_proto) |
|
|
|
self.assertEqual(model_builder.build(model_proto, is_training=True), 42) |
|
|