37

sigmoid 函数定义为

在此处输入图像描述

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

4

12 回答 12

45

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

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

  f(x) = x / (1 + abs(x))

如果 f(x) 的参数不接近于零,则对 exp(x) 使用级数展开的第一项不会有太大帮助,如果参数是 "大的”。

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

于 2012-05-24T08:33:53.560 回答
27

最好先在硬件上进行测量。只是一个快速的基准脚本显示,在我的机器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

我预计结果可能会因体系结构和使用的编译器而异,但是erf(x)(自 C99 起),tanh(x)并且x/(1.0+fabs(x))可能是执行速度最快的。

于 2013-03-29T13:13:31.480 回答
12

这里的人们最关心的是一个函数相对于另一个函数有多快,并创建微基准来查看是否f1(x)f2(x). 最大的问题是,这几乎是无关紧要的,因为重要的是你的网络用你的激活函数学习的速度有多快,试图最小化你的成本函数。

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

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

所以我建议扔掉微优化,看看哪个函数可以更快地学习(也看看其他各种成本函数)。

于 2015-08-14T04:51:08.550 回答
8

为了使 NN 更灵活,通常使用一些 alpha 率来将图形的角度更改为 0 左右。

sigmoid 函数如下所示:

f(x) = 1 / ( 1+exp(-x*alpha))

几乎等效的(但更快的功能)是:

f(x) = 0.5 * (x * alpha / (1 + abs(x*alpha))) + 0.5

您可以在此处查看图表

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

于 2013-04-27T11:24:37.187 回答
8

这个答案可能与大多数情况无关,但只是想把我发现对于 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);
}
于 2014-07-03T01:47:22.257 回答
6

您也可以使用粗略版本的 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);
    }

使用 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]);
    }

使用 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]);
    }
于 2015-12-24T06:52:10.013 回答
2

您可以使用两个公式来使用简单但有效的方法:

if x < 0 then f(x) = 1 / (0.5/(1+(x^2)))
if x > 0 then f(x) = 1 / (-0.5/(1+(x^2)))+1

这将如下所示:

sigmoid 的两个图 {蓝色:(0.5/(1+(x^2))),黄色:(-0.5/(1+(x^2)))+1}

于 2018-05-25T16:53:15.023 回答
1

使用 Eureqa 搜索 sigmoid 的近似值,我发现1/(1 + 0.3678749025^x)它是近似的。它非常接近,只需用 x 的否定来摆脱一个操作。

这里展示的其他一些功能很有趣,但是电源操作真的那么慢吗?我测试了它,它实际上比加法更快,但这可能只是侥幸。如果是这样,它应该和其他所有的一样快或更快。

编辑:0.5 + 0.5*tanh(0.5*x)不太准确,0.5 + 0.5*tanh(n)也有效。如果您不关心在范围 [0,1] 之间(如 sigmoid),您可以摆脱常量。但它假设 tanh 更快。

于 2014-08-22T08:02:11.173 回答
1

tanh 函数可以在某些语言中进行优化,使其比自定义定义的 x/(1+abs(x)) 更快,例如 Julia。

于 2016-05-07T07:16:23.207 回答
0

你也可以使用这个:

    y=x / (2 * ((x<0.0)*-x+(x>=0.0)*x) + 2) + 0.5;
    y'=y(1-y);

现在像 sigmoid 一样,因为 y(1-y)=y' 比 1/(2 (1 + abs(x))^2) 更像是快速 sigmoid;

于 2020-06-26T08:05:46.507 回答
0

试试这个 .NET Core 5+ 实现

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static unsafe float FastSigmoid(float v)
    {
        const float c1 = 0.03138777F;
        const float c2 = 0.276281267F;
        const float c_log2f = 1.442695022F;
        v *= c_log2f;
        int intPart = (int)v;
        float x = (v - intPart);
        float xx = x * x;
        float v1 = c_log2f + c2 * xx;
        float v2 = x + xx * c1 * x;
        float v3 = (v2 + v1);
        *((int*)&v3) += intPart << 24;
        float v4 = v2 - v1;
        float res = v3 / (v3 - v4); //for tanh change to (v3 + v4)/ (v3 - v4)
        return res;
    }
于 2021-07-08T22:59:53.967 回答
-2

我认为你不能比内置的 exp() 做得更好,但如果你想要另一种方法,你可以使用系列扩展。WolframAlpha可以为您计算它。

于 2012-05-24T06:31:01.360 回答