softmax.cc 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
  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. #include "ultra_infer/function/softmax.h"
  15. #include <cstdlib>
  16. #include "ultra_infer/function/eigen.h"
  17. #include "ultra_infer/utils/axis_utils.h"
  18. #include "ultra_infer/utils/utils.h"
  19. namespace ultra_infer {
  20. namespace function {
  21. template <typename T> struct ValueClip {
  22. T operator()(const T &x) const {
  23. const T kThreshold = static_cast<T>(-64.);
  24. return x < kThreshold ? kThreshold : x;
  25. }
  26. };
  27. template <typename T> struct SoftmaxEigen {
  28. void operator()(const FDTensor &x, FDTensor *out, int axis_dim) {
  29. constexpr int kBatchDim = 0;
  30. constexpr int kClassDim = 1;
  31. constexpr int kAxisDim = 1;
  32. auto logits = EigenMatrix<T>::From(x);
  33. auto softmax = EigenMatrix<T>::From(*out);
  34. const int batch_size = logits.dimension(kBatchDim);
  35. const int num_classes = logits.dimension(kClassDim);
  36. const int num_remain = num_classes / axis_dim;
  37. Eigen::DSizes<int, 1> along_axis(kAxisDim);
  38. Eigen::DSizes<int, 2> batch_classes(batch_size, num_classes);
  39. Eigen::DSizes<int, 2> batch_by_one(batch_size, 1);
  40. Eigen::DSizes<int, 2> one_by_class(1, num_classes);
  41. Eigen::DSizes<int, 3> batch_one_remain(batch_size, 1, num_remain);
  42. Eigen::DSizes<int, 3> one_axis_one(1, axis_dim, 1);
  43. Eigen::DSizes<int, 2> one_axis(1, axis_dim);
  44. Eigen::DSizes<int, 3> batch_axis_remain(batch_size, axis_dim, num_remain);
  45. const auto &dev = *EigenDeviceWrapper::GetInstance()->GetDevice();
  46. // For numerical stability, logits should be shifted by maximum number along
  47. // axis, calculate shifted_logits into softmax tensor for memory reuse.
  48. if (num_remain == 1) {
  49. // axis == -1, axis and class in same dimension, calculate along
  50. // class dimension directly for higher performance
  51. softmax.device(dev) = (logits - logits.maximum(along_axis)
  52. .eval()
  53. .reshape(batch_by_one)
  54. .broadcast(one_by_class))
  55. .unaryExpr(ValueClip<T>());
  56. } else {
  57. // axis != -1, class dimension split into (axis, remain), max and sum
  58. // should be calculated along axis dimension
  59. softmax.device(dev) =
  60. (logits.reshape(batch_axis_remain) - logits.reshape(batch_axis_remain)
  61. .maximum(along_axis)
  62. .eval()
  63. .reshape(batch_one_remain)
  64. .broadcast(one_axis_one)
  65. .reshape(batch_axis_remain))
  66. .reshape(batch_classes)
  67. .unaryExpr(ValueClip<T>());
  68. }
  69. softmax.device(dev) = softmax.exp();
  70. softmax.device(dev) = (softmax * softmax.reshape(batch_axis_remain)
  71. .sum(along_axis)
  72. .inverse()
  73. .eval()
  74. .broadcast(one_axis));
  75. }
  76. };
  77. template <typename T>
  78. void SoftmaxFunctor(const FDTensor &x, FDTensor *out, int axis) {
  79. SoftmaxEigen<T>()(x, out, axis);
  80. }
  81. template <typename T>
  82. void SoftmaxKernel(const FDTensor &x, FDTensor *out, int axis) {
  83. const int rank = x.shape.size();
  84. const int calc_axis = CanonicalAxis(axis, rank);
  85. int axis_dim = x.shape[calc_axis];
  86. out->Allocate(x.shape, x.dtype);
  87. if (out->Numel() == 0) {
  88. return;
  89. }
  90. const int n = SizeToAxis(calc_axis, x.shape);
  91. const int d = SizeFromAxis(calc_axis, x.shape);
  92. // Reshape to 2d tensor
  93. FDTensor x_2d, out_2d;
  94. x_2d.SetExternalData({n, d}, x.dtype, const_cast<void *>(x.Data()));
  95. out_2d.SetExternalData({n, d}, out->dtype, out->Data());
  96. SoftmaxFunctor<T>(x_2d, &out_2d, axis_dim);
  97. }
  98. void Softmax(const FDTensor &x, FDTensor *out, int axis) {
  99. FDASSERT(
  100. std::abs(axis) < x.shape.size(),
  101. "The absolute given axis should be smaller than the input's "
  102. "dimension. Expected absolute axis is smaller than %lu, but receive %d.",
  103. x.shape.size(), std::abs(axis));
  104. // Note(zhoushunjie): The FDTensor out may equal to FDTensor x, so firstly we
  105. // use out_temp to get the softmax result, then we move the out_temp to out.
  106. FDTensor out_tmp;
  107. FD_VISIT_FLOAT_TYPES(x.dtype, "SoftmaxKernel",
  108. ([&] { SoftmaxKernel<data_t>(x, &out_tmp, axis); }));
  109. *out = std::move(out_tmp);
  110. }
  111. } // namespace function
  112. } // namespace ultra_infer