Skip to content

Commit dcf3ffd

Browse files
kavyasrinetdzhwinter
authored andcommitted
Adding log loss operator (#5854)
* Adding log loss operator * Removing comments
1 parent f90c06b commit dcf3ffd

File tree

4 files changed

+245
-0
lines changed

4 files changed

+245
-0
lines changed

paddle/operators/log_loss_op.cc

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#include "paddle/operators/log_loss_op.h"
16+
17+
namespace paddle {
18+
namespace operators {
19+
20+
class LogLossOp : public framework::OperatorWithKernel {
21+
public:
22+
using framework::OperatorWithKernel::OperatorWithKernel;
23+
24+
void InferShape(framework::InferShapeContext* ctx) const override {
25+
PADDLE_ENFORCE(ctx->HasInput("Predicted"),
26+
"Input(Predicted) must be initialized.");
27+
PADDLE_ENFORCE(ctx->HasInput("Labels"),
28+
"Input(Labels) must be initialized.");
29+
30+
auto pred_dims = ctx->GetInputDim("Predicted");
31+
auto label_dims = ctx->GetInputDim("Labels");
32+
33+
PADDLE_ENFORCE_EQ(pred_dims, label_dims);
34+
PADDLE_ENFORCE_EQ(pred_dims.size(), 2,
35+
"The rank of Input(Predicted) must be 2 and the shape is "
36+
"[batch_size, 1].");
37+
PADDLE_ENFORCE_EQ(pred_dims[1], 1,
38+
"Each row of Input(Predicted) contains a real value, "
39+
"so the 2nd dimension of Input(X) must be 1.");
40+
41+
ctx->SetOutputDim("Loss", {pred_dims[0], 1});
42+
ctx->ShareLoD("Predicted", "Loss");
43+
}
44+
};
45+
46+
template <typename AttrType>
47+
class LogLossOpMaker : public framework::OpProtoAndCheckerMaker {
48+
public:
49+
LogLossOpMaker(framework::OpProto* proto,
50+
framework::OpAttrChecker* op_checker)
51+
: OpProtoAndCheckerMaker(proto, op_checker) {
52+
AddInput("Predicted",
53+
"The input value (Predicted) of Log loss op."
54+
"Predicted is a 2-D tensor with shape [batch_size, 1].");
55+
AddInput("Labels",
56+
"The target value (Labels) of Log loss op."
57+
"Labels is a 2-D tensor with shape [batch_size, 1].");
58+
AddOutput("Loss",
59+
"The output tensor with shape [batch_size, 1] "
60+
"which represents the log loss.");
61+
AddAttr<AttrType>("epsilon", "Epsilon in log loss.");
62+
AddComment(R"DOC(
63+
LogLoss Operator.
64+
65+
Log loss is a loss function used for binary classification. Log Loss quantifies
66+
the accuracy of a classifier by penalising false classifications. Minimising the
67+
Log Loss is equivalent to maximising the accuracy of the classifier. We define
68+
Predicted as the values predicted by our model and Labels as the target ground
69+
truth value. Log loss can evaluate how close the predicted values are to the
70+
target. The shapes of Predicted and Labels are both [batch_size, 1].
71+
The equation is:
72+
73+
$$
74+
Loss = - Labels * log(Predicted + \epsilon) -
75+
(1 - Labels) * log(1 - Predicted + \epsilon)
76+
$$
77+
78+
)DOC");
79+
}
80+
};
81+
82+
class LogLossGradOp : public framework::OperatorWithKernel {
83+
public:
84+
using framework::OperatorWithKernel::OperatorWithKernel;
85+
86+
void InferShape(framework::InferShapeContext* ctx) const override {
87+
PADDLE_ENFORCE(ctx->HasInput("Predicted"),
88+
"Input(Predicted) should not be null.");
89+
PADDLE_ENFORCE(ctx->HasInput("Labels"),
90+
"Input(Labels) should not be null.");
91+
PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Loss")),
92+
"Input(Loss@GRAD) should not be null.");
93+
PADDLE_ENFORCE(ctx->HasOutput(framework::GradVarName("Predicted")),
94+
"Output(Predicted@GRAD) should not be null.");
95+
96+
auto pred_dims = ctx->GetInputDim("Predicted");
97+
auto label_dims = ctx->GetInputDim("Labels");
98+
auto loss_grad_dims = ctx->GetInputDim(framework::GradVarName("Loss"));
99+
PADDLE_ENFORCE_EQ(loss_grad_dims, pred_dims);
100+
101+
auto pred_grad_name = framework::GradVarName("Predicted");
102+
ctx->SetOutputDim(pred_grad_name, pred_dims);
103+
}
104+
};
105+
106+
} // namespace operators
107+
} // namespace paddle
108+
109+
namespace ops = paddle::operators;
110+
REGISTER_OP(log_loss, ops::LogLossOp, ops::LogLossOpMaker<float>, log_loss_grad,
111+
ops::LogLossGradOp);
112+
REGISTER_OP_CPU_KERNEL(log_loss,
113+
ops::LogLossKernel<paddle::platform::CPUPlace, float>);
114+
REGISTER_OP_CPU_KERNEL(
115+
log_loss_grad, ops::LogLossGradKernel<paddle::platform::CPUPlace, float>);

paddle/operators/log_loss_op.cu

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#define EIGEN_USE_GPU
16+
#include "paddle/operators/log_loss_op.h"
17+
18+
namespace ops = paddle::operators;
19+
REGISTER_OP_GPU_KERNEL(log_loss,
20+
ops::LogLossKernel<paddle::platform::GPUPlace, float>);
21+
REGISTER_OP_GPU_KERNEL(
22+
log_loss_grad, ops::LogLossGradKernel<paddle::platform::GPUPlace, float>);

paddle/operators/log_loss_op.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#pragma once
16+
#include "paddle/framework/eigen.h"
17+
#include "paddle/framework/op_registry.h"
18+
19+
namespace paddle {
20+
namespace operators {
21+
22+
using Tensor = framework::Tensor;
23+
template <typename T, int MajorType = Eigen::RowMajor,
24+
typename IndexType = Eigen::DenseIndex>
25+
using EigenVector = framework::EigenVector<T, MajorType, IndexType>;
26+
27+
template <typename Place, typename T, typename AttrType = T>
28+
class LogLossKernel : public framework::OpKernel<T> {
29+
public:
30+
void Compute(const framework::ExecutionContext& ctx) const override {
31+
auto* loss_out = ctx.Output<Tensor>("Loss");
32+
33+
loss_out->mutable_data<T>(ctx.GetPlace());
34+
35+
auto epsilon = static_cast<T>(ctx.Attr<AttrType>("epsilon"));
36+
37+
auto prediction = EigenVector<T>::Flatten(*ctx.Input<Tensor>("Predicted"));
38+
auto label = EigenVector<T>::Flatten(*ctx.Input<Tensor>("Labels"));
39+
40+
auto loss = EigenVector<T>::Flatten(*loss_out);
41+
auto place = ctx.GetEigenDevice<Place>();
42+
43+
loss.device(place) = (-(label * (prediction + epsilon).log()) -
44+
((static_cast<T>(1) - label) *
45+
(static_cast<T>(1) - prediction + epsilon).log()));
46+
}
47+
};
48+
49+
template <typename Place, typename T, typename AttrType = T>
50+
class LogLossGradKernel : public framework::OpKernel<T> {
51+
public:
52+
void Compute(const framework::ExecutionContext& ctx) const override {
53+
auto epsilon = static_cast<T>(ctx.Attr<AttrType>("epsilon"));
54+
55+
auto prediction = EigenVector<T>::Flatten(*ctx.Input<Tensor>("Predicted"));
56+
auto label = EigenVector<T>::Flatten(*ctx.Input<Tensor>("Labels"));
57+
58+
auto* dloss = ctx.Input<Tensor>(framework::GradVarName("Loss"));
59+
auto* dpred = ctx.Output<Tensor>(framework::GradVarName("Predicted"));
60+
61+
auto dl = EigenVector<T>::Flatten(*dloss);
62+
auto place = ctx.GetEigenDevice<Place>();
63+
64+
if (dpred) {
65+
dpred->mutable_data<T>(ctx.GetPlace());
66+
auto dx = framework::EigenVector<T>::Flatten(*dpred);
67+
dx.device(place) = dl * (-(label / (prediction + epsilon)) +
68+
((static_cast<T>(1) - label) /
69+
(static_cast<T>(1) - prediction + epsilon)));
70+
}
71+
}
72+
};
73+
74+
} // namespace operators
75+
} // namespace paddle
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import unittest
2+
import numpy as np
3+
from op_test import OpTest
4+
5+
6+
class TestLogLossOp(OpTest):
7+
def setUp(self):
8+
self.op_type = 'log_loss'
9+
samples_num = 32
10+
11+
predicted = np.random.uniform(0.1, 1.0,
12+
(samples_num, 1)).astype("float32")
13+
labels = np.random.randint(0, 2, (samples_num, 1)).astype("float32")
14+
epsilon = 1e-4
15+
self.inputs = {
16+
'Predicted': predicted,
17+
'Labels': labels,
18+
}
19+
20+
self.attrs = {'epsilon': epsilon}
21+
loss = -labels * np.log(predicted + epsilon) - (
22+
1 - labels) * np.log(1 - predicted + epsilon)
23+
self.outputs = {'Loss': loss}
24+
25+
def test_check_output(self):
26+
self.check_output()
27+
28+
def test_check_grad(self):
29+
self.check_grad(['Predicted'], 'Loss', max_relative_error=0.03)
30+
31+
32+
if __name__ == '__main__':
33+
unittest.main()

0 commit comments

Comments
 (0)