diff --git a/wyk/07_Naiwny_klasyfikator_bayesowski.ipynb b/wyk/07_Naiwny_klasyfikator_bayesowski.ipynb new file mode 100644 index 0000000..8208481 --- /dev/null +++ b/wyk/07_Naiwny_klasyfikator_bayesowski.ipynb @@ -0,0 +1,316 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "damaged-senator", + "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", + "id": "explicit-gathering", + "metadata": {}, + "source": [ + "**Pytanie**: Czy można wyobrazić sobie zadanie klasyfikacji mejli, niebędące zadaniem klasyfikacji binarnej?" + ] + }, + { + "cell_type": "markdown", + "id": "material-watch", + "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", + "id": "referenced-hello", + "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", + "Jakie są wady i zalety regułowych filtrów antyspamowych?\n", + "\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", + "id": "cathedral-uganda", + "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", + "id": "powerful-engineer", + "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", + "* $x_2=(\\mathit{tani}, \\mathit{miejsce}, \\mathit{dla}, \\mathit{pana})$\n", + "* $x_3=(\\mathit{viagra}, \\mathit{viagra}, \\mathit{viagra})$\n", + "* $x_4=(\\mathit{kupić}, \\mathit{tani}, \\mathit{cartridge})$\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", + "$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", + "id": "controversial-rotation", + "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", + "id": "spatial-citizenship", + "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", + "id": "united-recognition", + "metadata": {}, + "source": [ + "Zastosujmy najpierw wzór Bayesa.\n", + "\n", + "$P(c|d) = \\frac{P(d|c) P(c)}{P(d)} \\propto P(d|c) P(c)$" + ] + }, + { + "cell_type": "markdown", + "id": "present-draft", + "metadata": {}, + "source": [ + "$P(\\bar{c}|d) = \\frac{P(d|\\bar{c}) P(\\bar{c})}{P(d)} \\propto P(d|\\bar{c}) P(\\bar{c}) $" + ] + }, + { + "cell_type": "markdown", + "id": "accepting-tamil", + "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", + "id": "equipped-outreach", + "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", + "id": "active-motor", + "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", + "* N_c - liczba dokumentow w zbiorze uczącym z klasą $c$\n" + ] + }, + { + "cell_type": "markdown", + "id": "trying-indonesian", + "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", + "id": "median-nomination", + "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", + "id": "romantic-verse", + "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", + "id": "interracial-today", + "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", + "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", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "accepting-stockholm", + "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|}$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "moral-ceremony", + "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 +}