mirror of https://github.com/alibaba/MNN.git
				
				
				
			
		
			
				
	
	
		
			139 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			139 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
| //
 | |
| //  GeometryLayerNorm.cpp
 | |
| //  MNN
 | |
| //
 | |
| //  Created by MNN on 2020/06/09.
 | |
| //  Copyright © 2018, Alibaba Group Holding Limited
 | |
| //
 | |
| 
 | |
| #include "geometry/GeometryComputer.hpp"
 | |
| #include "geometry/GeometryComputerUtils.hpp"
 | |
| #include "core/OpCommonUtils.hpp"
 | |
| namespace MNN {
 | |
| 
 | |
| class GeometryLayerNorm : public GeometryComputer {
 | |
| public:
 | |
|     virtual bool onCompute(const Op* op, const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs,
 | |
|                            Context& context, CommandBuffer& res) const override {
 | |
|         /* Target: Ensure reduce dimensions must be a sequence subset [-rank,...,rank-1] */
 | |
|         MNN_ASSERT(1 == outputs.size());
 | |
|         MNN_ASSERT(1 == inputs.size());
 | |
|         auto layernorm          = op->main_as_LayerNorm();
 | |
|         if (!layernorm->axis()) {
 | |
|             std::shared_ptr<Command> cmdP(new Command);
 | |
|             auto& cmd = *cmdP;
 | |
|             cmd.op      = op;
 | |
|             cmd.inputs  = {inputs[0]};
 | |
|             cmd.outputs = std::move(outputs);
 | |
|             res.command.emplace_back(std::move(cmdP));
 | |
|             return true;
 | |
|         }
 | |
|         auto reduceDims = layernorm->axis()->data();
 | |
|         int reduceDimensionCount = layernorm->axis()->size();
 | |
|         auto inputShape = inputs[0]->shape();
 | |
|         auto outputShape = outputs[0]->shape();
 | |
|         int rank = static_cast<int32_t>(inputShape.size());
 | |
| 
 | |
|         // Case1: Do not need permute
 | |
|         bool needPermute = true;
 | |
|         if (reduceDims[0] < 0 && reduceDims[reduceDimensionCount - 1] == -1) { // [-r,-r+1...]
 | |
|             needPermute = false;
 | |
|         }
 | |
|         if (reduceDims[reduceDimensionCount - 1] > 0 && reduceDims[reduceDimensionCount - 1] == rank - 1 ) { // [...,r-2,r-1]
 | |
|             needPermute = false;
 | |
|         }
 | |
|         if (reduceDims[0] == 0 && rank == 1) { // reduce dim:[0], input dimensions=1
 | |
|             needPermute = false;
 | |
|         }
 | |
|         std::vector<int> lastdims(reduceDimensionCount);
 | |
|         for (int i = 0; i < reduceDimensionCount; ++i) {
 | |
|             lastdims[i] = (reduceDims[i] + rank) % rank;
 | |
|         }
 | |
|         if (false == needPermute) {
 | |
|             std::shared_ptr<Command> cmdP(new Command);
 | |
|             auto& cmd = *cmdP;
 | |
|             cmd.op      = op;
 | |
|             cmd.inputs  = {inputs[0]};
 | |
|             cmd.outputs = std::move(outputs);
 | |
|             res.command.emplace_back(std::move(cmdP));
 | |
|             return true;
 | |
|         }
 | |
|         // Case2 : Need permute
 | |
|         int oldorder[MNN_MAX_TENSOR_DIM];
 | |
|         int neworder[MNN_MAX_TENSOR_DIM];
 | |
| 
 | |
|         {
 | |
|             int di = 0;
 | |
|             int idx = 0;
 | |
|             while (di < rank) {
 | |
|                 if (di < lastdims[0] || di > lastdims[reduceDimensionCount - 1]) {
 | |
|                     neworder[idx++] = di;
 | |
|                 }
 | |
|                 di++;
 | |
|             }
 | |
|             for (int i = 0; i < reduceDimensionCount; ++i) {
 | |
|                 neworder[idx++] = lastdims[i];
 | |
|             }
 | |
|         }
 | |
|         {
 | |
|             int idx = 0;
 | |
|             for (int i = 0; i < rank; ++i) {
 | |
|                 int j = 0;
 | |
|                 while (i != neworder[j]) {
 | |
|                     ++j;
 | |
|                 }
 | |
|                 oldorder[idx++] = j;
 | |
|             }
 | |
|         }
 | |
|         std::vector<int> newshape;
 | |
|         for (int i = 0; i < rank; ++i) {
 | |
|             newshape.emplace_back(inputShape[neworder[i]]);
 | |
|         }
 | |
|         
 | |
|         std::shared_ptr<Tensor> outputTensorPermute(Tensor::createDevice(newshape, inputs[0]->getType(), inputs[0]->getDimensionType()));
 | |
|         res.extras.emplace_back(outputTensorPermute);
 | |
|         GeometryComputer::ComputePermuteRegion(inputs[0], outputTensorPermute.get(), neworder, rank);
 | |
|         
 | |
|         // Create LayerNorm command
 | |
|         auto currentInput = outputTensorPermute.get();
 | |
|         float epislon = layernorm->epsilon();
 | |
|         bool  useRMS  = layernorm->useRMSNorm();
 | |
|         std::vector<int64_t> externalData;
 | |
|         if (layernorm->external()) {
 | |
|             externalData.resize(3);
 | |
|             externalData = {layernorm->external()->data()[0], layernorm->external()->data()[1], layernorm->external()->data()[2]};
 | |
|         }
 | |
|         std::vector<float> gamma, beta;
 | |
|         if (layernorm->gamma() && layernorm->beta()) {
 | |
|             int   gammaSize = layernorm->gamma()->size();
 | |
|             gamma.resize(gammaSize);
 | |
|             beta.resize(gammaSize);
 | |
|             int   group     = layernorm->group();
 | |
|             ::memcpy(gamma.data(), layernorm->gamma()->data(), gammaSize * sizeof(float));
 | |
|             ::memcpy(beta.data(), layernorm->beta()->data(), gammaSize * sizeof(float));
 | |
|         }
 | |
|         std::shared_ptr<Tensor> inputTensorLayernorm(Tensor::createDevice(newshape, inputs[0]->getType(), inputs[0]->getDimensionType()));
 | |
|         auto des = TensorUtils::getDescribe(inputTensorLayernorm.get());
 | |
|         des->memoryType = Tensor::InsideDescribe::MEMORY_VIRTUAL;
 | |
|         des->regions    = {TensorUtils::makeFullSlice(currentInput)};
 | |
|         res.extras.emplace_back(inputTensorLayernorm);
 | |
|         std::shared_ptr<Tensor> outputTensorLayerNorm(Tensor::createDevice(newshape, inputs[0]->getType(), inputs[0]->getDimensionType()));
 | |
|         res.extras.emplace_back(outputTensorLayerNorm);
 | |
|         {
 | |
|             auto cmd = GeometryComputerUtils::makeLayerNorm(inputTensorLayernorm.get(), outputTensorLayerNorm.get(), lastdims, epislon, gamma, beta, externalData, 1, useRMS);
 | |
|             res.command.emplace_back(std::move(cmd));
 | |
|         }
 | |
|         
 | |
|         GeometryComputer::ComputePermuteRegion(outputTensorLayerNorm.get(), outputs[0], oldorder, rank);
 | |
|         return true;
 | |
|     }
 | |
| };
 | |
| static void _create() {
 | |
|     std::shared_ptr<GeometryComputer> comp(new GeometryLayerNorm);
 | |
|     GeometryComputer::registerGeometryComputer(comp, {OpType_LayerNorm});
 | |
| }
 | |
| 
 | |
| REGISTER_GEOMETRY(GeometryLayerNorm, _create);
 | |
| 
 | |
| } // namespace MNN
 |