| #include "caffe2/operators/lpnorm_op.h" |
| |
| namespace caffe2 { |
| |
| template <> |
| bool LpNormOp<float, CPUContext>::RunOnDevice() { |
| const auto& X = Input(0); |
| auto* norm = Output(0); |
| norm->Resize(1); |
| const float* X_data = X.data<float>(); |
| const float size = average_ ? (float)X.size() : 1.0f; |
| CAFFE_ENFORCE_GT(size, 0); |
| if (p_ == 1) { |
| *(norm->mutable_data<float>()) = |
| (ConstEigenVectorMap<float>(X_data, X.size()).array()).abs().sum() / |
| size; |
| // L1(x) = sum(|x|), L1_average(x) = sum(\x\) / x.size() |
| } else if (p_ == 2) { |
| *(norm->mutable_data<float>()) = |
| (ConstEigenVectorMap<float>(X_data, X.size()).array()).square().sum() / |
| size; |
| // L2(x) = (sum(|x|^2)), L2_average(x) = sum(|x|^2) / x.size() |
| } |
| return true; |
| } |
| |
| template <> |
| bool LpNormGradientOp<float, CPUContext>::RunOnDevice() { |
| const auto& X = Input(0); |
| const auto& dnorm = Input(1); |
| auto* dX = Output(0); |
| CAFFE_ENFORCE_EQ(dnorm.ndim(), 1); |
| CAFFE_ENFORCE_EQ(dnorm.dim32(0), 1); |
| dX->ResizeLike(X); |
| const float kEps = 1e-12f; |
| const float size = average_ ? (float)X.size() : 1.0f; |
| if (p_ == 1) { |
| // Todo: implement in eigen |
| for (int i = 0; i < X.size(); ++i) { |
| float temp = (X.data<float>())[i]; |
| if (temp < -kEps) { |
| dX->mutable_data<float>()[i] = -(dnorm.data<float>())[0] / size; |
| } else if (temp > kEps) { |
| dX->mutable_data<float>()[i] = (dnorm.data<float>())[0] / size; |
| } else { |
| dX->mutable_data<float>()[i] = 0; |
| } |
| } |
| } else if (p_ == 2) { |
| EigenVectorMap<float>(dX->mutable_data<float>(), X.size()).array() = |
| ConstEigenVectorMap<float>(X.data<float>(), X.size()).array() * 2.0f * |
| ((dnorm.data<float>())[0] / size); |
| } |
| |
| return true; |
| } |
| |
| namespace { |
| // LpNorm |
| REGISTER_CPU_OPERATOR(LpNorm, LpNormOp<float, CPUContext>); |
| REGISTER_CPU_OPERATOR(LpNormGradient, LpNormGradientOp<float, CPUContext>); |
| |
| OPERATOR_SCHEMA(LpNorm) |
| .NumInputs(1) |
| .NumOutputs(1) |
| .SetDoc(R"DOC( |
| Given one input float tensor X, and produces one output float tensor |
| of the Lp norm of tensor X, computed as Lp(x) = sum over |x^p|, |
| in which p is either 1 or 2(currently only supports l1 and l2 norm), |
| determined by the argument p. |
| )DOC") |
| .Input(0, "X", "1D input tensor") |
| .Output(0, "Z", "1D output tensor") |
| .Arg("p", "Order of the norm in p-norm") |
| .Arg( |
| "average", |
| "whehther we calculate norm or averaged_norm." |
| "The Lp_averaged_norm(x) is defined as" |
| "Lp_averaged_norm(x) = LpNorm(x) / size(x)"); |
| |
| OPERATOR_SCHEMA(LpNormGradient) |
| .NumInputs(2) |
| .NumOutputs(1) |
| .SetDoc(R"DOC( |
| Given one input float tensor X, derivative dout, and produces one output |
| float tensor dX. dX is the derivative of the Lp norm of tensor X, computed as |
| dx = d(sum over |x^p|)/dx, in which p is either 1 or 2(currently only |
| supports l1 and l2 norm) determined by the argument p. |
| )DOC") |
| .Input(0, "X", "1D input tensor") |
| .Input(1, "dout", "1D input tensor") |
| .Output(0, "dx", "1D output tensor") |
| .Arg("p", "Order of the norm in p-norm") |
| .Arg( |
| "average", |
| "whehther we calculate norm or averaged_norm." |
| "The Lp_averaged_norm(x) is defined as" |
| "Lp_averaged_normgradient(x) = LpNormGradient(x) / size(x)"); |
| |
| class GetLpNormGradient : public GradientMakerBase { |
| using GradientMakerBase::GradientMakerBase; |
| vector<OperatorDef> GetGradientDefs() override { |
| return SingleGradientDef( |
| "LpNormGradient", |
| "", |
| vector<string>{I(0), GO(0)}, |
| vector<string>{GI(0)}); |
| } |
| }; |
| |
| REGISTER_GRADIENT(LpNorm, GetLpNormGradient); |
| } // namespace |
| |
| } // namespace caffe2 |