aitech-eks-pub/wyk/07_Naiwny_klasyfikator_bayesowski.ipynb

371 lines
14 KiB
Plaintext
Raw Normal View History

2021-04-21 08:32:06 +02:00
{
"cells": [
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "moderate-array",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"# Klasyfikacja binarna dla tekstu\n",
"\n",
"Zakładamy, że mamy dwie klasy: $c$ i jej dopełnienie ($\\bar{c}$).\n",
"\n",
"Typowym przykładem jest zadanie klasyfikacji mejla, czy należy do spamu, czy nie (_spam_ vs _ham_), czyli innymi słowy filtr antyspamowy."
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "correct-victory",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"**Pytanie**: Czy można wyobrazić sobie zadanie klasyfikacji mejli, niebędące zadaniem klasyfikacji binarnej?"
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "spiritual-diploma",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"Zakładamy paradygmat uczenia nadzorowanego, tzn. dysponujemy zbiorem uczącym.\n",
"\n",
"**Pytanie**: Czym jest i w jaki sposób powstaje zbiór uczący dla filtru antyspamowego?"
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "secure-performance",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"## Klasyfikacja regułowa\n",
"\n",
"Filtr anyspamowe _można_ zrealizować za pomocą metod innych niż opartych na uczeniu maszynowym. Można np. tworzyć reguły (np. wyrażenia regularne). Przykładem są (barokowe...) reguły w programie SpamAssassin, zob. fragment [pliku reguł](https://github.com/apache/spamassassin/blob/trunk/rules/20_advance_fee.cf):\n",
"\n",
"```\n",
"header __FRAUD_VQE\tSubject =~ /^(?:Re:|\\[.{1,10}\\])?\\s*(?:very )?urgent\\s+(?:(?:and|&)\\s+)?(?:confidential|assistance|business|attention|reply|response|help)\\b/i\n",
"\n",
"body __FRAUD_DBI\t/(?:\\bdollars?\\b|\\busd(?:ollars)?(?:[0-9]|\\b)|\\bus\\$|\\$[0-9,.]{6,}|\\$[0-9].{0,8}[mb]illion|\\$[0-9.,]{2,10} ?m|\\beuros?\\b|u[.]?s[.]? [0-9.]+ m)/i\n",
"body __FRAUD_KJV\t/(?:claim|concerning) (?:the|this) money/i\n",
"body __FRAUD_IRJ\t/(?:finance|holding|securit(?:ies|y)) (?:company|firm|storage house)/i\n",
"body __FRAUD_NEB\t/(?:government|bank) of nigeria/i\n",
"body __FRAUD_XJR\t/(?:who was a|as a|an? honest|you being a|to any) foreigner/i\n",
"```\n",
"\n",
2021-04-27 16:50:25 +02:00
"**Pytanie:** Jakie są wady i zalety regułowych filtrów antyspamowych?\n",
2021-04-21 08:32:06 +02:00
"\n",
"Współcześnie zdecydowanie dominuje użycie metod statystycznych (opartych na nadzorowanym uczeniu maszynowym). Do popularności tych metod przyczynił się artykuł [Plan for spam](http://www.paulgraham.com/spam.html) autorstwa Paula Grahama."
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "indoor-ending",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"## Podejście generatywne i dyskryminatywne\n",
"\n",
"W klasyfikacji (i w ogóle w uczeniu nadzorowanym) można wskazać dwa podejścia:\n",
"\n",
"* generatywne - wymyślamy pewną \"historyjkę\", w jaki sposób powstaje tekst, \"historyjka\" powinna mieć miejsca do wypełnienia (parametry), np. częstości wyrazów, na podstawie zbioru uczącego dobieramy wartości parametrów (przez rachunki wprost); \"historyjka\" nie musi być prawdziwa, wystarczy, że jakoś przybliża rzeczywistość\n",
"\n",
"* dyskryminatywne - nie zastanawiamy się, w jaki sposób powstają teksty, po prostu \"na siłę\" dobieramy wartości parametrów (wag) modelu, tak aby uzyskać jak najmniejszą wartość funkcji kosztu na zbiorze uczącym; zwykle odbywa się to w iteracyjnym procesie (tak jak przedstawiono na schemacie na poprzednim wykładzie).\n",
"\n",
"**Pytanie**: Jakie są wady i zalety obu podejść?"
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "pleased-clinic",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"## Nasz \"dyżurny\" przykład\n",
"\n",
"Zakładamy, że nasz zbiór uczący ($X$) składa się z 4 dokumentów:\n",
"\n",
"* $x_1=\\mathit{kup\\ pan\\ Viagrę}$\n",
"* $x_2=\\mathit{tanie\\ miejsce\\ dla\\ pana}$\n",
"* $x_3=\\mathit{viagra\\ viagra\\ viagra}$\n",
"* $x_4=\\mathit{kup\\ tanie\\ cartridge'e}$\n",
"\n",
"z następującymi etykietami:\n",
"\n",
"* $y_1=c$ (spam)\n",
"* $y_2=\\bar{c}$ (nie-spam)\n",
"* $y_3=c$\n",
"* $y_4=c$\n",
"\n",
"Zakładamy, że dokumenty podlegają lematyzacji i sprowadzeniu do mały liter, więc ostatecznie będziemy mieli następujące ciąg termów:\n",
"\n",
"* $x_1=(\\mathit{kupić}, \\mathit{pan}, \\mathit{viagra})$\n",
2021-04-27 16:50:25 +02:00
"* $x_2=(\\mathit{tani}, \\mathit{miejsce}, \\mathit{dla}, \\mathit{pan})$\n",
2021-04-21 08:32:06 +02:00
"* $x_3=(\\mathit{viagra}, \\mathit{viagra}, \\mathit{viagra})$\n",
"* $x_4=(\\mathit{kupić}, \\mathit{tani}, \\mathit{cartridge})$\n",
"\n",
2021-04-27 16:50:25 +02:00
"$P(tani|c) = (1+1)/(9+7) = 2/16 = 0.125$\n",
"$P(viagra|c) = \\frac{4+1}{9 + 7} = 5/16 = 0.3125 $\n",
"$P(dla|c) = \\frac{0+1}{9+7} = 1/16 = 0.0625$\n",
"$P(pan|c) = (1+1)/(9+7) = 2/16 = 0.125 $\n",
"$P(c) = 0.75$\n",
"\n",
"w wersji wielomianowej: $P(c)P(tani|c)P(tani|c)P(viagra|c)P(dla|c)P(pan|c) = 0.75 * 0.125 * 0.125 * 0.3125 * 0.0625 * 0.125= 0.0002861$\n",
"\n",
"w werjis Bernoulliego: $P(c)P(U_{dla}=1|c)P(U_{cartridge}=0|c)P(U_{viagra}=1|c)P(U_{pan}=1|c)P(U_{tani}=1|c)P(U_{miejsce}=0|c)P(U_{kup}=0|c)$\n",
"\n",
"$P(tani|\\bar{c}) = (1+1)/(4+7) = 2/11 =0.182 $\n",
"$P(viagra|\\bar{c}) = 1/11 = 0.091 $\n",
"$P(dla|\\bar{c}) = 2/11 = 0.182 $\n",
"$P(pan|\\bar{c}) = 2/11 = 0.182 $\n",
"$P(\\bar{c}) = 0.25$\n",
"\n",
"$P(\\bar{c})P(tani|\\bar{c})P(tani|\\bar{c})P(dla|\\bar{c})P(pan|\\bar{c}) = 0.25 * 0.182 * 0.182 * 0.091 * 0.182 * 0.182 = 0.00002496$\n",
"\n",
"\n",
"\n",
"Uczymy na tym zbiorze klasyfikator, który będziemy testować na dokumencie $d=\\mathit{tania\\ tania\\ viagra\\ dla\\ pana}$, tj. po normalizacji\n",
2021-04-21 08:32:06 +02:00
"$d=(\\mathit{tani}, \\mathit{tani}, \\mathit{viagra}, \\mathit{dla}, \\mathit{pan})$.\n",
"\n",
"**Uwaga:** Przykład jest oczywiście nierealistyczny i trudno będzie nam ocenić sensowność odpowiedzi. Za to będziemy w stanie policzyć ręcznie wynik.\n"
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "partial-military",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"## Naiwny klasyfikator bayesowski\n",
"\n",
"* _naiwny_ - niekoniecznie oznacza, że to \"głupi\", bezużyteczny klasyfikator\n",
"* _klasyfikator_ \n",
"* _bayesowski_ - będzie odwoływać się do wzoru Bayesa.\n",
"\n",
"Naiwny klasyfikator bayesowski raczej nie powinien być stosowany \"produkcyjnie\" (są lepsze metody). Natomiast jest to metoda bardzo prosta w implementacji dająca przyzwoity _baseline_.\n",
"\n",
"Naiwny klasyfikator bayesowski ma dwie odmiany:\n",
"\n",
"* wielomianową,\n",
"* Bernoulliego.\n",
"\n",
"Wielomianowy naiwny klasyfikator bayesowski jest częściej spotykany i od niego zaczniemy."
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "colonial-creature",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"Mamy dokument $d$ i dwie klasy $c$ i $\\bar{c}$. Policzymy prawdopodobieństwa $P(c|d)$ (mamy dokument $d$, jakie jest prawdopodobieństwo, że to klasa $c$) i $P(\\bar{c}|d)$. A właściwie będziemy te prawdopodobieństwa porównywać.\n",
"\n",
"**Uwaga**: nasz zapis to skrót notacyjny, właściwie powinniśmy podać zmienne losowe $P(C=c|D=d)$, ale zazwyczaj będziemy je pomijać. \n",
"\n",
"**Pytanie**: kiedy ostatecznie nasz klasyfikator zwróci informację, że klasa $c$, a kiedy że $\\bar{c}$? czy użytkownika interesują prawdopodobieństwa $P(c|d)$ i $P(\\bar{c}|d)$?"
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "governing-fiction",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"Zastosujmy najpierw wzór Bayesa.\n",
"\n",
2021-04-27 16:50:25 +02:00
"$P(c|d) = \\frac{P(d|c) P(c)}{P(d)}$"
2021-04-21 08:32:06 +02:00
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "northern-spine",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
2021-04-27 16:50:25 +02:00
"$P(\\bar{c}|d) = \\frac{P(d|\\bar{c}) P(\\bar{c})}{P(d)}$"
2021-04-21 08:32:06 +02:00
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "utility-induction",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"(Oczywiście skądinąd $P(\\bar{c}|d) = 1 - P(c|d)$, ale nie będziemy teraz tego wykorzystywali.)"
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "timely-force",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"Co możemy pominąć, jeśli tylko porównujemy $P(c|d)$ i $P(\\bar{c}|d)$?\n",
"\n",
"Użyjmy znaku proporcjonalności $\\propto$:\n",
"\n",
"$P(c|d) = \\frac{P(d|c) P(c)}{P(d)} \\propto P(d|c) P(c)$\n",
"\n",
"$P(\\bar{c}|d) = \\frac{P(d|\\bar{c}) P(\\bar{c})}{P(d)} \\propto P(d|\\bar{c}) P(\\bar{c})$\n",
"\n",
"**Pytanie:** czy iloczyn $P(d|c)P(c)$ można interpretować jako prawdopodobieństwo?"
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "embedded-involvement",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"#### Prawdopodobieństwo _a priori_\n",
"\n",
"$P(c)$ - prawdopodobieństwo a priori klasy $c$\n",
"\n",
"$\\hat{P}(c) = \\frac{N_c}{N}$\n",
"\n",
"gdzie\n",
"\n",
"* N - liczba wszystkich dokumentów w zbiorze uczącym\n",
2021-04-27 16:50:25 +02:00
"* N_c - liczba dokumentow w zbiorze uczącym z klasą $c$\n",
"\n",
"$\\hat{P}(c) = 0,75$\n",
"\n",
"$\\hat{P}(\\bar{c}) = 0,25$\n"
2021-04-21 08:32:06 +02:00
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "virgin-premiere",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"#### Prawdopodobieństwo _a posteriori_\n",
"\n",
"Jak interpretować $P(d|c)$?\n",
"\n",
"Wymyślmy sobie model generatywny, $P(d|c)$ będzie prawdopodobieństwem, że spamer (jeśli $c$ to spam) wygeneruje tekst.\n",
"\n",
"Załóżmy, że dokument $d$ to ciąg $n$ termów, $d = (t_1\\dots t_n)$. Na razie niewiele z tego wynika."
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "acting-zimbabwe",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"$P(d|c) = P(t_1\\dots t_n|c)$\n",
"\n",
"Żeby pójść dalej musimy doszczegółowić nasz model generatywny. Przyjmijmy bardzo naiwny i niezgodny z rzeczywistością model spamera (i nie-spamera): spamer wyciąga wyrazy z worka i wrzuca je z powrotem (losowanie ze zwracaniem). Jedyne co odróżnia spamera i nie-spamera, to **prawdopodobieństwo wylosowania wyrazu** (np. spamer wylosuje słowo _Viagra_ z dość dużym prawdopodobieństwem, nie-spamer - z bardzo niskim).\n",
"\n",
"**Pytanie:** Ile może wynosić $P(\\mathit{Viagra}|c)$?\n",
"\n",
"Po przyjęciu takich \"naiwnych założeń\":\n",
"\n",
"$$P(d|c) = P(t_1\\dots t_n|c) \\approx P(t_1|c)\\dots P(t_n|c) = \\prod_i^n P(t_i|c)$$"
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "adjustable-disney",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"Jak oszacować $\\hat{P}(t|c)$?\n",
"\n",
"$$\\hat{P}(t|c) = \\frac{\\#(t,c)}{\\sum_i^{|V|} \\#(t_i,c)} = \\frac{\\mathit{ile\\ razy\\ term\\ t\\ pojawił\\ się\\ w\\ dokumentach\\ klasy\\ c}}{liczba\\ wyrazów\\ w\\ klasie\\ c}$$"
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "associate-variance",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"### Wygładzanie\n",
"\n",
"Mamy problem z zerowymi prawdopodobieństwami.\n",
"\n",
"Czy jeśli w naszym zbiorze uczącym spamerzy ani razu nie użyli słowa _wykładzina_, to $P(\\mathit{wykładzina}|c) = 0$?.\n",
"\n",
"Musimy zastosować wygładzanie (_smoothing_). Spróbujmy wymyślić wygładzanie wychodząc od zdroworozsądkowych aksjomatów.\n",
"\n",
"#### Aksjomaty wygładzania.\n",
"\n",
"Założmy, że mamy dyskretną przestrzeń probabilistyczną $\\Omega = \\{\\omega_1,\\dots,\\omega_m\\}$, zdarzenie $\\omega_i$ w naszym zbiorze uczącym wystąpiło $k_i$ razy. Wprost prawdopodobieństwa byśmy oszacowali tak: $P(\\omega_i) = \\frac{k_i}{\\sum_j^m k_j}$.\n",
"Szukamy zamiast tego funkcji wygładzającej $f(m, k, T)$ (innej niż $f(m, k, T) = \\frac{k}{T}$), która spełniałaby następujące aksjomaty:\n",
"\n",
"1. $f(m, k, T) \\in [0, 1]$\n",
"2. $f(m, k, T) \\in (0, 1)$ jeśli $m > 1$\n",
"3. $\\sum_i f(m, k_i, T) = 1$, jeśli $\\sum_i k_i = T$\n",
"4. $f(m, 0, 0) = \\frac{1}{m}$\n",
"5. $\\lim_{T \\to \\inf} f(m, k, T) = \\frac{k}{T}$\n",
"\n",
2021-04-27 16:50:25 +02:00
"\n",
"m=2, k1=2, k2=4, T=6, 2/6 => f(2, 2, 6) > 0.333, f(2, 4, 6) < 0.666 \n",
"\n",
2021-04-21 08:32:06 +02:00
"Jaka funkcja spełnia te aksjomaty?\n",
"\n",
"$$f(m, k, T) = \\frac{k+1}{T+m}$$\n",
"\n",
"Jest to wygładzanie +1, albo wygładzanie Laplace'a.\n",
"\n",
2021-04-27 16:50:25 +02:00
"**Pytanie:** Wymyślić jakiś inny przykład funkcji, która będzie spełniała aksjomaty.\n",
"\n",
2021-04-21 08:32:06 +02:00
"\n",
"\n",
"\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
2021-04-27 16:50:25 +02:00
"id": "complimentary-airplane",
2021-04-21 08:32:06 +02:00
"metadata": {},
"source": [
"Po zastosowaniu do naszego naiwnego klasyfikatora otrzymamy:\n",
" \n",
"$$\\hat{P}(t|c) = \\frac{\\#(t,c) + 1}{\\sum_i^{|V|} \\#(t_i,c) + |V|}$$"
]
},
2021-04-27 16:50:25 +02:00
{
"cell_type": "markdown",
"id": "comprehensive-junior",
"metadata": {},
"source": [
"### Metoda Bernoulliego"
]
},
{
"cell_type": "markdown",
"id": "vocational-spanish",
"metadata": {},
"source": [
"$$P(𝑑|𝑐) \\approx P(U=u_1|c)\\dots P(U=u_{|v|})$$, gdzie $u_i = 1$, $t_i$ pojawił się w dokumencie $d$, 0 - w przeciwnym razie\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "enabling-manitoba",
"metadata": {},
"source": [
"$\\hat{P}(U_{viagra}=1|c) = \\frac{\\#(viagra,N_c) + 1}{N_c + 2}$"
]
},
2021-04-21 08:32:06 +02:00
{
"cell_type": "code",
"execution_count": null,
2021-04-27 16:50:25 +02:00
"id": "bearing-execution",
2021-04-21 08:32:06 +02:00
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}