1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00

Merge pull request #28104 from nklskyoy:rms-norm

RMSNorm: reference cpu impl #28104

https://onnx.ai/onnx/operators/onnx__RMSNormalization.html

### 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
- [ ] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
nklskyoy
2025-12-13 07:41:44 +01:00
committed by GitHub
parent 4c8646bb78
commit d218732a70
8 changed files with 150 additions and 23 deletions
@@ -1544,6 +1544,13 @@ CV__DNN_INLINE_NS_BEGIN
static Ptr<TopK2Layer> create(const LayerParams &params);
};
class CV_EXPORTS RMSNormLayer : public Layer
{
public:
static Ptr<RMSNormLayer> create(const LayerParams& params);
};
//! @}
//! @}
CV__DNN_INLINE_NS_END
+1
View File
@@ -195,6 +195,7 @@ void initializeLayerFactory()
CV_DNN_REGISTER_LAYER_CLASS(Gather2, Gather2Layer);
CV_DNN_REGISTER_LAYER_CLASS(GatherElements, GatherElementsLayer);
CV_DNN_REGISTER_LAYER_CLASS(LayerNormalization, LayerNormLayer);
CV_DNN_REGISTER_LAYER_CLASS(RMSNormalization, RMSNormLayer);
CV_DNN_REGISTER_LAYER_CLASS(Expand, ExpandLayer);
CV_DNN_REGISTER_LAYER_CLASS(InstanceNormalization, InstanceNormLayer);
CV_DNN_REGISTER_LAYER_CLASS(Attention, AttentionLayer);
@@ -42,7 +42,7 @@ void fastNorm(const Mat &input, Mat &output, float epsilon, size_t normalized_ax
parallel_for_(Range(0, loops), fn, nstripes);
}
void fastNorm(const Mat &input, const Mat &scale, Mat &output, float epsilon, size_t normalized_axis) {
void fastNorm(const Mat &input, const Mat &scale, Mat &output, float epsilon, size_t normalized_axis, bool recenter) {
const auto input_shape = shape(input);
CV_CheckLT(normalized_axis, input_shape.size(), "fastNorm: axis out of range");
@@ -61,7 +61,8 @@ void fastNorm(const Mat &input, const Mat &scale, Mat &output, float epsilon, si
float mean = 0.f, mean_square = 0.f;
for (int j = 0; j < norm_size; j++) {
float v = x[j];
mean += v;
if (recenter)
mean += v;
mean_square += v * v;
}
@@ -13,7 +13,7 @@ namespace cv { namespace dnn {
void fastNorm(const Mat &input, Mat &output, float epsilon, size_t normalized_axis = 0, bool normalize_variance = true);
// Normalization speedup by multi-threading with absent bias. Mainly for LayerNormalization.
void fastNorm(const Mat &input, const Mat &scale, Mat &output, float epsilon, size_t normalized_axis = 0);
void fastNorm(const Mat &input, const Mat &scale, Mat &output, float epsilon, size_t normalized_axis = 0, bool recenter=true);
// Normalization speedup by multi-threading with scale and bias. Mainly for LayerNormalization.
void fastNorm(const Mat &input, const Mat &scale, const Mat &bias, Mat &output, float epsilon, size_t normalized_axis = 0);
+94
View File
@@ -0,0 +1,94 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "../precomp.hpp"
#include "layers_common.hpp"
#include "cpu_kernels/fast_norm.hpp"
namespace cv { namespace dnn {
// https://github.com/onnx/onnx/blob/main/docs/Operators.md#LayerNormalization
class RMSNormLayerImpl CV_FINAL : public RMSNormLayer
{
#ifdef HAVE_OPENCL
UMat weight_umat, bias_umat;
#endif
public:
int axis0;
RMSNormLayerImpl(const LayerParams& params)
{
setParamsFrom(params);
// standard attr
axis = params.get<int>("axis", -1);
epsilon = params.get<float>("epsilon", 1e-5);
}
virtual bool supportBackend(int backendId) CV_OVERRIDE
{
return backendId == DNN_BACKEND_OPENCV;
}
virtual bool getMemoryShapes(const std::vector<MatShape> &inputs,
const int requiredOutputs,
std::vector<MatShape> &outputs,
std::vector<MatShape> &internals) const CV_OVERRIDE
{
const int n_inputs = inputs.size();
CV_Check(n_inputs, n_inputs == 2, "RMSNorm: require two (x, scale) inputs");
auto x_shape = inputs[0];
auto scale_shape = inputs[1];
const int normalized_axis = normalize_axis(axis, static_cast<int>(x_shape.size()));
const int x_ndims = static_cast<int>(x_shape.size());
CV_CheckTrue(scale_shape.size() == x_ndims - normalized_axis,
"RMSNorm: scale shape should match normalized shape");
for (int i = 0; i + normalized_axis < x_ndims; ++i)
{
CV_CheckTrue(
x_shape[i + normalized_axis] == scale_shape[i],
"RMSNorm: scale shape should match normalized shape");
}
outputs.assign(1, inputs[0]);
return true;
}
void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
{
CV_TRACE_FUNCTION();
CV_TRACE_ARG_VALUE(name, "name", name.c_str());
if (inputs_arr.depth() == CV_16F)
{
forward_fallback(inputs_arr, outputs_arr, internals_arr);
return;
}
std::vector<Mat> inputs, outputs;
inputs_arr.getMatVector(inputs);
outputs_arr.getMatVector(outputs);
const auto &input = inputs[0];
const auto &scale = inputs[1];
auto &output = outputs[0];
axis = normalize_axis(axis, input.dims);
fastNorm(input, scale, output, epsilon, static_cast<size_t>(axis), false);
}
private:
int axis;
float epsilon;
};
Ptr<RMSNormLayer> RMSNormLayer::create(const LayerParams& params)
{
return makePtr<RMSNormLayerImpl>(params);
}
}} // cv::dnn
+6 -1
View File
@@ -247,7 +247,7 @@ protected:
void parseBitShift (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseBitwise (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseBitwiseNot (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseRotaryEmbedding (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseRMSNormalization (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseRotaryEmbedding (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
// Domain: com.microsoft
// URL: https://github.com/microsoft/onnxruntime/blob/master/docs/ContribOperators.md
void parseAttention (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2014,6 +2014,11 @@ void ONNXImporter2::parseQuantizeLinear(LayerParams& layerParams, const opencv_o
addLayer(layerParams, node_proto);
}
void ONNXImporter2::parseRMSNormalization(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
addLayer(layerParams, node_proto);
}
// BUG: https://github.com/opencv/opencv/issues/26310
/*void ONNXImporter2::parseQConv(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto_)
{
@@ -2764,6 +2764,44 @@ CASE(test_rotary_embedding_with_rotary_dim)
SKIP;
CASE(test_rotary_embedding_with_rotary_dim_expanded)
SKIP;
CASE(test_rms_normalization_2d_axis0)
SKIP;
CASE(test_rms_normalization_2d_axis1)
SKIP;
CASE(test_rms_normalization_2d_axis_negative_1)
SKIP;
CASE(test_rms_normalization_2d_axis_negative_2)
SKIP;
CASE(test_rms_normalization_3d_axis0_epsilon)
SKIP;
CASE(test_rms_normalization_3d_axis1_epsilon)
SKIP;
CASE(test_rms_normalization_3d_axis2_epsilon)
SKIP;
CASE(test_rms_normalization_3d_axis_negative_1_epsilon)
SKIP;
CASE(test_rms_normalization_3d_axis_negative_2_epsilon)
SKIP;
CASE(test_rms_normalization_3d_axis_negative_3_epsilon)
SKIP;
CASE(test_rms_normalization_4d_axis0)
SKIP;
CASE(test_rms_normalization_4d_axis1)
SKIP;
CASE(test_rms_normalization_4d_axis2)
SKIP;
CASE(test_rms_normalization_4d_axis3)
SKIP;
CASE(test_rms_normalization_4d_axis_negative_1)
SKIP;
CASE(test_rms_normalization_4d_axis_negative_2)
SKIP;
CASE(test_rms_normalization_4d_axis_negative_3)
SKIP;
CASE(test_rms_normalization_4d_axis_negative_4)
SKIP;
CASE(test_rms_normalization_default_axis)
SKIP;
#if SKIP_SET_1
SKIP;
#endif
@@ -534,43 +534,24 @@
"test_resize_upsample_sizes_nearest_not_smaller",
"test_reversesequence_batch", // Issue:: Parser: Can't create layer "onnx_node_output_0!y" of type "ReverseSequence" in function 'getLayerInstance'
"test_reversesequence_time", // ---- same as above ---
"test_rms_normalization_2d_axis0",
"test_rms_normalization_2d_axis0_expanded",
"test_rms_normalization_2d_axis1",
"test_rms_normalization_2d_axis1_expanded",
"test_rms_normalization_2d_axis_negative_1",
"test_rms_normalization_2d_axis_negative_1_expanded",
"test_rms_normalization_2d_axis_negative_2",
"test_rms_normalization_2d_axis_negative_2_expanded",
"test_rms_normalization_3d_axis0_epsilon",
"test_rms_normalization_3d_axis0_epsilon_expanded",
"test_rms_normalization_3d_axis1_epsilon",
"test_rms_normalization_3d_axis1_epsilon_expanded",
"test_rms_normalization_3d_axis2_epsilon",
"test_rms_normalization_3d_axis2_epsilon_expanded",
"test_rms_normalization_3d_axis_negative_1_epsilon",
"test_rms_normalization_3d_axis_negative_1_epsilon_expanded",
"test_rms_normalization_3d_axis_negative_2_epsilon",
"test_rms_normalization_3d_axis_negative_2_epsilon_expanded",
"test_rms_normalization_3d_axis_negative_3_epsilon",
"test_rms_normalization_3d_axis_negative_3_epsilon_expanded",
"test_rms_normalization_4d_axis0",
"test_rms_normalization_4d_axis0_expanded",
"test_rms_normalization_4d_axis1",
"test_rms_normalization_4d_axis1_expanded",
"test_rms_normalization_4d_axis2",
"test_rms_normalization_4d_axis2_expanded",
"test_rms_normalization_4d_axis3",
"test_rms_normalization_4d_axis3_expanded",
"test_rms_normalization_4d_axis_negative_1",
"test_rms_normalization_4d_axis_negative_1_expanded",
"test_rms_normalization_4d_axis_negative_2",
"test_rms_normalization_4d_axis_negative_2_expanded",
"test_rms_normalization_4d_axis_negative_3",
"test_rms_normalization_4d_axis_negative_3_expanded",
"test_rms_normalization_4d_axis_negative_4",
"test_rms_normalization_4d_axis_negative_4_expanded",
"test_rms_normalization_default_axis",
"test_rms_normalization_default_axis_expanded",
"test_rnn_seq_length", // Issue:: Parser: Can't create layer "onnx_node_output_1!Y_h" of type "RNN" in function 'getLayerInstance'
"test_roialign_aligned_false", // Issue:: Parser: Layer does not exist (RoiAlign)