Почему октава округляется до 1 "раньше", чем 0?
Контекст:
В Octave я написал код для функции Sigmoid, которая возвращает значения от 0 до 1; в идеальном мире он будет возвращать только 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), и границы стали +/-37 для 2 и 1 соответственно - это заставляет меня думать, что это действительно связано с 0 в частности, а не только функции и ее нижней границы в частности.
Если это как-то связано с моим компьютером, то что может вызвать такую вещь?
1 ответ
Прежде всего, просмотрите этот блестящий ответ gnovice о представлении с плавающей точкой.
Теперь давайте посмотрим, что вы видите здесь: вы можете вычислить значение, очень близкое к нулю: 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 в показателе степени.
В любом случае, если вы зависите от точных значений сигмоидальной функции так далеко от места перехода, значит, что-то не так с вашей логикой кода.
Если вы хотите сделать эту функцию симметричной, все, что вы можете сделать, это снизить точность на нижнем уровне. Есть два способа сделать это:
Просто установите на ноль очень маленькие значения, чтобы
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