3

在从事涉及神经网络的 C++ 项目时,我受到 NaN 结果的阻碍。经过大量追踪(试图找到 NaN 的起源),我意识到它的来源是我的 sigmoid 导函数,如下所示。

double sigDer(double n){
    return 2*exp(-n) / pow(1 + exp(-n), 2);
}

尽管它有一个全实数域,但诸如 -1008.3 这样的值会导致 NaN 的结果。根据mathematica,正确的结果应该非常接近于零——2.522*10^-438。我已经通过以下方式避免了这个问题:

double sigDer(double n){
    double res = 2*exp(-n) / pow(1 + exp(-n), 2);
    if( isnan(res) ){
        return 0;
    } else{
        return res;
    }
} 

有了这个简单的假设,我的代码按预期运行;但是,我仍然不明白为什么 sigDer(<# with large mass>) 不返回〜0。有人可以告诉我C++(Xcode IDE)中NaN的原因,而不是除以零并取负的偶数根吗?

提前致谢!我还想知道为什么 signer(-1008.3) 返回 NaN 以及如何更好/更有效地跟踪 NaN 值的来源。

4

3 回答 3

3

好吧,如果分子和分母最终都为 0,那么你正在做 0/0,即 NaN。

我没有做计算,但我想exp(-1008)它小于2E-308,即 中的最小可表示值double

有人可以告诉我NaN的原因吗

维基百科涵盖了这一点。

于 2013-07-21T22:24:48.093 回答
2

青柠

在每种情况下,分母都试图达到无穷大(这意味着整个分数都试图达到 0)。但是,这意味着您正在定义某处除以无穷大(结合有限的双倍范围)。

对此的解释在于c++ exp 函数,如果返回值不能表示为双精度,则返回 +-HUGE_VAL。

话虽如此,当您的结果不能包含在 double 变量中时,它将导致除以无穷大,从而导致nan

顺便说一句,如果您想对大数字进行操作,您可以实现一个将数字存储在字符串和重载运算符中的类。

于 2013-07-22T00:01:12.877 回答
1

如果您的输入n是一个很大的负数

exp(-n) -> inf

pow(1 + exp(-n), 2) -> pow(1 + inf, 2) -> inf

inf / inf -> nan

如果您的输入n是一个很大的数,您将永远不会得到 nan0/0

pow(1 + exp(-n), 2) -> pow(1 + 0, 2) != 0
于 2013-07-21T22:28:06.863 回答