Clasificatorii Naive Bayes (NBC) sunt algoritmi de învățare automată simpli, dar puternici. Acestea se bazează pe probabilitatea condiționată și pe teorema lui Bayes.
În această postare, explic „trucul” din spatele NBC și vă voi da un exemplu pe care îl putem folosi pentru a rezolva o problemă de clasificare.
În secțiunile următoare, voi vorbi despre matematica din spatele NBC. Simțiți-vă liber să săriți acele secțiuni și să accesați partea de implementare dacă nu sunteți interesat de matematică.
În secțiunea de implementare, vă voi arăta un algoritm NBC simplu. Apoi îl vom folosi pentru a rezolva o problemă de clasificare. Sarcina va fi de a determina dacă un anumit pasager de pe Titanic a supraviețuit sau nu accidentului.
Probabilitate condițională
Înainte de a vorbi despre algoritmul în sine, să vorbim despre matematica simplă din spatele acestuia. Trebuie să înțelegem ce este probabilitatea condiționată și cum putem folosi teorema lui Bayes pentru a o calcula.
Gândiți-vă la o moară corectă cu șase părți. Care este probabilitatea de a obține un șase când arunci matrița? Este ușor, este 1/6. Avem șase rezultate posibile și la fel de probabile, dar suntem interesați doar de unul dintre ele. Deci, 1/6 este.
Dar ce se întâmplă dacă îți spun că am aruncat deja matrița și rezultatul este un număr par? Care este probabilitatea ca acum să avem un șase?
De data aceasta, posibilele rezultate sunt doar trei, deoarece există doar trei numere pare pe matriță. Suntem încă interesați doar de unul dintre aceste rezultate, așa că acum probabilitatea este mai mare: 1/3. Care este diferența dintre ambele cazuri?
În primul caz, nu am avut nr anterior informații despre rezultat. Astfel, trebuia să luăm în considerare fiecare rezultat posibil.
În cel de-al doilea caz, ni s-a spus că rezultatul a fost un număr par, astfel încât am putea reduce spațiul posibilelor rezultate la doar cele trei numere pare care apar într-o matriță cu șase fețe obișnuită.
În general, la calcularea probabilității unui eveniment A, având în vedere apariția unui alt eveniment B, spunem că calculăm probabilitate condițională a lui A dat B, sau doar probabilitatea lui A dat B. Îl denotăm P(A|B)
.
De exemplu, probabilitatea de a obține un șase având în vedere că numărul pe care îl avem este egal: P(Six|Even) = 1/3
. Aici noi, denotați cu Şase evenimentul de a obține un șase și cu Chiar evenimentul de a obține un număr par.
Dar, cum calculăm probabilitățile condiționate? Există o formulă?
Cum se calculează probe condiționale și teorema lui Bayes
Acum, vă voi oferi câteva formule pentru calcularea probelor condiționate. Promit că nu vor fi grele și sunt importante dacă doriți să înțelegeți ideile algoritmilor de învățare automată despre care vom vorbi mai târziu.
Probabilitatea unui eveniment A dată fiind apariția unui alt eveniment B poate fi calculată după cum urmează:
P(A|B) = P(A,B)/P(B)
Unde P(A,B)
denotă probabilitatea ca atât A cât și B să apară în același timp și P(B)
denotă probabilitatea de B.
Observați că avem nevoie P(B) > 0
deoarece nu are sens să vorbim despre probabilitatea lui A dată B dacă apariția lui B nu este posibilă.
De asemenea, putem calcula probabilitatea unui eveniment A, având în vedere apariția mai multor evenimente B1, B2, …, Bn:
P(A|B1,B2,...,Bn) = P(A,B1,B2,...,Bn)/P(B1,B2,...,Bn)
Există un alt mod de calculare a probelor condiționate. În acest fel este așa-numita teoremă a lui Bayes.
P(A|B) = P(B|A)P(A)/P(B)
P(A|B1,B2,...,Bn) = P(B1,B2,...,Bn|A)P(A)/P(B1,B2,...,Bn)
Observați că calculăm probabilitatea evenimentului A dat evenimentului B, de inversând ordinea apariției evenimentelor.
Acum presupunem că evenimentul A a avut loc și vrem să calculăm probele evenimentului B (sau evenimentele B1, B2, …, Bn în al doilea și mai general exemplu).
Un fapt important care poate fi derivat din această teoremă este formula de calculat P(B1,B2,...,Bn,A)
. Aceasta se numește regula lanțului pentru probabilități.
P(B1,B2,...,Bn,A) = P(B1 | B2, B3, ..., Bn, A)P(B2,B3,...,Bn,A)
= P(B1 | B2, B3, ..., Bn, A)P(B2 | B3, B4, ..., Bn, A)P(B3, B4, ..., Bn, A)
= P(B1 | B2, B3, ..., Bn, A)P(B2 | B3, B4, ..., Bn, A)...P(Bn | A)P(A)
Aceasta este o formulă urâtă, nu-i așa? Dar, în anumite condiții, putem face o soluție și o putem evita.
Să vorbim despre ultimul concept pe care trebuie să îl cunoaștem pentru a înțelege algoritmii.
Independenţă
Ultimul concept despre care vom vorbi este independența. Spunem că evenimentele A și B sunt independente dacă
P(A|B) = P(A)
Asta înseamnă că probul evenimentului A nu este afectat de apariția evenimentului B. O consecință directă este că P(A,B) = P(A)P(B)
.
În engleză simplă, aceasta înseamnă că probul apariției atât a lui A cât și a lui B în același timp este egal cu produsul probelor evenimentelor A și B care apar separat.
Dacă A și B sunt independenți, susține, de asemenea, că:
P(A,B|C) = P(A|C)P(B|C)
Acum suntem gata să vorbim despre clasificatoarele Naive Bayes!
Clasificatoare Naive Bayes
Să presupunem că avem un vector X de n caracteristici și dorim să determinăm clasa acelui vector dintr-un set de k clase y1, y2, …, yk. De exemplu, dacă vrem să stabilim dacă va ploua azi sau nu.
Avem două clase posibile (k = 2): ploaie, nu ploaie, iar lungimea vectorului de caracteristici ar putea fi 3 (n = 3).
Prima caracteristică ar putea fi dacă este înnorat sau însorit, a doua caracteristică ar putea fi dacă umiditatea este ridicată sau scăzută, iar a treia caracteristică ar fi dacă temperatura este ridicată, medie sau scăzută.
Deci, aceștia ar putea fi posibili vectori de caracteristici.
<Cloudy, H_High, T_Low>
<Sunny, H_Low, T_Medium>
<Cloudy, H_Low, T_High>
Sarcina noastră este de a determina dacă va ploua sau nu, având în vedere caracteristicile meteo.
După ce am aflat despre probabilitățile condiționate, pare firesc să abordăm problema încercând să calculăm probele de ploaie, având în vedere caracteristicile:
R = P(Rain | Cloudy, H_High, T_Low)
NR = P(NotRain | Cloudy, H_High, T_Low)
Dacă R > NR
noi răspundem că va ploua, altfel spunem că nu va.
În general, dacă avem k clase y1, y2, …, yk, și un vector de n caracteristici X =
P(yi | X1, X2, ..., Xn) = P(X1, X2,..., Xn, yi)/P(X1, X2, ..., Xn)
Observați că numitorul este constant și nu depinde de clasă yi. Deci, îl putem ignora și ne putem concentra doar asupra numărătorului.
Într-o secțiune anterioară, am văzut cum se calculează P(X1, X2,..., Xn, yi)
descompunându-l într-un produs de probabilități condiționale (formula urâtă):
P(X1, X2,..., Xn, yi) = P(X1 | X2,..., Xn, yi)P(X2 | X3,..., Xn, yi)...P(Xn | yi)P(yi)
Presupunând toate caracteristicile Xi sunt independenți și folosind teorema lui Bayes, putem calcula probabilitatea condițională după cum urmează:
P(yi | X1, X2,..., Xn) = P(X1, X2,..., Xn | yi)P(yi)/P(X1, X2, ..., Xn)
= P(X1 | yi)P(X2 | yi)...P(Xn | yi)P(yi)/P(X1, X2, ..., Xn)
Și trebuie doar să ne concentrăm asupra numărătorului.
Prin găsirea clasei yi care maximizează expresia anterioară, clasificăm vectorul de intrare. Dar, cum putem obține toate aceste probabilități?
Cum se calculează probabilitățile
Când rezolvăm acest tip de probleme, trebuie să avem un set de exemple clasificate anterior.
De exemplu, în problema ghicirii dacă va ploua sau nu, trebuie să avem mai multe exemple de vectori de caracteristici și clasificările lor, care ar fi obținute din prognozele meteo din trecut.
Deci, am avea așa ceva:
...
<Cloudy, H_High, T_Low> -> Rain
<Sunny, H_Low, T_Medium> -> Not Rain
<Cloudy, H_Low, T_High> -> Not Rain
...
Să presupunem că trebuie să clasificăm un vector nou <Coudy, H_Low, T_Low>
. Trebuie să calculăm:
P(Rain | Cloudy, H_Low, T_Low) = P(Cloudy | H_Low, T_Low, Rain)P(H_Low | T_Low, Rain)P(T_Low | Rain)P(Rain)/P(Cloudy, H_Low, T_Low)
Obținem expresia anterioară prin aplicarea definiției probabilității condiționale și a regulii lanțului. Amintiți-vă că trebuie să ne concentrăm doar pe numărător, astfel încât să putem renunța la numitor.
De asemenea, trebuie să calculăm probul pentru NotRain
, dar putem face acest lucru într-un mod similar.
Noi putem gasi P(Rain) = # Rain/Total
. Aceasta înseamnă numărarea intrărilor din setul de date care sunt clasificate cu Ploaie și împărțirea acelui număr la dimensiunea setului de date.
A calcula P(Cloudy | H_Low, T_Low, Rain)
trebuie să numărăm toate intrările care au caracteristici H_Low, T_Low și Noros. Aceste intrări trebuie, de asemenea, să fie clasificate ca Rain
. Apoi, acel număr este împărțit la cantitatea totală de date. Calculăm restul factorilor formulei într-un mod similar.
Efectuarea acestor calcule pentru fiecare clasă posibilă este foarte scumpă și lentă. Deci, trebuie să facem presupuneri cu privire la problema care simplifică calculele.
Clasificatorii Naive Bayes presupun că toate caracteristicile sunt independente unele de altele. Astfel, putem rescrie formula noastră aplicând teorema lui Bayes și asumând independența între fiecare pereche de caracteristici:
P(Rain | Cloudy, H_Low, T_Low) = P(Cloudy | Rain)P(H_Low | Rain)P(T_Low | Rain)P(Rain)/P(Cloudy, H_Low, T_Low)
Acum calculăm P(Cloudy | Rain)
numărând numărul de intrări care sunt clasificate ca Rain
și au fost Cloudy
.
Se numește algoritmul Naiv din cauza acestei presupuneri de independență. Există dependențe între caracteristici de cele mai multe ori. Nu putem spune că în viața reală nu există o dependență între umiditate și temperatură, de exemplu. Clasificatoarele Naive Bayes se mai numesc Independence Bayes sau Simple Bayes.
Formula generală ar fi:
P(yi | X1, X2, ..., Xn) = P(X1 | yi)P(X2 | yi)...P(Xn | yi)P(yi)/P(X1, X2, ..., Xn)
Amintiți-vă că puteți scăpa de numitor. Calculăm doar numărătorul și răspundem la clasa care îl maximizează.
Acum, să implementăm NBC-ul nostru și să îl folosim într-o problemă.
Să codificăm!
Vă voi arăta o implementare a unui NBC simplu și apoi o vom vedea în practică.
Problema pe care o vom rezolva este de a stabili dacă un pasager de pe Titanic a supraviețuit sau nu, având în vedere unele caracteristici precum sexul și vârsta lor.
Aici puteți vedea implementarea unui NBC foarte simplu:
class NaiveBayesClassifier:
def __init__(self, X, y):
'''
X and y denotes the features and the target labels respectively
'''
self.X, self.y = X, y
self.N = len(self.X) # Length of the training set
self.dim = len(self.X[0]) # Dimension of the vector of features
self.attrs = [[] for _ in range(self.dim)] # Here we'll store the columns of the training set
self.output_dom = {} # Output classes with the number of ocurrences in the training set. In this case we have only 2 classes
self.data = [] # To store every row [Xi, yi]
for i in range(len(self.X)):
for j in range(self.dim):
# if we have never seen this value for this attr before,
# then we add it to the attrs array in the corresponding position
if not self.X[i][j] in self.attrs[j]:
self.attrs[j].append(self.X[i][j])
# if we have never seen this output class before,
# then we add it to the output_dom and count one occurrence for now
if not self.y[i] in self.output_dom.keys():
self.output_dom[self.y[i]] = 1
# otherwise, we increment the occurrence of this output in the training set by 1
else:
self.output_dom[self.y[i]] += 1
# store the row
self.data.append([self.X[i], self.y[i]])
def classify(self, entry):
solve = None # Final result
max_arg = -1 # partial maximum
for y in self.output_dom.keys():
prob = self.output_dom[y]/self.N # P(y)
for i in range(self.dim):
cases = [x for x in self.data if x[0][i] == entry[i] and x[1] == y] # all rows with Xi = xi
n = len(cases)
prob *= n/self.N # P *= P(Xi = xi)
# if we have a greater prob for this output than the partial maximum...
if prob > max_arg:
max_arg = prob
solve = y
return solve
Aici, presupunem că fiecare caracteristică are un domeniu discret. Asta înseamnă că iau o valoare dintr-un set finit de posibile valori.
La fel se întâmplă și cu cursurile. Observați că stocăm unele date în __init__
metodă, deci nu este nevoie să repetăm unele operații. Clasificarea unei noi intrări se efectuează în classify
metodă.
Acesta este un exemplu simplu de implementare. În aplicațiile din lumea reală nu aveți nevoie (și este mai bine dacă nu creați) propria dvs. implementare. De exemplu,
sklearn
biblioteca din Python conține câteva implementări bune ale NBC-urilor.
Observați cât de ușor este să-l implementați!
Acum, să aplicăm noul nostru clasificator pentru a rezolva o problemă. Avem un set de date cu descrierea a 887 de pasageri pe Titanic. De asemenea, putem vedea dacă un anumit pasager a supraviețuit sau nu tragediei.
Așadar, sarcina noastră este de a determina dacă un alt pasager care nu este inclus în setul de antrenament a reușit sau nu.
În acest exemplu, voi folosi pandas
bibliotecă pentru a citi și prelucra datele. Nu folosesc niciun alt instrument.
Datele sunt stocate într-un fișier numit titanic.csv, astfel încât primul pas este să citiți datele și să obțineți o imagine de ansamblu asupra acestora.
import pandas as pd
data = pd.read_csv('titanic.csv')
print(data.head())
Ieșirea este:
Survived Pclass Name
0 0 3 Mr. Owen Harris Braund
1 1 1 Mrs. John Bradley (Florence Briggs Thayer) Cum...
2 1 3 Miss. Laina Heikkinen
3 1 1 Mrs. Jacques Heath (Lily May Peel) Futrelle
4 0 3 Mr. William Henry Allen
Sex Age Siblings/Spouses Aboard Parents/Children Aboard Fare
0 male 22.0 1 0 7.2500
1 female 38.0 1 0 71.2833
2 female 26.0 0 0 7.9250
3 female 35.0 1 0 53.1000
4 male 35.0 0 0 8.0500
Observați că avem numele fiecărui pasager. Nu vom folosi această caracteristică pentru clasificatorul nostru, deoarece nu este semnificativă pentru problema noastră. De asemenea, vom scăpa de funcția Tarif, deoarece este continuă și caracteristicile noastre trebuie să fie discrete.
Există clasificatoare Naive Bayes care acceptă caracteristici continue. De exemplu, clasificatorul Gaussian Naive Bayes.
y = list(map(lambda v: 'yes' if v == 1 else 'no', data['Survived'].values)) # target values as string
# We won't use the 'Name' nor the 'Fare' field
X = data[['Pclass', 'Sex', 'Age', 'Siblings/Spouses Aboard', 'Parents/Children Aboard']].values # features values
Apoi, trebuie să separăm setul nostru de date într-un set de instruire și un set de validare. Ultima este utilizată pentru a valida cât de bine merge algoritmul nostru.
print(len(y)) # >> 887
# We'll take 600 examples to train and the rest to the validation process
y_train = y[:600]
y_val = y[600:]
X_train = X[:600]
X_val = X[600:]
Ne creăm NBC cu setul de antrenament și apoi clasificăm fiecare intrare din setul de validare.
Măsurăm acuratețea algoritmului nostru prin împărțirea numărului de intrări clasificat corect la numărul total de intrări din setul de validare.
## Creating the Naive Bayes Classifier instance with the training data
nbc = NaiveBayesClassifier(X_train, y_train)
total_cases = len(y_val) # size of validation set
# Well classified examples and bad classified examples
good = 0
bad = 0
for i in range(total_cases):
predict = nbc.classify(X_val[i])
# print(y_val[i] + ' --------------- ' + predict)
if y_val[i] == predict:
good += 1
else:
bad += 1
print('TOTAL EXAMPLES:', total_cases)
print('RIGHT:', good)
print('WRONG:', bad)
print('ACCURACY:', good/total_cases)
Ieșirea:
TOTAL EXAMPLES: 287
RIGHT: 200
WRONG: 87
ACCURACY: 0.6968641114982579
Nu este grozav, dar este ceva. Putem obține o îmbunătățire a preciziei cu aproximativ 10% dacă scăpăm de alte caracteristici precum Frați / soți la bord și Părinți / copii la bord.
Puteți vedea un notebook cu codul și setul de date aici
Concluzii
Astăzi, avem rețele neuronale și alți algoritmi ML complexi și costisitori peste tot.
NBC-urile sunt algoritmi foarte simpli care ne permit să obținem rezultate bune în unele probleme de clasificare fără a avea nevoie de o mulțime de resurse. De asemenea, escalează foarte bine, ceea ce înseamnă că putem adăuga mult mai multe caracteristici, iar algoritmul va fi în continuare rapid și fiabil.
Chiar și într-un caz în care NBC-urile nu erau potrivite pentru problema pe care încercam să o rezolvăm, acestea ar putea fi foarte utile ca bază.
Am putea încerca mai întâi să rezolvăm problema folosind un NBC cu câteva linii de cod și puțin efort. Apoi am putea încerca să obținem rezultate mai bune cu algoritmi mai complexi și mai scumpi.
Acest proces ne poate economisi mult timp și ne oferă un feedback imediat cu privire la faptul dacă algoritmii complexi merită cu adevărat pentru sarcina noastră.
În acest articol citiți despre probabilitățile condiționate, independența și teorema lui Bayes. Acestea sunt conceptele matematice din spatele clasificatorilor Naive Bayes.
După aceea, am văzut o simplă implementare a unui NBC și am rezolvat problema determinării dacă un pasager de pe Titanic a supraviețuit accidentului.
Sper că ți s-a părut util acest articol. Puteți citi despre subiecte legate de informatică în blog personal și urmărindu-mă mai departe Stare de nervozitate.