Bayes naiv gaussian, explicat

Bayes naiv gaussian, explicat

Nodul sursă: 2021431

Bayes naiv gaussian, explicat
Regiunea de decizie a unui clasificator Bayes naiv gaussian. Imaginea Autorului.

 

Cred că acesta este un clasic la începutul fiecărei cariere în știința datelor: the Clasificator Naive Bayes. Sau mai degrabă ar trebui să spun familie de clasificatori Bayes naivi, deoarece vin în multe arome. De exemplu, există un Bayes naiv multinomial, un Bayes naiv Bernoulli și, de asemenea, un clasificator Bayes naiv gaussian, fiecare diferit doar într-un mic detaliu, după cum vom afla. Algoritmii naivi Bayes sunt destul de simpli în design, dar s-au dovedit utili în multe situații complexe din lumea reală.

În acest articol, puteți învăța

  • cum funcționează naivii clasificatori Bayes,
  • de ce are sens să le definim așa cum sunt și
  • cum să le implementați în Python folosind NumPy.

Puteți găsi codul pe Github-ul meu.

S-ar putea să vă ajute puțin să-mi verific manualul despre statisticile bayesiene O introducere blândă în inferența bayesiană pentru a se obișnui cu formula Bayes. Deoarece vom implementa clasificatorul într-un mod scikit learning-conform, merită, de asemenea, să consultați articolul meu Construiește-ți propria regresie personalizată scikit-learn. Cu toate acestea, costul general pentru scikit-learn este destul de mic și ar trebui să puteți urmări oricum.

Vom începe să explorăm teoria uimitor de simplă a clasificării naive Bayes și apoi vom trece la implementare.

Ce ne interesează cu adevărat când clasificăm? Ce facem de fapt, care este intrarea și ieșirea? Raspunsul este simplu:

Având în vedere un punct de date x, care este probabilitatea ca x să aparțină unei clase c?

Cu asta vrem să răspundem Orice clasificare. Puteți modela direct această afirmație ca o probabilitate condiționată: p(c|x).

De exemplu, dacă există

  • clase de 3 c₁c₂c₃, și
  • constă din 2 caracteristici x₁x₂,

rezultatul unui clasificator ar putea fi ceva de genul p(c₁|x₁x₂) = 0.3, p(c₂|x₁x₂)=0.5 și p(c₃|x₁x₂)=0.2. Dacă ne pasă de o singură etichetă ca rezultat, am alege-o pe cea cu cea mai mare probabilitate, adică c₂ cu o probabilitate de 50% aici.

Clasificatorul naiv Bayes încearcă să calculeze direct aceste probabilități.

Bayes naiv

Ok, deci dat un punct de date x, vrem să calculăm p(c|x) pentru toate clasele și apoi scoateți c cu cea mai mare probabilitate. În formule vezi adesea asta ca

 

Bayes naiv gaussian, explicat
Imaginea Autorului.

 

Notă: max p(c|x) returnează probabilitatea maximă în timp ce argmax p(c|x) returnează c cu această probabilitate cea mai mare.

Dar înainte să putem optimiza p(c|x), trebuie să putem să-l calculăm. Pentru aceasta, folosim Teorema lui Bayes:

 

Bayes naiv gaussian, explicat
teorema Bayes. Imaginea Autorului.

 

Aceasta este partea Bayes din Bayesul naiv. Dar acum, avem următoarea problemă: Ce sunt p(x|c) Şi p(c)?

Acesta este ceea ce înseamnă antrenamentul unui clasificator Bayes naiv.

Antrenamentul

Pentru a ilustra totul, să folosim un set de date de jucărie cu două trăsături reale x₁x₂, și trei clase c₁c₂c₃ în cele ce urmează.

 

Bayes naiv gaussian, explicat
Datele, vizualizate. Imaginea Autorului.

 

Puteți crea acest set de date exact prin

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

Să începem cu probabilitatea de clasă p(c), probabilitatea ca o anumită clasă c este observată în setul de date etichetat. Cel mai simplu mod de a estima acest lucru este doar să calculați frecvențele relative ale claselor și să le folosiți ca probabilități. Ne putem folosi setul de date pentru a vedea ce înseamnă asta exact.

Există 7 din 20 de puncte etichetate pentru clasă c₁ (albastru) în setul de date, prin urmare spunem p(c₁)=7/20. Avem 7 puncte pentru clasă c₂ (roșu), de asemenea, de aceea am stabilit p(c₂)=7/20. Ultima clasa c₃ (galben) are doar 6 puncte, prin urmare p(c₃)=6/20.

Acest calcul simplu al probabilităților de clasă seamănă cu o abordare de maximă probabilitate. Puteți, totuși, să utilizați și altul anterior distribuție, dacă doriți. De exemplu, dacă știți că acest set de date nu este reprezentativ pentru populația adevărată, deoarece clasă c₃ ar trebui să apară în 50% din cazuri, apoi setați p(c₁) = 0.25, p(c₂)=0.25 și p(c₃)=0.5. Orice vă ajută să îmbunătățiți performanța setului de testare.

Acum apelăm la probabilitate p(x|c)=p(x₁x₂|c). O abordare pentru a calcula această probabilitate este filtrarea setului de date pentru mostre cu etichetă și apoi încercați să găsiți o distribuție (de exemplu, un gaussian bidimensional) care surprinde caracteristicile x₁x₂.

Din păcate, de obicei, nu avem suficiente mostre pe clasă pentru a face o estimare adecvată a probabilității.

Pentru a putea construi un model mai robust, realizăm presupunere naivă că trăsăturile x₁x₂ sunt independent din punct de vedere stocastic, dat c. Acesta este doar un mod elegant de a face matematica mai ușoară prin intermediul

 

Bayes naiv gaussian, explicat
Imaginea Autorului

 

pentru fiecare clasa c. Acesta este locul unde naiv parte din Bayes naiv provine din cauză că această ecuație nu este valabilă în general. Totuși, chiar și atunci naivul Bayes dă rezultate bune, uneori remarcabile în practică. În special pentru problemele NLP cu caracteristici de tip „bag-of-words”, naivul multinomial Bayes strălucește.

Argumentele prezentate mai sus sunt aceleași pentru orice clasificator Bayes naiv pe care îl puteți găsi. Acum depinde doar de cum modelezi p(x₁|c₁), p(x₂|c₁), p(x₁|c₂), p(x₂|c₂), p(x₁|c₃) și p(x₂|c₃).

Dacă caracteristicile dvs. sunt numai 0 și 1, puteți utiliza a distribuția Bernoulli. Dacă sunt numere întregi, a Distribuție multinomială. Cu toate acestea, avem valori reale ale caracteristicilor și decidem pentru a Gaussian distribuție, de unde și numele gaussian naiv Bayes. Ne asumăm următoarea formă

 

Bayes naiv gaussian, explicat
Imaginea Autorului.

 

Unde μᵢ,ⱼ este media și σᵢ,ⱼ este abaterea standard pe care trebuie să o estimam din date. Aceasta înseamnă că obținem o medie pentru fiecare caracteristică i cuplat cu o clasă cⱼ, în cazul nostru 2*3=6 înseamnă. Același lucru este valabil și pentru abaterile standard. Acest lucru necesită un exemplu.

Sa incercam sa estimam μ₂,₁ și σ₂,₁. pentru că j=1, ne interesează doar clasă c₁, să păstrăm doar mostre cu această etichetă. Rămân următoarele mostre:

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

Acum, din cauza i=2 trebuie să luăm în considerare doar a doua coloană. μ₂,₁ este media și σ₂,₁ abaterea standard pentru această coloană, adică μ₂,₁ = 0.49985176 și σ₂,₁ = 0.9789976.

Aceste numere au sens dacă te uiți din nou la diagrama de dispersie de sus. Caracteristicile x₂ a mostrelor din clasa c₁ sunt în jur de 0.5, după cum puteți vedea din imagine.

Calculăm acest lucru acum pentru celelalte cinci combinații și am terminat!

În Python, puteți proceda astfel:

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

Noi primim

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

Acesta este rezultatul antrenamentului unui clasificator Bayes naiv gaussian.

Realizarea previziunilor

Formula de predicție completă este

 

Bayes naiv gaussian, explicat
Imaginea Autorului.

 

Să presupunem un nou punct de date x*=(-2, 5) intră.

 

Bayes naiv gaussian, explicat
Imaginea Autorului.

 

Pentru a vedea cărei clase îi aparține, să calculăm p(c|x*) pentru toate clasele. Din imagine, ar trebui să aparțină clasei c₃ = 2, dar să vedem. Să ignorăm numitorul p(x) Pentru o secundă. Folosind următoarea buclă, s-au calculat numitorii pentru 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}" )

Noi primim

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

Desigur, acestea probabilități (nu ar trebui să le numim așa) nu adăugați unul, deoarece am ignorat numitorul. Cu toate acestea, aceasta nu este o problemă, deoarece putem doar să luăm aceste probabilități nenormalizate și să le împărțim la suma lor, apoi se vor aduna până la unul. Deci, împărțind aceste trei valori la suma lor de aproximativ 0.00032569, obținem

 

Bayes naiv gaussian, explicat
Imaginea Autorului.

 

Un câștigător clar, așa cum ne așteptam. Acum, haideți să o implementăm!

Această implementare nu este de departe eficientă, nu este stabilă numeric, servește doar unui scop educațional. Am discutat majoritatea lucrurilor, așa că ar trebui să fie ușor de urmărit acum. Puteți ignora toate check funcții, sau citiți articolul meu Construiește-ți propriul scikit-learn personalizat dacă sunteți interesat de ceea ce fac ei exact.

Doar rețineți că am implementat un predict_proba metoda mai întâi de a calcula probabilitățile. Metoda predict apelează doar această metodă și returnează indexul (=clasa) cu cea mai mare probabilitate folosind o funcție argmax (iată din nou!). Clasa așteaptă cursuri de la 0 la k-1, unde k este numărul de clase.

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)

Testarea implementării

Deși codul este destul de scurt, este încă prea lung pentru a fi complet sigur că nu am făcut nicio greșeală. Deci, haideți să verificăm cum se comportă față de scikit-learn clasificator GaussianNB.

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

iesiri

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

Predicțiile folosind predict metoda sunt

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

Acum, să folosim scikit-learn. Introducerea unui cod

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

randamentele

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

Numerele arată oarecum asemănătoare cu cele ale clasificatorului nostru, dar sunt puțin îndepărtate în ultimele cifre afișate. Am făcut ceva greșit? Nu. Versiunea scikit-learn folosește doar un alt hiperparametru var_smoothing=1e-09 . Dacă îl setăm pe acesta zero, primim exact numerele noastre. Perfect!

Aruncă o privire la regiunile de decizie ale clasificatorului nostru. Am marcat și cele trei puncte pe care le-am folosit pentru testare. Acel punct aproape de graniță are șanse de doar 56.9% să aparțină clasei roșii, după cum puteți vedea din predict_proba iesiri. Celelalte două puncte sunt clasificate cu mult mai mare încredere.

 

Bayes naiv gaussian, explicat

Regiunile de decizie cu cele 3 puncte noi. Imaginea Autorului.

 

În acest articol, am aflat cum funcționează clasificatorul Bayes naiv gaussian și am dat o intuiție despre motivul pentru care a fost proiectat în acest fel - este o abordare directă a modelării probabilității de interes. Comparați acest lucru cu regresia logistică: acolo, probabilitatea este modelată folosind o funcție liniară cu o funcție sigmoidă aplicată deasupra acesteia. Este încă un model ușor, dar nu se simte la fel de natural ca un clasificator Bayes naiv.

Am continuat calculând câteva exemple și colectând câteva bucăți de cod utile pe parcurs. În cele din urmă, am implementat un clasificator Bayes naiv Gaussian complet într-un mod care funcționează bine cu scikit-learn. Aceasta înseamnă că îl puteți utiliza în conducte sau în căutarea în grilă, de exemplu.

În cele din urmă, am făcut o mică verificare prin importul scikit-learns propriul clasificator Gaussian naiv Bayes și testând dacă ambele, clasificatorul nostru și scikit-learn dau același rezultat. Acest test a avut succes.

 
 
Dr. Robert Kübler este Data Scientist la Publicis Media și autor la Towards Data Science.

 
Original. Repostat cu permisiunea.
 

Timestamp-ul:

Mai mult de la KDnuggets