{ "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 }