Я читал о трюке log-sum-exp во многих местах (например, здесь и здесь ), но никогда не видел пример того, как он применяется конкретно к наивному байесовскому классификатору (например, с дискретными функциями и двумя классами)
Как именно можно избежать проблемы числового снижения при использовании этого трюка?
naive-bayes
underflow
мистифицировать
источник
источник
Ответы:
В
и знаменатель, и числитель могут стать очень маленькими, как правило, потому что может быть близко к 0, и мы умножаем многие из них друг на друга. Чтобы предотвратить потери, можно просто взять журнал числителя, но для знаменателя нужно использовать трюк log-sum-exp.p(xi|Ck)
Более конкретно, для предотвращения недопущения:
Если нам важно знать, к какому классу вход скорее всего, относится с максимальным апостериорным (MAP) правилом принятия решений, мы не будем нужно применить трюк log-sum-exp, так как нам не нужно вычислять знаменатель в этом случае. Для числителя можно просто взять журнал, чтобы предотвратить потери: . Более конкретно:( х = х 1 , ... , х п ) л о г ( р ( х | Y = С ) р ( Y = С ) )(y^) (x=x1,…,xn) log(p(x|Y=C)p(Y=C))
который становится после взятия журнала:
Если мы хотим вычислить вероятность класса , нам нужно вычислить знаменатель:p(Y=C|x)
Элемент может опуститься из-за того, что может быть очень маленьким: это та же проблема, что и в числителе, но на этот раз у нас есть суммирование внутри логарифма, что не позволяет нам преобразовать (может быть близко к 0) в (отрицательно и больше не близко к 0, поскольку ). Чтобы обойти эту проблему, мы можем использовать тот факт, что чтобы получить:log( ∑|C|k=1p(x|Y=Ck)p(Y=Ck)) p(xi|Ck) p(xi|Ck) log(p(xi|Ck)) 0≤p(xi|Ck)≤1 p(xi|Ck)=exp(log(p(xi|Ck)))
В этот момент возникает новая проблема: может быть весьма отрицательным, что означает, что может стать очень близким к 0, т.е. Вот где мы используем трюк log-sum-exp :log(p(x|Y=Ck)p(Y=Ck)) exp(log(p(x|Y=Ck)p(Y=Ck)))
с участием:
Мы можем видеть, что введение переменной позволяет избежать потерь. Например, с , имеем:A k=2,a1=−245,a2=−255
Используя трюк log-sum-exp, мы избегаем : :A=max(−245,−255)=−245 log∑keak=log∑keakeA−A=A+log∑keak−A=−245+log∑keak+245=−245+log(e−245+245+e−255+245)=−245+log(e0+e−10)
Мы избежали потери, поскольку намного дальше от 0, чем или .e−10 3.96143×10−107 1.798486×10−111
источник
Предположим, мы хотим определить, какая из двух баз данных с большей вероятностью сгенерировала фразу (например, из какого романа эта фраза с большей вероятностью пришла). Мы могли бы предполагать независимость слов, обусловленных базой данных (наивное байесовское предположение).
Теперь найдите вторую ссылку, которую вы опубликовали. Здесь будет представлять собой общую вероятность соблюдения предложения для данной базы данных, а s будет представлять вероятность наблюдения каждого из слов в предложении.a ebt
источник
Из этого ответа мы можем видеть, что наименьшее число в Python (просто возьмем его, например)
5e-324
связано с IEEE754 , а аппаратная причина относится и к другим языкам.И любое число с плавающей запятой меньше этого приведет к 0.
И давайте посмотрим на функцию наивного Байеса,
with discrete features and two classes
как вам нужно:Позвольте мне описать эту функцию простой задачей НЛП ниже.
Мы решили определить, является ли приходящее письмо спамом ( ) или не спамом ( ), и у нас есть словарный запас размером 5000 слов ( ), и единственное беспокойство вызывает то, что слово ( ) встречается ( ) в электронном письме или нет ( ) для простоты ( Бернулли наивный Байес ).S=1 S=0 n=5,000 wi p(wi|S=1) 1−p(wi|S=1)
Мы можем видеть, что будет очень маленьким из-за вероятностей (оба и будет между 0 и 1) в , и, следовательно, мы уверены, что произведение будет меньше и мы просто получим .p(S=s)∏ni=1p(wi|S=s) p(wi|S=1) 1−p(wi|S=1) ∏5000i 5e−324 0/0
Тогда возникает проблема: как мы можем вычислить вероятность того, что письмо является спамом ? Или как мы можем вычислить числитель и знаменатель?p(S=1|w1,...wn)
Мы можем увидеть официальную реализацию в sklearn :
Для числителя он преобразовал произведение вероятностей в сумму логарифмического правдоподобия, а для знаменателя он использовал logsumexp в scipy, который:
Потому что мы не можем добавить две совместные вероятности, добавив их совместную логарифмическую вероятность, и мы должны выйти из логарифмического пространства в вероятностное пространство. Но мы не можем добавить две истинные вероятности, потому что они слишком малы, и мы должны масштабировать их и сделать сложение: и вернуть результат обратно в пространство журнала затем измените его масштаб: в пространстве журнала, добавив .∑s={0,1}ejlls−max_jll log∑s={0,1}ejlls−max_jll max_jll+log∑s={0,1}ejlls−max_jll max_jll
А вот и вывод:
где - это в коде.max_jll a_max
Как только мы получим как числитель, так и знаменатель в лог-пространстве, мы можем получить условную вероятность ( ), вычитая знаменатель из числителя :logp(S=1|w1,...wn)
Надеюсь, это поможет.
Ссылка:
1. Наивный байесовский классификатор Бернулли
2. Фильтрация спама наивным байесовским фильтром - какие наивные байесовские?
источник