Skip to content

Commit 878dd88

Browse files
authored
Refine evaluator op types (#5208)
* refine evaluator op types * update * follow comments * update * fix v2 mnist case * fix v2 mnist case * update * update
1 parent f122a5d commit 878dd88

File tree

8 files changed

+108
-73
lines changed

8 files changed

+108
-73
lines changed

paddle/operators/accuracy_op.cc

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,35 @@ class AccuracyOp : public framework::OperatorWithKernel {
2222
using framework::OperatorWithKernel::OperatorWithKernel;
2323

2424
void InferShape(framework::InferShapeContext *ctx) const override {
25-
PADDLE_ENFORCE(ctx->HasInput("Inference"),
26-
"Input(Inference) of AccuracyOp should not be null.");
25+
PADDLE_ENFORCE(ctx->HasInput("Out"),
26+
"Input (Out) of accuracy op should not be null.");
27+
PADDLE_ENFORCE(ctx->HasInput("Indices"),
28+
"Input (Indices) of accuracy op should not be null.");
2729
PADDLE_ENFORCE(ctx->HasInput("Label"),
28-
"Input(Label) of AccuracyOp should not be null.");
30+
"Input (Label) of accuracy op should not be null.");
2931
PADDLE_ENFORCE(ctx->HasOutput("Accuracy"),
30-
"Output(Accuracy) of AccuracyOp should not be null.");
32+
"Output (Accuracy) of AccuracyOp should not be null.");
3133

32-
auto inference_dim = ctx->GetInputDim("Inference");
34+
auto inference_dim = ctx->GetInputDim("Out");
3335
auto label_dim = ctx->GetInputDim("Label");
36+
// Assume indices has same shape with infernece, because
37+
// it's the output of topk.
3438

3539
PADDLE_ENFORCE_EQ(label_dim.size(), 2, "label's rank must be 2.");
3640
PADDLE_ENFORCE_EQ(label_dim[1], 1, "label's second dimension must be 1");
3741
PADDLE_ENFORCE_EQ(inference_dim[0], label_dim[0],
38-
"inference size must be the same as label size");
42+
"the inference tensor's num_rows must be"
43+
" the same as label.");
3944

4045
ctx->SetOutputDim("Accuracy", {1});
41-
ctx->ShareLoD("Inference", /*->*/ "Accuracy");
46+
ctx->ShareLoD("Out", /*->*/ "Accuracy");
47+
}
48+
49+
protected:
50+
// IndicateDataType
51+
framework::DataType IndicateDataType(
52+
const framework::ExecutionContext &ctx) const override {
53+
return framework::ToDataType(ctx.Input<Tensor>("Out")->type());
4254
}
4355
};
4456

@@ -48,7 +60,8 @@ class AccuracyOpMaker : public framework::OpProtoAndCheckerMaker {
4860
framework::OpAttrChecker *op_checker)
4961
: OpProtoAndCheckerMaker(proto, op_checker) {
5062
// TODO(typhoonzero): support both inference value and indices.
51-
AddInput("Inference", "topk(indices) the network output");
63+
AddInput("Out", "topk (inferences) the network output");
64+
AddInput("Indices", "topk (indices) the network output");
5265
AddInput("Label", "Label of the training data");
5366
// TODO(typhoonzero): AddInput("Weight", ...
5467
AddOutput("Accuracy", "The accuracy of current batch");
@@ -59,7 +72,7 @@ The accuracy is:
5972
.. math::
6073
accuracy = \\frac{NumOfCorrectPredicts}{NumOfAllSamples})
6174
62-
Both the input `Inference` and `Label` can carry the LoD (Level of Details)
75+
Both the input `Out` and `Label` can carry the LoD (Level of Details)
6376
information, or not. But the output only shares the LoD with input `Inference`.
6477
)DOC");
6578
}
@@ -71,6 +84,8 @@ information, or not. But the output only shares the LoD with input `Inference`.
7184
namespace ops = paddle::operators;
7285
REGISTER_OPERATOR(accuracy, ops::AccuracyOp, ops::AccuracyOpMaker,
7386
paddle::framework::EmptyGradOpMaker);
74-
REGISTER_OP_CPU_KERNEL(
75-
accuracy, ops::AccuracyKernel<paddle::platform::CPUPlace, int>,
76-
ops::AccuracyKernel<paddle::platform::CPUPlace, int64_t>);
87+
// FIXME(typhoonzero): types of T is for infernece data.
88+
// label data is always int.
89+
REGISTER_OP_CPU_KERNEL(accuracy,
90+
ops::AccuracyKernel<paddle::platform::CPUPlace, float>,
91+
ops::AccuracyKernel<paddle::platform::CPUPlace, double>);

paddle/operators/accuracy_op.cu

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ namespace paddle {
2121
namespace operators {
2222
using platform::PADDLE_CUDA_NUM_THREADS;
2323

24-
template <typename T, int BlockSize>
25-
__global__ void AccuracyCudaKernel(const int N, const int D, const T* Xdata,
26-
const T* labeldata, float* accuracy) {
24+
template <int BlockSize>
25+
__global__ void AccuracyCudaKernel(const int N, const int D,
26+
const int64_t* Xdata,
27+
const int64_t* labeldata, float* accuracy) {
2728
int count = 0;
2829
__shared__ int total[BlockSize];
2930

@@ -52,13 +53,14 @@ class AccuracyOpCUDAKernel : public framework::OpKernel<T> {
5253
void Compute(const framework::ExecutionContext& ctx) const override {
5354
PADDLE_ENFORCE(platform::is_gpu_place(ctx.GetPlace()),
5455
"It must use GPUPlace.");
55-
auto* inference = ctx.Input<Tensor>("Inference");
56+
auto* inference = ctx.Input<Tensor>("Out");
57+
auto* indices = ctx.Input<Tensor>("Indices");
5658
auto* label = ctx.Input<Tensor>("Label");
5759
auto* accuracy = ctx.Output<Tensor>("Accuracy");
5860
// FIXME(typhoonzero): only support indices currently
5961
// if add support for output values, how to detect the data type?
60-
const T* inference_data = inference->data<T>();
61-
const T* label_data = label->data<T>();
62+
const int64_t* indices_data = indices->data<int64_t>();
63+
const int64_t* label_data = label->data<int64_t>();
6264
float* accuracy_data = accuracy->mutable_data<float>(ctx.GetPlace());
6365

6466
size_t num_samples = inference->dims()[0];
@@ -69,17 +71,19 @@ class AccuracyOpCUDAKernel : public framework::OpKernel<T> {
6971
return;
7072
}
7173

72-
AccuracyCudaKernel<T, PADDLE_CUDA_NUM_THREADS><<<
74+
AccuracyCudaKernel<PADDLE_CUDA_NUM_THREADS><<<
7375
1, PADDLE_CUDA_NUM_THREADS, 0,
7476
reinterpret_cast<const platform::CUDADeviceContext&>(
7577
ctx.device_context())
76-
.stream()>>>(num_samples, infer_width, inference_data, label_data,
78+
.stream()>>>(num_samples, infer_width, indices_data, label_data,
7779
accuracy_data);
7880
}
7981
};
8082

8183
} // namespace operators
8284
} // namespace paddle
8385

84-
REGISTER_OP_GPU_KERNEL(accuracy, paddle::operators::AccuracyOpCUDAKernel<int>,
85-
paddle::operators::AccuracyOpCUDAKernel<int64_t>);
86+
// FIXME(typhoonzero): types of T is for infernece data.
87+
// label data is always int
88+
REGISTER_OP_GPU_KERNEL(accuracy, paddle::operators::AccuracyOpCUDAKernel<float>,
89+
paddle::operators::AccuracyOpCUDAKernel<double>);

paddle/operators/accuracy_op.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,15 @@ template <typename Place, typename T>
3838
class AccuracyKernel : public framework::OpKernel<T> {
3939
public:
4040
void Compute(const framework::ExecutionContext& ctx) const override {
41-
auto* inference = ctx.Input<Tensor>("Inference");
41+
auto* inference = ctx.Input<Tensor>("Out");
42+
auto* indices = ctx.Input<Tensor>("Indices");
4243
auto* label = ctx.Input<Tensor>("Label");
4344
auto* accuracy = ctx.Output<Tensor>("Accuracy");
4445

4546
float* accuracy_data = accuracy->mutable_data<float>(ctx.GetPlace());
4647

47-
const T* inference_data = inference->data<T>();
48-
const T* label_data = label->data<T>();
48+
const int64_t* indices_data = indices->data<int64_t>();
49+
const int64_t* label_data = label->data<int64_t>();
4950

5051
size_t num_samples = inference->dims()[0];
5152
size_t class_dim = inference->dims()[1];
@@ -60,7 +61,7 @@ class AccuracyKernel : public framework::OpKernel<T> {
6061
for (size_t i = 0; i < num_samples; ++i) {
6162
PADDLE_ENFORCE_GE(label_data[i], 0, "label must >= 0");
6263
for (size_t j = 0; j < class_dim; ++j) {
63-
if (inference_data[i * class_dim + j] == label_data[i]) {
64+
if (indices_data[i * class_dim + j] == label_data[i]) {
6465
++num_correct;
6566
break;
6667
}

paddle/operators/auc_op.cc

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,45 @@ class AucOp : public framework::OperatorWithKernel {
2323

2424
protected:
2525
void InferShape(framework::InferShapeContext *ctx) const override {
26-
PADDLE_ENFORCE(ctx->HasInput("Inference"),
27-
"Input of Inference must be initialized.");
26+
PADDLE_ENFORCE(ctx->HasInput("Out"), "Input of Out must be initialized.");
27+
PADDLE_ENFORCE(ctx->HasInput("Indices"),
28+
"Input of Indices must be initialized.");
2829
PADDLE_ENFORCE(ctx->HasInput("Label"),
2930
"Input of Label must be initialized.");
30-
auto inference_dim = ctx->GetInputDim("Inference");
31-
auto label_dim = ctx->GetInputDim("Label");
31+
auto inference_height = ctx->GetInputDim("Out")[0];
32+
auto label_height = ctx->GetInputDim("Label")[0];
3233

33-
PADDLE_ENFORCE_EQ(inference_dim, label_dim,
34-
"inference and label should have same shape");
34+
PADDLE_ENFORCE_EQ(inference_height, label_height,
35+
"Out and Label should have same height.");
3536

3637
ctx->SetOutputDim("AUC", {1});
37-
ctx->ShareLoD("Inference", /*->*/ "AUC");
38+
ctx->ShareLoD("Out", /*->*/ "AUC");
39+
}
40+
41+
protected:
42+
// IndicateDataType
43+
framework::DataType IndicateDataType(
44+
const framework::ExecutionContext &ctx) const override {
45+
return framework::ToDataType(ctx.Input<Tensor>("Out")->type());
3846
}
3947
};
4048

4149
class AucOpMaker : public framework::OpProtoAndCheckerMaker {
4250
public:
4351
AucOpMaker(framework::OpProto *proto, framework::OpAttrChecker *op_checker)
4452
: OpProtoAndCheckerMaker(proto, op_checker) {
45-
AddInput("Inference",
46-
"A floating point tensor of arbitrary shape and whose values"
47-
"are in the range [0, 1].");
53+
AddInput("Out",
54+
"A floating point 2D tensor, values are in the range [0, 1]."
55+
"Each row is descend sorted. This input should be the"
56+
"output of topk."
57+
"Typically, this tensor indicates the probability of each label");
58+
AddInput("Indices",
59+
"An int 2D tensor, indicating the indices of original"
60+
"tensor before sort. Typically, this tensor indicates which label"
61+
"the probability stands for.");
4862
AddInput("Label",
49-
"A tensor whose shape matches "
50-
"Inference. Will be cast to bool.");
63+
"A 2D int tensor indicating the label of the training data."
64+
"The height is batch size and width is always 1.");
5165
// TODO(typhoonzero): support weight input
5266
AddOutput("AUC",
5367
"A scalar representing the "

paddle/operators/auc_op.h

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ template <typename Place, typename T>
2929
class AucKernel : public framework::OpKernel<T> {
3030
public:
3131
void Compute(const framework::ExecutionContext& ctx) const override {
32-
auto* inference = ctx.Input<Tensor>("Inference");
32+
auto* inference = ctx.Input<Tensor>("Out");
3333
auto* label = ctx.Input<Tensor>("Label");
3434
auto* auc = ctx.Output<Tensor>("AUC");
3535

@@ -46,18 +46,11 @@ class AucKernel : public framework::OpKernel<T> {
4646
thresholds_list[0] = 0.0f - kEpsilon;
4747
thresholds_list[num_thresholds - 1] = 1.0f + kEpsilon;
4848

49-
size_t num_samples = inference->numel();
49+
size_t batch_size = inference->dims()[0];
50+
size_t inference_width = inference->dims()[1];
5051

5152
const T* inference_data = inference->data<T>();
52-
Tensor label_casted;
53-
label_casted.Resize(label->dims());
54-
bool* label_casted_data = label_casted.mutable_data<bool>(ctx.GetPlace());
55-
56-
const int* label_data = label->data<int>();
57-
// cast label_data to bool
58-
for (size_t i = 0; i < num_samples; i++) {
59-
label_casted_data[i] = static_cast<bool>(label_data[i]);
60-
}
53+
const int64_t* label_data = label->data<int64_t>();
6154

6255
// Create local tensor for storing the curve: TP, FN, TN, FP
6356
// TODO(typhoonzero): use eigen op to caculate these values.
@@ -68,23 +61,27 @@ class AucKernel : public framework::OpKernel<T> {
6861
true_negative.Resize({num_thresholds});
6962
false_positive.Resize({num_thresholds});
7063

71-
int* tp_data = true_positive.mutable_data<int>(ctx.GetPlace());
72-
int* fn_data = false_negative.mutable_data<int>(ctx.GetPlace());
73-
int* tn_data = true_negative.mutable_data<int>(ctx.GetPlace());
74-
int* fp_data = false_positive.mutable_data<int>(ctx.GetPlace());
64+
int64_t* tp_data = true_positive.mutable_data<int64_t>(ctx.GetPlace());
65+
int64_t* fn_data = false_negative.mutable_data<int64_t>(ctx.GetPlace());
66+
int64_t* tn_data = true_negative.mutable_data<int64_t>(ctx.GetPlace());
67+
int64_t* fp_data = false_positive.mutable_data<int64_t>(ctx.GetPlace());
7568

7669
for (int idx_thresh = 0; idx_thresh < num_thresholds; idx_thresh++) {
7770
// caculate TP, FN, TN, FP for current thresh
78-
int tp = 0, fn = 0, tn = 0, fp = 0;
79-
for (size_t i = 0; i < num_samples; i++) {
80-
if (label_casted_data[i]) {
81-
if (inference_data[i] >= (thresholds_list[idx_thresh])) {
71+
int64_t tp = 0, fn = 0, tn = 0, fp = 0;
72+
for (size_t i = 0; i < batch_size; i++) {
73+
// NOTE: label_data used as bool, labels >0 will be treated as true.
74+
if (label_data[i]) {
75+
// use first(max) data in each row
76+
if (inference_data[i * inference_width] >=
77+
(thresholds_list[idx_thresh])) {
8278
tp++;
8379
} else {
8480
fn++;
8581
}
8682
} else {
87-
if (inference_data[i] >= (thresholds_list[idx_thresh])) {
83+
if (inference_data[i * inference_width] >=
84+
(thresholds_list[idx_thresh])) {
8885
fp++;
8986
} else {
9087
tn++;

python/paddle/v2/framework/layers.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,11 @@ def accuracy(input, label, k=1, **kwargs):
243243
acc_out = helper.create_tmp_variable(dtype=acc_out_dtype)
244244
helper.append_op(
245245
type="accuracy",
246-
inputs={"Inference": [topk_indices],
247-
"Label": [label]},
246+
inputs={
247+
"Out": [topk_out],
248+
"Indices": [topk_indices],
249+
"Label": [label]
250+
},
248251
outputs={"Accuracy": [acc_out]})
249252
return acc_out
250253

python/paddle/v2/framework/tests/test_accuracy_op.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ class TestAccuracyOp(OpTest):
77
def setUp(self):
88
self.op_type = "accuracy"
99
n = 8192
10-
infer = np.random.randint(0, 2, (n, 1)).astype("int")
11-
label = np.random.randint(0, 2, (n, 1)).astype("int")
12-
self.inputs = {'Inference': infer, "Label": label}
10+
infer = np.random.random((n, 1)).astype("float32")
11+
indices = np.random.randint(0, 2, (n, 1))
12+
label = np.random.randint(0, 2, (n, 1))
13+
self.inputs = {'Out': infer, 'Indices': indices, "Label": label}
1314
num_correct = 0
1415
for rowid in xrange(n):
15-
for ele in infer[rowid]:
16-
if ele == label[rowid][0]:
16+
for ele in indices[rowid]:
17+
if ele == label[rowid]:
1718
num_correct += 1
1819
break
1920
self.outputs = {

python/paddle/v2/framework/tests/test_auc_op.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
class TestAucOp(OpTest):
77
def setUp(self):
88
self.op_type = "auc"
9-
pred = np.random.random((128)).astype("float32")
10-
labels = np.random.randint(0, 2, (128, ))
9+
pred = np.random.random((128, 2)).astype("float32")
10+
indices = np.random.randint(0, 2, (128, 2))
11+
labels = np.random.randint(0, 2, (128, 1))
1112
num_thresholds = 200
12-
self.inputs = {'Inference': pred, 'Label': labels}
13+
self.inputs = {'Out': pred, 'Indices': indices, 'Label': labels}
1314
self.attrs = {'curve': 'ROC', 'num_thresholds': num_thresholds}
1415
# NOTE: sklearn use a different way to generate thresholds
1516
# which will cause the result differs slightly:
@@ -31,12 +32,12 @@ def setUp(self):
3132
tp, fn, tn, fp = 0, 0, 0, 0
3233
for i, lbl in enumerate(labels):
3334
if lbl:
34-
if pred[i] >= thresh:
35+
if pred[i, 0] >= thresh:
3536
tp += 1
3637
else:
3738
fn += 1
3839
else:
39-
if pred[i] >= thresh:
40+
if pred[i, 0] >= thresh:
4041
fp += 1
4142
else:
4243
tn += 1
@@ -62,6 +63,5 @@ def test_check_output(self):
6263
self.check_output()
6364

6465

65-
# TODO(typhoonzero): add this back till we fix it
66-
#if __name__ == "__main__":
67-
# unittest.main()
66+
if __name__ == "__main__":
67+
unittest.main()

0 commit comments

Comments
 (0)