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