"Underflow" ve "log-sum-exp" hilesi
Saklı Markov modelleri ile çalışırken "underflow", yani değerlerin bilgisayarda saklanamayacak derecede küçülüp pratikte sıfır olması temel bir uygulama sorunu olarak karşımıza çıkar. Çünkü hesaplamalarda alttakine benzer bir işlem yapmamız gerekir:
$$\log\sum\limits_k e^{x_k}$$
$e^{x_k}$ değerlerinin -1000lerde seyrettiğini düşünün. $e^{1000}$ gibi bir sayıyı açıkça hesaplamaya çalışırsak sıfır elde ederiz.
Probleme tane tane değil, tümden bakalım. Elimizde 3 tane değer olsun: -1000, -1005 ve -1010. Bunları işleyelim. $e^{1005}$, $e^{1000}$'e göre oldukça küçük. $e^{1010}$ ise daha da küçük. Burada baskın olan $e^{1000}$ değeri. Yani, en büyük değer baskın görünüyor. En büyük değere $y$ diyelim ve bunu hesaba katarak sihirli değneğimizi kullanalım:
\begin{align}
\log\sum\limits_k e^{x_k}
& = \log\left[\left(\sum\limits_k e^{x_k}\right)e^{-y} e^y \right] \\
& = \log\left[\left(\sum\limits_k e^{x_k}\right)e^{-y}\right] + y \\
& = \log\left[\sum\limits_k e^{x_k - y}\right] + y
\end{align}
Yani öncelikle tüm değerlerden en büyüğünü çıkartacağız (yeni dizilimin en büyük değeri 0 olacak) ve işlemi yaptıktan sonra bu değeri geri ekleyeceğiz.
Fonksiyonumuz çok basit:
import numpy as np def logSumExp(x): maxX = np.max(x) r = maxX + np.log(np.sum(np.exp(x-maxX))) return r
Deneyelim:
r = logSumExp(np.array([-1213, -1214])) # sonuç: -1212.6867383124818 r2 = np.log(np.sum(np.exp(np.array([-1213, -1214])))) # sonuç: -Inf
Yöntemimiz sonucu başarıyla hesapladı. Açıkça yaptığımızda ise underflow oldu ve log(0)
hesabı yüzünden -sonsuz sonucu döndü.
Not: Kaynak olarak şu videoya bakabilirsiniz.