diff --git a/ppdet/modeling/ops.py b/ppdet/modeling/ops.py index 3a8c8798c767f83da43011b7a1c0798b03741db0..2f4241601c26035b8ff8fdf65a81badb42685c69 100644 --- a/ppdet/modeling/ops.py +++ b/ppdet/modeling/ops.py @@ -34,7 +34,6 @@ __all__ = [ 'yolo_box', 'multiclass_nms', 'distribute_fpn_proposals', - 'collect_fpn_proposals', 'matrix_nms', 'batch_norm', 'mish', @@ -395,119 +394,6 @@ def iou_similarity(x, y, box_normalized=True, name=None): return out -@paddle.jit.not_to_static -def collect_fpn_proposals(multi_rois, - multi_scores, - min_level, - max_level, - post_nms_top_n, - rois_num_per_level=None, - name=None): - """ - - **This OP only supports LoDTensor as input**. Concat multi-level RoIs - (Region of Interest) and select N RoIs with respect to multi_scores. - This operation performs the following steps: - - 1. Choose num_level RoIs and scores as input: num_level = max_level - min_level - 2. Concat multi-level RoIs and scores - 3. Sort scores and select post_nms_top_n scores - 4. Gather RoIs by selected indices from scores - 5. Re-sort RoIs by corresponding batch_id - - Args: - multi_rois(list): List of RoIs to collect. Element in list is 2-D - LoDTensor with shape [N, 4] and data type is float32 or float64, - N is the number of RoIs. - multi_scores(list): List of scores of RoIs to collect. Element in list - is 2-D LoDTensor with shape [N, 1] and data type is float32 or - float64, N is the number of RoIs. - min_level(int): The lowest level of FPN layer to collect - max_level(int): The highest level of FPN layer to collect - post_nms_top_n(int): The number of selected RoIs - rois_num_per_level(list, optional): The List of RoIs' numbers. - Each element is 1-D Tensor which contains the RoIs' number of each - image on each level and the shape is [B] and data type is - int32, B is the number of images. If it is not None then return - a 1-D Tensor contains the output RoIs' number of each image and - the shape is [B]. Default: None - name(str, optional): For detailed information, please refer - to :ref:`api_guide_Name`. Usually name is no need to set and - None by default. - - Returns: - Variable: - - fpn_rois(Variable): 2-D LoDTensor with shape [N, 4] and data type is - float32 or float64. Selected RoIs. - - rois_num(Tensor): 1-D Tensor contains the RoIs's number of each - image. The shape is [B] and data type is int32. B is the number of - images. - - Examples: - .. code-block:: python - - import paddle - from ppdet.modeling import ops - paddle.enable_static() - multi_rois = [] - multi_scores = [] - for i in range(4): - multi_rois.append(paddle.static.data( - name='roi_'+str(i), shape=[None, 4], dtype='float32', lod_level=1)) - for i in range(4): - multi_scores.append(paddle.static.data( - name='score_'+str(i), shape=[None, 1], dtype='float32', lod_level=1)) - - fpn_rois = ops.collect_fpn_proposals( - multi_rois=multi_rois, - multi_scores=multi_scores, - min_level=2, - max_level=5, - post_nms_top_n=2000) - """ - check_type(multi_rois, 'multi_rois', list, 'collect_fpn_proposals') - check_type(multi_scores, 'multi_scores', list, 'collect_fpn_proposals') - num_lvl = max_level - min_level + 1 - input_rois = multi_rois[:num_lvl] - input_scores = multi_scores[:num_lvl] - - if in_dygraph_mode(): - assert rois_num_per_level is not None, "rois_num_per_level should not be None in dygraph mode." - attrs = ('post_nms_topN', post_nms_top_n) - output_rois, rois_num = core.ops.collect_fpn_proposals( - input_rois, input_scores, rois_num_per_level, *attrs) - return output_rois, rois_num - - else: - helper = LayerHelper('collect_fpn_proposals', **locals()) - dtype = helper.input_dtype('multi_rois') - check_dtype(dtype, 'multi_rois', ['float32', 'float64'], - 'collect_fpn_proposals') - output_rois = helper.create_variable_for_type_inference(dtype) - output_rois.stop_gradient = True - - inputs = { - 'MultiLevelRois': input_rois, - 'MultiLevelScores': input_scores, - } - outputs = {'FpnRois': output_rois} - if rois_num_per_level is not None: - inputs['MultiLevelRoIsNum'] = rois_num_per_level - rois_num = helper.create_variable_for_type_inference(dtype='int32') - rois_num.stop_gradient = True - outputs['RoisNum'] = rois_num - else: - rois_num = None - helper.append_op( - type='collect_fpn_proposals', - inputs=inputs, - outputs=outputs, - attrs={'post_nms_topN': post_nms_top_n}) - return output_rois, rois_num - - @paddle.jit.not_to_static def distribute_fpn_proposals(fpn_rois, min_level, @@ -1207,111 +1093,6 @@ def matrix_nms(bboxes, return output, rois_num, index -def bipartite_match(dist_matrix, - match_type=None, - dist_threshold=None, - name=None): - """ - - This operator implements a greedy bipartite matching algorithm, which is - used to obtain the matching with the maximum distance based on the input - distance matrix. For input 2D matrix, the bipartite matching algorithm can - find the matched column for each row (matched means the largest distance), - also can find the matched row for each column. And this operator only - calculate matched indices from column to row. For each instance, - the number of matched indices is the column number of the input distance - matrix. **The OP only supports CPU**. - - There are two outputs, matched indices and distance. - A simple description, this algorithm matched the best (maximum distance) - row entity to the column entity and the matched indices are not duplicated - in each row of ColToRowMatchIndices. If the column entity is not matched - any row entity, set -1 in ColToRowMatchIndices. - - NOTE: the input DistMat can be LoDTensor (with LoD) or Tensor. - If LoDTensor with LoD, the height of ColToRowMatchIndices is batch size. - If Tensor, the height of ColToRowMatchIndices is 1. - - NOTE: This API is a very low level API. It is used by :code:`ssd_loss` - layer. Please consider to use :code:`ssd_loss` instead. - - Args: - dist_matrix(Tensor): This input is a 2-D LoDTensor with shape - [K, M]. The data type is float32 or float64. It is pair-wise - distance matrix between the entities represented by each row and - each column. For example, assumed one entity is A with shape [K], - another entity is B with shape [M]. The dist_matrix[i][j] is the - distance between A[i] and B[j]. The bigger the distance is, the - better matching the pairs are. NOTE: This tensor can contain LoD - information to represent a batch of inputs. One instance of this - batch can contain different numbers of entities. - match_type(str, optional): The type of matching method, should be - 'bipartite' or 'per_prediction'. None ('bipartite') by default. - dist_threshold(float32, optional): If `match_type` is 'per_prediction', - this threshold is to determine the extra matching bboxes based - on the maximum distance, 0.5 by default. - name(str, optional): For detailed information, please refer - to :ref:`api_guide_Name`. Usually name is no need to set and - None by default. - - Returns: - Tuple: - - matched_indices(Tensor): A 2-D Tensor with shape [N, M]. The data - type is int32. N is the batch size. If match_indices[i][j] is -1, it - means B[j] does not match any entity in i-th instance. - Otherwise, it means B[j] is matched to row - match_indices[i][j] in i-th instance. The row number of - i-th instance is saved in match_indices[i][j]. - - matched_distance(Tensor): A 2-D Tensor with shape [N, M]. The data - type is float32. N is batch size. If match_indices[i][j] is -1, - match_distance[i][j] is also -1.0. Otherwise, assumed - match_distance[i][j] = d, and the row offsets of each instance - are called LoD. Then match_distance[i][j] = - dist_matrix[d+LoD[i]][j]. - - Examples: - - .. code-block:: python - import paddle - from ppdet.modeling import ops - from ppdet.modeling.utils import iou_similarity - - paddle.enable_static() - - x = paddle.static.data(name='x', shape=[None, 4], dtype='float32') - y = paddle.static.data(name='y', shape=[None, 4], dtype='float32') - iou = iou_similarity(x=x, y=y) - matched_indices, matched_dist = ops.bipartite_match(iou) - """ - check_variable_and_dtype(dist_matrix, 'dist_matrix', - ['float32', 'float64'], 'bipartite_match') - - if in_dygraph_mode(): - match_indices, match_distance = core.ops.bipartite_match( - dist_matrix, "match_type", match_type, "dist_threshold", - dist_threshold) - return match_indices, match_distance - - helper = LayerHelper('bipartite_match', **locals()) - match_indices = helper.create_variable_for_type_inference(dtype='int32') - match_distance = helper.create_variable_for_type_inference( - dtype=dist_matrix.dtype) - helper.append_op( - type='bipartite_match', - inputs={'DistMat': dist_matrix}, - attrs={ - 'match_type': match_type, - 'dist_threshold': dist_threshold, - }, - outputs={ - 'ColToRowMatchIndices': match_indices, - 'ColToRowMatchDist': match_distance - }) - return match_indices, match_distance - - @paddle.jit.not_to_static def box_coder(prior_box, prior_box_var, diff --git a/ppdet/modeling/tests/test_ops.py b/ppdet/modeling/tests/test_ops.py index d4b5747487d3ee49627e4fe8aecec31cf2759ae2..b725cacfd7996ce27ae670db1a32ccd8c40c19a5 100644 --- a/ppdet/modeling/tests/test_ops.py +++ b/ppdet/modeling/tests/test_ops.py @@ -50,127 +50,6 @@ def softmax(x): return exps / np.sum(exps) -class TestCollectFpnProposals(LayerTest): - def test_collect_fpn_proposals(self): - multi_bboxes_np = [] - multi_scores_np = [] - rois_num_per_level_np = [] - for i in range(4): - bboxes_np = np.random.rand(5, 4).astype('float32') - scores_np = np.random.rand(5, 1).astype('float32') - rois_num = np.array([2, 3]).astype('int32') - multi_bboxes_np.append(bboxes_np) - multi_scores_np.append(scores_np) - rois_num_per_level_np.append(rois_num) - - with self.static_graph(): - multi_bboxes = [] - multi_scores = [] - rois_num_per_level = [] - for i in range(4): - bboxes = paddle.static.data( - name='rois' + str(i), - shape=[5, 4], - dtype='float32', - lod_level=1) - scores = paddle.static.data( - name='scores' + str(i), - shape=[5, 1], - dtype='float32', - lod_level=1) - rois_num = paddle.static.data( - name='rois_num' + str(i), shape=[None], dtype='int32') - - multi_bboxes.append(bboxes) - multi_scores.append(scores) - rois_num_per_level.append(rois_num) - - fpn_rois, rois_num = ops.collect_fpn_proposals( - multi_bboxes, - multi_scores, - 2, - 5, - 10, - rois_num_per_level=rois_num_per_level) - feed = {} - for i in range(4): - feed['rois' + str(i)] = multi_bboxes_np[i] - feed['scores' + str(i)] = multi_scores_np[i] - feed['rois_num' + str(i)] = rois_num_per_level_np[i] - fpn_rois_stat, rois_num_stat = self.get_static_graph_result( - feed=feed, fetch_list=[fpn_rois, rois_num], with_lod=True) - fpn_rois_stat = np.array(fpn_rois_stat) - rois_num_stat = np.array(rois_num_stat) - - with self.dynamic_graph(): - multi_bboxes_dy = [] - multi_scores_dy = [] - rois_num_per_level_dy = [] - for i in range(4): - bboxes_dy = base.to_variable(multi_bboxes_np[i]) - scores_dy = base.to_variable(multi_scores_np[i]) - rois_num_dy = base.to_variable(rois_num_per_level_np[i]) - multi_bboxes_dy.append(bboxes_dy) - multi_scores_dy.append(scores_dy) - rois_num_per_level_dy.append(rois_num_dy) - fpn_rois_dy, rois_num_dy = ops.collect_fpn_proposals( - multi_bboxes_dy, - multi_scores_dy, - 2, - 5, - 10, - rois_num_per_level=rois_num_per_level_dy) - fpn_rois_dy = fpn_rois_dy.numpy() - rois_num_dy = rois_num_dy.numpy() - - self.assertTrue(np.array_equal(fpn_rois_stat, fpn_rois_dy)) - self.assertTrue(np.array_equal(rois_num_stat, rois_num_dy)) - - def test_collect_fpn_proposals_error(self): - def generate_input(bbox_type, score_type, name): - multi_bboxes = [] - multi_scores = [] - for i in range(4): - bboxes = paddle.static.data( - name='rois' + name + str(i), - shape=[10, 4], - dtype=bbox_type, - lod_level=1) - scores = paddle.static.data( - name='scores' + name + str(i), - shape=[10, 1], - dtype=score_type, - lod_level=1) - multi_bboxes.append(bboxes) - multi_scores.append(scores) - return multi_bboxes, multi_scores - - with self.static_graph(): - bbox1 = paddle.static.data( - name='rois', shape=[5, 10, 4], dtype='float32', lod_level=1) - score1 = paddle.static.data( - name='scores', shape=[5, 10, 1], dtype='float32', lod_level=1) - bbox2, score2 = generate_input('int32', 'float32', '2') - self.assertRaises( - TypeError, - ops.collect_fpn_proposals, - multi_rois=bbox1, - multi_scores=score1, - min_level=2, - max_level=5, - post_nms_top_n=2000) - self.assertRaises( - TypeError, - ops.collect_fpn_proposals, - multi_rois=bbox2, - multi_scores=score2, - min_level=2, - max_level=5, - post_nms_top_n=2000) - - paddle.disable_static() - - class TestDistributeFpnProposals(LayerTest): def test_distribute_fpn_proposals(self): rois_np = np.random.rand(10, 4).astype('float32') @@ -383,31 +262,6 @@ class TestIoUSimilarity(LayerTest): self.assertTrue(np.array_equal(iou_np, iou_dy_np)) -class TestBipartiteMatch(LayerTest): - def test_bipartite_match(self): - distance = np.random.random((20, 10)).astype('float32') - with self.static_graph(): - x = paddle.static.data(name='x', shape=[20, 10], dtype='float32') - - match_indices, match_dist = ops.bipartite_match( - x, match_type='per_prediction', dist_threshold=0.5) - match_indices_np, match_dist_np = self.get_static_graph_result( - feed={'x': distance, }, - fetch_list=[match_indices, match_dist], - with_lod=False) - - with self.dynamic_graph(): - x_dy = base.to_variable(distance) - - match_indices_dy, match_dist_dy = ops.bipartite_match( - x_dy, match_type='per_prediction', dist_threshold=0.5) - match_indices_dy_np = match_indices_dy.numpy() - match_dist_dy_np = match_dist_dy.numpy() - - self.assertTrue(np.array_equal(match_indices_np, match_indices_dy_np)) - self.assertTrue(np.array_equal(match_dist_np, match_dist_dy_np)) - - class TestYoloBox(LayerTest): def test_yolo_box(self):