ONE - On-device Neural Engine
Loading...
Searching...
No Matches
Softmax.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
3 * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "Softmax.h"
19#include "Common.h"
20#include "QuantizationHelpers.h"
21
22#include <mir/ShapeRange.h>
23#include <mir/Tensor.h>
24
25#include <cmath>
26
27namespace mir_interpreter
28{
29
30static inline void PopulateSoftmaxLookupTable(float *table, float input_scale, float beta)
31{
32 const float scale = -input_scale * beta;
33 const int32_t max_uint8 = std::numeric_limits<uint8_t>::max();
34 for (int32_t val = 0; val <= max_uint8; ++val)
35 table[max_uint8 - val] = expf(scale * val);
36}
37
38template <typename T> struct SoftmaxImpl
39{
40 static void run(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result);
41};
42
43template <typename T>
45{
46 mir::Tensor<T> arg_accessor(arg);
47 mir::Tensor<T> res_accessor(result);
48
49 mir::Shape expsum_shape = arg.getShape();
50 expsum_shape.dim(axis) = 1;
51 mir::TensorType expsum_type(arg.getElementType(), expsum_shape);
52 mir::TensorVariant expsum(expsum_type);
53 mir::Tensor<T> expsum_accessor(expsum);
54
55 for (const auto &expsum_index : mir::ShapeRange(expsum_shape))
56 {
57 T sum = 0;
58 mir::Index arg_index = expsum_index;
59 std::int32_t axis_size = arg.getShape().dim(axis);
60 for (std::int32_t i = 0; i < axis_size; ++i)
61 {
62 arg_index.at(axis) = i;
63 sum += std::exp(arg_accessor.at(arg_index));
64 }
65 expsum_accessor.at(expsum_index) = sum;
66 }
67
68 for (const auto &res_index : mir::ShapeRange(result.getShape()))
69 {
70 mir::Index expsum_index = res_index;
71 expsum_index.at(axis) = 0;
72 res_accessor.at(res_index) =
73 std::exp(arg_accessor.at(res_index)) / expsum_accessor.at(expsum_index);
74 }
75}
76
77template <> struct SoftmaxImpl<uint8_t>
78{
79 static void run(const mir::TensorVariant &input, int axis, mir::TensorVariant &output);
80};
81
83 mir::TensorVariant &output)
84{
85 const auto &input_type = input.getType();
86 const auto &output_type = output.getType();
87
88 assert(input_type.isQuantized());
89 assert(output_type.isQuantized());
90
91 const auto input_shape = input_type.getShape();
92
93 assert(input_type.getElementType() == mir::DataType::UINT8);
94 assert(axis == input_shape.rank() - 1); // supported only last dim axis
95 (void)axis;
96
97 double input_scale = input_type.getQuantization().getScale();
98 double output_scale = output_type.getQuantization().getScale();
99
100 const int trailing_dim = input_shape.rank() - 1;
101 int excluding_last_dim = 1;
102 for (int32_t i = 0; i < input_shape.rank() - 1; i++)
103 {
104 excluding_last_dim *= input_shape.dim(i);
105 }
106 const int last_dim = input_shape.dim(trailing_dim);
107
108 const int32_t clamp_max = std::numeric_limits<uint8_t>::max();
109 const int32_t clamp_min = std::numeric_limits<uint8_t>::min();
110
111 uint8_t *input_data = reinterpret_cast<uint8_t *>(input.atOffset(0));
112
113 float table[256];
114 PopulateSoftmaxLookupTable(table, input_scale, 1.f);
115
116 uint8_t *output_data = reinterpret_cast<uint8_t *>(output.atOffset(0));
117
118 for (int i = 0; i < excluding_last_dim; ++i)
119 {
120 int32_t max_val = std::numeric_limits<uint8_t>::min();
121 // Find max quantized value.
122 for (int j = 0; j < last_dim; ++j)
123 {
124 max_val = std::max(max_val, static_cast<int32_t>(input_data[j]));
125 }
126
127 float sum_exp = 0.0f;
128 const int32_t max_uint8 = std::numeric_limits<uint8_t>::max();
129 const float *table_offset = &table[max_uint8 - max_val];
130 // Calculate normalizer sum(exp(x)).
131 for (int j = 0; j < last_dim; ++j)
132 {
133 sum_exp += table_offset[input_data[j]];
134 }
135
136 const float inv_sum_exp = 1.0f / (sum_exp * output_scale);
137 // Normalize and quantize probabilities.
138 for (int j = 0; j < last_dim; ++j)
139 {
140 const float prob_rescaled = table_offset[input_data[j]] * inv_sum_exp;
141 const int32_t prob_quantized = static_cast<int32_t>(prob_rescaled + 0.5);
142 output_data[j] =
143 static_cast<uint8_t>(std::max(std::min(clamp_max, prob_quantized), clamp_min));
144 }
145 input_data += last_dim;
146 output_data += last_dim;
147 }
148}
149
150void Softmax(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result)
151{
152 dispatch<SoftmaxImpl>(arg.getElementType(), arg, axis, result);
153};
154
155} // namespace mir_interpreter
int32_t & at(int32_t axis)
return position on given axis
Definition Index.h:64
int32_t & dim(int32_t axis) noexcept
Definition Shape.h:47
T at(const Index &id) const
Definition Tensor.h:31
void Softmax(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result)
Definition Softmax.cpp:150
void PopulateSoftmaxLookupTable(float *table, float input_scale, float beta)
Definition SoftMax.h:148
static void run(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result)
Definition Softmax.cpp:44