/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. 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. */ #pragma once #include "paddle/framework/eigen.h" #include "paddle/framework/op_registry.h" namespace paddle { namespace operators { using Tensor = framework::Tensor; template using EigenMatrix = framework::EigenMatrix; template class SoftmaxKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& context) const override { auto X = context.Input("Logits"); auto Y = context.Output("Out"); Y->mutable_data(context.GetPlace()); auto logits = EigenMatrix::From(*X); auto out = EigenMatrix::From(*Y); const int kBatchDim = 0; const int kClassDim = 1; const int batch_size = logits.dimension(kBatchDim); const int num_classes = logits.dimension(kClassDim); Eigen::DSizes along_class(kClassDim); Eigen::DSizes batch_by_one(batch_size, 1); Eigen::DSizes one_by_class(1, num_classes); auto shifted_logits = (logits - logits.maximum(along_class) .eval() .reshape(batch_by_one) .broadcast(one_by_class)); out.device(context.GetEigenDevice()) = shifted_logits.exp(); out.device(context.GetEigenDevice()) = (out * out.sum(along_class) .inverse() .eval() .reshape(batch_by_one) .broadcast(one_by_class)); } }; template class SoftmaxGradKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& context) const override { std::shared_ptr scale_ = std::make_shared(); auto Y = context.Input("Out"); auto dY = context.Input(framework::GradVarName("Out")); auto dX = context.Output(framework::GradVarName("Logits")); dX->mutable_data(context.GetPlace()); const int batch_size = Y->dims()[0]; const int class_num = Y->dims()[1]; Eigen::DSizes along_class(1); Eigen::DSizes batch_by_one(batch_size, 1); Eigen::DSizes one_by_class(1, class_num); auto Y_eigen = EigenMatrix::From(*Y); auto dY_eigen = EigenMatrix::From(*dY); auto dX_eigen = EigenMatrix::From(*dX); auto place = context.GetEigenDevice(); auto dot = (Y_eigen * dY_eigen) .sum(along_class) .eval() .reshape(batch_by_one) .broadcast(one_by_class); dX_eigen.device(place) = (dY_eigen - dot) * Y_eigen; } }; } // namespace operators } // namespace paddle