快速sigmoid算法

时间:2012-05-24 06:08:29

标签: algorithm neural-network

sigmoid函数定义为

enter image description here

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

的值

12 个答案:

答案 0 :(得分:28)

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

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

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

如果f(x)的参数不接近于零,则使用exp(x)的系列展开的第一项将不会有太多帮助,并且如果sigmoid函数的系列扩展存在同样的问题参数是“大”的。

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

答案 1 :(得分:16)

最好先测量硬件。只需一个快速的基准script显示,我的计算机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))可能是表现最快的。

答案 2 :(得分:12)

这里的人们最关心的是一个函数相对于另一个函数的速度有多快,并创建微基准测试以查看f2(x)是否比Test.aggregate([ { $group: { _id:"$categoryId", totalAmount:{$sum: "$amount"} }} ], function (err, result) { if (err) { console.log(err); return; } console.log(result); res.json({ data:result }); }); 快0.0001 ms。最大的问题是,这几乎是无关紧要的,因为重要的是您的网络通过激活功能学习的速度,以尽量减少您的成本函数。

截至目前的理论,rectifier function and softplus enter image description here

  

与sigmoid函数或类似的激活函数相比,允许   用于更快更有效地训练深度神经结构   大而复杂的数据集。

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

答案 3 :(得分:6)

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

sigmoid函数如下所示:

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

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

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

您可以查看图表here

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

答案 4 :(得分: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);
}

答案 5 :(得分:4)

你也可以使用sigmoid的粗略版本(它与原版的差异不大于0.2%):

<script>
    function toggle_display(id){
        if(id=='mails'){
            document.getElementById('mails').style.display = 'block';
            document.getElementById('subscribers').style.display = 'none'; 
        }
        if(id=='subscribers'){
            document.getElementById('mails').style.display = 'none'; 
            document.getElementById('subscribers').style.display = 'block';
        }
}</script>
<input type="button" class="btn" onclick="toggle_display('mails');" value="Mails"/>
<input type="button" class="btn" value="Subscribers" onclick="toggle_display('subscribers');"/>

        <div class="row well well-lg hidden" id="mails">
               <table class="col-sm-12" border="1px solid black">
               <tr>
                    <th class="col-sm-2">Name</th>
                    <th class="col-sm-2">Email</th>
                    <th class="col-sm-2">Phone</th>
                    <th class="col-sm-2">Subject</th>
                    <th class="col-sm-3">Message</th>
               </tr>
               </table>
          </div>

          <div class="row well well-lg hidden" id="subscribers">
              <table class="col-sm-12" border="1px solid black">
                   <tr>
                      <th class="col-sm-2">ID</th>
                      <th class="col-sm-5">Email</th>
                      <th class="col-sm-3">Action</th>
                   </tr>
              </table>
          </div>

使用SSE优化RoughSigmoid功能:

    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);
    }

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

答案 6 :(得分: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

这将是这样的:

Two graphs for a sigmoid {Blue: (0.5/(1+(x^2))), Yellow: (-0.5/(1+(x^2)))+1}

答案 7 :(得分:1)

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

这里显示的其他一些功能很有意思,但功率操作真的很慢吗?我测试了它,它实际上比添加更快,但这可能只是一个侥幸。如果是这样,它应该和其他所有人一样快或者快。

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

答案 8 :(得分:1)

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

答案 9 :(得分:0)

您还可以使用此:

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

现在表现得像个S型,因为y(1-y)= y'比1 /(2(1(+ abs(x))^ 2) 行为更像是乙状结肠;

答案 10 :(得分:0)

试试这个 .NET Core 5+ 实现

.....
.....
$mpdf->SetProtection(array(), 'enang', 'sumarna');


//place before this
$mpdf->WriteHTML(utf8_encode($html));

答案 11 :(得分:-2)

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