mirror of https://github.com/alibaba/MNN.git
172 lines
5.8 KiB
C++
172 lines
5.8 KiB
C++
//
|
|
// CPUNormalize.cpp
|
|
// MNN
|
|
//
|
|
// Created by MNN on 2018/07/18.
|
|
// Copyright © 2018, Alibaba Group Holding Limited
|
|
//
|
|
|
|
#include "backend/cpu/CPUNormalize.hpp"
|
|
#include <math.h>
|
|
#include "backend/cpu/CPUBackend.hpp"
|
|
#include "backend/cpu/compute/CommonOptFunction.h"
|
|
|
|
namespace MNN {
|
|
CPUNormalize::CPUNormalize(Backend* b, const MNN::Op* op) : MNN::Execution(b) {
|
|
auto normalize = op->main_as_Normalize();
|
|
mAcrossSpatial = normalize->acrossSpatial();
|
|
mChannelShared = normalize->channelShared();
|
|
|
|
mEps = normalize->eps();
|
|
mScale.reset(normalize->scale()->size());
|
|
::memcpy(mScale.get(), normalize->scale()->data(), normalize->scale()->size() * sizeof(float));
|
|
}
|
|
|
|
ErrorCode CPUNormalize::onResize(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs) {
|
|
auto inputTensor = inputs[0];
|
|
int totalSize = 1;
|
|
auto outputTensor = outputs[0];
|
|
|
|
MNN_ASSERT(1 == inputTensor->batch());
|
|
MNN_ASSERT(1 == outputTensor->batch());
|
|
|
|
// Across channel
|
|
int inside = inputTensor->width() * inputTensor->height();
|
|
int axis = inputTensor->channel();
|
|
int outside = 1;
|
|
|
|
// Across Spatial
|
|
if (mAcrossSpatial) {
|
|
inside = 1;
|
|
axis = inputTensor->width() * inputTensor->height() * inputTensor->channel();
|
|
outside = 1;
|
|
}
|
|
for (int i = 1; i < inputTensor->buffer().dimensions; ++i) {
|
|
totalSize *= inputTensor->buffer().dim[i].extent;
|
|
}
|
|
mSourceStorage.buffer().dim[0].extent = 1;
|
|
mSourceStorage.buffer().dim[1].extent = totalSize;
|
|
mSourceStorage.buffer().dim[2].extent = 1;
|
|
mSourceStorage.buffer().dim[3].extent = 1;
|
|
|
|
mSummer.buffer().dim[0].extent = 1;
|
|
mSummer.buffer().dim[1].extent = inside * outside;
|
|
mSummer.buffer().dim[2].extent = 1;
|
|
mSummer.buffer().dim[3].extent = 1;
|
|
|
|
backend()->onAcquireBuffer(&mSummer, Backend::DYNAMIC);
|
|
backend()->onAcquireBuffer(&mSourceStorage, Backend::DYNAMIC);
|
|
|
|
backend()->onReleaseBuffer(&mSummer, Backend::DYNAMIC);
|
|
backend()->onReleaseBuffer(&mSourceStorage, Backend::DYNAMIC);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
static void _normalize(const float* input, float* summer, float* output, int inside, int outside, int axis, float eps) {
|
|
// Compute summer
|
|
::memset(summer, 0, inside * outside * sizeof(float));
|
|
for (int z = 0; z < outside; ++z) {
|
|
float* summerZ = summer + inside * z;
|
|
const float* inputZ = input + axis * inside * z;
|
|
for (int y = 0; y < axis; ++y) {
|
|
const float* inputY = inputZ + y * inside;
|
|
for (int x = 0; x < inside; ++x) {
|
|
summerZ[x] += inputY[x] * inputY[x];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compute scale
|
|
for (int i = 0; i < inside * outside; ++i) {
|
|
summer[i] = 1.0f / sqrtf(summer[i] + eps);
|
|
}
|
|
|
|
// Scale
|
|
for (int z = 0; z < outside; ++z) {
|
|
float* summerZ = summer + inside * z;
|
|
const float* inputZ = input + axis * inside * z;
|
|
float* outputZ = output + axis * inside * z;
|
|
for (int y = 0; y < axis; ++y) {
|
|
const float* inputY = inputZ + y * inside;
|
|
float* outputY = outputZ + y * inside;
|
|
for (int x = 0; x < inside; ++x) {
|
|
outputY[x] = inputY[x] * summerZ[x];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void _scaleChannel(const float* input, float* output, float* scale, int area, int channel) {
|
|
for (int z = 0; z < channel; ++z) {
|
|
float* outputZ = output + z * area;
|
|
const float* inputZ = input + z * area;
|
|
float s = scale[z];
|
|
for (int i = 0; i < area; ++i) {
|
|
outputZ[i] = inputZ[i] * s;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void _scaleSingleValue(const float* input, float* output, float* scale, int area, int channel) {
|
|
float s = scale[0];
|
|
for (int z = 0; z < channel; ++z) {
|
|
float* outputZ = output + z * area;
|
|
const float* inputZ = input + z * area;
|
|
for (int i = 0; i < area; ++i) {
|
|
outputZ[i] = inputZ[i] * s;
|
|
}
|
|
}
|
|
}
|
|
ErrorCode CPUNormalize::onExecute(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs) {
|
|
MNN_ASSERT(!mAcrossSpatial);
|
|
MNN_ASSERT(!mChannelShared);
|
|
auto inputTensor = inputs[0];
|
|
auto outputTensor = outputs[0];
|
|
|
|
MNN_ASSERT(1 == inputTensor->batch());
|
|
MNN_ASSERT(1 == outputTensor->batch());
|
|
|
|
// Across channel
|
|
int inside = inputTensor->width() * inputTensor->height();
|
|
int axis = inputTensor->channel();
|
|
int outside = 1;
|
|
|
|
// Across Spatial
|
|
if (mAcrossSpatial) {
|
|
inside = 1;
|
|
axis = inputTensor->width() * inputTensor->height() * inputTensor->channel();
|
|
outside = 1;
|
|
}
|
|
|
|
int area = inputTensor->width() * inputTensor->height();
|
|
|
|
const float* inputData = inputTensor->host<float>();
|
|
MNNUnpackC4(mSourceStorage.host<float>(), inputData, area, inputTensor->channel());
|
|
|
|
float* outputData = outputTensor->host<float>();
|
|
_normalize(mSourceStorage.host<float>(), mSummer.host<float>(), mSourceStorage.host<float>(), inside, outside, axis,
|
|
mEps);
|
|
|
|
if (mChannelShared) {
|
|
_scaleSingleValue(mSourceStorage.host<float>(), mSourceStorage.host<float>(), mScale.get(), area,
|
|
inputTensor->channel());
|
|
} else {
|
|
_scaleChannel(mSourceStorage.host<float>(), mSourceStorage.host<float>(), mScale.get(), area,
|
|
inputTensor->channel());
|
|
}
|
|
MNNPackC4(outputData, mSourceStorage.host<float>(), area, outputTensor->channel());
|
|
|
|
return NO_ERROR;
|
|
}
|
|
class CPUNormalizeCreator : public CPUBackend::Creator {
|
|
public:
|
|
virtual Execution* onCreate(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs,
|
|
const MNN::Op* op, Backend* backend) const override {
|
|
return new CPUNormalize(backend, op);
|
|
}
|
|
};
|
|
|
|
REGISTER_CPU_OP_CREATOR(CPUNormalizeCreator, OpType_Normalize);
|
|
} // namespace MNN
|