Почему октава округляется до 1 «раньше», чем 0? - PullRequest
0 голосов
/ 12 февраля 2019

Контекст:

В 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 в частности, а не только с функцией и ее нижней границей в частностиar.

Если это как-то связано с моим компьютером, то что может вызвать такую ​​вещь?

1 Ответ

0 голосов
/ 13 февраля 2019

Прежде всего, просмотрите этот блестящий ответ от 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 в показателе степени.

В любом случае, если вы зависите от точных значенийфункция сигмоида находится так далеко от места перехода, что-то не так с вашей логикой кода.

Если вы хотите сделать эту функцию симметричной, все, что вы можете сделать, это снизить точность на младшем конце.Есть два способа сделать это:

  1. Просто установить на ноль очень маленькие значения, чтобы z==0 достигался в той же точке, что и z==1 на другой стороне:

    function z = sigmoid(x)
      z = 1.0 ./ (1.0+exp(-x));
      z(z < eps(1)) = 0;
    end
    
  2. Всегда вычисляйте правую половину функции, затем уменьшайте отрицательные значения.Это делает ошибку вычисления по обеим сторонам x=0 симметричной:

    function z = sigmoid(x)
      z = 1.0 ./ (1.0+exp(-abs(x)));
      I = x < 0;
      z(I) = 0.5 - z(I);
    end
    
...