Gaussowski naiwny Bayes, wyjaśniony

Gaussowski naiwny Bayes, wyjaśniony

Węzeł źródłowy: 2021431

Gaussowski naiwny Bayes, wyjaśniony
Region decyzyjny gaussowskiego naiwnego klasyfikatora Bayesa. Obraz Autora.

 

Myślę, że jest to klasyk na początku każdej kariery w data science: the Naiwny klasyfikator Bayesa. A raczej powinienem powiedzieć członków Twojej rodziny naiwnych klasyfikatorów Bayesa, ponieważ występują one w wielu odmianach. Na przykład, istnieje wielomianowy naiwny klasyfikator Bayesa, naiwny Bayes Bernoulliego, a także Gaussowski naiwny klasyfikator Bayesa, z których każdy różni się tylko jednym małym szczegółem, jak się przekonamy. Naiwne algorytmy Bayesa są dość proste w konstrukcji, ale okazały się przydatne w wielu złożonych sytuacjach w świecie rzeczywistym.

Z tego artykułu dowiesz się

  • jak działają naiwne klasyfikatory Bayesa,
  • dlaczego ma sens definiowanie ich takimi, jakimi są i
  • jak je zaimplementować w Pythonie za pomocą NumPy.

Kod znajdziesz na mój Github.

Może trochę pomóc sprawdzenie mojego podkładu na temat statystyki bayesowskiej Delikatne wprowadzenie do wnioskowania bayesowskiego przyzwyczaić się do wzoru Bayesa. Ponieważ zaimplementujemy klasyfikator w sposób zgodny z nauką scikit, warto również zapoznać się z moim artykułem Zbuduj własną niestandardową regresję scikit-learn. Jednak narzut związany z uczeniem się scikit jest dość mały i i tak powinieneś być w stanie nadążyć.

Zaczniemy od zgłębiania zdumiewająco prostej teorii naiwnej klasyfikacji Bayesa, a następnie przejdziemy do jej implementacji.

Co nas tak naprawdę interesuje podczas klasyfikowania? Co właściwie robimy, jakie jest wejście i wyjście? Odpowiedź jest prosta:

Biorąc pod uwagę punkt danych x, jakie jest prawdopodobieństwo, że x należy do jakiejś klasy c?

To wszystko, na co chcemy odpowiedzieć każdy Klasyfikacja. Możesz bezpośrednio modelować to stwierdzenie jako prawdopodobieństwo warunkowe: p(c|x).

Na przykład, jeśli istnieją

  • klasy 3 c₁c₂c₃,
  • składa się z 2 funkcji x₁x₂,

wynikiem klasyfikatora może być np p(c₁|x₁x₂) = 0.3, p(c₂|x₁x₂)=0.5 i p(c₃|x₁x₂)=0.2. Jeśli zależy nam na jednej etykiecie jako wyjściu, wybralibyśmy tę z największym prawdopodobieństwem, tj c₂ tutaj z prawdopodobieństwem 50%.

Naiwny klasyfikator Bayesa próbuje bezpośrednio obliczyć te prawdopodobieństwa.

Naiwny Bayes

Ok, więc biorąc pod uwagę punkt danych x, chcemy obliczyć p(c|x) dla wszystkich klas a następnie wyprowadź c z największym prawdopodobieństwem. W formułach często widzisz to jako

 

Gaussowski naiwny Bayes, wyjaśniony
Obraz Autora.

 

Uwaga: max p(c|x) zwraca maksymalne prawdopodobieństwo, podczas gdy argmax p(c|x) zwraca c z tym największym prawdopodobieństwem.

Ale zanim będziemy mogli zoptymalizować p(c|x), musimy umieć to obliczyć. W tym celu używamy Twierdzenie Bayesa:

 

Gaussowski naiwny Bayes, wyjaśniony
Twierdzenie Bayesa. Obraz Autora.

 

To jest Bayesowska część naiwnego Bayesa. Ale teraz mamy następujący problem: Czym są p(x|c) i p(c)?

Na tym właśnie polega szkolenie naiwnego klasyfikatora Bayesa.

Trening

Aby wszystko zilustrować, użyjmy zabawkowego zestawu danych z dwie prawdziwe cechy x₁x₂trzy klasy c₁c₂c₃ w dalszej części.

 

Gaussowski naiwny Bayes, wyjaśniony
Dane, zwizualizowane. Obraz Autora.

 

Możesz utworzyć dokładnie ten zestaw danych za pomocą

from sklearn.datasets import make_blobs X, y = make_blobs(n_samples=20, centers=[(0,0), (5,5), (-5, 5)], random_state=0)

Zacznijmy od prawdopodobieństwo klasy p(c), prawdopodobieństwo, że jakaś klasa c obserwuje się w oznaczonym zbiorze danych. Najprostszym sposobem oszacowania tego jest po prostu obliczenie względnych częstości klas i użycie ich jako prawdopodobieństw. Możemy użyć naszego zbioru danych, aby zobaczyć, co to dokładnie oznacza.

Istnieje 7 z 20 punktów oznaczonych klasą c₁ (niebieski) w zbiorze danych, dlatego mówimy p(c₁)=7/20. Mamy 7 punktów za klasę c₂ (czerwony) również, dlatego ustawiamy p(c₂)=7/20. Ostatnia klasa c₃ (żółty) ma więc tylko 6 punktów p(c₃)=6/20.

To proste obliczenie prawdopodobieństw klasowych przypomina podejście największego prawdopodobieństwa. Możesz jednak użyć innego wcześniejszy dystrybucja, jeśli chcesz. Na przykład, jeśli wiesz, że ten zestaw danych nie jest reprezentatywny dla prawdziwej populacji, ponieważ klasa c₃ powinien pojawić się w 50% przypadków, to ustawiasz p(c₁) = 0.25, p(c₂)=0.25 i p(c₃)=0.5. Cokolwiek pomaga poprawić wydajność zestawu testowego.

Teraz zwracamy się do prawdopodobieństwo p(x|c)=p(x₁x₂|c). Jednym ze sposobów obliczania tego prawdopodobieństwa jest filtrowanie zestawu danych pod kątem próbek z etykietą a następnie spróbuj znaleźć rozkład (np. 2-wymiarowy Gaussa), który uchwyci cechy x₁x₂.

Niestety, zwykle nie mamy wystarczającej liczby próbek na klasę, aby właściwie oszacować prawdopodobieństwo.

Aby móc zbudować solidniejszy model, tworzymy naiwne założenie że cechy x₁x₂ jest stochastycznie niezależny, dany c. To tylko fantazyjny sposób na ułatwienie matematyki

 

Gaussowski naiwny Bayes, wyjaśniony
Obraz autora

 

dla każdej klasy c. To jest, gdzie naiwny część naiwnego Bayesa pochodzi z tego, że to równanie nie jest ogólnie spełnione. Jednak nawet wtedy naiwny Bayes daje w praktyce dobre, czasem wybitne rezultaty. Szczególnie w przypadku problemów NLP z cechami worka słów, wielomianowy naiwny Bayes błyszczy.

Argumenty podane powyżej są takie same dla każdego naiwnego klasyfikatora Bayesa, jaki można znaleźć. Teraz wszystko zależy od tego, jak modelujesz p(x₁|c₁), p(x₂|c₁), p(x₁|c₂), p(x₂|c₂), p(x₁|c₃)p(x₂|c₃).

Jeśli twoje funkcje to tylko 0 i 1, możesz użyć a Rozkład Bernoulliego. Jeśli są liczbami całkowitymi, a Rozkład wielomianowy. Mamy jednak rzeczywiste wartości cech i decydujemy się na a Gaussian dystrybucja, stąd nazwa Gaussa naiwnego Bayesa. Przyjmujemy następującą postać

 

Gaussowski naiwny Bayes, wyjaśniony
Obraz Autora.

 

gdzie μᵢ,ⱼ jest średnią i σᵢ,ⱼ jest odchyleniem standardowym, które musimy oszacować na podstawie danych. Oznacza to, że otrzymujemy jedną średnią dla każdej cechy i połączone z klasą cⱼ, w naszym przypadku 2*3=6 oznacza. To samo dotyczy odchyleń standardowych. To wymaga przykładu.

Spróbujmy oszacować µ₂,₁σ₂,₁, Bo j=1, interesuje nas tylko klasa c₁, trzymajmy tylko próbki z tą etykietą. Pozostały następujące próbki:

# samples with label = c_1 array([[ 0.14404357, 1.45427351], [ 0.97873798, 2.2408932 ], [ 1.86755799, -0.97727788], [ 1.76405235, 0.40015721], [ 0.76103773, 0.12167502], [-0.10321885, 0.4105985 ], [ 0.95008842, -0.15135721]])

Teraz z powodu i=2 musimy wziąć pod uwagę tylko drugą kolumnę. μ₂,₁ jest średnią i σ₂,₁ odchylenie standardowe dla tej kolumny, tj μ₂,₁ = 0.49985176 i σ₂,₁ = 0.9789976.

Liczby te mają sens, jeśli ponownie spojrzysz na wykres punktowy z góry. Cechy x₂ próbek z klasy c₁ wynoszą około 0.5, jak widać na obrazku.

Obliczamy to teraz dla pozostałych pięciu kombinacji i gotowe!

W Pythonie możesz to zrobić w ten sposób:

from sklearn.datasets import make_blobs
import numpy as np # Create the data. The classes are c_1=0, c_2=1 and c_3=2.
X, y = make_blobs( n_samples=20, centers=[(0, 0), (5, 5), (-5, 5)], random_state=0
) # The class probabilities.
# np.bincounts counts the occurence of each label.
prior = np.bincount(y) / len(y) # np.where(y==i) returns all indices where the y==i.
# This is the filtering step.
means = np.array([X[np.where(y == i)].mean(axis=0) for i in range(3)])
stds = np.array([X[np.where(y == i)].std(axis=0) for i in range(3)])

Otrzymujemy

# priors
array([0.35, 0.35, 0.3 ])
# means array([[ 0.90889988, 0.49985176], [ 5.4111385 , 4.6491892 ], [-4.7841679 , 5.15385848]])
# stds
array([[0.6853714 , 0.9789976 ], [1.40218915, 0.67078568], [0.88192625, 1.12879666]])

Jest to wynik treningu gaussowskiego naiwnego klasyfikatora Bayesa.

Dokonywanie prognoz

Pełna formuła przewidywania to

 

Gaussowski naiwny Bayes, wyjaśniony
Obraz Autora.

 

Załóżmy nowy punkt danych x*=(-2, 5) wchodzi.

 

Gaussowski naiwny Bayes, wyjaśniony
Obraz Autora.

 

Aby zobaczyć, do której klasy należy, wykonajmy obliczenia p(c|x*) dla wszystkich klas. Ze zdjęcia powinien należeć do klasy c₃ = 2, ale zobaczmy. Pomińmy mianownik p(x) na chwilę. Korzystając z następującej pętli, obliczono nominatory dla j = 1, 2, 3.

x_new = np.array([-2, 5]) for j in range(3): print( f"Probability for class {j}: {(1/np.sqrt(2*np.pi*stds[j]**2)*np.exp(-0.5*((x_new-means[j])/stds[j])**2)).prod()*p[j]:.12f}" )

Otrzymujemy

Probability for class 0: 0.000000000263
Probability for class 1: 0.000000044359
Probability for class 2: 0.000325643718

Oczywiście te prawdopodobieństwa (nie powinniśmy ich tak nazywać) nie sumują się do jednego, ponieważ zignorowaliśmy mianownik. Nie stanowi to jednak problemu, ponieważ możemy po prostu wziąć te nieznormalizowane prawdopodobieństwa i podzielić je przez ich sumę, wtedy sumują się one do jednego. Tak więc, dzieląc te trzy wartości przez ich sumę około 0.00032569, otrzymujemy

 

Gaussowski naiwny Bayes, wyjaśniony
Obraz Autora.

 

Wyraźny zwycięzca, tak jak się spodziewaliśmy. A teraz zaimplementujmy to!

Ta implementacja jest zdecydowanie nie wydajna, nie stabilna liczbowo, służy jedynie celom edukacyjnym. Omówiliśmy większość rzeczy, więc teraz powinno być łatwo śledzić. Możesz zignorować wszystkie check funkcje lub przeczytaj mój artykuł Zbuduj własną niestandardową naukę scikit jeśli jesteś zainteresowany tym, co dokładnie robią.

Zauważ tylko, że zaimplementowałem a predict_proba pierwsza metoda obliczania prawdopodobieństw. Metoda predict po prostu wywołuje tę metodę i zwraca indeks (=klasa) z najwyższym prawdopodobieństwem za pomocą funkcji argmax (znowu!). Klasa oczekuje na zajęcia od 0 do k-1, gdzie k to liczba klas

import numpy as np
from sklearn.base import BaseEstimator, ClassifierMixin
from sklearn.utils.validation import check_X_y, check_array, check_is_fitted class GaussianNaiveBayesClassifier(BaseEstimator, ClassifierMixin): def fit(self, X, y): X, y = check_X_y(X, y) self.priors_ = np.bincount(y) / len(y) self.n_classes_ = np.max(y) + 1 self.means_ = np.array( [X[np.where(y == i)].mean(axis=0) for i in range(self.n_classes_)] ) self.stds_ = np.array( [X[np.where(y == i)].std(axis=0) for i in range(self.n_classes_)] ) return self def predict_proba(self, X): check_is_fitted(self) X = check_array(X) res = [] for i in range(len(X)): probas = [] for j in range(self.n_classes_): probas.append( ( 1 / np.sqrt(2 * np.pi * self.stds_[j] ** 2) * np.exp(-0.5 * ((X[i] - self.means_[j]) / self.stds_[j]) ** 2) ).prod() * self.priors_[j] ) probas = np.array(probas) res.append(probas / probas.sum()) return np.array(res) def predict(self, X): check_is_fitted(self) X = check_array(X) res = self.predict_proba(X) return res.argmax(axis=1)

Testowanie implementacji

Chociaż kod jest dość krótki, to wciąż jest zbyt długi, aby mieć całkowitą pewność, że nie popełniliśmy żadnych błędów. Sprawdźmy zatem, jak wypada w porównaniu z tzw scikit-learn klasyfikator GaussianNB.

my_gauss = GaussianNaiveBayesClassifier()
my_gauss.fit(X, y)
my_gauss.predict_proba([[-2, 5], [0,0], [6, -0.3]])

Wyjścia

array([[8.06313823e-07, 1.36201957e-04, 9.99862992e-01], [1.00000000e+00, 4.23258691e-14, 1.92051255e-11], [4.30879705e-01, 5.69120295e-01, 9.66618838e-27]])

Prognozy za pomocą predict metoda są

# my_gauss.predict([[-2, 5], [0,0], [6, -0.3]])
array([2, 0, 1])

Teraz użyjmy scikit-learn. Wrzucanie jakiegoś kodu

from sklearn.naive_bayes import GaussianNB gnb = GaussianNB()
gnb.fit(X, y)
gnb.predict_proba([[-2, 5], [0,0], [6, -0.3]])

plony

array([[8.06314158e-07, 1.36201959e-04, 9.99862992e-01], [1.00000000e+00, 4.23259111e-14, 1.92051343e-11], [4.30879698e-01, 5.69120302e-01, 9.66619630e-27]])

Liczby wyglądają trochę podobnie do tych z naszego klasyfikatora, ale są trochę przekłamane w kilku ostatnich wyświetlanych cyfrach. Czy zrobiliśmy coś złego? Nie. Wersja scikit-learn po prostu używa innego hiperparametru var_smoothing=1e-09 . Jeśli ustawimy ten na zero, otrzymujemy dokładnie nasze liczby. Doskonały!

Przyjrzyjmy się regionom decyzyjnym naszego klasyfikatora. Zaznaczyłem również trzy punkty, które wykorzystaliśmy do testów. Ten jeden punkt blisko granicy ma tylko 56.9% szans na przynależność do klasy czerwonej, jak widać z predict_proba wyjścia. Pozostałe dwa punkty są klasyfikowane ze znacznie większą pewnością.

 

Gaussowski naiwny Bayes, wyjaśniony

Regiony decyzyjne z 3 nowymi punktami. Obraz Autora.

 

W tym artykule dowiedzieliśmy się, jak działa gaussowski naiwny klasyfikator Bayesa i przedstawiliśmy intuicję, dlaczego został zaprojektowany w ten sposób — jest to bezpośrednie podejście do modelowania prawdopodobieństwa zainteresowania. Porównaj to z regresją logistyczną: tam prawdopodobieństwo jest modelowane za pomocą funkcji liniowej z nałożoną na nią funkcją sigmoidalną. To wciąż łatwy model, ale nie wydaje się tak naturalny jak naiwny klasyfikator Bayesa.

Kontynuowaliśmy, obliczając kilka przykładów i zbierając po drodze kilka przydatnych fragmentów kodu. Wreszcie, zaimplementowaliśmy kompletny Gaussowski naiwny klasyfikator Bayesa w sposób, który dobrze współpracuje z scikit-learn. Oznacza to, że możesz go używać na przykład w wyszukiwaniu potoków lub sieci.

Na koniec przeprowadziliśmy małą kontrolę poprawności, importując własny Gaussowski naiwny klasyfikator Bayesa i sprawdzając, czy zarówno nasz, jak i klasyfikator scikit-learn dają ten sam wynik. Ten test zakończył się sukcesem.

 
 
dr Robert Kübler jest analitykiem danych w Publicis Media i autorem w Towards Data Science.

 
Oryginalny. Przesłane za zgodą.
 

Znak czasu:

Więcej z Knuggety