mpsic_projekt_1_bayes_class.../projekt.ipynb

14 KiB

Naiwna klasyfikacja bayesowska

Naiwna klasyfikacja bayesowska jest to klasyfikacja polegająca na przydzielaniu obiektom prawdopodobieństwa przynależności do danej klasy. Naiwny klasyfikator jest to bardzo prosta metoda klasyfikacji jednak mimo swej prostoty sprawdza się w wielu przypadkach gdy bardziej złożone metody zawodzą.Jednym z powodów dla których naiwny klasyfikator wypadak dobrze jest jego założenie o niezależności predykatów, własnie dlatego nazywany jest klasyfikatorem naiwnym, naiwnie zakłada niezależność atrybutów opisujących dany przykład, co często nie ma odzwierciedlenia w rzeczywistości. Klasyfikator nazywany jets bayesowskim dlatego że wykorzystuje twierdzenie bayesa do obliczania prawdopodobieństw.

Twierdzenie Bayesa

Twierdzneie bayesa jets to twierdzenie tworii prawdopodobieństwa wiążące prawdopodobieństwa warunkowe dwóch zdarzeń warunkujących się nawzajem. Prawdopodobieństwo wystąpienia zdarzenia A gdy wystąpiło zdarzenie B oblicza się wzorem:

$P(A|B) = \frac{P(B|A) P(A)}{P(B)}$

Wykorzystanie twierdzenia Bayesa w naiwnej klasyfikacji bayesowskiej

1) Nawiązując do twierdzenia bayesa prawdopodobieństwo problem klasyfikacji danego obiektu możemy zapisać w następujący sposb:


$P(K|X)$ Zapis ten oznacza że mając obiekt X chcemy obliczyć prawdopodobieństo przynaleźności do klasy K

$P(K|X) = \frac{P(X|K) P(K)}{P(X)}$

$P(K)$ - jest to prawdopodobieństwo a-priori klasy K

$P(X|K)$ - ten zapis możemy interpretować w taki sposób: jeżeli klasa K to szansa że X do niej należy

$P(X)$ - ale jak interpretować prawdopodobieństwo od odibkeu X? okazuje się że nie trzeba tego obliczać gdyż obliczając prawpodobieństwa przynależności X do klasy K w mianowniku zawsze bęzie $P(X)$ więc możemy to pomijać

Najlepiej będzie pokazać to na przykładzie:



2) Prawdopobobieństwa a-priori


Prawdopodobieństwo a priori jest to prawdopodobieństwo obliczane przed realizacją odpowiednich eksperymentów lub obserwacji.

klasy

Powyższa ilustracja przedstawia 2 klasy czerwona(20 obiektów) i zielona(40 obiektów), prawdopodobieństwa a priori tym przykładzie to nic innego jak

$\frac{liczebność\ klasy}{liczebność\ wszystkich\ elementów}$, więc prawdopodobieńswta a priori dla powyższego przykładu są równe

a priori klasy zielonej = $\frac{2}{3}\\$ a priori klasy czerwonej = $\frac{1}{3}\\$

To jest nasze $P(K)$



3) Klasyfikacja nowego obiektu


nowy obiekt

Klasyfikując nowy obiekt korzystamy z prawdopodobieństw a-priori, jednak same prawdopodobieństwa a-priori są niweystarczające, w tym przykładzie rozsądnym założeniem jest klasyfikowanie obiektu również na podstawie jego najbliższych sąsiadów. Zaznaczając okręgiem obszar możemy obliczyć prawdopodobieństwo że nowy obiekt będzie czerwony albo zielony, prawdopodobieństwa te obliczane są ze wzoru, prawdopodobieństwa te zostaną użyte do obliczenia prawdopodobieństwa a posteriori.

$\frac{liczba\ obiektów\ danej\ klasy\ w\ sąsiedztwie\ nowego\ obiektu}{liczba\ obiektów\ danej\ klasy}$ i wynoszą odpowiednio:

prawdopodobieństow że obiekt będzie w klasie zielonej = $\frac{1}{40}\\$ prawdopodobieństow że obiekt będzie w klasie czerwonej = $\frac{3}{20}\\$

To jets nasze $P(X|K)$



4) Prawdopodobieństwo a posteriori


Mając już obliczone wszystkie potrzebne prawdopodobieństwa możemy obliczyć prawdopodobieństwa a posteriori

Prawdopodobieństwo a posteriori jest to prawdopodobieństwo pewnego zdarzenia gdy wiedza o tym zdarzeniu wzbogacona jest przez pewne obserwacje lub eksperymenty.

W naszym przykładzie prawdopodobieństwo a posteriori obliczymy ze wzoru

$(prawdopodobieństwo\ a\ priori\ przynależności\ do\ danej\ klasy) * (prawdopodobieństwo\ że\ nowy\ obiekt\ będzie\ w\ danej\ klasie\ na\ podstawie\ jego\ sąsiadów)$ czyli $P(X|K) P(K)$

A więc prawdopodobieństwa a posteriori są równe:

Prawdopodobieństwo a posteriori ze nowy obiekt będzie w klasie zielonej = $\frac{2}{3} * \frac{1}{40} = \frac{1}{60}\\$ Prawdopodobieństwo a posteriori ze nowy obiekt będzie w klasie czerwonej = $\frac{1}{3} * \frac{3}{40} = \frac{1}{40}\\$

Naiwna klasyfikacja bayesowska przy wielu cechach


Klasyfikator bayesowski możemy stosować na wielu cechach, wykonuje się to w następujący sposób:

W naszym przypadku obiekt X posiada wiele cech $X = (x1, x2, x3, ... xn)$ wtedy stosując znany już wzór:

$P(K|X) = \frac{P(X|K) P(K)}{P(X)}$

$P(K)$ pozostaje niezmienne jest to $\frac{liczba\ elementów\ klasy\ K}{wszystkie\ elementy\ zbioru}$

Element X posiada wiele cech więc:

$P(X|K) = P(x1|K)_P(x2|K)...*P(xn|K)$

Zauażmy że w rzeczywistości $P(X|K)$ obliczymy z twierdzenia bayesa, jednak dzięki temu że nasz klasyfikator jest naiwny zakładamy niezależność cech $x1, x2,...,xn$ więc możemy uprościć obliczenia do powyższego wzoru


$P(xk|K) = \frac{Liczba\ elementów\ klasy\ K\ dla\ których\ wartość\ cechy\ Ak\ jest\ równa\ xk}{liczba\ wszystkich\ obiektów\ klasy\ K}$



Powyższy wzór ma jedna pewne problemy z zerowymi prawdopodobieństwami


Co w przypadku gdy dla którejś cechy $P(xk|K) = 0$

Może się tak zdarzyć gdy cecha $Ak$ nie będzie przyjmowała wartości $xk$ danego obiektu $X$ wtedy zgodnie ze wzorem otrzymamy $\frac{0}{liczba\ wszystkich\ obiektów\ klasy\ K}$ Gdy tak się stanie obliczone prawdopodobieństwo będzie równe 0, a przecież brak wartości xk dla cechy Ak klasy K nie musi wcale oznaczać śe dany obiekt nie może należeć do klasy K. Aby temu zaradzić stosuje się wygładzanie


Wygładzanie Laplace'a


Wygładzanie Laplace'a zwane jest również wygładzaniem + 1, jest to bardzo prosty sposób wystarczy dla każdego $P(xk|K)$ dodać do licznika 1 a do mianownika dodać liczbę klas obiektu, dodając 1 do licznika możemy to interpretować jako dodanie nowego obiektu do klasy, robiąc to dla każdej klasy "mamy" dodatkowe obiekty równe liczbie klas dlatego do mianownika musimy dodać liczbę klas.

$P(xk|K) = \frac{(Liczba\ elementów\ klasy\ K\ dla\ których\ wartość\ cechy\ Ak\ jest\ równa\ xk\\)\ + 1}{(liczba\ wszystkich\ obiektów\ klasy\ K\\) + liczba klas}$

Łatwo można zauważyć że samo dodanie 1 do licznika nie jest wystarczające ponieważ wtedy $P(xk|K)$ mogłoby być $> 1$


Dzięki wygładzaniu Laplace'a $P(X|K)$ nigdy nie bęzie zerowe minimalna wartość to

$\frac{1}{n *(liczba\ wszystkich\ obiektów\ klasy\ K\\) + liczba\ klas}$


Implementacja

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
#przygotowanie prawdopodobienstw wartosci danych cech w zaleznosci od klasy
def getDictOfAttribProbs(clas,className,attribsNames,data):
    dictionaries = {}
    for value in clas:
        classFreq = {}
        for i in range(len(attribsNames)):
            classData = data[data[className] == value]
            freq = {}
            attribData = classData[attribsNames[i]]
            for attrib in attribData:
                count = freq.get(attrib,1) + 1
                freq[attrib] = count
            freq = {k : v / len(classData) for k,v in freq.items()}
            classFreq[attribsNames[i]] = freq
        dictionaries[value] = classFreq
    return dictionaries
#a priori dla klas
def classProb(clas,data):
    x = len(data[data['edible']==clas]['edible'])
    y = len(data['edible'])
    return x/y
#prawdopodobienstwo dla wartosic danej cechy w zaelznosci od klasy
def getAttribProbs(attrib,value,data,clas,dictProbs):
    return dictProbs[clas][attrib].get(value,1.0/len(data))
#a posteriori dla danego obiektu 
def getPosteriori(attribs,attribsNames,clas,dictProbs):
    dic = {}
    for i in range(len(attribs)):
        dic[attribsNames[i]] = attribs[i]
    sum = 1.0
    for key in dic:
        sum = sum * getAttribProbs(key,dic[key],X_train,clas,dictProbs)
    return sum * classProb(clas,X_train)
#predykcja dla danych
def predict(data, clas,model):
    attribNames = data.columns
    predictions = []
    for i in range(len(data)):
        probs = {}
        for name in clas:
            probs[name] = getPosteriori(list(data.iloc[i]),list(attribNames),name,model)
        keyMax = max(zip(probs.values(),probs.keys()))[1]
        predictions.append(keyMax)
    return predictions
features=[
    'edible',
    'cap-shape',
    'cap-surface',
    'cap-color',
    'bruises',
    'odor',
    'gill-attachment',
    'gill-spacing',
    'gill-size',
    'gill-color',
    'stalk-shape',
    'stalk-root',
    'stalk-surface-above-ring',
    'stalk-surface-below-ring',
    'stalk-color-above-ring',
    'stalk-color-below-ring',
    'veil-type','veil-color',
    'ring-number','ring-type',
    'spore-print-color',
    'population',
    'habitat'
]

mushrooms = pd.read_csv('mushrooms.tsv', sep='\t',names=features)
X_train, X_test = train_test_split(mushrooms, test_size=0.2, random_state=0,stratify=mushrooms['edible'])
columns = ['odor','cap-color','gill-spacing','stalk-surface-above-ring','habitat']
className = 'edible'
classValue = list(set(X_train['edible']))
model = getDictOfAttribProbs(classValue,className,columns,X_train)
X_test_data = X_test[columns]
X_test_results = X_test[className]
pred = predict(X_test_data,classValue,model)
print(accuracy_score(list(X_test_results),pred))
0.9889807162534435