1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

Merge pull request #23047 from fengyuentau:layer_norm

dnn: add layer normalization for vision transformers

* add layer norm onnx parser, impl and tests

* add onnx graph simplifier for layer norm expanded

* handle the case when constants are of type Initializer

* add test case for layer norm expanded with initializers

* use CV_Assert & CV_CheckType in place of CV_Assert_N; use forward_fallback for OCL_FP16

* use const ref / ref in parameters of invoker::run; extract inner const if from nested loop; use size_t in place of ull

* template hasBias

* remove trailing whitespace

* use pointer parameter with null check; move normSize division & mean_square division outside of loop; use std::max to ensure positive value before std::sqrt

* refactor implementation, optimize parallel_for

* disable layer norm expanded

* remove the removal of layer norm optional outputs
This commit is contained in:
Yuantao Feng
2023-01-27 21:35:59 +08:00
committed by GitHub
parent 52855a39ad
commit 4d918ba40b
7 changed files with 636 additions and 0 deletions
+208
View File
@@ -417,6 +417,212 @@ PERF_TEST_P_(Layer_ScatterND, DISABLED_ScatterND_add)
test_layer({N, C, H , W}, "add");
}
struct Layer_LayerNorm : public TestBaseWithParam<tuple<Backend, Target> >
{
void test_layer(const std::vector<int>& x_shape)
{
int backendId = get<0>(GetParam());
int targetId = get<1>(GetParam());
Mat x(x_shape, CV_32FC1);
Mat scale(x_shape.back(), 1, CV_32FC1);
Mat b(x_shape.back(), 1, CV_32FC1);
randu(x, 0.f, 1.f);
randu(scale, 0.f, 1.f);
randu(b, 0.f, 1.f);
Net net;
LayerParams lp;
lp.type = "LayerNormalization";
lp.name = "testLayer";
lp.set("axis", 2);
lp.set("hasBias", true);
int id = net.addLayerToPrev(lp.name, lp.type, lp);
net.connect(0, 0, id, 0);
net.connect(0, 1, id, 1);
net.connect(0, 2, id, 2);
// warmup
{
std::vector<String> inpNames(3);
inpNames[0] = "x";
inpNames[1] = "scale";
inpNames[2] = "b";
net.setInputsNames(inpNames);
net.setInput(x, inpNames[0]);
net.setInput(scale, inpNames[1]);
net.setInput(b, inpNames[2]);
net.setPreferableBackend(backendId);
net.setPreferableTarget(targetId);
Mat out = net.forward();
}
TEST_CYCLE()
{
Mat res = net.forward();
}
SANITY_CHECK_NOTHING();
}
int N = 1;
int H = 50;
int W = 768;
};
PERF_TEST_P_(Layer_LayerNorm, LayerNorm)
{
test_layer({N, H ,W});
}
struct Layer_LayerNormExpanded : public TestBaseWithParam<tuple<Backend, Target> >
{
void test_layer(const std::vector<int>& x_shape)
{
int backendId = get<0>(GetParam());
int targetId = get<1>(GetParam());
Mat x(x_shape, CV_32FC1);
Mat scale(1, x_shape.back(), CV_32FC1); // transpose to pass shape check
Mat b(1, x_shape.back(), CV_32FC1); // transpose to pass shape check
randu(x, 0.f, 1.f);
randu(scale, 0.f, 1.f);
randu(b, 0.f, 1.f);
// sub graph structure:
// -> ReduceMean -> -> Pow(2) -> ReduceMean -> Add(epsilon) -> Sqrt ->
// x Sub Div -> Mul(scale) -> Add(bias)
// ---------------> ------------------------------------------------->
Net net;
LayerParams lp_rm;
lp_rm.type = "Reduce";
lp_rm.name = "reducemean1";
lp_rm.set("reduce", "AVE");
std::vector<int> deleteDims(1, x_shape.back());
lp_rm.set("deleted_dims", DictValue::arrayInt(&deleteDims[0], deleteDims.size()));
std::vector<int> targetDims(x_shape.begin(), x_shape.end());
targetDims[x_shape.size() - 1] = 1;
lp_rm.set("target_dims", DictValue::arrayInt(&targetDims[0], targetDims.size()));
int id_rm = net.addLayerToPrev(lp_rm.name, lp_rm.type, lp_rm);
net.connect(0, 0, id_rm, 0);
LayerParams lp_sub;
lp_sub.type = "NaryEltwise";
lp_sub.name = "sub1";
lp_sub.set("operation", "sub");
int id_sub = net.addLayer(lp_sub.name, lp_sub.type, lp_sub);
net.connect(0, 0, id_sub, 0);
net.connect(id_rm, 0, id_sub, 1);
Mat pow_const(1, 1, CV_32FC1);
pow_const.at<float>(0) = 2.f;
LayerParams lp_pow_const;
lp_pow_const.type = "Const";
lp_pow_const.name = "const1";
lp_pow_const.blobs.push_back(pow_const);
int id_pow_const = net.addLayer(lp_pow_const.name, lp_pow_const.type, lp_pow_const);
LayerParams lp_pow;
lp_pow.type = "NaryEltwise";
lp_pow.name = "pow1";
lp_pow.set("operation", "pow");
int id_pow = net.addLayer(lp_pow.name, lp_pow.type, lp_pow);
net.connect(id_sub, 0, id_pow, 0);
net.connect(id_pow_const, 0, id_pow, 1);
LayerParams lp_rm1;
lp_rm1.type = "Reduce";
lp_rm1.name = "reducemean2";
lp_rm1.set("reduce", "AVE");
lp_rm1.set("deleted_dims", DictValue::arrayInt(&deleteDims[0], deleteDims.size()));
lp_rm1.set("target_dims", DictValue::arrayInt(&targetDims[0], targetDims.size()));
int id_rm1 = net.addLayer(lp_rm1.name, lp_rm1.type, lp_rm1);
net.connect(id_pow, 0, id_rm1, 0);
Mat add_const(1, 1, CV_32F);
add_const.at<float>(0) = 1e-5;
LayerParams lp_add_const;
lp_add_const.type = "Const";
lp_add_const.name = "const2";
lp_add_const.blobs.push_back(add_const);
int id_add_const = net.addLayer(lp_add_const.name, lp_add_const.type, lp_add_const);
LayerParams lp_add;
lp_add.type = "NaryEltwise";
lp_add.name = "add1";
lp_add.set("operation", "add");
int id_add = net.addLayer(lp_add.name, lp_add.type, lp_add);
net.connect(id_rm1, 0, id_add, 0);
net.connect(id_add_const, 0, id_add, 1);
LayerParams lp_sqrt;
lp_sqrt.type = "Sqrt";
lp_sqrt.name = "sqrt1";
int id_sqrt = net.addLayer(lp_sqrt.name, lp_sqrt.type, lp_sqrt);
net.connect(id_add, 0, id_sqrt, 0);
LayerParams lp_div;
lp_div.type = "NaryEltwise";
lp_div.name = "div1";
lp_div.set("operation", "div");
int id_div = net.addLayer(lp_div.name, lp_div.type, lp_div);
net.connect(id_sub, 0, id_div, 0);
net.connect(id_sqrt, 0, id_div, 1);
LayerParams lp_mul;
lp_mul.type = "NaryEltwise";
lp_mul.name = "mul1";
lp_mul.set("operation", "mul");
int id_mul = net.addLayer(lp_mul.name, lp_mul.type, lp_mul);
net.connect(id_div, 0, id_mul, 0);
net.connect(0, 1, id_mul, 1);
LayerParams lp_add1;
lp_add1.type = "NaryEltwise";
lp_add1.name = "add2";
lp_add1.set("operation", "add");
int id_add1 = net.addLayer(lp_add1.name, lp_add1.type, lp_add1);
net.connect(id_mul, 0, id_add1, 0);
net.connect(0, 2, id_add1, 1);
// warmup
{
std::vector<String> inpNames(3);
inpNames[0] = "x";
inpNames[1] = "scale";
inpNames[2] = "b";
net.setInputsNames(inpNames);
net.setInput(x, inpNames[0]);
net.setInput(scale, inpNames[1]);
net.setInput(b, inpNames[2]);
net.setPreferableBackend(backendId);
net.setPreferableTarget(targetId);
Mat out = net.forward();
}
TEST_CYCLE()
{
Mat res = net.forward();
}
SANITY_CHECK_NOTHING();
}
int N = 1;
int H = 50;
int W = 768;
};
PERF_TEST_P_(Layer_LayerNormExpanded, DISABLED_LayerNormExpanded)
{
test_layer({N, H ,W});
}
INSTANTIATE_TEST_CASE_P(/**/, Layer_Slice, dnnBackendsAndTargets(false, false));
INSTANTIATE_TEST_CASE_P(/**/, Layer_NaryEltwise, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU)));
#ifdef HAVE_CUDA
@@ -424,5 +630,7 @@ INSTANTIATE_TEST_CASE_P(CUDA, Layer_NaryEltwise, testing::Values(std::make_tuple
#endif
INSTANTIATE_TEST_CASE_P(/**/, Layer_Scatter, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU)));
INSTANTIATE_TEST_CASE_P(/**/, Layer_ScatterND, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU)));
INSTANTIATE_TEST_CASE_P(/**/, Layer_LayerNorm, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU)));
INSTANTIATE_TEST_CASE_P(/**/, Layer_LayerNormExpanded, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU)));
} // namespace