// // CPUNormalize.cpp // MNN // // Created by MNN on 2018/07/18. // Copyright © 2018, Alibaba Group Holding Limited // #include "backend/cpu/CPUNormalize.hpp" #include #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& inputs, const std::vector& 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& inputs, const std::vector& 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(); MNNUnpackC4(mSourceStorage.host(), inputData, area, inputTensor->channel()); float* outputData = outputTensor->host(); _normalize(mSourceStorage.host(), mSummer.host(), mSourceStorage.host(), inside, outside, axis, mEps); if (mChannelShared) { _scaleSingleValue(mSourceStorage.host(), mSourceStorage.host(), mScale.get(), area, inputTensor->channel()); } else { _scaleChannel(mSourceStorage.host(), mSourceStorage.host(), mScale.get(), area, inputTensor->channel()); } MNNPackC4(outputData, mSourceStorage.host(), area, outputTensor->channel()); return NO_ERROR; } class CPUNormalizeCreator : public CPUBackend::Creator { public: virtual Execution* onCreate(const std::vector& inputs, const std::vector& outputs, const MNN::Op* op, Backend* backend) const override { return new CPUNormalize(backend, op); } }; REGISTER_CPU_OP_CREATOR(CPUNormalizeCreator, OpType_Normalize); } // namespace MNN