|  | /** | 
|  | * Copyright (c) 2016-present, Facebook, Inc. | 
|  | * | 
|  | * 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 "caffe2/operators/normalize_op.h" | 
|  |  | 
|  | #include "caffe2/core/tensor.h" | 
|  | #include "caffe2/utils/math.h" | 
|  |  | 
|  | namespace caffe2 { | 
|  |  | 
|  | template <typename T, class Context> | 
|  | void NormalizeOp<T, Context>::DoNormalize( | 
|  | const T* xData, | 
|  | T* yData, | 
|  | const int m, | 
|  | const int n, | 
|  | const int sf) { | 
|  | using InnerStride = Eigen::InnerStride<Eigen::Dynamic>; | 
|  | using StridedVec = | 
|  | Eigen::Map<Eigen::Matrix<T, 1, Eigen::Dynamic>, 0, InnerStride>; | 
|  | using ConstStridedVec = | 
|  | Eigen::Map<const Eigen::Matrix<T, 1, Eigen::Dynamic>, 0, InnerStride>; | 
|  |  | 
|  | for (int i = 0; i < n; ++i) { | 
|  | auto base = (i / sf) * sf * m + (i % sf); | 
|  | ConstStridedVec xVec(xData + base, 1, m, InnerStride(sf)); | 
|  | auto norm = xVec.template lpNorm<2>(); | 
|  | if (norm != 0) { | 
|  | StridedVec yVec(yData + base, 1, m, InnerStride(sf)); | 
|  | yVec = xVec / norm; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <typename T, class Context> | 
|  | void NormalizeGradientOp<T, Context>::DoNormalize( | 
|  | const T* xData, | 
|  | const T* gOutData, | 
|  | T* gInData, | 
|  | const int m, | 
|  | const int n, | 
|  | const int sf) { | 
|  | using InnerStride = Eigen::InnerStride<Eigen::Dynamic>; | 
|  | using StridedVec = | 
|  | Eigen::Map<Eigen::Matrix<T, 1, Eigen::Dynamic>, 0, InnerStride>; | 
|  | using ConstStridedVec = | 
|  | Eigen::Map<const Eigen::Matrix<T, 1, Eigen::Dynamic>, 0, InnerStride>; | 
|  |  | 
|  | for (int i = 0; i < n; ++i) { | 
|  | auto base = (i / sf) * sf * m + (i % sf); | 
|  | ConstStridedVec xVec(xData + base, 1, m, InnerStride(sf)); | 
|  | ConstStridedVec gOutVec(gOutData + base, 1, m, InnerStride(sf)); | 
|  |  | 
|  | auto row_sum = xVec.dot(gOutVec); | 
|  | auto row_norm = xVec.template lpNorm<2>(); | 
|  | auto row_norm_3 = pow(row_norm, 3); | 
|  | if (row_norm != 0) { | 
|  | StridedVec gInVec(gInData + base, 1, m, InnerStride(sf)); | 
|  | gInVec = (gOutVec / row_norm) - ((xVec / row_norm_3) * row_sum); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | REGISTER_CPU_OPERATOR(Normalize, NormalizeOp<float, CPUContext>); | 
|  | OPERATOR_SCHEMA(Normalize) | 
|  | .NumInputs(1) | 
|  | .NumOutputs(1) | 
|  | .Arg("axis", "axis to normalize") | 
|  | .SetDoc(R"DOC( | 
|  | Given a matrix, apply L2-normalization along the specified dimension. | 
|  | )DOC") | 
|  | .IdenticalTypeAndShape(); | 
|  |  | 
|  | REGISTER_CPU_OPERATOR( | 
|  | NormalizeGradient, | 
|  | NormalizeGradientOp<float, CPUContext>); | 
|  | OPERATOR_SCHEMA(NormalizeGradient) | 
|  | .NumInputs(2) | 
|  | .NumOutputs(1) | 
|  | .Arg("axis", "axis to normalize"); | 
|  |  | 
|  | class GetNormalizeGradient final : public GradientMakerBase { | 
|  | using GradientMakerBase::GradientMakerBase; | 
|  | vector<OperatorDef> GetGradientDefs() override { | 
|  | CAFFE_ENFORCE_EQ(def_.input_size(), 1); | 
|  | return SingleGradientDef( | 
|  | "NormalizeGradient", | 
|  | "", | 
|  | vector<string>{I(0), GO(0)}, | 
|  | vector<string>{GI(0)}); | 
|  | } | 
|  | }; | 
|  | REGISTER_GRADIENT(Normalize, GetNormalizeGradient); | 
|  |  | 
|  | template <typename T, class Context> | 
|  | void NormalizeL1Op<T, Context>::DoNormalize( | 
|  | const T* xData, | 
|  | T* yData, | 
|  | const int m, | 
|  | const int n, | 
|  | const int sf) { | 
|  | using InnerStride = Eigen::InnerStride<Eigen::Dynamic>; | 
|  | using StridedVec = | 
|  | Eigen::Map<Eigen::Matrix<T, 1, Eigen::Dynamic>, 0, InnerStride>; | 
|  | using ConstStridedVec = | 
|  | Eigen::Map<const Eigen::Matrix<T, 1, Eigen::Dynamic>, 0, InnerStride>; | 
|  |  | 
|  | for (int i = 0; i < n; ++i) { | 
|  | auto base = (i / sf) * sf * m + (i % sf); | 
|  | ConstStridedVec xVec(xData + base, 1, m, InnerStride(sf)); | 
|  | auto norm = xVec.template lpNorm<1>(); | 
|  | if (norm != 0) { | 
|  | StridedVec yVec(yData + base, 1, m, InnerStride(sf)); | 
|  | yVec = xVec / norm; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | REGISTER_CPU_OPERATOR(NormalizeL1, NormalizeL1Op<float, CPUContext>); | 
|  | OPERATOR_SCHEMA(NormalizeL1) | 
|  | .NumInputs(1) | 
|  | .NumOutputs(1) | 
|  | .Arg("axis", "axis to normalize") | 
|  | .SetDoc(R"DOC( | 
|  | Given a matrix, apply L1-normalization along the specified axis. | 
|  | )DOC"); | 
|  |  | 
|  | } // namespace caffe2 |