summaryrefslogtreecommitdiff
path: root/runtime/neurun/backend/cpu/kernel/SoftMaxLayer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/neurun/backend/cpu/kernel/SoftMaxLayer.cc')
-rw-r--r--runtime/neurun/backend/cpu/kernel/SoftMaxLayer.cc172
1 files changed, 172 insertions, 0 deletions
diff --git a/runtime/neurun/backend/cpu/kernel/SoftMaxLayer.cc b/runtime/neurun/backend/cpu/kernel/SoftMaxLayer.cc
new file mode 100644
index 000000000..58ba109b4
--- /dev/null
+++ b/runtime/neurun/backend/cpu/kernel/SoftMaxLayer.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * 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 "SoftMaxLayer.h"
+
+#include <cker/operation/SoftMax.h>
+
+#include "OperationUtils.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+namespace kernel
+{
+
+SoftMaxLayer::SoftMaxLayer()
+ : _inputData(), _outputData(), _beta(0.0), _inputDescr(), _outputDescr(),
+ _inputType(OperandType::FLOAT32)
+{
+ // DO NOTHING
+}
+
+// Performs softmax along the input of size (input_size * batch_size).
+void Softmax(const float *in, const int input_size, const int batch_size, const float beta,
+ float *out)
+{
+ assert(input_size > 0);
+
+ // For each batch
+ for (int b = 0; b < batch_size; b++)
+ {
+ // Find the max coeff.
+ float max_coeff = in[0];
+ for (int i = 1; i < input_size; i++)
+ {
+ if (in[i] > max_coeff)
+ max_coeff = in[i];
+ }
+
+ // Compute the normalized sum of exps.
+ float exp_sum = 0.0;
+ for (int i = 0; i < input_size; i++)
+ {
+ out[i] = std::exp((in[i] - max_coeff) * beta);
+ exp_sum += out[i];
+ }
+
+ // Divide by the sum of exps.
+ float reciprocal_sum_exp = 1.f / exp_sum;
+ for (int i = 0; i < input_size; i++)
+ {
+ out[i] *= reciprocal_sum_exp;
+ }
+
+ // Advance in and out pointers for the next batch.
+ in += input_size;
+ out += input_size;
+ }
+}
+
+void SoftMaxLayer::softmaxFloat32()
+{
+ TensorDescriptor descrIn4D;
+
+ if (getNumberOfDimensions(_inputDescr) == 2)
+ {
+ uint32_t batch_size = getSizeOfDimension(_inputDescr, 0);
+ if (batch_size == 0)
+ throw std::runtime_error("batch_size should not be 0");
+
+ uint32_t input_size = getNumberOfElements(_inputDescr) / batch_size;
+ Softmax(_inputData.f, input_size, batch_size, _beta, _outputData.f);
+ }
+ else if (getNumberOfDimensions(_inputDescr) == 4)
+ {
+ nnfw::cker::SoftmaxParams op_params;
+ op_params.beta = _beta;
+ nnfw::cker::Softmax(op_params, convertTensorDescriptorToCkerShape(_inputDescr), _inputData.f,
+ convertTensorDescriptorToCkerShape(_outputDescr), _outputData.f);
+ }
+ else
+ {
+ throw std::runtime_error{"only 2D and 4D tensors supported"};
+ }
+}
+
+void SoftMaxLayer::softmaxQuant8()
+{
+ TensorDescriptor descrIn4D = _inputDescr;
+
+ if (getNumberOfDimensions(_inputDescr) == 2)
+ {
+ uint32_t batch_size = getSizeOfDimension(_inputDescr, 0);
+ if (batch_size == 0)
+ throw std::runtime_error("batch_size should not be 0");
+
+ uint32_t input_size = getNumberOfElements(_inputDescr) / batch_size;
+ descrIn4D.dimensions = {batch_size, 1, 1, input_size};
+ }
+ else if (getNumberOfDimensions(_inputDescr) == 4)
+ {
+ descrIn4D = _inputDescr;
+ }
+ else
+ {
+ throw std::runtime_error{"only 2D and 4D tensors supported"};
+ }
+ if (_outputDescr.offset != 0 || _outputDescr.scale != 1.f / 256)
+ {
+ throw std::runtime_error{"incorrect scale / offset for output"};
+ }
+ static const int32_t kScaledDiffIntegerBits = 5;
+ const double input_beta_real_multiplier = std::min(
+ 1.0 * _beta * _inputDescr.scale * (1 << (31 - kScaledDiffIntegerBits)), (1ll << 31) - 1.0);
+ int32_t input_multiplier = 0;
+ int32_t input_left_shift = 0;
+ QuantizeMultiplierGreaterThanOne(input_beta_real_multiplier, &input_multiplier,
+ &input_left_shift);
+ float diff_min = -1.0f * CalculateInputRadius(kScaledDiffIntegerBits, input_left_shift);
+
+ nnfw::cker::SoftmaxParams op_params;
+ op_params.input_multiplier = input_multiplier;
+ op_params.input_left_shift = input_left_shift;
+ op_params.diff_min = diff_min;
+ nnfw::cker::Softmax(op_params, convertTensorDescriptorToCkerShape(descrIn4D), _inputData.u8,
+ convertTensorDescriptorToCkerShape(descrIn4D), _outputData.u8);
+}
+
+void SoftMaxLayer::configure(uint8_t *inputData, const TensorDescriptor &inputDescr,
+ const float beta, uint8_t *outputData,
+ const TensorDescriptor &outputDescr)
+{
+ _inputData.u8 = inputData;
+ _inputDescr = inputDescr;
+ _inputType = inputDescr.type;
+ _outputData.u8 = outputData;
+ _outputDescr = outputDescr;
+ _beta = beta;
+}
+
+void SoftMaxLayer::run()
+{
+ if (_inputType == OperandType::FLOAT32)
+ {
+ softmaxFloat32();
+ }
+ else if (_inputType == OperandType::QUANT8_ASYMM)
+ {
+ softmaxQuant8();
+ }
+}
+
+} // namespace kernel
+} // namespace cpu
+} // namespace backend
+} // namespace neurun