为什么 Octave 舍入为 1 'earlier' 而不是 0?
Why does Octave round to 1 'earlier' than 0?
上下文:
在 Octave 中,我为 returns 值介于 0 和 1 之间的 Sigmoid 函数编写了代码;在理想情况下,它只会 return 0 代表 -Inf 和 1 代表 +Inf 但由于浮点不精确,非常接近这两个值的值被四舍五入。
问题:
我的问题是为什么会出现以下情况:0 和 1 的舍入边界明显不同:
>> sigmoid(-709)
ans = 1.2168e-308
>> sigmoid(-710)
ans = 0
>> sigmoid(36)
ans = 1.00000
>> sigmoid(37)
ans = 1
>> (sigmoid(37)-1)==0
ans = 1
>> (sigmoid(36)-1)==0
ans = 0
>> sigmoid(-710)==0
ans = 1
>> sigmoid(-709)==0
ans = 0
在示例中,可以看到将输出四舍五入为 1 所需的值比四舍五入为 0 所需的值小得多。37 与 -710 相比是一个非常大的差异,因为它们应该是大小相同但符号相反...
我的代码:
可能是我的功能有问题:
function [z] = sigmoid(x)
z = 1.0 ./(1.0+exp(-x));
endfunction
我尝试过的:
另一点是,我更改了函数以将结果加 1(本质上是将图形向上平移 1),并且 2 和 1 的边界分别变为 +/-37 - 这让我觉得它确实如此特别是与 0 有关,而不仅仅是函数及其下限。
如果是我电脑的问题,那是什么原因造成的?
首先,复习this brilliant answer by gnovice on floating-point representation。
顺便说一句,让我们看看您在这里看到的内容:您可以计算出一个非常接近于零的值:sigmoid(-709)
约等于 1.2e-308
,但您不能计算一个类似接近 1 的值:sigmoid(709)
正好等于 1,而不是 1 - 1.2e-308
,甚至 sigmoid(36) == 1
,而不是略小于 1 的值。
但是当我们知道浮点数在内存中是如何存储的时候,我们就会发现1 - 1.2e-308
是无法精确表示的。我们需要 308 位十进制数字来准确表示这个数字。双精度浮点数(Octave 中的默认值)有大约 15 个十进制数字。即1 - 1e-16
可以表示,1 - 1e-17
不能表示
eps(1)
的值为2.2204e-16
,这是我们可以用双精度浮点数编码的与1的最小差值。
但是接近 0 的值可以更精确地表示:eps(0)
是 4.9407e-324
。这是因为 1.2e-308
这样的值不需要表示 308 个十进制数字,而只需要 2 个,指数中的值为 -308。
无论如何,如果您依赖于远离转换位置的 sigmoid 函数的精确值,那么您的代码逻辑有问题。
如果你想让这个函数对称,你所能做的就是降低低端的精度。有两种方法可以做到这一点:
只需将非常小的值设置为零,这样 z==0
与另一侧的 z==1
在同一点到达:
function z = sigmoid(x)
z = 1.0 ./ (1.0+exp(-x));
z(z < eps(1)) = 0;
end
始终计算函数的右半部分,然后返回负输入。这使得x=0
两边的计算误差对称:
function z = sigmoid(x)
z = 1.0 ./ (1.0+exp(-abs(x)));
I = x < 0;
z(I) = 0.5 - z(I);
end
上下文:
在 Octave 中,我为 returns 值介于 0 和 1 之间的 Sigmoid 函数编写了代码;在理想情况下,它只会 return 0 代表 -Inf 和 1 代表 +Inf 但由于浮点不精确,非常接近这两个值的值被四舍五入。
问题:
我的问题是为什么会出现以下情况:0 和 1 的舍入边界明显不同:
>> sigmoid(-709)
ans = 1.2168e-308
>> sigmoid(-710)
ans = 0
>> sigmoid(36)
ans = 1.00000
>> sigmoid(37)
ans = 1
>> (sigmoid(37)-1)==0
ans = 1
>> (sigmoid(36)-1)==0
ans = 0
>> sigmoid(-710)==0
ans = 1
>> sigmoid(-709)==0
ans = 0
在示例中,可以看到将输出四舍五入为 1 所需的值比四舍五入为 0 所需的值小得多。37 与 -710 相比是一个非常大的差异,因为它们应该是大小相同但符号相反...
我的代码:
可能是我的功能有问题:
function [z] = sigmoid(x)
z = 1.0 ./(1.0+exp(-x));
endfunction
我尝试过的:
另一点是,我更改了函数以将结果加 1(本质上是将图形向上平移 1),并且 2 和 1 的边界分别变为 +/-37 - 这让我觉得它确实如此特别是与 0 有关,而不仅仅是函数及其下限。
如果是我电脑的问题,那是什么原因造成的?
首先,复习this brilliant answer by gnovice on floating-point representation。
顺便说一句,让我们看看您在这里看到的内容:您可以计算出一个非常接近于零的值:sigmoid(-709)
约等于 1.2e-308
,但您不能计算一个类似接近 1 的值:sigmoid(709)
正好等于 1,而不是 1 - 1.2e-308
,甚至 sigmoid(36) == 1
,而不是略小于 1 的值。
但是当我们知道浮点数在内存中是如何存储的时候,我们就会发现1 - 1.2e-308
是无法精确表示的。我们需要 308 位十进制数字来准确表示这个数字。双精度浮点数(Octave 中的默认值)有大约 15 个十进制数字。即1 - 1e-16
可以表示,1 - 1e-17
不能表示
eps(1)
的值为2.2204e-16
,这是我们可以用双精度浮点数编码的与1的最小差值。
但是接近 0 的值可以更精确地表示:eps(0)
是 4.9407e-324
。这是因为 1.2e-308
这样的值不需要表示 308 个十进制数字,而只需要 2 个,指数中的值为 -308。
无论如何,如果您依赖于远离转换位置的 sigmoid 函数的精确值,那么您的代码逻辑有问题。
如果你想让这个函数对称,你所能做的就是降低低端的精度。有两种方法可以做到这一点:
只需将非常小的值设置为零,这样
z==0
与另一侧的z==1
在同一点到达:function z = sigmoid(x) z = 1.0 ./ (1.0+exp(-x)); z(z < eps(1)) = 0; end
始终计算函数的右半部分,然后返回负输入。这使得
x=0
两边的计算误差对称:function z = sigmoid(x) z = 1.0 ./ (1.0+exp(-abs(x))); I = x < 0; z(I) = 0.5 - z(I); end