在Octave中,我为Sigmoid函数编写了代码,该函数返回0到1之间的值;在理想情况下,对于-Inf只会返回0,对于+ Inf只会返回1,但是由于浮点数的不精确性,与这两个值极其接近的值都经过了四舍五入。
我的问题是为什么会发生以下情况:取整的边界明显不同于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
在该示例中,您可以看到将输出四舍五入所需的值的幅度要比四舍五入至0所需的值小得多。与-710相比,37的差异非常大,考虑到它们应该相同。幅度但符号相反...
也许这是我的功能出现的问题:
function [z] = sigmoid(x)
z = 1.0 ./(1.0+exp(-x));
endfunction
另一点是,我更改了将结果加1的函数(本质上将图形向上平移了1),并且边界分别变为2和1的+/- 37-这使我认为它确实是尤其要处理0,而不仅要处理函数及其下限。
如果这与我的计算机有关,那么会导致什么呢?
答案 0 :(得分:9)
首先,查看this brilliant answer by gnovice on floating-point representation。
在此之后,让我们看看您在这里看到的内容:您可以计算出非常接近零的值:sigmoid(-709)
大约等于1.2e-308
,但是您无法计算出类似地接近一个值:sigmoid(709)
等于1,而不是1 - 1.2e-308
,甚至是sigmoid(36) == 1
,而不是略小于1的值。
但是,当我们知道浮点数如何存储在内存中时,我们将意识到1 - 1.2e-308
无法准确表示。我们需要308个十进制数字才能准确表示该数字。双精度浮点数(八度中的默认值)大约有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