Skip to content

Commit 0e1f82f

Browse files
authored
Fix bug in detection mAP evaluator. (#8778)
* Fix mAP evaluator bug. * Fix bug in detection mAP evaluator. * Fix unit testing. * Support to set background label index in detection mAP op.
1 parent 82b149c commit 0e1f82f

File tree

7 files changed

+69
-47
lines changed

7 files changed

+69
-47
lines changed

paddle/fluid/operators/detection_map_op.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,15 @@ class DetectionMAPOpMaker : public framework::OpProtoAndCheckerMaker {
142142
AddOutput("MAP",
143143
"(Tensor) A tensor with shape [1], store the mAP evaluate "
144144
"result of the detection.");
145-
145+
AddAttr<int>("class_num",
146+
"(int) "
147+
"The class number.");
148+
AddAttr<int>(
149+
"background_label",
150+
"(int, defalut: 0) "
151+
"The index of background label, the background label will be ignored. "
152+
"If set to -1, then all categories will be considered.")
153+
.SetDefault(0);
146154
AddAttr<float>(
147155
"overlap_threshold",
148156
"(float) "

paddle/fluid/operators/detection_map_op.h

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class DetectionMAPOpKernel : public framework::OpKernel<T> {
6969
float overlap_threshold = ctx.Attr<float>("overlap_threshold");
7070
float evaluate_difficult = ctx.Attr<bool>("evaluate_difficult");
7171
auto ap_type = GetAPType(ctx.Attr<std::string>("ap_type"));
72+
int class_num = ctx.Attr<int>("class_num");
7273

7374
auto label_lod = in_label->lod();
7475
auto detect_lod = in_detect->lod();
@@ -95,17 +96,19 @@ class DetectionMAPOpKernel : public framework::OpKernel<T> {
9596

9697
if (in_pos_count != nullptr && state) {
9798
GetInputPos(*in_pos_count, *in_true_pos, *in_false_pos, label_pos_count,
98-
true_pos, false_pos);
99+
true_pos, false_pos, class_num);
99100
}
100101

101102
CalcTrueAndFalsePositive(gt_boxes, detect_boxes, evaluate_difficult,
102103
overlap_threshold, label_pos_count, true_pos,
103104
false_pos);
104105

105-
T map = CalcMAP(ap_type, label_pos_count, true_pos, false_pos);
106+
int background_label = ctx.Attr<int>("background_label");
107+
T map = CalcMAP(ap_type, label_pos_count, true_pos, false_pos,
108+
background_label);
106109

107110
GetOutputPos(ctx, label_pos_count, true_pos, false_pos, *out_pos_count,
108-
*out_true_pos, *out_false_pos);
111+
*out_true_pos, *out_false_pos, class_num);
109112

110113
T* map_data = out_map->mutable_data<T>(ctx.GetPlace());
111114
map_data[0] = map;
@@ -190,24 +193,20 @@ class DetectionMAPOpKernel : public framework::OpKernel<T> {
190193
const std::map<int, std::vector<std::pair<T, int>>>& false_pos,
191194
framework::Tensor& output_pos_count,
192195
framework::LoDTensor& output_true_pos,
193-
framework::LoDTensor& output_false_pos) const {
194-
int max_class_id = 0;
196+
framework::LoDTensor& output_false_pos, const int class_num) const {
195197
int true_pos_count = 0;
196198
int false_pos_count = 0;
197-
for (auto it = label_pos_count.begin(); it != label_pos_count.end(); ++it) {
198-
int label = it->first;
199-
if (label > max_class_id) max_class_id = label;
200-
int label_num_pos = it->second;
201-
if (label_num_pos == 0 || true_pos.find(label) == true_pos.end())
202-
continue;
203-
auto label_true_pos = true_pos.find(label)->second;
204-
auto label_false_pos = false_pos.find(label)->second;
205-
true_pos_count += label_true_pos.size();
206-
false_pos_count += label_false_pos.size();
199+
for (auto it = true_pos.begin(); it != true_pos.end(); ++it) {
200+
auto tp = it->second;
201+
true_pos_count += tp.size();
202+
}
203+
for (auto it = false_pos.begin(); it != false_pos.end(); ++it) {
204+
auto fp = it->second;
205+
false_pos_count += fp.size();
207206
}
208207

209208
int* pos_count_data = output_pos_count.mutable_data<int>(
210-
framework::make_ddim({max_class_id + 1, 1}), ctx.GetPlace());
209+
framework::make_ddim({class_num, 1}), ctx.GetPlace());
211210

212211
T* true_pos_data = output_true_pos.mutable_data<T>(
213212
framework::make_ddim({true_pos_count, 2}), ctx.GetPlace());
@@ -217,7 +216,7 @@ class DetectionMAPOpKernel : public framework::OpKernel<T> {
217216
false_pos_count = 0;
218217
std::vector<size_t> true_pos_starts = {0};
219218
std::vector<size_t> false_pos_starts = {0};
220-
for (int i = 0; i <= max_class_id; ++i) {
219+
for (int i = 0; i < class_num; ++i) {
221220
auto it_count = label_pos_count.find(i);
222221
pos_count_data[i] = 0;
223222
if (it_count != label_pos_count.end()) {
@@ -258,17 +257,16 @@ class DetectionMAPOpKernel : public framework::OpKernel<T> {
258257
return;
259258
}
260259

261-
void GetInputPos(
262-
const framework::Tensor& input_pos_count,
263-
const framework::LoDTensor& input_true_pos,
264-
const framework::LoDTensor& input_false_pos,
265-
std::map<int, int>& label_pos_count,
266-
std::map<int, std::vector<std::pair<T, int>>>& true_pos,
267-
std::map<int, std::vector<std::pair<T, int>>>& false_pos) const {
260+
void GetInputPos(const framework::Tensor& input_pos_count,
261+
const framework::LoDTensor& input_true_pos,
262+
const framework::LoDTensor& input_false_pos,
263+
std::map<int, int>& label_pos_count,
264+
std::map<int, std::vector<std::pair<T, int>>>& true_pos,
265+
std::map<int, std::vector<std::pair<T, int>>>& false_pos,
266+
const int class_num) const {
268267
constexpr T kEPS = static_cast<T>(1e-6);
269-
int class_number = input_pos_count.dims()[0];
270268
const int* pos_count_data = input_pos_count.data<int>();
271-
for (int i = 0; i < class_number; ++i) {
269+
for (int i = 0; i < class_num; ++i) {
272270
label_pos_count[i] = pos_count_data[i];
273271
}
274272

@@ -391,17 +389,19 @@ class DetectionMAPOpKernel : public framework::OpKernel<T> {
391389
}
392390
}
393391

394-
T CalcMAP(
395-
APType ap_type, const std::map<int, int>& label_pos_count,
396-
const std::map<int, std::vector<std::pair<T, int>>>& true_pos,
397-
const std::map<int, std::vector<std::pair<T, int>>>& false_pos) const {
392+
T CalcMAP(APType ap_type, const std::map<int, int>& label_pos_count,
393+
const std::map<int, std::vector<std::pair<T, int>>>& true_pos,
394+
const std::map<int, std::vector<std::pair<T, int>>>& false_pos,
395+
const int background_label) const {
398396
T mAP = 0.0;
399397
int count = 0;
400398
for (auto it = label_pos_count.begin(); it != label_pos_count.end(); ++it) {
401399
int label = it->first;
402400
int label_num_pos = it->second;
403-
if (label_num_pos == 0 || true_pos.find(label) == true_pos.end())
401+
if (label_num_pos == background_label ||
402+
true_pos.find(label) == true_pos.end()) {
404403
continue;
404+
}
405405
auto label_true_pos = true_pos.find(label)->second;
406406
auto label_false_pos = false_pos.find(label)->second;
407407
// Compute average precision.
@@ -450,7 +450,7 @@ class DetectionMAPOpKernel : public framework::OpKernel<T> {
450450
}
451451
}
452452
if (count != 0) mAP /= count;
453-
return mAP * 100;
453+
return mAP;
454454
}
455455
}; // namespace operators
456456

paddle/fluid/operators/multiclass_nms_op.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ class MultiClassNMSOpMaker : public framework::OpProtoAndCheckerMaker {
324324
" Please note, M is equal to the 1st dimension of BBoxes. ");
325325
AddAttr<int>(
326326
"background_label",
327-
"(int64_t, defalut: 0) "
327+
"(int, defalut: 0) "
328328
"The index of background label, the background label will be ignored. "
329329
"If set to -1, then all categories will be considered.")
330330
.SetDefault(0);

python/paddle/fluid/evaluator.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,10 @@ class DetectionMAP(Evaluator):
312312
bounding box (bbox), which is a LoDTensor [N, 1].
313313
gt_box (Variable): The ground truth bounding box (bbox), which is a
314314
LoDTensor with shape [N, 6]. The layout is [xmin, ymin, xmax, ymax].
315+
class_num (int): The class number.
316+
background_label (int): The index of background label, the background
317+
label will be ignored. If set to -1, then all categories will be
318+
considered, 0 by defalut.
315319
overlap_threshold (float): The threshold for deciding true/false
316320
positive, 0.5 by defalut.
317321
evaluate_difficult (bool): Whether to consider difficult ground truth
@@ -345,6 +349,8 @@ def __init__(self,
345349
gt_label,
346350
gt_box,
347351
gt_difficult,
352+
class_num,
353+
background_label=0,
348354
overlap_threshold=0.5,
349355
evaluate_difficult=True,
350356
ap_version='integral'):
@@ -358,6 +364,8 @@ def __init__(self,
358364
map = layers.detection_map(
359365
input,
360366
label,
367+
class_num,
368+
background_label,
361369
overlap_threshold=overlap_threshold,
362370
evaluate_difficult=evaluate_difficult,
363371
ap_version=ap_version)
@@ -377,6 +385,8 @@ def __init__(self,
377385
accum_map = layers.detection_map(
378386
input,
379387
label,
388+
class_num,
389+
background_label,
380390
overlap_threshold=overlap_threshold,
381391
evaluate_difficult=evaluate_difficult,
382392
has_state=self.has_state,

python/paddle/fluid/layers/detection.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ class number, M is number of bounding boxes. For each category
151151
@autodoc()
152152
def detection_map(detect_res,
153153
label,
154+
class_num,
155+
background_label=0,
154156
overlap_threshold=0.3,
155157
evaluate_difficult=True,
156158
has_state=None,
@@ -192,7 +194,8 @@ def __create_var(type):
192194
attrs={
193195
'overlap_threshold': overlap_threshold,
194196
'evaluate_difficult': evaluate_difficult,
195-
'ap_type': ap_version
197+
'ap_type': ap_version,
198+
'class_num': class_num,
196199
})
197200
return map_out
198201

python/paddle/fluid/tests/test_detection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def test_detection_map(self):
158158
append_batch_size=False,
159159
dtype='float32')
160160

161-
map_out = layers.detection_map(detect_res=detect_res, label=label)
161+
map_out = layers.detection_map(detect_res, label, 21)
162162
self.assertIsNotNone(map_out)
163163
self.assertEqual(map_out.shape, (1, ))
164164
print(str(program))

python/paddle/fluid/tests/unittests/test_detection_map_op.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222

2323
class TestDetectionMAPOp(OpTest):
2424
def set_data(self):
25+
self.class_num = 4
2526
self.init_test_case()
26-
2727
self.mAP = [self.calc_map(self.tf_pos, self.tf_pos_lod)]
2828
self.label = np.array(self.label).astype('float32')
2929
self.detect = np.array(self.detect).astype('float32')
@@ -53,7 +53,8 @@ def set_data(self):
5353
self.attrs = {
5454
'overlap_threshold': self.overlap_threshold,
5555
'evaluate_difficult': self.evaluate_difficult,
56-
'ap_type': self.ap_type
56+
'ap_type': self.ap_type,
57+
'class_num': self.class_num
5758
}
5859

5960
self.out_class_pos_count = np.array(self.out_class_pos_count).astype(
@@ -126,12 +127,7 @@ def get_input_pos(class_pos_count, true_pos, true_pos_lod, false_pos,
126127
return class_pos_count_dict, true_pos_dict, false_pos_dict
127128

128129
def get_output_pos(label_count, true_pos, false_pos):
129-
max_label = 0
130-
for (label, label_pos_num) in label_count.items():
131-
if max_label < label:
132-
max_label = label
133-
134-
label_number = max_label + 1
130+
label_number = self.class_num
135131

136132
out_class_pos_count = []
137133
out_true_pos_lod = [0]
@@ -220,11 +216,16 @@ def get_accumulation(pos_list):
220216

221217
mAP += average_precisions
222218
count += 1
223-
self.out_class_pos_count, self.out_true_pos, self.out_true_pos_lod, self.out_false_pos, self.out_false_pos_lod = get_output_pos(
224-
label_count, true_pos, false_pos)
219+
pcnt, tp, tp_lod, fp, fp_lod = get_output_pos(label_count, true_pos,
220+
false_pos)
221+
self.out_class_pos_count = pcnt
222+
self.out_true_pos = tp
223+
self.out_true_pos_lod = tp_lod
224+
self.out_false_pos = fp
225+
self.out_false_pos_lod = fp_lod
225226
if count != 0:
226227
mAP /= count
227-
return mAP * 100.0
228+
return mAP
228229

229230
def setUp(self):
230231
self.op_type = "detection_map"

0 commit comments

Comments
 (0)