快速sigmoid算法

use*_*983 31 algorithm neural-network

sigmoid函数定义为

在此输入图像描述

我发现使用C内置函数exp()计算的值f(x)很慢.有没有更快的算法来计算值f(x)

Ant*_*ima 33

您不必在神经网络算法中使用实际的,精确的sigmoid函数,但可以用具有类似属性但计算速度更快的近似版本替换它.

例如,您可以使用"快速sigmoid"功能

  f(x) = x / (1 + abs(x))
Run Code Online (Sandbox Code Playgroud)

如果f(x)的参数不接近于零,则使用exp(x)的级数展开的第一项将无助于过多,并且如果参数为"sigmoid函数的串联扩展,则存在相同的问题"大".

另一种方法是使用表查找.也就是说,您为给定数量的数据点预先计算sigmoid函数的值,然后根据需要在它们之间进行快速(线性)插值.

  • 不应该是`f(x) = 0.5 * (x / (1 + abs(x)) + 1)`来近似提问者的sigmoid函数`f(x) = 1 / (1 + exp(-x) )`? (3认同)

sas*_*nin 19

最好先测量硬件.只是一个快速的基准测试脚本显示,在我的机器上1/(1+|x|)是最快的,并且tanh(x)紧随其后.错误功能erf也很快.

% gcc -Wall -O2 -lm -o sigmoid-bench{,.c} -std=c99 && ./sigmoid-bench
atan(pi*x/2)*2/pi   24.1 ns
atan(x)             23.0 ns
1/(1+exp(-x))       20.4 ns
1/sqrt(1+x^2)       13.4 ns
erf(sqrt(pi)*x/2)    6.7 ns
tanh(x)              5.5 ns
x/(1+|x|)            5.5 ns
Run Code Online (Sandbox Code Playgroud)

我希望结果可能会因架构和使用的编译器而异,但erf(x)(自C99起),tanh(x)并且x/(1.0+fabs(x))可能是表现最快的.

  • 还应该相信您是说`x / sqrt(1 + x ^ 2)`而不是`1 / sqrt(1 + x ^ 2)`。 (2认同)

Sal*_*ali 12

这里的人们最关心的是一个函数相对于另一个函数的速度有多快,并创建微基准测试,以查看f1(x)运行速度是否快于0.0001毫秒f2(x).最大的问题是,这几乎是无关紧要的,因为重要的是您的网络通过激活功能学习的速度,以尽量减少您的成本函数.

截至目前的理论,整流功能和softplus 在此输入图像描述

与sigmoid函数或类似的激活函数相比,允许在大型和复杂数据集上更快,更有效地训练深度神经架构.

所以我建议抛弃微优化,看看哪个功能允许更快的学习(同时考虑各种其他成本函数).


Nos*_*ara 8

为了使NN更灵活,通常使用一些alpha率来改变0附近的图形角度.

sigmoid函数如下所示:

f(x) = 1 / ( 1+exp(-x*alpha))
Run Code Online (Sandbox Code Playgroud)

几乎相同的(但更快的功能)是:

f(x) = 0.5 * (x * alpha / (1 + abs(x*alpha))) + 0.5
Run Code Online (Sandbox Code Playgroud)

您可以在此处查看图表

当我使用abs功能时,网络变得更快100倍.


pqn*_*pqn 6

这个答案可能与大多数情况无关,但只是想扔掉那里,对于CUDA计算我发现x/sqrt(1+x^2)它是迄今为止最快的功能.

例如,使用单精度浮动内在函数完成:

__device__ void fooCudaKernel(/* some arguments */) {
    float foo, sigmoid;
    // some code defining foo
    sigmoid = __fmul_rz(rsqrtf(__fmaf_rz(foo,foo,1)),foo);
}
Run Code Online (Sandbox Code Playgroud)


Erm*_*mIg 5

另外,您可能会使用Sigmoid的粗糙版本(与原始版本的差异不大于0.2%):

    inline float RoughSigmoid(float value)
    {
        float x = ::abs(value);
        float x2 = x*x;
        float e = 1.0f + x + x2*0.555f + x2*x2*0.143f;
        return 1.0f / (1.0f + (value > 0 ? 1.0f / e : e));
    }

    void RoughSigmoid(const float * src, size_t size, const float * slope, float * dst)
    {
        float s = slope[0];
        for (size_t i = 0; i < size; ++i)
            dst[i] = RoughSigmoid(src[i] * s);
    }
Run Code Online (Sandbox Code Playgroud)

使用SSE优化RoughSigmoid函数:

    #include <xmmintrin.h>

    void RoughSigmoid(const float * src, size_t size, const float * slope, float * dst)
    {
        size_t alignedSize =  size/4*4;
        __m128 _slope = _mm_set1_ps(*slope);
        __m128 _0 = _mm_set1_ps(-0.0f);
        __m128 _1 = _mm_set1_ps(1.0f);
        __m128 _0555 = _mm_set1_ps(0.555f);
        __m128 _0143 = _mm_set1_ps(0.143f);
        size_t i = 0;
        for (; i < alignedSize; i += 4)
        {
            __m128 _src = _mm_loadu_ps(src + i);
            __m128 x = _mm_andnot_ps(_0, _mm_mul_ps(_src, _slope));
            __m128 x2 = _mm_mul_ps(x, x);
            __m128 x4 = _mm_mul_ps(x2, x2);
            __m128 series = _mm_add_ps(_mm_add_ps(_1, x), _mm_add_ps(_mm_mul_ps(x2, _0555), _mm_mul_ps(x4, _0143)));
            __m128 mask = _mm_cmpgt_ps(_src, _0);
            __m128 exp = _mm_or_ps(_mm_and_ps(_mm_rcp_ps(series), mask), _mm_andnot_ps(mask, series));
            __m128 sigmoid = _mm_rcp_ps(_mm_add_ps(_1, exp));
            _mm_storeu_ps(dst + i, sigmoid);
        }
        for (; i < size; ++i)
            dst[i] = RoughSigmoid(src[i] * slope[0]);
    }
Run Code Online (Sandbox Code Playgroud)

使用AVX优化RoughSigmoid函数:

    #include <immintrin.h>

    void RoughSigmoid(const float * src, size_t size, const float * slope, float * dst)
    {
        size_t alignedSize = size/8*8;
        __m256 _slope = _mm256_set1_ps(*slope);
        __m256 _0 = _mm256_set1_ps(-0.0f);
        __m256 _1 = _mm256_set1_ps(1.0f);
        __m256 _0555 = _mm256_set1_ps(0.555f);
        __m256 _0143 = _mm256_set1_ps(0.143f);
        size_t i = 0;
        for (; i < alignedSize; i += 8)
        {
            __m256 _src = _mm256_loadu_ps(src + i);
            __m256 x = _mm256_andnot_ps(_0, _mm256_mul_ps(_src, _slope));
            __m256 x2 = _mm256_mul_ps(x, x);
            __m256 x4 = _mm256_mul_ps(x2, x2);
            __m256 series = _mm256_add_ps(_mm256_add_ps(_1, x), _mm256_add_ps(_mm256_mul_ps(x2, _0555), _mm256_mul_ps(x4, _0143)));
            __m256 mask = _mm256_cmp_ps(_src, _0, _CMP_GT_OS);
            __m256 exp = _mm256_or_ps(_mm256_and_ps(_mm256_rcp_ps(series), mask), _mm256_andnot_ps(mask, series));
            __m256 sigmoid = _mm256_rcp_ps(_mm256_add_ps(_1, exp));
            _mm256_storeu_ps(dst + i, sigmoid);
        }
        for (; i < size; ++i)
            dst[i] = RoughSigmoid(src[i] * slope[0]);
    }
Run Code Online (Sandbox Code Playgroud)