317 lines
12 KiB
Plaintext
317 lines
12 KiB
Plaintext
|
{
|
||
|
"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
|
||
|
}
|