Jak vyřešit numerické potíže?

Při práci na klasifikátorech (především u naivního bayesovského klasifikátoru) můžete narazit na různé typy numerických problémů. Zde jsou uvedeny cesty, kterými je můžete zkusit vyřešit. Byly představeny už na přednášce, zde jsou uvedeny jako reference.

Řídkost trénovacích dat

Při odhadu pravděpodobností $P(x[i]|s)$ z trénovacích dat se snadno stane, že některé z těchto pravděpodobností budou nulové. Např. při odhadu $P(x[0]=255|s=1)$ se v trénovacích datech nevyskytne žádný obrázek číslice “1”, který by v prvním pixelu ($x[0]$) měl bílou barvu (odstín 255). Když pak budeme chtít klasifikovat nový obrázek $x$ číslice “1”, pro který bude platit $x[0]=255$, vyjde nám $P(s=1|x) = 0$, a obrázek bude zaklasifikován jako jiná číslice (jejíž alespoň 1 trénovací obrázek měl první pixel bílý).

Může se dokonce stát, že v žádném trénovacím obrázku nezávisle na číslici se nevyskytne $x[0]=255$. Při klasifikaci nového obrázku $x$ pak vyjdou všechny pravděpodobnosti $P(s=0|x) = P(s=1|x) = \ldots = P(s=9|x) = 0$ a my nebudeme mít podle čeho rozhodnout, o jakou číslici se jedná.

Co s tím?

Laplacova korekce

Jedním ze způsobů řešení tohoto problému je Laplacova korekce (viz přednáška). Např. pravděpodobnost $P(x[0]=255|s=1)$ se odhadne jako $$P(x[0]=255|s=1) = \frac{N(x[0]=255, s=1)}{N(s=1)},$$ tj. jako podíl počtu trénovacích obrázků číslice 1, které mají bílý první pixel, ku celkovému počtu trénovacích obrázků číslice 1. Pokud žádný trénovací obrázek číslice 1 nemá první pixel bílý, je čitatel zlomku i celá pravděpodobnost rovna 0.

Laplacova korekce spočívá v tom, že budeme předstírat, že kromě skutečných trénovacích obrázků obsahují trénovací data ještě $k$ obrázků pro každou úroveň šedi, tedy $k$ obrázků číslice 1 s $x[0]=0$, $k$ obrázků číslice 1 s $x[0]=1$ až $k$ obrázků číslice 1 s $x[0]=255$. Odhad pravděpodobnosti se pak změní na $$P(x[0]=255|s=1) = \frac{N(x[0]=255, s=1) + k}{N(s=1) + 255k}.$$ V našem příkladu by to znamenalo, že pokud máme 100 trénovacích obrázků číslice 1, $k=1$ a obrázek s $x[0]=255$ v trénovacích datech vůbec nebude, odhad pravděpodobnosti se z 0 změní na $$P(x[0]=255|s=1) = \frac{N(x[0]=255, s=1) + k}{N(s=1) + 255k} = \frac{0 + 1}{100+255} = \frac{1}{355}.$$

Snížení počtu úrovní jasu obrázku

Dalším způsobem, jak snížit pravděpodobnost, že se v trénovacích datech neobjeví žádný obrázek s nějakou úrovní šedi nějakého pixelu, je snížit počet úrovní šedi, které obrázek používá. V extrémním případě bychom počet odstínů mohli zredukovat na 2, tj. namapovat odstíny 0-127 na hodnotu 0 a odstíny 128-255 na hodnotu 1. Vhodnější ale může být kompromis, tj. použití např. 16 odstínů šedi, tj. namapovat odstíny 0-15 na hodnotu 0, odstíny 16-31 na hodnotu 1, atd. Implementace je jednoduchá, lze využít celočíselné dělení.

Pamatujte ale na 2 věci:

  • Aplikujete-li tuto transformaci při odhadu pravděpodobností na trénovací data, nezapomeňte stejnou transformaci aplikovat také na testovací obrázky, které chcete klasifikovat.
  • Tato metoda dokáže velmi snížit pravděpodobnost toho, že by v trénovacích datech chyběly nějaké případy, ale nevyřeší problém zcela. Je vhodné ji kombinovat s předchozí metodou (Laplacova korekce).

Násobení mnoha pravděpodobností

Naivní bayesovský klasifikátor rozhoduje o třídě tak, že pro klasifikovaný obrázek $x$ vypočte $P(s|x)$ pro všechny třídy $s$ a obrázek zaklasifikuje do třídy, pro kterou je $P(s|x)$ největší. Výraz $$P(s|x) = \frac{P(x|s)P(s)}{P(x)}$$ lze zjednodušit, když si uvědomíme, že pro jeden konkrétní obrázek $x$ je $P(x)$ stejné pro všechny třídy. Jmenovatel tedy můžeme ignorovat a stačí vědět, že $$P(s|x) \propto P(x|s)P(s),$$ kde symbol $\propto$ znamená, že výraz na levé straně je úměrný výrazu na pravé straně. (Pokud trénovací data obsahují stejný počet příkladů všech tříd, tj. jsou-li všechny třídy stejně pravděpodobné, lze dokonce psát $P(s|x) \propto P(x|s)$. Obecně to ale neplatí.) Díky podmíněné nezávislosti složek vektoru $x$ při dané třídě $s$ můžeme dále psát, že $$P(s|x) \propto P(x|s)P(s) = P(s) \cdot P(x[0]|s) \cdot P(x[1]|s) \cdot \ldots \cdot P(x[n]|s).$$

A tady narazíme na další problém - násobení mnoha pravděpodobností. I když zabráníme tomu, aby některé z $P(x[i]|s)$ byly nulové (viz výše), ve výpočtu budeme násobit tolik pravděpodobností, že nám brzy začne vycházet tak malé číslo, které nedokážeme v počítači reprezentovat, a stane se z něj 0.

Naštěstí nám nejde ani tak o přesný výpočet pravděpodobností $P(s|x)$ jako spíš o zjištění, která z těchto pravděpodobností je největší. Využijeme proto malý trik: použijeme logaritmy. Protože je $\log(.)$ monotónní funkce, $P(s|x)$ bude nabývat maxima pro stejnou třídu jako $\log P(s|x)$. Můžeme tedy psát, že $$\arg\max_s P(s|x) =$$ $$= \arg\max_s \log\left(P(s|x)\right)=$$ $$= \arg\max_s \log\left(P(s) \cdot P(x[0]|s) \cdot P(x[1]|s) \cdot \ldots \cdot P(x[n]|s)\right)=$$ $$= \arg\max_s \log\left(P(s)\right) + \log\left(P(x[0]|s)\right) + \log\left(P(x[1]|s)\right) + \ldots + \log\left(P(x[n]|s)\right)$$

Sčítání mnoha záporných čísel by už problém být neměl. I zde bychom ale měli zajistit, že žádná z pravděpodobností nebude 0, protože $\log(0) = -\infty$.

courses/b3b33kui/semestralni_ulohy/5_ml/numerical_issues.txt · Last modified: 2023/05/12 16:14 by xposik