Gaussian Naive Bayes, spiegato

Gaussian Naive Bayes, spiegato

Nodo di origine: 2021431

Gaussian Naive Bayes, spiegato
La regione di decisione di un classificatore di Bayes ingenuo gaussiano. Immagine dell'Autore.

 

Penso che questo sia un classico all'inizio di ogni carriera nel campo della scienza dei dati: il Classificatore Naive Bayes. O dovrei piuttosto dire il famiglia di ingenui classificatori Bayes, poiché sono disponibili in molti gusti. Ad esempio, esiste un Bayes ingenuo multinomiale, un Bayes ingenuo di Bernoulli e anche un classificatore Bayes ingenuo gaussiano, ognuno diverso solo in un piccolo dettaglio, come scopriremo. Gli ingenui algoritmi di Bayes sono abbastanza semplici nel design ma si sono rivelati utili in molte complesse situazioni del mondo reale.

In questo articolo puoi imparare

  • come funzionano gli ingenui classificatori di Bayes,
  • perché ha senso definirli così come sono e
  • come implementarli in Python usando NumPy.

Puoi trovare il codice su il mio GitHub.

Potrebbe aiutare un po 'controllare il mio primer sulle statistiche bayesiane Una gentile introduzione all'inferenza bayesiana abituarsi alla formula di Bayes. Dato che implementeremo il classificatore in modo conforme all'apprendimento scikit, vale anche la pena dare un'occhiata al mio articolo Costruisci la tua regressione scikit-learn personalizzata. Tuttavia, l'overhead di scikit-learn è piuttosto ridotto e dovresti essere in grado di seguirlo comunque.

Inizieremo ad esplorare la teoria sorprendentemente semplice dell'ingenua classificazione di Bayes e poi passeremo all'implementazione.

Cosa ci interessa veramente quando classifichiamo? Cosa stiamo effettivamente facendo, qual è l'input e l'output? La risposta è semplice:

Dato un punto dati x, qual è la probabilità che x appartenga a qualche classe c?

Questo è tutto ciò con cui vogliamo rispondere in qualsiasi classificazione. Puoi modellare direttamente questa affermazione come probabilità condizionata: p(c|x).

Ad esempio, se ci sono

  • classi 3 c₁c₂c₃e
  • consiste di 2 caratteristiche x₁x₂,

il risultato di un classificatore potrebbe essere qualcosa di simile p(c₁|x₁x₂) = 0.3, p(c₂|x₁x₂)=0.5 e p(c₃|x₁x₂)=0.2. Se ci prendiamo cura di una singola etichetta come output, sceglieremmo quella con la probabilità più alta, ad es c₂ con una probabilità del 50% qui.

L'ingenuo classificatore di Bayes cerca di calcolare direttamente queste probabilità.

Ingenuo Bayes

Ok, quindi dato un punto dati x, vogliamo calcolare p(c|x) per tutte le classi e quindi emettere il file c con la massima probabilità. Nelle formule lo vedi spesso come

 

Gaussian Naive Bayes, spiegato
Immagine dell'Autore.

 

Nota: max p(c|x) restituisce la probabilità massima mentre argmax p(c|x) restituisce il c con questa massima probabilità.

Ma prima di poter ottimizzare p(c|x), dobbiamo essere in grado di calcolarlo. Per questo usiamo Teorema di Bayes:

 

Gaussian Naive Bayes, spiegato
Teorema di Bayes. Immagine dell'Autore.

 

Questa è la parte bayesiana dell'ingenuo Bayes. Ma ora, abbiamo il seguente problema: Cosa sono p(x|c) e p(c)?

Questo è lo scopo dell'addestramento di un ingenuo classificatore di Bayes.

L'allenamento

Per illustrare tutto, usiamo un set di dati giocattolo con due caratteristiche reali x₁x₂tre classi c₁c₂c₃ nel seguito.

 

Gaussian Naive Bayes, spiegato
I dati, visualizzati. Immagine dell'Autore.

 

È possibile creare questo set di dati esatto tramite

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

Cominciamo con il probabilità di classe p(c), la probabilità che qualche class c è osservato nel set di dati etichettato. Il modo più semplice per stimare questo è semplicemente calcolare le frequenze relative delle classi e usarle come probabilità. Possiamo usare il nostro set di dati per vedere cosa significa esattamente.

Ci sono 7 punti su 20 etichettati come classe c₁ (blu) nel set di dati, quindi diciamo p(c₁)=7/20. Abbiamo 7 punti per classe c₂ (rosso), quindi impostiamo p(c₂)=7/20. L'ultima classe c₃ (giallo) ha solo 6 punti, quindi p(c₃)=6/20.

Questo semplice calcolo delle probabilità di classe assomiglia a un approccio di massima verosimiglianza. Puoi, tuttavia, usarne anche un altro precedente distribuzione, se vuoi. Ad esempio, se sai che questo set di dati non è rappresentativo della popolazione reale perché class c₃ dovrebbe apparire nel 50% dei casi, quindi si imposta p(c₁) = 0.25, p(c₂)=0.25 e p(c₃)=0.5. Qualunque cosa ti aiuti a migliorare le prestazioni sul set di test.

Passiamo ora al probabilità p(x|c)=p(x₁x₂|c). Un approccio per calcolare questa probabilità consiste nel filtrare il set di dati per campioni con etichetta e quindi provare a trovare una distribuzione (ad esempio una gaussiana bidimensionale) che catturi le caratteristiche x₁x₂.

Sfortunatamente, di solito non abbiamo abbastanza campioni per classe per fare una corretta stima della probabilità.

Per essere in grado di costruire un modello più robusto, realizziamo il file supposizione ingenua che le caratteristiche x₁x₂ sono stocasticamente indipendente, dato c. Questo è solo un modo elegante per semplificare la matematica tramite

 

Gaussian Naive Bayes, spiegato
Immagine dell'Autore

 

per ogni classe c. Qui è dove il file ingenuo parte dell'ingenuo Bayes deriva dal fatto che questa equazione non vale in generale. Tuttavia, anche allora l'ingenuo Bayes produce risultati buoni, a volte eccezionali, nella pratica. Soprattutto per i problemi di PNL con le caratteristiche del sacco di parole, l'ingenuo multinomiale Bayes brilla.

Gli argomenti sopra riportati sono gli stessi per qualsiasi classificatore Bayes ingenuo che puoi trovare. Ora dipende solo da come modelli p(x₁|c₁), p(x₂|c₁), p(x₁|c₂), p(x₂|c₂), p(x₁|c₃) ed p(x₂|c₃).

Se le tue caratteristiche sono solo 0 e 1, potresti usare a Distribuzione Bernoulli. Se sono numeri interi, a Distribuzione multinomiale. Tuttavia, abbiamo valori di funzionalità reali e decidiamo per a Gaussiana distribuzione, da cui il nome Gaussian naive Bayes. Assumiamo la seguente forma

 

Gaussian Naive Bayes, spiegato
Immagine dell'Autore.

 

where μᵢ,ⱼ è la media e σᵢ,ⱼ è la deviazione standard che dobbiamo stimare dai dati. Ciò significa che otteniamo una media per ogni caratteristica i accoppiato con una classe cⱼ, nel nostro caso 2*3=6 significa. Lo stesso vale per le deviazioni standard. Questo richiede un esempio.

Proviamo a stimare µ₂,₁ ed σ₂,₁. Perché j=1, siamo interessati solo alla classe c₁, conserviamo solo campioni con questa etichetta. Rimangono i seguenti campioni:

# 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]])

Ora, a causa di i=2 dobbiamo solo considerare la seconda colonna. µ₂,₁ è la media e σ₂,₁ la deviazione standard per questa colonna, ad es µ₂,₁ = 0.49985176 e σ₂,₁ = 0.9789976.

Questi numeri hanno senso se guardi di nuovo il grafico a dispersione dall'alto. Le caratteristiche x₂ dei campioni della classe c₁ sono circa 0.5, come puoi vedere dalla foto.

Calcoliamo questo ora per le altre cinque combinazioni e abbiamo finito!

In Python, puoi farlo in questo modo:

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)])

Riceviamo

# 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]])

Questo è il risultato dell'addestramento di un classificatore Bayes ingenuo gaussiano.

Fare previsioni

La formula di previsione completa è

 

Gaussian Naive Bayes, spiegato
Immagine dell'Autore.

 

Supponiamo un nuovo punto dati x*=(-2, 5) entra.

 

Gaussian Naive Bayes, spiegato
Immagine dell'Autore.

 

Per vedere a quale classe appartiene, calcoliamo p(c|x*) per tutte le classi. Dalla foto, dovrebbe appartenere alla classe c₃ = 2, ma vediamo. Ignoriamo il denominatore p(x) per un secondo. Usando il ciclo seguente sono stati calcolati i nominatori per 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}" )

Riceviamo

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

Certo, questi probabilità (non dovremmo chiamarli così) non sommarli a uno poiché abbiamo ignorato il denominatore. Tuttavia, questo non è un problema poiché possiamo semplicemente prendere queste probabilità non normalizzate e dividerle per la loro somma, quindi la loro somma darà uno. Quindi, dividendo questi tre valori per la loro somma di circa 0.00032569, otteniamo

 

Gaussian Naive Bayes, spiegato
Immagine dell'Autore.

 

Un chiaro vincitore, come ci aspettavamo. Ora, implementiamolo!

Questa implementazione è di gran lunga non efficiente, non numericamente stabile, ha solo uno scopo educativo. Abbiamo discusso la maggior parte delle cose, quindi dovrebbe essere facile seguirle ora. Puoi ignorare tutto il check funzioni, o leggi il mio articolo Costruisci il tuo scikit-learn personalizzato se sei interessato a cosa fanno esattamente.

Basta notare che ho implementato a predict_proba metodo per calcolare le probabilità. Il metodo predict chiama semplicemente questo metodo e restituisce l'indice (=classe) con la massima probabilità utilizzando una funzione argmax (eccolo di nuovo!). La classe attende le lezioni dalle 0 alle k-1, dove k è il numero di classi.

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)

Testare l'implementazione

Sebbene il codice sia piuttosto breve, è ancora troppo lungo per essere completamente sicuri di non aver commesso errori. Quindi, controlliamo come va contro il scikit-learn Classificatore GaussianNB.

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

uscite

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]])

Le previsioni utilizzando il predict metodo sono

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

Ora usiamo scikit-learn. Inserimento di un codice

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

i rendimenti

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]])

I numeri sembrano un po' simili a quelli del nostro classificatore, ma sono un po' fuori posto nelle ultime cifre visualizzate. Abbiamo fatto qualcosa di sbagliato? No. La versione scikit-learn utilizza semplicemente un altro iperparametro var_smoothing=1e-09 . Se impostiamo questo su zero, otteniamo esattamente i nostri numeri. Perfetto!

Dai un'occhiata alle regioni decisionali del nostro classificatore. Ho anche segnato i tre punti che abbiamo usato per il test. Quel punto vicino al confine ha solo il 56.9% di possibilità di appartenere alla classe rossa, come puoi vedere dal predict_proba uscite. Gli altri due punti sono classificati con una confidenza molto maggiore.

 

Gaussian Naive Bayes, spiegato

La decisione regioni con i 3 nuovi punti. Immagine dell'Autore.

 

In questo articolo, abbiamo appreso come funziona il classificatore Bayes ingenuo gaussiano e abbiamo dato un'intuizione sul motivo per cui è stato progettato in questo modo: è un approccio diretto per modellare la probabilità di interesse. Confronta questo con la regressione logistica: lì, la probabilità è modellata usando una funzione lineare con una funzione sigmoidea applicata sopra di essa. È ancora un modello facile, ma non sembra naturale come un ingenuo classificatore di Bayes.

Abbiamo continuato calcolando alcuni esempi e raccogliendo alcuni utili pezzi di codice lungo il percorso. Infine, abbiamo implementato un classificatore Bayes ingenuo gaussiano completo in un modo che funziona bene con scikit-learn. Ciò significa che puoi usarlo nelle pipeline o nella ricerca sulla griglia, ad esempio.

Alla fine, abbiamo fatto un piccolo controllo di integrità importando scikit-learns proprio classificatore Bayes naive gaussiano e verificando se entrambi, il nostro classificatore e quello di scikit-learn producono lo stesso risultato. Questo test ha avuto successo.

 
 
Dr. Robert Kubler è un Data Scientist presso Publicis Media e Autore presso Towards Data Science.

 
Originale. Ripubblicato con il permesso.
 

Timestamp:

Di più da KDnuggets