Algorithm 快速sigmoid算法

Algorithm 快速sigmoid算法,algorithm,neural-network,Algorithm,Neural Network,sigmoid函数定义为 我发现使用C内置函数exp()来计算f(x)的值很慢。是否有更快的算法来计算f(x)?我不认为你能比内置的exp()做得更好,但是如果你想要另一种方法,你可以使用级数展开。可以为您计算。您不必在神经网络算法中使用实际的、精确的sigmoid函数,但可以用具有类似特性但计算速度更快的近似版本替换它 例如,您可以使用“快速S形”功能 如果f(x)的参数不接近零,则对exp(x)使用级数展开的第一项不会有太大帮助,并且如果参数为“大”,则sigmoid函数的级数展开也会有同

sigmoid函数定义为


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

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

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

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

如果f(x)的参数不接近零,则对exp(x)使用级数展开的第一项不会有太大帮助,并且如果参数为“大”,则sigmoid函数的级数展开也会有同样的问题


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

最好先在硬件上进行测量。只是一个快速的基准测试表明,在我的机器上,
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))
可能是执行速度最快的。为了更灵活地执行NN,通常使用一些alpha速率在0左右改变图形的角度

sigmoid函数如下所示:

f(x) = 1 / ( 1+exp(-x*alpha))
几乎相等的(但更快的功能)是:

你可以查看图表


当我使用abs功能时,网络速度会加快100倍以上

这个答案可能与大多数情况无关,但我只是想指出,对于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);
}

使用Eureqa搜索我发现的乙状结肠的近似值
1/(1+0.3678749025^x)
近似值。非常接近,只要去掉一个x取反的运算

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


编辑:
0.5+0.5*tanh(0.5*x)
准确度较低,
0.5+0.5*tanh(n)
也有效。如果你不想得到像sigmoid一样的[0,1]范围,你可以去掉常数。但它假设tanh更快。

这里的人最关心的是一个函数相对于另一个函数的速度有多快,并创建微基准,以查看
f1(x)
是否比
f2(x)
运行快0.0001毫秒。最大的问题是,这基本上是不相关的,因为重要的是你的网络学习的速度有多快,你的激活函数试图最小化你的成本函数

从目前的理论来看,

与sigmoid函数或类似激活函数相比,允许 用于在计算机上快速有效地训练深层神经结构 大型和复杂的数据集


因此,我建议扔掉微观优化,看看哪个函数允许更快的学习(也看看其他各种成本函数)。

此外,您可能会使用sigmoid的粗略版本(与原始版本的差异不超过0.2%):

inline float-RoughSigmoid(浮点值)
{
浮动x=::绝对值(值);
浮点数x2=x*x;
浮子e=1.0f+x+x2*0.555f+x2*x2*0.143f;
返回1.0f/(1.0f+(值>0?1.0f/e:e));
}
虚空粗糙度S形(常量浮动*src、大小\u t大小、常量浮动*坡度、浮动*dst)
{
浮动s=斜率[0];
对于(大小i=0;i
使用SSE优化粗糙S形函数:

    #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]);
    }
#包括
虚空粗糙度S形(常量浮动*src、大小\u t大小、常量浮动*坡度、浮动*dst)
{
尺寸对齐尺寸=尺寸/4*4;
__m128 _斜率=_mm_设置1_ps(*斜率);
__m128 _0=_mm_set1_ps(-0.0f);
__m128 1=mm_u设置1_ps(1.0f);
__m128 0555=毫米设置1 ps(0.555f);
__m128 0143=毫米设置1 ps(0.143f);
尺寸i=0;
对于(;i
使用AVX优化Sigmoid函数:

    #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]);
    }
#包括
虚空粗糙度S形(常量浮动*src、大小\u t大小、常量浮动*坡度、浮动*dst)
{
尺寸对齐尺寸=尺寸/8*8;
__m256 _斜率=_mm256_set1_ps(*斜率);
__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);
尺寸i=0;
对于(;i    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);
    }
    #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]);
    }
    #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]);
    }
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
    y=x / (2 * ((x<0.0)*-x+(x>=0.0)*x) + 2) + 0.5;
    y'=y(1-y);