Я знаю что Java Double.NaN
. У меня есть некоторый код Java, который производит NaN
.
// calculate errors
delta = m1 + m2 - M;
eta = f1 + f2 - F;
for (int i = 0; i < numChildren; i++) {
epsilon[i] = p[i]*m1+(1-p[i])*m2+q[i]*f1+(1-q[i])*f2-C[i];
}
// use errors in gradient descent
// set aside differences for the p's and q's
float mDiff = m1 - m2;
float fDiff = f1 - f2;
// first update m's and f's
m1 -= rate*delta;
m2 -= rate*delta;
f1 -= rate*eta;
f2 -= rate*eta;
for (int i = 0; i < numChildren; i++) {
m1 -= rate*epsilon[i]*p[i];
m2 -= rate*epsilon[i]*(1-p[i]);
f1 -= rate*epsilon[i]*q[i];
f2 -= rate*epsilon[i]*(1-q[i]);
}
// now update the p's and q's
for (int i = 0; i < numChildren; i++) {
p[i] -= rate*epsilon[i]*mDiff;
q[i] -= rate*epsilon[i]*fDiff;
}
При каких обстоятельствах Java произведет значение NaN?
Учитывая то, что я знаю о градиентном спуске, вы, скорее всего, выпрыгиваете в бесконечность, потому что у вас нет адаптивной скорости
(т.е. ваша скорость
слишком велика).
NaN
запускается следующими событиями:
Извините за такой общий ответ, но я надеюсь, что это помогло.
Пробовали ли вы посыпать ваш код System.out.println
утверждениями, чтобы определить точно, где начинают возникать NaNs?
Согласно Википедии:
Существует три вида операций, возвращающих
NaN
:
- Операции с
NaN
в качестве хотя бы одного операнда- Неопределённые формы
- Деление 0/0, ∞/∞, ∞/-∞, -∞/∞ и -∞/-∞
- Умножение 0×∞ и 0×-∞
- Сила 1∞
- Сложения ∞ + (-∞), (-∞) + ∞ и эквивалентные вычитания.
- Вещественные операции с комплексными результатами:
- Квадратный корень из отрицательного числа
- Логарифм отрицательного числа
- Тангенс нечетного числа, кратного 90 градусам (или π/2 радиана)
- Обратный синус или косинус числа, которое меньше -1 или больше +1.
Этот фрагмент Java иллюстрирует все вышеперечисленное, кроме тангенса (подозреваю, что из-за ограниченной точности double
):
import java.util.*;
import static java.lang.Double.NaN;
import static java.lang.Double.POSITIVE_INFINITY;
import static java.lang.Double.NEGATIVE_INFINITY;
public class NaN {
public static void main(String args[]) {
double[] allNaNs = {
0D/0D,
POSITIVE_INFINITY / POSITIVE_INFINITY,
POSITIVE_INFINITY / NEGATIVE_INFINITY,
NEGATIVE_INFINITY / POSITIVE_INFINITY,
NEGATIVE_INFINITY / NEGATIVE_INFINITY,
0 * POSITIVE_INFINITY,
0 * NEGATIVE_INFINITY,
Math.pow(1, POSITIVE_INFINITY),
POSITIVE_INFINITY + NEGATIVE_INFINITY,
NEGATIVE_INFINITY + POSITIVE_INFINITY,
POSITIVE_INFINITY - POSITIVE_INFINITY,
NEGATIVE_INFINITY - NEGATIVE_INFINITY,
Math.sqrt(-1),
Math.log(-1),
Math.asin(-2),
Math.acos(+2),
};
System.out.println(Arrays.toString(allNaNs));
// prints "[NaN, NaN...]"
System.out.println(NaN == NaN); // prints "false"
System.out.println(Double.isNaN(NaN)); // prints "true"
}
}
Если один из операндов равен
NaN
, то результат==
равенfalse
, а результат!=
равенtrue
. Действительно, тестx!=x
являетсяистинным
тогда и только тогда, когда значениеx
равноNaN
. (МетодыFloat.isNaN
иDouble.isNaN
также могут использоваться для проверки того, является ли значениеNaN
).