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

Merge pull request #28750 from abhishek-gola:activation_fusion

Extended fusion for activation functions in new DNN engine #28750

After this fusion, we see following improvements in YOLO models:

| Model | Before (`ENGINE_NEW`) | After (`ENGINE_NEW`) | `ENGINE_ORT` | % Improvement (Before v/s After) |
| :--- | :--- | :--- | :--- | :--- |
| **YOLOv8n** | 18.89  ms| 12.06 ms| 12.15 ms| 36.16% |
| **YOLOv5n** | 17.12  ms| 9.29 ms| 9.23 ms| 45.73% |
| **YOLOX-S** | 38.78  ms| 25.56 ms| 25.16 ms| 34.09% |

Device details: 
      - Model name: Intel(R) Core(TM) i9-14900KS, x86, 32 Cores, ubuntu 24.04,
### 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:
Abhishek Gola
2026-04-14 11:38:14 +05:30
committed by GitHub
parent 53d9a67cf3
commit 3d77645a3a
13 changed files with 561 additions and 23 deletions
+1
View File
@@ -13,6 +13,7 @@ ocv_add_dispatched_file_force_all("layers/cpu_kernels/fast_gemm_kernels" AVX AVX
ocv_add_dispatched_file("layers/cpu_kernels/conv2_depthwise" AVX AVX2 NEON NEON_FP16)
ocv_add_dispatched_file("layers/cpu_kernels/conv2_kernels" AVX AVX2 NEON NEON_FP16)
ocv_add_dispatched_file_force_all("int8layers/conv2_int8_kernels" AVX2)
ocv_add_dispatched_file("layers/cpu_kernels/activation_kernels" AVX AVX2 NEON NEON_FP16)
ocv_add_module(dnn opencv_core opencv_imgproc WRAP python java objc js)
@@ -918,6 +918,36 @@ CV__DNN_INLINE_NS_BEGIN
static Ptr<Pad2Layer> create(const LayerParams& params);
};
/* Activation function pointer type.
Used for fast, platform-optimized activation implementations.
@param input pointer to input data
@param output pointer to output data (can be same as input for in-place)
@param len number of elements
@param params activation-specific parameters (e.g., alpha, beta)
*/
typedef void (*ActivationFunc)(const void* input, void* output,
size_t len, const float* params);
/** Activation type enumeration for dispatched activation function retrieval. */
enum ActivationType {
ACTIV_NONE = 0,
ACTIV_MISH,
ACTIV_SWISH,
ACTIV_SIGMOID,
ACTIV_TANH,
ACTIV_ELU,
ACTIV_HARDSWISH,
ACTIV_HARDSIGMOID,
ACTIV_GELU,
ACTIV_GELU_APPROX,
ACTIV_RELU,
ACTIV_CLIP
};
/** Returns a platform-optimized activation function pointer for the given type.
The returned function is selected via CPU dispatch for the best available ISA. */
CV_EXPORTS ActivationFunc getActivationFunc(int activationType);
/* Activations */
class CV_EXPORTS ActivationLayer : public Layer
{
@@ -932,6 +962,13 @@ CV__DNN_INLINE_NS_BEGIN
size_t /*outPlaneSize*/, int /*cn0*/, int /*cn1*/) const {}
virtual void forwardSlice(const int8_t* /*src*/, const int8_t* /*lut*/, int8_t* /*dst*/, int /*len*/,
size_t /*outPlaneSize*/, int /*cn0*/, int /*cn1*/) const {}
/** Returns a platform-optimized activation function pointer for this layer.
@return function pointer, or nullptr if not available for the given depth
*/
virtual ActivationFunc getActivationFunc(int /*depth*/,
std::vector<float>& /*activParams*/) const
{ return nullptr; }
};
class CV_EXPORTS ReLULayer : public ActivationLayer
@@ -340,7 +340,7 @@ public:
if (inpshape != prevInpshape) {
cs.initConv(inpshape, wshape0, outshape, ngroups,
strides, dilations, pads, auto_pad, ceil_mode,
FAST_ACTIV_NONE, {});
FAST_ACTIV_NONE, nullptr, {});
prevInpshape = inpshape;
}
+10 -6
View File
@@ -13,11 +13,14 @@ CV__DNN_INLINE_NS_BEGIN
std::string fastActivationToString(FastActivation fastActivation)
{
return fastActivation == FAST_ACTIV_RELU ? "ReLU" :
fastActivation == FAST_ACTIV_LEAKY_RELU ? "LeakyReLU" :
fastActivation == FAST_ACTIV_PRELU ? "PReLU" :
fastActivation == FAST_ACTIV_CLIP ? "Clip" :
fastActivation == FAST_ACTIV_NONE ? "None" : format("unknown(%d)", int(fastActivation));
switch (fastActivation) {
case FAST_ACTIV_NONE: return "None";
case FAST_ACTIV_RELU: return "ReLU";
case FAST_ACTIV_LEAKY_RELU: return "LeakyReLU";
case FAST_ACTIV_PRELU: return "PReLU";
case FAST_ACTIV_CLIP: return "Clip";
default: return format("unknown(%d)", int(fastActivation));
}
}
AutoPadding getAutoPadding(const LayerParams& params)
@@ -175,6 +178,7 @@ void ConvState::initConv(const MatShape& inpshape_,
const std::vector<int>& pads_,
AutoPadding autoPad, bool ceilMode,
FastActivation fastActivation_,
ActivationFunc activationFunc_,
const std::vector<float>& activParams_)
{
nspatialdims = wshape_.dims - 2;
@@ -201,7 +205,7 @@ void ConvState::initConv(const MatShape& inpshape_,
}
fastActivation = fastActivation_;
activation = nullptr;
activation = activationFunc_;
activParams = activParams_;
CV_Assert(wshape_[0] > 0 && wshape_[1] > 0);
+2 -4
View File
@@ -5,7 +5,7 @@
#ifndef __OPENCV_DNN_LAYERS_CONV2_COMMON_HPP__
#define __OPENCV_DNN_LAYERS_CONV2_COMMON_HPP__
#include <opencv2/dnn.hpp>
#include <opencv2/dnn/all_layers.hpp>
#include <array>
namespace cv
@@ -33,9 +33,6 @@ enum FastActivation {
std::string fastActivationToString(FastActivation fastActivation);
typedef void (*ActivationFunc)(const void* input, void* output,
size_t len, const float* params);
struct ConvState
{
enum { MAX_CONV_DIMS = 3 };
@@ -67,6 +64,7 @@ struct ConvState
const std::vector<int>& pads,
AutoPadding autoPad, bool ceilMode,
FastActivation fastActivation,
ActivationFunc activationFunc,
const std::vector<float>& activParams);
// initializes the structure of parameters for 1D/2D/3D
+13 -6
View File
@@ -34,6 +34,7 @@ public:
ngroups = params.get<int>("group", 1);
fusedBatchNorm = false;
fastActivation = FAST_ACTIV_NONE;
activationFunc = nullptr;
addResidual = false;
}
@@ -73,10 +74,11 @@ public:
strm << "batch_norm: true,\n";
}
if (fastActivation != FAST_ACTIV_NONE || !activ.empty()) {
if (fastActivation != FAST_ACTIV_NONE || activationFunc != nullptr || !activ.empty()) {
prindent(strm, indent);
strm << "fused_activation: " <<
(fastActivation != FAST_ACTIV_NONE ? fastActivationToString(fastActivation) :
activationFunc != nullptr ? "ActivationFunc" :
activ->type) << ",\n";
}
@@ -186,8 +188,10 @@ public:
virtual bool fuseActivation(const Ptr<Layer>& activlayer) override
{
ActivationLayer* activ_ptr = dynamic_cast<ActivationLayer*>(activlayer.get());
if (!activ_ptr || fastActivation != FAST_ACTIV_NONE || !activ.empty())
if (!activ_ptr || fastActivation != FAST_ACTIV_NONE ||
activationFunc != nullptr || !activ.empty())
return false;
ReLULayer* activRelu = dynamic_cast<ReLULayer*>(activ_ptr);
ReLU6Layer* activClip = dynamic_cast<ReLU6Layer*>(activ_ptr);
ChannelsPReLULayer* activPRelu = dynamic_cast<ChannelsPReLULayer*>(activ_ptr);
@@ -211,15 +215,17 @@ public:
int nslopes = int(slopes.total());
Mat(1, &nslopes, slopesType, (void*)slopes.data).convertTo(activParams, CV_32F);
} else {
//activ = activlayer;
return false;
activationFunc = activ_ptr->getActivationFunc(CV_32F, activParams);
if (!activationFunc)
return false;
}
return true;
}
virtual bool fuseAddResidual(Arg residual) CV_OVERRIDE
{
if (activ.empty() && fastActivation == FAST_ACTIV_NONE && !addResidual && residual.idx >= 0) {
if (activ.empty() && fastActivation == FAST_ACTIV_NONE &&
activationFunc == nullptr && !addResidual && residual.idx >= 0) {
addResidual = true;
inputs.push_back(residual);
return true;
@@ -345,7 +351,7 @@ public:
if (inpshape != prevInpshape) {
cs.initConv(inpshape, wshape0, outshape, ngroups,
strides, dilations, pads, auto_pad, ceil_mode,
fastActivation, activParams);
fastActivation, activationFunc, activParams);
prevInpshape = inpshape;
}
@@ -400,6 +406,7 @@ public:
ConvState cs;
bool fusedBatchNorm;
FastActivation fastActivation;
ActivationFunc activationFunc;
std::vector<float> activParams;
bool addResidual;
};
@@ -0,0 +1,19 @@
// 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 <opencv2/dnn/all_layers.hpp>
#include "activation_kernels.simd.hpp"
#include "layers/cpu_kernels/activation_kernels.simd_declarations.hpp"
namespace cv { namespace dnn {
CV__DNN_INLINE_NS_BEGIN
ActivationFunc getActivationFunc(int type)
{
CV_CPU_DISPATCH(getActivationFunc_, (type), CV_CPU_DISPATCH_MODES_ALL);
}
CV__DNN_INLINE_NS_END
}}
@@ -0,0 +1,321 @@
// 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.
// Copyright (C) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include <opencv2/dnn/all_layers.hpp>
#include "opencv2/core/hal/intrin.hpp"
#include <math.h>
namespace cv {
namespace dnn {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
cv::dnn::ActivationFunc getActivationFunc_(int type);
CV_CPU_OPTIMIZATION_NAMESPACE_END
}}
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
namespace cv {
namespace dnn {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
// Mish: x * tanh(softplus(x))
// Uses numerically stable form: x * (1 + 2*y) / (1 + 2*y + 2*y*y) where y = exp(-x) for large x
static void activationMish(const void* input, void* output,
size_t len, const float* /*params*/)
{
const float* inp = (const float*)input;
float* out = (float*)output;
const float MISH_THRESHOLD = -36.73f;
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vlanes = VTraits<v_float32>::vlanes();
v_float32 v_threshold = vx_setall_f32(MISH_THRESHOLD);
v_float32 one = vx_setall_f32(1.f), z = vx_setzero_f32();
for (; i + vlanes <= len; i += vlanes) {
v_float32 x = vx_load(inp + i);
x = v_select(v_le(x, v_threshold), z, x);
v_float32 y = v_exp(v_sub(z, x));
v_float32 _2y = v_add(y, y);
v_float32 _2ya1 = v_add(_2y, one);
x = v_div(v_mul(x, _2ya1), v_add(_2ya1, v_mul(_2y, y)));
vx_store(out + i, x);
}
#endif
for (; i < len; i++) {
float x = inp[i];
if (x <= MISH_THRESHOLD) { out[i] = 0.f; continue; }
float y = expf(-x);
float _2y = 2.f * y;
out[i] = x * (1.f + _2y) / (1.f + _2y + _2y * y);
}
}
// Swish/SiLU: x / (1 + exp(-x))
static void activationSwish(const void* input, void* output,
size_t len, const float* /*params*/)
{
const float* inp = (const float*)input;
float* out = (float*)output;
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vlanes = VTraits<v_float32>::vlanes();
v_float32 one = vx_setall_f32(1.f), z = vx_setzero_f32();
for (; i + vlanes <= len; i += vlanes) {
v_float32 x = vx_load(inp + i);
v_float32 t = v_exp(v_sub(z, x));
t = v_div(x, v_add(one, t));
vx_store(out + i, t);
}
#endif
for (; i < len; i++) {
float x = inp[i];
out[i] = x / (1.f + expf(-x));
}
}
// Sigmoid: 1 / (1 + exp(-x))
static void activationSigmoid(const void* input, void* output,
size_t len, const float* /*params*/)
{
const float* inp = (const float*)input;
float* out = (float*)output;
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vlanes = VTraits<v_float32>::vlanes();
v_float32 one = vx_setall_f32(1.f), z = vx_setzero_f32();
for (; i + vlanes <= len; i += vlanes) {
v_float32 x = vx_load(inp + i);
v_float32 t = v_exp(v_sub(z, x));
t = v_div(one, v_add(one, t));
vx_store(out + i, t);
}
#endif
for (; i < len; i++) {
float x = inp[i];
out[i] = 1.f / (1.f + expf(-x));
}
}
// TanH: uses v_exp SIMD for (exp(2x)-1)/(exp(2x)+1)
static void activationTanH(const void* input, void* output,
size_t len, const float* /*params*/)
{
const float* inp = (const float*)input;
float* out = (float*)output;
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vlanes = VTraits<v_float32>::vlanes();
v_float32 one = vx_setall_f32(1.f), two = vx_setall_f32(2.f);
for (; i + vlanes <= len; i += vlanes) {
v_float32 x = vx_load(inp + i);
v_float32 e2x = v_exp(v_mul(two, x));
v_float32 t = v_div(v_sub(e2x, one), v_add(e2x, one));
vx_store(out + i, t);
}
#endif
for (; i < len; i++) {
out[i] = tanhf(inp[i]);
}
}
// ELU: x >= 0 ? x : alpha*(exp(x)-1)
static void activationELU(const void* input, void* output,
size_t len, const float* params)
{
const float* inp = (const float*)input;
float* out = (float*)output;
float alpha = params[0];
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vlanes = VTraits<v_float32>::vlanes();
v_float32 v_alpha = vx_setall_f32(alpha);
v_float32 one = vx_setall_f32(1.f), z = vx_setzero_f32();
for (; i + vlanes <= len; i += vlanes) {
v_float32 x = vx_load(inp + i);
v_float32 t = v_mul(v_alpha, v_sub(v_exp(x), one));
x = v_select(v_ge(x, z), x, t);
vx_store(out + i, x);
}
#endif
for (; i < len; i++) {
float x = inp[i];
out[i] = x >= 0.f ? x : alpha * (expf(x) - 1.f);
}
}
// HardSwish: x * clip(x/6 + 0.5, 0, 1)
static void activationHardSwish(const void* input, void* output,
size_t len, const float* /*params*/)
{
const float* inp = (const float*)input;
float* out = (float*)output;
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vlanes = VTraits<v_float32>::vlanes();
v_float32 zero = vx_setzero_f32(), one = vx_setall_f32(1.f);
v_float32 half = vx_setall_f32(0.5f), sixth = vx_setall_f32(1.f / 6.f);
for (; i + vlanes <= len; i += vlanes) {
v_float32 x = vx_load(inp + i);
v_float32 t = v_min(one, v_max(zero, v_add(v_mul(x, sixth), half)));
vx_store(out + i, v_mul(x, t));
}
#endif
for (; i < len; i++) {
float x = inp[i];
out[i] = x * std::min(std::max(x / 6.f + 0.5f, 0.f), 1.f);
}
}
// HardSigmoid: clip(alpha*x + beta, 0, 1)
static void activationHardSigmoid(const void* input, void* output,
size_t len, const float* params)
{
const float* inp = (const float*)input;
float* out = (float*)output;
float alpha = params[0];
float beta = params[1];
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vlanes = VTraits<v_float32>::vlanes();
v_float32 v_alpha = vx_setall_f32(alpha), v_beta = vx_setall_f32(beta);
v_float32 zero = vx_setzero_f32(), one = vx_setall_f32(1.f);
for (; i + vlanes <= len; i += vlanes) {
v_float32 x = vx_load(inp + i);
x = v_min(one, v_max(zero, v_add(v_mul(v_alpha, x), v_beta)));
vx_store(out + i, x);
}
#endif
for (; i < len; i++) {
out[i] = std::min(std::max(alpha * inp[i] + beta, 0.f), 1.f);
}
}
// GELU exact: 0.5 * x * (1 + erf(x / sqrt(2)))
static void activationGELU(const void* input, void* output,
size_t len, const float* /*params*/)
{
const float* inp = (const float*)input;
float* out = (float*)output;
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vlanes = VTraits<v_float32>::vlanes();
v_float32 half = vx_setall_f32(0.5f), one = vx_setall_f32(1.f);
v_float32 rsqrt2 = vx_setall_f32((float)M_SQRT1_2);
for (; i + vlanes <= len; i += vlanes) {
v_float32 x = vx_load(inp + i);
v_float32 t = v_add(one, v_erf(v_mul(rsqrt2, x)));
vx_store(out + i, v_mul(v_mul(half, x), t));
}
#endif
for (; i < len; i++) {
float x = inp[i];
out[i] = 0.5f * x * (1.f + erff(x * (float)M_SQRT1_2));
}
}
// GELU approximate: 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3)))
static void activationGELUApprox(const void* input, void* output,
size_t len, const float* /*params*/)
{
const float* inp = (const float*)input;
float* out = (float*)output;
const float sqrt2_pi = 0.7978845834732056f; // sqrt(2/pi)
const float coeff = 0.044715f * sqrt2_pi;
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vlanes = VTraits<v_float32>::vlanes();
v_float32 half = vx_setall_f32(0.5f), one = vx_setall_f32(1.f);
v_float32 v_s2pi = vx_setall_f32(sqrt2_pi), v_coeff = vx_setall_f32(coeff);
v_float32 two = vx_setall_f32(2.f);
for (; i + vlanes <= len; i += vlanes) {
v_float32 x = vx_load(inp + i);
// inner = sqrt(2/pi) * x + coeff * x^3 = x * (sqrt(2/pi) + coeff * x^2)
v_float32 inner = v_mul(x, v_add(v_s2pi, v_mul(v_coeff, v_mul(x, x))));
// tanh via exp: (exp(2*inner)-1)/(exp(2*inner)+1)
v_float32 e2 = v_exp(v_mul(two, inner));
v_float32 t = v_div(v_sub(e2, one), v_add(e2, one));
vx_store(out + i, v_mul(v_mul(half, x), v_add(one, t)));
}
#endif
for (; i < len; i++) {
float x = inp[i];
float inner = x * (sqrt2_pi + coeff * x * x);
out[i] = 0.5f * x * (1.f + tanhf(inner));
}
}
// ReLU: max(0, x) or LeakyReLU: x >= 0 ? x : alpha*x
// params[0] = negative slope (0 for plain ReLU)
static void activationReLU(const void* input, void* output,
size_t len, const float* params)
{
const float* inp = (const float*)input;
float* out = (float*)output;
float alpha = params ? params[0] : 0.f;
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vlanes = VTraits<v_float32>::vlanes();
v_float32 v_alpha = vx_setall_f32(alpha), z = vx_setzero_f32();
for (; i + vlanes <= len; i += vlanes) {
v_float32 x = vx_load(inp + i);
x = v_select(v_ge(x, z), x, v_mul(x, v_alpha));
vx_store(out + i, x);
}
#endif
for (; i < len; i++) {
float x = inp[i];
out[i] = x >= 0.f ? x : alpha * x;
}
}
// Clip: clamp(x, minval, maxval)
// params[0] = minval, params[1] = maxval
static void activationClip(const void* input, void* output,
size_t len, const float* params)
{
const float* inp = (const float*)input;
float* out = (float*)output;
float minval = params[0];
float maxval = params[1];
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vlanes = VTraits<v_float32>::vlanes();
v_float32 v_lo = vx_setall_f32(minval), v_hi = vx_setall_f32(maxval);
for (; i + vlanes <= len; i += vlanes) {
v_float32 x = vx_load(inp + i);
x = v_min(v_max(x, v_lo), v_hi);
vx_store(out + i, x);
}
#endif
for (; i < len; i++) {
out[i] = std::min(std::max(inp[i], minval), maxval);
}
}
ActivationFunc getActivationFunc_(int type)
{
switch (type) {
case ACTIV_MISH: return activationMish;
case ACTIV_SWISH: return activationSwish;
case ACTIV_SIGMOID: return activationSigmoid;
case ACTIV_TANH: return activationTanH;
case ACTIV_ELU: return activationELU;
case ACTIV_HARDSWISH: return activationHardSwish;
case ACTIV_HARDSIGMOID: return activationHardSigmoid;
case ACTIV_GELU: return activationGELU;
case ACTIV_GELU_APPROX: return activationGELUApprox;
case ACTIV_RELU: return activationReLU;
case ACTIV_CLIP: return activationClip;
default: return nullptr;
}
}
CV_CPU_OPTIMIZATION_NAMESPACE_END
}} // cv::dnn::
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
@@ -24,9 +24,6 @@ namespace cv {
namespace dnn {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
//
// [TODO] add special branch for 3x3 depthwise convolution
//
static void depthwiseConv32f(const void* inp__, const void* residual__,
void* out__, const ConvState& cs,
const void* weights__, const float* scale__,
@@ -88,7 +85,7 @@ static void depthwiseConv32f(const void* inp__, const void* residual__,
} else if (fastActivation == FAST_ACTIV_PRELU) {
CV_Assert(cs.activParams.size() == size_t(C));
} else {
CV_Assert(fastActivation == FAST_ACTIV_NONE);
// FAST_ACTIV_NONE: activation (if any) is handled via function pointer
defaultAlpha = 1.f;
}
@@ -424,7 +424,7 @@ static void setupActivation(const ConvState& cs, int K,
} else if (fastActivation == FAST_ACTIV_PRELU) {
CV_Assert(cs.activParams.size() == size_t(K));
} else {
CV_Assert(fastActivation == FAST_ACTIV_NONE);
// FAST_ACTIV_NONE: activation (if any) is handled via function pointer
defaultAlpha = 1.f;
}
}
@@ -255,6 +255,27 @@ public:
if (src.type() == CV_32F && dst.type() == CV_32F)
{
// Try fast activation function path first
std::vector<float> activParams_;
ActivationFunc activFunc = func.getActivationFunc(CV_32F, activParams_);
if (activFunc) {
const float* params = activParams_.empty() ? nullptr : activParams_.data();
size_t total = src.total();
const float* srcptr = src.ptr<float>();
float* dstptr = dst.ptr<float>();
const size_t BLOCK_SIZE = 1 << 16;
parallel_for_(Range(0, (int)((total + BLOCK_SIZE - 1) / BLOCK_SIZE)),
[&](const Range& r) {
for (int b = r.start; b < r.end; b++) {
size_t start = b * BLOCK_SIZE;
size_t len = std::min(BLOCK_SIZE, total - start);
activFunc(srcptr + start, dstptr + start, len, params);
}
});
continue;
}
const int nstripes = getNumThreads();
PBody body(func, src, dst, nstripes);
parallel_for_(Range(0, nstripes), body, nstripes);
@@ -281,6 +302,11 @@ public:
func.apply(src, dst, -1, len, planeSize, cn0, cn1);
}
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const CV_OVERRIDE
{
return func.getActivationFunc(depth, activParams);
}
#ifdef HAVE_CUDA
Ptr<BackendNode> initCUDA(
void *context_,
@@ -326,6 +352,9 @@ struct BaseFunctor
bool tryFuse(Ptr<dnn::Layer>&) { return false; }
void getScaleShift(Mat&, Mat&) const {}
ActivationFunc getActivationFunc(int /*depth*/, std::vector<float>& /*activParams*/) const
{ return nullptr; }
};
struct ReLUFunctor : public BaseFunctor
@@ -335,6 +364,13 @@ struct ReLUFunctor : public BaseFunctor
explicit ReLUFunctor(float slope_=1.f) : slope(slope_) {}
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const
{
if (depth != CV_32F) return nullptr;
activParams = {slope};
return cv::dnn::getActivationFunc(ACTIV_RELU);
}
bool supportBackend(int backendId, int)
{
#ifdef HAVE_DNN_NGRAPH
@@ -507,6 +543,13 @@ struct ReLU6Functor : public BaseFunctor
CV_Assert(minValue <= maxValue);
}
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const
{
if (depth != CV_32F) return nullptr;
activParams = {minValue, maxValue};
return cv::dnn::getActivationFunc(ACTIV_CLIP);
}
bool supportBackend(int backendId, int)
{
#ifdef HAVE_INF_ENGINE
@@ -759,6 +802,13 @@ struct GeluFunctor : public BaseFunctor {
#endif
}
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const
{
if (depth != CV_32F) return nullptr;
activParams.clear();
return cv::dnn::getActivationFunc(ACTIV_GELU);
}
bool supportBackend(int backendId, int)
{
return backendId == DNN_BACKEND_OPENCV ||
@@ -892,6 +942,13 @@ struct GeluApproximationFunctor : public BaseDefaultFunctor<GeluApproximationFun
explicit GeluApproximationFunctor() {}
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const
{
if (depth != CV_32F) return nullptr;
activParams.clear();
return cv::dnn::getActivationFunc(ACTIV_GELU_APPROX);
}
bool supportBackend(int backendId, int)
{
return backendId == DNN_BACKEND_OPENCV;
@@ -913,6 +970,13 @@ struct TanHFunctor : public BaseDefaultFunctor<TanHFunctor>
{
typedef TanHLayer Layer;
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const
{
if (depth != CV_32F) return nullptr;
activParams.clear();
return cv::dnn::getActivationFunc(ACTIV_TANH);
}
bool supportBackend(int backendId, int)
{
#ifdef HAVE_INF_ENGINE
@@ -984,6 +1048,13 @@ struct SwishFunctor : public BaseDefaultFunctor<SwishFunctor>
#endif
}
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const
{
if (depth != CV_32F) return nullptr;
activParams.clear();
return cv::dnn::getActivationFunc(ACTIV_SWISH);
}
bool supportBackend(int backendId, int)
{
return backendId == DNN_BACKEND_OPENCV ||
@@ -1089,6 +1160,13 @@ struct MishFunctor : public BaseDefaultFunctor<MishFunctor>
#endif
}
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const
{
if (depth != CV_32F) return nullptr;
activParams.clear();
return cv::dnn::getActivationFunc(ACTIV_MISH);
}
bool supportBackend(int backendId, int)
{
return backendId == DNN_BACKEND_OPENCV ||
@@ -1174,6 +1252,13 @@ struct SigmoidFunctor : public BaseDefaultFunctor<SigmoidFunctor>
{
typedef SigmoidLayer Layer;
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const
{
if (depth != CV_32F) return nullptr;
activParams.clear();
return cv::dnn::getActivationFunc(ACTIV_SIGMOID);
}
bool supportBackend(int backendId, int)
{
#ifdef HAVE_INF_ENGINE
@@ -1253,6 +1338,13 @@ struct ELUFunctor : public BaseDefaultFunctor<ELUFunctor>
#endif
}
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const
{
if (depth != CV_32F) return nullptr;
activParams = {alpha};
return cv::dnn::getActivationFunc(ACTIV_ELU);
}
bool supportBackend(int backendId, int)
{
#ifdef HAVE_INF_ENGINE
@@ -1900,6 +1992,13 @@ struct HardSwishFunctor : public BaseDefaultFunctor<HardSwishFunctor>
#endif
}
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const
{
if (depth != CV_32F) return nullptr;
activParams.clear();
return cv::dnn::getActivationFunc(ACTIV_HARDSWISH);
}
bool supportBackend(int backendId, int)
{
return backendId == DNN_BACKEND_OPENCV ||
@@ -2181,6 +2280,13 @@ struct HardSigmoidFunctor : public BaseDefaultFunctor<HardSigmoidFunctor>
explicit HardSigmoidFunctor(float alpha_ = 0.2f, float beta_ = 0.5f) : alpha(alpha_), beta(beta_) {}
ActivationFunc getActivationFunc(int depth, std::vector<float>& activParams) const
{
if (depth != CV_32F) return nullptr;
activParams = {alpha, beta};
return cv::dnn::getActivationFunc(ACTIV_HARDSIGMOID);
}
bool supportBackend(int backendId, int)
{
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA;
@@ -1033,6 +1033,43 @@ private:
int hardSigmoidId;
};
// Swish/SiLU: x * Sigmoid(x)
class SwishSubgraph : public Subgraph
{
public:
SwishSubgraph()
{
int input = addNodeToMatch("");
sigmoidId = addNodeToMatch("Sigmoid", input);
mulId = addNodeToMatch("Mul", input, sigmoidId);
setFusedNode("Swish", input);
}
virtual bool match(const Ptr<ImportGraphWrapper>& net, int nodeId,
std::vector<int>& matchedNodesIds) CV_OVERRIDE
{
if (Subgraph::match(net, nodeId, matchedNodesIds))
{
// Verify both Mul inputs trace to the same tensor as Sigmoid's input.
Ptr<ImportNodeWrapper> mulNode = net->getNode(matchedNodesIds[mulId]);
Ptr<ImportNodeWrapper> sigmoidNode = net->getNode(matchedNodesIds[sigmoidId]);
std::string sigmoidInput = sigmoidNode->getInputName(0);
std::string sigmoidOutput = net->getOutputName(matchedNodesIds[sigmoidId], 0);
for (int i = 0; i < mulNode->getNumInputs(); i++)
{
std::string mulInput = mulNode->getInputName(i);
if (mulInput != sigmoidOutput)
return mulInput == sigmoidInput;
}
}
return false;
}
private:
int sigmoidId, mulId;
};
class CeluSubgraph : public Subgraph
{
public:
@@ -1706,6 +1743,7 @@ void simplifySubgraphs(opencv_onnx::GraphProto& net)
subgraphs.push_back(makePtr<SoftMaxSubgraph>());
subgraphs.push_back(makePtr<SoftMaxSubgraph2>());
subgraphs.push_back(makePtr<LogSoftMaxSubgraph>());
subgraphs.push_back(makePtr<SwishSubgraph>());
subgraphs.push_back(makePtr<HardSwishSubgraph>());
subgraphs.push_back(makePtr<CeluSubgraph>());
subgraphs.push_back(makePtr<NormalizeSubgraph1>());
+11 -1
View File
@@ -2389,6 +2389,15 @@ public:
activationParams.set("scale", 0.3f);
activationParams.set("shift", 0.6f);
}
else if (activationParams.type == "ELU")
{
activationParams.set("alpha", 1.0f);
}
else if (activationParams.type == "HardSigmoid")
{
activationParams.set("alpha", 0.2f);
activationParams.set("beta", 0.5f);
}
}
static void makeDefaultTestEltwiseLayer(LayerParams& eltwiseParams, const std::string& op, bool withCoefficients)
@@ -2460,7 +2469,8 @@ public:
static testing::internal::ParamGenerator<std::string> activationLayersList()
{
// TODO: automate list generation
return Values("ReLU", "ReLU6", "ChannelsPReLU", "TanH", "Swish", "Mish", "Sigmoid", "ELU", "AbsVal", "BNLL", "Power", "Exp");
return Values("ReLU", "ReLU6", "ChannelsPReLU", "TanH", "Swish", "Mish", "Sigmoid", "ELU",
"AbsVal", "BNLL", "Power", "Exp", "HardSwish", "HardSigmoid", "Gelu", "GeluApproximation");
}
static testing::internal::ParamGenerator<tuple<Backend, Target> > dnnBackendsAndTargetsForFusionTests()