commit
99c9dbf505
@ -0,0 +1,184 @@
|
||||
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. */
|
||||
|
||||
#include "paddle/fluid/operators/detection_map_op.h"
|
||||
|
||||
namespace paddle {
|
||||
namespace operators {
|
||||
|
||||
using Tensor = framework::Tensor;
|
||||
|
||||
class DetectionMAPOp : public framework::OperatorWithKernel {
|
||||
public:
|
||||
using framework::OperatorWithKernel::OperatorWithKernel;
|
||||
|
||||
void InferShape(framework::InferShapeContext* ctx) const override {
|
||||
PADDLE_ENFORCE(ctx->HasInput("DetectRes"),
|
||||
"Input(DetectRes) of DetectionMAPOp should not be null.");
|
||||
PADDLE_ENFORCE(ctx->HasInput("Label"),
|
||||
"Input(Label) of DetectionMAPOp should not be null.");
|
||||
PADDLE_ENFORCE(
|
||||
ctx->HasOutput("AccumPosCount"),
|
||||
"Output(AccumPosCount) of DetectionMAPOp should not be null.");
|
||||
PADDLE_ENFORCE(
|
||||
ctx->HasOutput("AccumTruePos"),
|
||||
"Output(AccumTruePos) of DetectionMAPOp should not be null.");
|
||||
PADDLE_ENFORCE(
|
||||
ctx->HasOutput("AccumFalsePos"),
|
||||
"Output(AccumFalsePos) of DetectionMAPOp should not be null.");
|
||||
PADDLE_ENFORCE(ctx->HasOutput("MAP"),
|
||||
"Output(MAP) of DetectionMAPOp should not be null.");
|
||||
|
||||
auto det_dims = ctx->GetInputDim("DetectRes");
|
||||
PADDLE_ENFORCE_EQ(det_dims.size(), 2UL,
|
||||
"The rank of Input(DetectRes) must be 2, "
|
||||
"the shape is [N, 6].");
|
||||
PADDLE_ENFORCE_EQ(det_dims[1], 6UL,
|
||||
"The shape is of Input(DetectRes) [N, 6].");
|
||||
auto label_dims = ctx->GetInputDim("Label");
|
||||
PADDLE_ENFORCE_EQ(label_dims.size(), 2UL,
|
||||
"The rank of Input(Label) must be 2, "
|
||||
"the shape is [N, 6].");
|
||||
PADDLE_ENFORCE_EQ(label_dims[1], 6UL,
|
||||
"The shape is of Input(Label) [N, 6].");
|
||||
|
||||
if (ctx->HasInput("PosCount")) {
|
||||
PADDLE_ENFORCE(ctx->HasInput("TruePos"),
|
||||
"Input(TruePos) of DetectionMAPOp should not be null when "
|
||||
"Input(TruePos) is not null.");
|
||||
PADDLE_ENFORCE(
|
||||
ctx->HasInput("FalsePos"),
|
||||
"Input(FalsePos) of DetectionMAPOp should not be null when "
|
||||
"Input(FalsePos) is not null.");
|
||||
}
|
||||
|
||||
ctx->SetOutputDim("MAP", framework::make_ddim({1}));
|
||||
}
|
||||
|
||||
protected:
|
||||
framework::OpKernelType GetExpectedKernelType(
|
||||
const framework::ExecutionContext& ctx) const override {
|
||||
return framework::OpKernelType(
|
||||
framework::ToDataType(
|
||||
ctx.Input<framework::Tensor>("DetectRes")->type()),
|
||||
ctx.device_context());
|
||||
}
|
||||
};
|
||||
|
||||
class DetectionMAPOpMaker : public framework::OpProtoAndCheckerMaker {
|
||||
public:
|
||||
DetectionMAPOpMaker(OpProto* proto, OpAttrChecker* op_checker)
|
||||
: OpProtoAndCheckerMaker(proto, op_checker) {
|
||||
AddInput("DetectRes",
|
||||
"(LoDTensor) A 2-D LoDTensor with shape [M, 6] represents the "
|
||||
"detections. Each row has 6 values: "
|
||||
"[label, confidence, xmin, ymin, xmax, ymax], M is the total "
|
||||
"number of detect results in this mini-batch. For each instance, "
|
||||
"the offsets in first dimension are called LoD, the number of "
|
||||
"offset is N + 1, if LoD[i + 1] - LoD[i] == 0, means there is "
|
||||
"no detected data.");
|
||||
AddInput("Label",
|
||||
"(LoDTensor) A 2-D LoDTensor with shape[N, 6] represents the"
|
||||
"Labeled ground-truth data. Each row has 6 values: "
|
||||
"[label, is_difficult, xmin, ymin, xmax, ymax], N is the total "
|
||||
"number of ground-truth data in this mini-batch. For each "
|
||||
"instance, the offsets in first dimension are called LoD, "
|
||||
"the number of offset is N + 1, if LoD[i + 1] - LoD[i] == 0, "
|
||||
"means there is no ground-truth data.");
|
||||
AddInput("PosCount",
|
||||
"(Tensor) A tensor with shape [Ncls, 1], store the "
|
||||
"input positive example count of each class, Ncls is the count of "
|
||||
"input classification. "
|
||||
"This input is used to pass the AccumPosCount generated by the "
|
||||
"previous mini-batch when the multi mini-batches cumulative "
|
||||
"calculation carried out. "
|
||||
"When the input(PosCount) is empty, the cumulative "
|
||||
"calculation is not carried out, and only the results of the "
|
||||
"current mini-batch are calculated.")
|
||||
.AsDispensable();
|
||||
AddInput("TruePos",
|
||||
"(LoDTensor) A 2-D LoDTensor with shape [Ntp, 2], store the "
|
||||
"input true positive example of each class."
|
||||
"This input is used to pass the AccumTruePos generated by the "
|
||||
"previous mini-batch when the multi mini-batches cumulative "
|
||||
"calculation carried out. ")
|
||||
.AsDispensable();
|
||||
AddInput("FalsePos",
|
||||
"(LoDTensor) A 2-D LoDTensor with shape [Nfp, 2], store the "
|
||||
"input false positive example of each class."
|
||||
"This input is used to pass the AccumFalsePos generated by the "
|
||||
"previous mini-batch when the multi mini-batches cumulative "
|
||||
"calculation carried out. ")
|
||||
.AsDispensable();
|
||||
AddOutput("AccumPosCount",
|
||||
"(Tensor) A tensor with shape [Ncls, 1], store the "
|
||||
"positive example count of each class. It combines the input "
|
||||
"input(PosCount) and the positive example count computed from "
|
||||
"input(Detection) and input(Label).");
|
||||
AddOutput("AccumTruePos",
|
||||
"(LoDTensor) A LoDTensor with shape [Ntp', 2], store the "
|
||||
"true positive example of each class. It combines the "
|
||||
"input(TruePos) and the true positive examples computed from "
|
||||
"input(Detection) and input(Label).");
|
||||
AddOutput("AccumFalsePos",
|
||||
"(LoDTensor) A LoDTensor with shape [Nfp', 2], store the "
|
||||
"false positive example of each class. It combines the "
|
||||
"input(FalsePos) and the false positive examples computed from "
|
||||
"input(Detection) and input(Label).");
|
||||
AddOutput("MAP",
|
||||
"(Tensor) A tensor with shape [1], store the mAP evaluate "
|
||||
"result of the detection.");
|
||||
|
||||
AddAttr<float>(
|
||||
"overlap_threshold",
|
||||
"(float) "
|
||||
"The lower bound jaccard overlap threshold of detection output and "
|
||||
"ground-truth data.")
|
||||
.SetDefault(.3f);
|
||||
AddAttr<bool>("evaluate_difficult",
|
||||
"(bool, default true) "
|
||||
"Switch to control whether the difficult data is evaluated.")
|
||||
.SetDefault(true);
|
||||
AddAttr<std::string>("ap_type",
|
||||
"(string, default 'integral') "
|
||||
"The AP algorithm type, 'integral' or '11point'.")
|
||||
.SetDefault("integral")
|
||||
.InEnum({"integral", "11point"})
|
||||
.AddCustomChecker([](const std::string& ap_type) {
|
||||
PADDLE_ENFORCE_NE(GetAPType(ap_type), APType::kNone,
|
||||
"The ap_type should be 'integral' or '11point.");
|
||||
});
|
||||
AddComment(R"DOC(
|
||||
Detection mAP evaluate operator.
|
||||
The general steps are as follows. First, calculate the true positive and
|
||||
false positive according to the input of detection and labels, then
|
||||
calculate the mAP evaluate value.
|
||||
Supporting '11 point' and 'integral' mAP algorithm. Please get more information
|
||||
from the following articles:
|
||||
https://sanchom.wordpress.com/tag/average-precision/
|
||||
https://arxiv.org/abs/1512.02325
|
||||
|
||||
)DOC");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace operators
|
||||
} // namespace paddle
|
||||
|
||||
namespace ops = paddle::operators;
|
||||
REGISTER_OP_WITHOUT_GRADIENT(detection_map, ops::DetectionMAPOp,
|
||||
ops::DetectionMAPOpMaker);
|
||||
REGISTER_OP_CPU_KERNEL(
|
||||
detection_map, ops::DetectionMAPOpKernel<paddle::platform::CPUPlace, float>,
|
||||
ops::DetectionMAPOpKernel<paddle::platform::CPUPlace, double>);
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,76 @@
|
||||
# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
|
||||
import paddle.v2.fluid.layers as layers
|
||||
import paddle.v2.fluid.framework as framework
|
||||
import paddle.v2.fluid as fluid
|
||||
|
||||
|
||||
class TestPythonOperatorOverride(unittest.TestCase):
|
||||
def check_result(self, fn, place, dtype):
|
||||
shape = [9, 10]
|
||||
|
||||
x_data = np.random.random(size=shape).astype(dtype)
|
||||
y_data = np.random.random(size=shape).astype(dtype)
|
||||
python_out = fn(x_data, y_data)
|
||||
|
||||
x_var = layers.create_global_var(
|
||||
name='x', shape=shape, value=0.0, dtype=dtype, persistable=True)
|
||||
y_var = layers.create_global_var(
|
||||
name='y', shape=shape, value=0.0, dtype=dtype, persistable=True)
|
||||
out = fn(x_var, y_var)
|
||||
|
||||
exe = fluid.Executor(place)
|
||||
|
||||
exe.run(fluid.default_startup_program())
|
||||
fluid_out = exe.run(fluid.default_main_program(),
|
||||
feed={'x': x_data,
|
||||
'y': y_data},
|
||||
fetch_list=[out])
|
||||
|
||||
np.testing.assert_array_equal(python_out, fluid_out[0])
|
||||
|
||||
def test_override(self):
|
||||
# compare func to check
|
||||
compare_fns = [
|
||||
lambda _a, _b: _a == _b,
|
||||
lambda _a, _b: _a != _b,
|
||||
lambda _a, _b: _a < _b,
|
||||
lambda _a, _b: _a <= _b,
|
||||
lambda _a, _b: _a > _b,
|
||||
lambda _a, _b: _a >= _b,
|
||||
]
|
||||
|
||||
# places to check
|
||||
places = [fluid.CPUPlace()]
|
||||
if fluid.core.is_compiled_with_cuda():
|
||||
places.append(fluid.CUDAPlace(0))
|
||||
|
||||
# dtypes to check
|
||||
dtypes = ['int32', 'float32']
|
||||
|
||||
for place in places:
|
||||
for dtype in dtypes:
|
||||
for compare_fn in compare_fns:
|
||||
with framework.program_guard(framework.Program(),
|
||||
framework.Program()):
|
||||
self.check_result(compare_fn, place, dtype)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in new issue