mirror of https://github.com/alibaba/MNN.git
145 lines
4.7 KiB
C++
145 lines
4.7 KiB
C++
//
|
|
// CPULayerNorm.cpp
|
|
// MNN
|
|
//
|
|
// Created by MNN on 2020/07/15.
|
|
// Copyright © 2018, Alibaba Group Holding Limited
|
|
//
|
|
|
|
#include <cmath>
|
|
|
|
#include "core/Execution.hpp"
|
|
#include "core/Concurrency.h"
|
|
#include "backend/cpu/CPUBackend.hpp"
|
|
#include "backend/cpu/compute/CommonOptFunction.h"
|
|
#include "MNN_generated.h"
|
|
|
|
|
|
namespace MNN {
|
|
|
|
class CPULayerNorm : public Execution {
|
|
public:
|
|
explicit CPULayerNorm(const MNN::Op* op, Backend* backend);
|
|
virtual ~CPULayerNorm();
|
|
|
|
ErrorCode onExecute(const std::vector<Tensor*> &inputs, // NOLINT
|
|
const std::vector<Tensor*> &outputs) override;
|
|
|
|
ErrorCode onResize(const std::vector<Tensor*> &inputs, // NOLINT
|
|
const std::vector<Tensor*> &outputs) override;
|
|
|
|
private:
|
|
std::vector<int> axis_;
|
|
int inner_size_ = 1;
|
|
int outter_size_ = 1;
|
|
int group_ = 1;
|
|
float epsilon_ = 0.001;
|
|
|
|
std::unique_ptr<Tensor> gamma_;
|
|
std::unique_ptr<Tensor> beta_;
|
|
bool has_gamma_beta_ = false;
|
|
};
|
|
|
|
CPULayerNorm::CPULayerNorm(const MNN::Op* op, Backend* backend)
|
|
: Execution(backend) {
|
|
const auto* layer_norm_param = op->main_as_LayerNorm();
|
|
int axis_size = layer_norm_param->axis()->size();
|
|
axis_.resize(axis_size);
|
|
for (int i = 0; i < axis_size; ++i) {
|
|
axis_[i] = layer_norm_param->axis()->Get(i);
|
|
}
|
|
group_ = layer_norm_param->group();
|
|
epsilon_ = layer_norm_param->epsilon();
|
|
|
|
if (layer_norm_param->gamma() && layer_norm_param->beta()) {
|
|
has_gamma_beta_ = true;
|
|
int size = layer_norm_param->gamma()->size();
|
|
gamma_.reset(Tensor::createDevice<float>({size}));
|
|
auto status = backend->onAcquireBuffer(gamma_.get(), Backend::STATIC);
|
|
if (!status) {
|
|
MNN_ERROR("Out of memory when gamma is acquired in CPULayerNorm.\n");
|
|
}
|
|
const float* gamma_data = layer_norm_param->gamma()->data();
|
|
memcpy(gamma_->host<float>(), gamma_data, size * sizeof(float));
|
|
|
|
if (layer_norm_param->beta()->size() != size) {
|
|
MNN_ERROR("Size of gamma and beta are not match in CPULayerNorm.\n");
|
|
}
|
|
beta_.reset(Tensor::createDevice<float>({size}));
|
|
status = backend->onAcquireBuffer(beta_.get(), Backend::STATIC);
|
|
if (!status) {
|
|
MNN_ERROR("Out of memory when beta is acquired in CPULayerNorm.\n");
|
|
}
|
|
const float* beta_data = layer_norm_param->beta()->data();
|
|
memcpy(beta_->host<float>(), beta_data, size * sizeof(float));
|
|
}
|
|
}
|
|
|
|
ErrorCode CPULayerNorm::onExecute(const std::vector<Tensor*> &inputs,
|
|
const std::vector<Tensor*> &outputs) {
|
|
const float* gamma = has_gamma_beta_ ? gamma_->host<float>() : nullptr;
|
|
const float* beta = has_gamma_beta_ ? beta_->host<float>() : nullptr;
|
|
|
|
const float* input = inputs.at(0)->host<float>();
|
|
float* output = outputs.at(0)->host<float>();
|
|
MNN_CONCURRENCY_BEGIN(tId, outter_size_) {
|
|
const float* inner_input = input + tId * inner_size_;
|
|
float* inner_output = output + tId * inner_size_;
|
|
MNNNorm(inner_output, inner_input, gamma, beta, epsilon_, inner_size_);
|
|
}
|
|
MNN_CONCURRENCY_END();
|
|
return NO_ERROR;
|
|
}
|
|
|
|
ErrorCode CPULayerNorm::onResize(const std::vector<Tensor*> &inputs,
|
|
const std::vector<Tensor*> &outputs) {
|
|
outter_size_ = 1;
|
|
inner_size_ = 1;
|
|
int rank = inputs.at(0)->dimensions();
|
|
if (group_ > 1) {
|
|
outter_size_ = inputs.at(0)->length(0) * group_;
|
|
for (int i = 1; i < rank; i++) {
|
|
inner_size_ *= inputs.at(0)->length(i);
|
|
}
|
|
inner_size_ /= group_;
|
|
return NO_ERROR;
|
|
}
|
|
std::vector<int> axis(axis_.size());
|
|
for (int i = 0; i < axis_.size(); ++i) {
|
|
if (axis_[i] < 0) {
|
|
axis[i] += rank;
|
|
}
|
|
}
|
|
std::sort(axis.begin(), axis.end());
|
|
|
|
for (int i = 0; i < rank - axis.size(); ++i) {
|
|
outter_size_ *= inputs.at(0)->length(i);
|
|
}
|
|
for (int i = rank - axis.size(); i < rank; ++i) {
|
|
inner_size_ *= inputs.at(0)->length(i);
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
CPULayerNorm::~CPULayerNorm() {
|
|
if (gamma_.get()) {
|
|
backend()->onReleaseBuffer(gamma_.get(), Backend::STATIC);
|
|
}
|
|
if (beta_.get()) {
|
|
backend()->onReleaseBuffer(beta_.get(), Backend::STATIC);
|
|
}
|
|
}
|
|
|
|
class CPULayerNormCreator : public CPUBackend::Creator {
|
|
public:
|
|
Execution* onCreate(const std::vector<Tensor*>& inputs,
|
|
const std::vector<Tensor*>& outputs,
|
|
const MNN::Op* op, Backend* backend) const override {
|
|
return new CPULayerNorm(op, backend);
|
|
}
|
|
};
|
|
|
|
REGISTER_CPU_OP_CREATOR(CPULayerNormCreator, OpType_LayerNorm);
|
|
|
|
} // namespace MNN
|