在Java中加速数学计算

16

我有一个用Java编写的神经网络,其中使用sigmoid转移函数定义如下:

private static double sigmoid(double x)
{
    return 1 / (1 + Math.exp(-x));
}

在训练和使用网络进行计算时,这个操作被多次调用。有没有什么方法可以加速它?并不是说它很慢,只是因为它经常被使用,所以在这里进行小优化将会产生大的整体收益。


3
这个方法调用时,x的值会重复吗?还是说每次调用时它们很可能都不同? - DaveJohnston
@Dave - 这取决于所需的精度,但它们都是浮点数,因此几乎是唯一的。 - Simon
@Matthew - 很好的问题,但我猜测我需要至少4个并且可能是6个小数位。问题在于,在没有“正确”答案的现实世界问题中,很难确立必要的精度。 - Simon
2
你是否对该程序进行过性能分析,以确定优化此部分代码是否能显著提高整体性能? - Refactor
1
@重构 加速这个计算实际上是神经网络专家们经常讨论的话题,所以我发现那个典型的 StackOverflow 式的回答非常有趣 :) - Philip Guin
显示剩余6条评论
4个回答

24

对于神经网络,您不需要精确计算Sigmoid函数的值。因此,您可以预先计算100个值并重复使用最接近输入的值,甚至更好的方法是(如评论所述)从相邻的值进行插值。

在这篇文章中描述了如何做到这一点:article(链接来源于s-lott的答案)。

这是Sigmoid函数的表达式:Sigmoid function graph

正如您所看到的,只有 -10 < x < 10 的值是有趣的。另外,正如另一条评论所述,该函数是对称的。您只需要存储其中一半的值。


编辑:很抱歉我在这里展示了错误的图形。我已经更正了它。


1
如果您想要更高的精度,可能需要超过100个值。在我看来,一个5000个(甚至是1000个)值的查找表绝对足够。 - nico
2
为了更精确,最好在最近的两个值之间进行线性插值。 - Jouni K. Seppänen
2
问题是对称的,所以您只需要一半的值。计算另一侧很容易。 - Peter Lawrey
1
这是一种完全不同的函数图。erf(x) 很难计算,而 exp(x) 则不然。 - Ha.
请注意衡量查找与计算。对于执行一个浮点数指令的CPU来说,可能比插值两个值更快(当然,这取决于CPU架构)。 - Merijn Vogel
显示剩余3条评论

6
如果您有许多节点,其中x的值在-10..+10框之外,您可以完全忽略计算这些值,例如,像这样...
if( x < -10 )
    y = 0;
else if( x > 10 )
    y = 1;
else
    y = 1 / (1 + Math.exp(-x));
return y;

当然,这会增加每次计算的条件检查开销,因此只有在有大量饱和节点时才值得使用。
另一个值得一提的事情是,如果您正在使用反向传播,并且必须处理函数的斜率,最好将其分段计算而不是按原样计算。
我现在无法回忆起斜率,但以下是使用双极Sigmoid作为示例的内容。与其按照以下方式计算:
y = (1 - exp(-x)) / (1 + exp(-x));

如果您需要多次使用exp()函数,可以将昂贵的计算缓存到临时变量中,如下所示:

temp = exp(-x);
y = (1 - temp) / (1 + temp);

在BP网络中有很多地方可以利用这种东西。


1
从数学角度来看,我没有看到任何优化的可能性。

1

这是一个非常平滑的函数,因此查找和插值方案可能已经足够。

当我在-10 <= x <= 10的范围内绘制该函数时,在极端情况下可以获得五位精度。对于您的应用程序来说,这是否足够好呢?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接