mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 07:43:03 +04:00
Merge pull request #24409 from fengyuentau:norm_kernel
dnn: add shared fastNorm kernel for mvn, instance norm and layer norm #24409 Relates https://github.com/opencv/opencv/pull/24378#issuecomment-1756906570 TODO: - [x] add fastNorm - [x] refactor layer norm with fastNorm - [x] refactor mvn with fastNorm - [ ] add onnx mvn in importer (in a new PR?) - [ ] refactor instance norm with fastNorm (in another PR https://github.com/opencv/opencv/pull/24378, need to merge this one first though) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include "cpu_kernels/fast_norm.hpp"
|
||||
|
||||
namespace cv { namespace dnn {
|
||||
|
||||
@@ -15,11 +16,8 @@ public:
|
||||
setParamsFrom(params);
|
||||
|
||||
// standard attr
|
||||
axis = params.get<int>("axis", 0);
|
||||
axis = params.get<int>("axis", -1);
|
||||
epsilon = params.get<float>("epsilon", 1e-5);
|
||||
|
||||
// opencv attr
|
||||
hasBias = params.get<bool>("hasBias", false);
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||
@@ -46,104 +44,25 @@ public:
|
||||
CV_CheckEQ(x_ndims - axis, w_ndims, "LayerNorm: shape of weight does not match with given axis and shape of input");
|
||||
for (int i = 0; i < w_ndims; ++i)
|
||||
CV_CheckEQ(x_shape[axis+i], w_shape[i], "LayerNorm: weight dimensions does not match with input dimensions");
|
||||
if (hasBias)
|
||||
if (inputs.size() == static_cast<int>(3))
|
||||
{
|
||||
CV_CheckEQ(inputs.size(), (size_t)3, "");
|
||||
auto b_shape = inputs[2];
|
||||
CV_CheckEQ(w_shape.size(), b_shape.size(), "LayerNorm: shape of weight does not match with shape of bias");
|
||||
for (size_t i = 0; i < w_shape.size(); ++i)
|
||||
CV_CheckEQ(w_shape[i], b_shape[i], "LayerNorm: bias dimensions does not match with weight dimensions");
|
||||
}
|
||||
|
||||
// only one output is needed; Mean & InvStdDev are not needed
|
||||
// in inference and should beomitted in onnx importer
|
||||
outputs.assign(1, inputs[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
template<bool hasBias>
|
||||
class LayerNormInvoker : public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
const Mat& src;
|
||||
const float* scaleData;
|
||||
const float* biasData;
|
||||
Mat& dst;
|
||||
virtual void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr) CV_OVERRIDE {
|
||||
std::vector<Mat> inputs;
|
||||
inputs_arr.getMatVector(inputs);
|
||||
|
||||
float epsilon;
|
||||
|
||||
int total;
|
||||
int normSize;
|
||||
float invNormSize;
|
||||
|
||||
LayerNormInvoker(const Mat& src_, const Mat& scale, const Mat* b, Mat& dst_, int axis, float epsilon_)
|
||||
: src(src_), scaleData(scale.ptr<float>()), biasData(nullptr), dst(dst_), epsilon(epsilon_)
|
||||
{
|
||||
if (hasBias)
|
||||
{
|
||||
CV_Assert(b != nullptr);
|
||||
CV_Assert(b->isContinuous());
|
||||
biasData = (const float*)b->ptr<float>();
|
||||
}
|
||||
|
||||
auto dstShape = shape(dst);
|
||||
total = std::accumulate(dstShape.begin(), dstShape.begin() + axis, 1, std::multiplies<int>());
|
||||
normSize = std::accumulate(dstShape.begin() + axis, dstShape.end(), 1, std::multiplies<int>());
|
||||
invNormSize = 1.0f / normSize;
|
||||
}
|
||||
|
||||
static void run(const Mat& src, const Mat& scale, const Mat* b, Mat& dst, int axis, float epsilon)
|
||||
{
|
||||
CV_Assert(src.isContinuous());
|
||||
CV_Assert(dst.isContinuous());
|
||||
CV_CheckTypeEQ(src.type(), CV_32F, "DNN/LayerNorm: only support float32");
|
||||
CV_CheckTypeEQ(src.type(), dst.type(), "");
|
||||
CV_Assert(scale.isContinuous());
|
||||
|
||||
CV_CheckGE(epsilon, 0.0f, "");
|
||||
|
||||
LayerNormInvoker p(src, scale, b, dst, axis, epsilon);
|
||||
|
||||
double nstripes = ((size_t)p.total * p.normSize) * (1 / 1024.0);
|
||||
// double nstripes = ((size_t)p.total) * (1 / 1024.0);
|
||||
parallel_for_(Range(0, p.total), p, nstripes);
|
||||
}
|
||||
|
||||
void operator()(const Range& r) const CV_OVERRIDE
|
||||
{
|
||||
int stripeStart = r.start;
|
||||
int stripeEnd = r.end;
|
||||
|
||||
const float* srcData = src.ptr<float>();
|
||||
float* dstData = dst.ptr<float>();
|
||||
|
||||
for (int ofs = stripeStart; ofs < stripeEnd; ++ofs)
|
||||
{
|
||||
const float* first = srcData + ofs * normSize;
|
||||
float* dstFirst = dstData + ofs * normSize;
|
||||
|
||||
float mean = 0;
|
||||
float meanSquare = 0;
|
||||
for (int h = 0; h < normSize; ++h)
|
||||
{
|
||||
float v = first[h];
|
||||
mean += v;
|
||||
meanSquare += v * v;
|
||||
}
|
||||
mean *= invNormSize;
|
||||
meanSquare = std::sqrt(std::max(0.f, meanSquare * invNormSize - mean * mean) + epsilon);
|
||||
float invMeanSquare = 1.0f / meanSquare;
|
||||
for (int h = 0; h < normSize; ++h)
|
||||
{
|
||||
float v = (first[h] - mean) * invMeanSquare * scaleData[h];
|
||||
if (hasBias) {
|
||||
v = v + biasData[h];
|
||||
}
|
||||
dstFirst[h] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const auto input_shape = shape(inputs[0]);
|
||||
axis = normalize_axis(axis, static_cast<int>(input_shape.size()));
|
||||
}
|
||||
|
||||
void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
|
||||
{
|
||||
@@ -160,10 +79,15 @@ public:
|
||||
inputs_arr.getMatVector(inputs);
|
||||
outputs_arr.getMatVector(outputs);
|
||||
|
||||
if (hasBias) {
|
||||
LayerNormInvoker<true>::run(inputs[0], inputs[1], &inputs[2], outputs[0], axis, epsilon);
|
||||
const auto &input = inputs[0];
|
||||
const auto &scale = inputs[1];
|
||||
auto &output = outputs[0];
|
||||
|
||||
if (inputs.size() == 3) {
|
||||
const auto &bias = inputs[2];
|
||||
fastNorm(input, scale, bias, output, epsilon, static_cast<size_t>(axis));
|
||||
} else {
|
||||
LayerNormInvoker<false>::run(inputs[0], inputs[1], nullptr, outputs[0], axis, epsilon);
|
||||
fastNorm(input, scale, output, epsilon, static_cast<size_t>(axis));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user