1 Sigmoid

Sigmoid激活函数的公式:

sigmoid(x) = \frac{1}{1+e^{-x} }

相同的,该函数也可以写成:

sigmoid(x) = \frac{1}{1+e^{-x} } = \frac{e^{x} }{1+e^{x}} = \frac{1}{2} tanh(\frac{x}{2} )+\frac{1}{2}

Sigmoid激活函数的函数曲线:

Sigmoid激活函数的快速替代函数以及相应的C++实现-StubbornHuang Blog

相应的C++代码为:

float sigmoid(float x)
{
    return (1 / (1 + exp(-x)));
}

但是exp函数的执行效率非常低,如果要对大数组中张量进行sigmoid运算,性能是比较大的瓶颈。那有没有什么好的快速方案既可以在数值上逼近Sigmoid函数,也可以拥有比较好的计算效率,节省计算开销呢?本文将总结一些Sigmoid函数的替代方案用于在一些场景替换Sigmoid函数并具有较高的计算性能。

2 Sigmoid的近似与快速替代方案

2.1 方案1

公式:

sigmoid-replace(x) = \frac{1}{2} (\frac{x}{1+\left | x \right | } )+\frac{1}{2}

相应的C++代码为:

float sigmoid(float x)
{
    return 0.5 * (x / (1 + fabs(x))) + 0.5;
}

与Sigmoid函数的曲线对比

Sigmoid激活函数的快速替代函数以及相应的C++实现-StubbornHuang Blog

这个替代方案速度非常快,在计算上几乎没有延迟,但是精度与原始的Sigmoid函数相比,在-100-+100的区间相差比较大。

2.2 方案2

公式:

sigmoid-replace(x) = \frac{1}{2} (\frac{x}{\frac{0.99}{\left | x \right | } +\left | x \right | } )+\frac{1}{2}

相应的C++代码为:

float sigmoid(float x)
{
    return 0.5*(x/(0.99/abs(x) + abs(x))) + 0.5;
}

与Sigmoid函数的曲线对比

Sigmoid激活函数的快速替代函数以及相应的C++实现-StubbornHuang Blog

这个方案是在方案1基础上进行的近一步的修改,因为方案1的精度相差较大,所以通过

\frac{0.99}{\left | x \right | }

替代方案1中的常数项1,使得在近0区间更加拟合Sigmoid函数曲线。

此方案既可以保持较高拟合精度,也可以保证计算效率。

2.3 方案3

公式:

sigmoid-replace(x) = \frac{1}{2} tanh(\frac{x}{2} )+\frac{1}{2}

相应的C++代码为:

float sigmoid(float x)
{
    return 0.5 + 0.5 * tanh(x / 2);
}

与Sigmoid函数的曲线对比

Sigmoid激活函数的快速替代函数以及相应的C++实现-StubbornHuang Blog

为什么把这个原有Sigmoid的函数放在快速替代方案里面呢?因为在有些机器上,tanh函数的执行效率比exp函数更快,目前在我的应用环境里面,使用此种方案比原始Sigmoid函数快了将近一半,并且精度没有任何损失。

2.4 方案4

公式:

\begin{array}{c}
\left\{\begin{matrix}
\frac{1}{1+\frac{1}{1+\left | x \right | +x^{2}\times 0.555+x^{4}\times 0.143 } } & x \ge 0\\
\frac{1}{1+{1+\left | x \right | +x^{2}\times 0.555+x^{4}\times 0.143 }} & x < 0
\end{matrix}\right.
\end{array}

与Sigmoid函数的曲线对比

Sigmoid激活函数的快速替代函数以及相应的C++实现-StubbornHuang Blog

该方案基本上拟合了Sigmoid函数曲线,但是需要进行if判断,计算性能上并没有多大的提升。

参考链接