| #include "caffe2/operators/thresholded_relu_op.h" |
| |
| #include "caffe2/utils/eigen_utils.h" |
| #include "caffe2/utils/math.h" |
| |
| namespace caffe2 { |
| |
| template <> |
| bool ThresholdedReluOp<float, CPUContext>::RunOnDevice() { |
| auto& X = Input(0); |
| |
| auto* Y = Output(0, X.sizes(), at::dtype<float>()); |
| |
| ConstEigenVectorArrayMap<float> Xvec(X.data<float>(), X.numel()); |
| EigenVectorArrayMap<float> Yvec( |
| Y->template mutable_data<float>(), Y->numel()); |
| Yvec = (Xvec > alpha_).select(Xvec, 0.f); |
| /* Naive implementation |
| const float* Xdata = X.data<float>(); |
| float* Ydata = Y->template mutable_data<float>(); |
| for (int i = 0; i < X.size(); ++i) { |
| Xdata[i] -= alpha_; |
| Ydata[i] = std::max(Xdata[i], 0.0f); |
| } |
| */ |
| return true; |
| } |
| |
| template <> |
| bool ThresholdedReluGradientOp<float, CPUContext>::RunOnDevice() { |
| auto& Y = Input(0); |
| auto& dY = Input(1); |
| |
| CAFFE_ENFORCE_EQ(dY.numel(), Y.numel()); |
| auto* dX = Output(0, Y.sizes(), at::dtype<float>()); |
| |
| const float* Ydata = Y.data<float>(); |
| const float* dYdata = dY.data<float>(); |
| float* dXdata = dX->template mutable_data<float>(); |
| EigenVectorArrayMap<float> dXvec(dXdata, dX->numel()); |
| ConstEigenVectorArrayMap<float> Yvec(Ydata, Y.numel()); |
| ConstEigenVectorArrayMap<float> dYvec(dYdata, dY.numel()); |
| dXvec = dYvec * Yvec.cwiseSign(); |
| /* Non vectorized implementation |
| for (int i = 0; i < Y.size(); ++i) { |
| dXdata[i] = Ydata[i] > 0 ? dYdata[i] : 0; |
| } |
| */ |
| return true; |
| } |
| |
| REGISTER_CPU_OPERATOR(ThresholdedRelu, ThresholdedReluOp<float, CPUContext>); |
| REGISTER_CPU_OPERATOR( |
| ThresholdedReluGradient, |
| ThresholdedReluGradientOp<float, CPUContext>); |
| |
| // Input: X, output: Y |
| OPERATOR_SCHEMA(ThresholdedRelu) |
| .NumInputs(1) |
| .NumOutputs(1) |
| .AllowInplace({{0, 0}}) |
| .CostInferenceFunction(PointwiseCostInference<2>) |
| .IdenticalTypeAndShape() |
| .SetDoc(R"DOC( |
| ThresholdedRelu takes one input data (Tensor) and produces one output data |
| (Tensor) where the rectified linear function, y = x for x > alpha, y = 0 |
| otherwise, is applied to the tensor elementwise. |
| )DOC") |
| .Arg("alpha", "(float) defaults to 1.0.") |
| .Input(0, "X", "1D input tensor") |
| .Output(0, "Y", "1D input tensor"); |
| |
| // Input: Y, dY, output: dX |
| OPERATOR_SCHEMA(ThresholdedReluGradient) |
| .NumInputs(2) |
| .NumOutputs(1) |
| .AllowInplace({{1, 0}}) |
| .SetDoc(R"DOC( |
| ThresholdedReluGradient takes both Y and dY and uses this to update dX |
| according to the chain rule and derivatives of the rectified linear function. |
| )DOC"); |
| |
| class GetThresholdedReluGradient : public GradientMakerBase { |
| using GradientMakerBase::GradientMakerBase; |
| vector<OperatorDef> GetGradientDefs() override { |
| return SingleGradientDef( |
| def_.type() + "Gradient", |
| "", |
| vector<string>{O(0), GO(0)}, |
| vector<string>{GI(0)}); |
| } |
| }; |
| REGISTER_GRADIENT(ThresholdedRelu, GetThresholdedReluGradient); |
| |
| } // namespace caffe2 |