Предотвращение числового переполнения при расчете значения И градиента функции логистических потерь

В настоящее время я пытаюсь реализовать алгоритм машинного обучения, который включает функцию логистических потерь. в МАТЛАБ. К сожалению, у меня возникли проблемы из-за числового переполнения.

В общем, для заданного входа s значение логистической функции равно:

 log(1 + exp(s))

а наклон функции логистических потерь:

 exp(s)./(1 + exp(s)) = 1./(1 + exp(-s))

В моем алгоритме значение s = X*beta. Здесь X — это матрица с N точками данных и P функциями на точку данных (т. е. size(X)=[N,P]), а beta — это вектор из P коэффициентов для каждой функции, таких что size(beta)=[P 1].

Меня особенно интересует вычисление среднего значения и градиента логистической функции для заданного значения beta.

Среднее значение логистической функции по отношению к значению beta равно:

 L = 1/N * sum(log(1+exp(X*beta)),1)

Среднее значение наклона логистической функции относительно к значению b равно:

 dL = 1/N * sum((exp(X*beta)./(1+exp(X*beta))' X, 1)'

Обратите внимание, что size(dL) = [P 1].

Моя проблема в том, что эти выражения продолжают создавать числовые переполнения. Проблема фактически возникает из-за того, что exp(s)=Inf когда s>1000 и exp(s)=0 когда s<-1000.

Я ищу решение, при котором s может принимать любое значение в арифметике с плавающей запятой. В идеале я также был бы очень признателен за решение, которое позволяет мне оценивать значение и градиент векторизованным/эффективным способом.


comment
Я знаю два файла, которые могут решить обе ваши проблемы. См. это для высокоточной плавающей запятой. См. это для обработки произвольно больших чисел.   -  person Autonomous    schedule 20.11.2013
comment
Берк У.: Вам помог один из ответов?   -  person A. Donda    schedule 27.11.2013
comment
@A.Donda A.Donda Они были проницательны, но не справились с проблемой недополнения. Я нашел еще один ответ, который скоро опубликую!   -  person Berk U.    schedule 27.11.2013
comment
Хм, но я дал приближения как для больших, так и для малых s, чтобы можно было избежать потери значимости. Если нет, пожалуйста, прокомментируйте подробно.   -  person A. Donda    schedule 27.11.2013


Ответы (2)


Как насчет следующих приближений:

– Для вычисления L, если s велико, то exp(s) будет намного больше 1:

1 + exp(s) ≅ exp(s)

и следовательно

log(1 + exp(s)) ≅ log(exp(s)) = s.

Если s мало, то с помощью ряда Тейлора функции exp()

exp(s) ≅ 1 + s

и используя серию Тейлора log()

log(1 + exp(s)) ≅ log(2 + s) ≅ log(2) + s / 2.

– Для вычислений dL, для больших s

exp(s) ./ (1 + exp(s)) ≅ 1

и для маленьких s

exp(s) ./ (1 + exp(s)) ≅ 1/2 + s / 4.

– Код для вычисления L может выглядеть, например, так:

s = X*beta;
l = log(1+exp(s));
ind = isinf(l);
l(ind) = s(ind);
ind = (l == 0);
l(ind) = log(2) + s(ind) / 2;
L = 1/N * sum(l,1)
person A. Donda    schedule 20.11.2013
comment
Бинго. На самом деле вы можете получить строгие ограничения на то, насколько большим должно быть s, чтобы ваше второе равенство выполнялось в арифметике с плавающей запятой. (Что, конечно, совершенно излишне, но дает очень веское обоснование того, почему это законно.) - person tmyklebu; 20.11.2013
comment
@tmyklebu, интересно, не подумал об этом. - person A. Donda; 20.11.2013

Я нашел хорошую статью об этой проблеме .

Избавившись от множества слов, мы можем упростить рассуждение, заявив, что исходное выражение

log(1 + exp(s)) 

можно переписать как

log(exp(s)*(exp(-s) + 1))
= log(exp(s)) + log(exp(-s) + 1)
= s + log(exp(-s) + 1)

Это предотвратит возникновение переполнения — оно не предотвратит недополнение, но к тому времени, когда это произойдет, у вас будет ответ (а именно, s). Вы не можете просто использовать это вместо оригинала, так как это все равно создаст вам проблемы. Однако теперь у нас есть основа для функции, которую можно написать, которая будет точной и не приведет к переполнению/недополнению:

function LL = logistic(s)
if s<0
  LL = log(1 + exp(s));
else
  LL = s + logistic(-s);

Я думаю, что это поддерживает достаточно хорошую точность.

РЕДАКТИРОВАТЬ теперь к сути вашего вопроса - сделать это векторизованным, а также разрешить расчет наклона. Давайте возьмем их по одному:

function LL = logisticVec(s)
  LL = zeros(size(s));
  LL(s<0) = log(1 + exp(s(s<0)));
  LL(s>=0) = s(s>=0) + log(1 + exp(-s(s>=0)));

Чтобы получить среднее значение, которое вы хотели:

L = logisticVec(X*beta) / N;

Склон немного сложнее; note Я полагаю, что в вашем выражении может быть опечатка (отсутствует знак умножения).

dL/dbeta = sum(X * exp(X*beta) ./ (1 + exp(X*beta))) / N;

Если мы разделим верх и низ на exp(X*beta), получим

dL = sum(X ./ (exp(-X*beta) + 1)) / N;

Еще раз, переполнение исчезло, и мы остались с недостаточным переполнением, но, поскольку к нему добавлено 1, ошибка, которую это создает, незначительна.

person Floris    schedule 20.11.2013
comment
Вам нужно обработать случай, когда s тоже очень отрицательное, и вы столкнетесь с точно такой же проблемой. - person tmyklebu; 20.11.2013
comment
Ага. По сути, это логарифмическая сумма экспонент. - person horchler; 20.11.2013