Gaussin naiivi Bayes, selitetty

Gaussin naiivi Bayes, selitetty

Lähdesolmu: 2021431

Gaussin naiivi Bayes, selitetty
Gaussin naiivin Bayes-luokittimen päätösalue. Kuva tekijältä.

 

Mielestäni tämä on klassikko jokaisen datatieteen uran alussa: Naiivi Bayes -luokitin. Tai minun pitäisi pikemminkin sanoa perhe naiiveista Bayes-luokittelijoista, koska niitä on monia makuja. Esimerkiksi on olemassa moninominen naiivi Bayes, Bernoullin naiivi Bayes ja myös Gaussin naiivi Bayes, joista jokainen eroaa vain yhdessä pienessä yksityiskohdassa, kuten tulemme selvittämään. Naiivit Bayes-algoritmit ovat suunnittelultaan melko yksinkertaisia, mutta osoittautuivat hyödyllisiksi monissa monimutkaisissa reaalimaailman tilanteissa.

Tässä artikkelissa voit oppia

  • kuinka naiivit Bayes-luokittelut toimivat,
  • miksi on järkevää määritellä ne sellaisina kuin ne ovat ja
  • kuinka ne otetaan käyttöön Pythonissa NumPyn avulla.

Löydät koodin osoitteesta minun Github.

Saattaa hieman auttaa, kun tutustun Bayesin tilastoihin Lempeä johdanto Bayesilaiseen päättelyyn tottua Bayesin kaavaan. Koska toteutamme luokittelijan scikit-oppimismukaisella tavalla, kannattaa myös tutustua artikkeliini Rakenna oma mukautettu scikit-learn Regression. Scikit-learnin lisäkustannukset ovat kuitenkin melko pienet ja sinun pitäisi pystyä seuraamaan mukana joka tapauksessa.

Aloitamme naiivin Bayes-luokituksen hämmästyttävän yksinkertaisen teorian tutkimisen ja siirrymme sitten toteutukseen.

Mistä olemme todella kiinnostuneita luokittelussa? Mitä me oikeastaan ​​teemme, mikä on panos ja tulos? Vastaus on yksinkertainen:

Millä todennäköisyydellä x kuuluu johonkin luokkaan c, kun on annettu datapiste x?

Siinä kaikki, mitä haluamme vastata Kaikki luokittelu. Voit mallintaa tämän lauseen suoraan ehdollisena todennäköisyytenä: p(c|x).

Esimerkiksi jos niitä on

  • 3-luokat c₁c₂c₃ja
  • koostuu 2 ominaisuudesta xXNUMXx₂,

luokittelun tulos voisi olla jotain tällaista p(c₁|xXNUMXx₂)=0.3, p(c₂|xXNUMXx₂)=0.5 ja p(c₃|xXNUMXx₂)=0.2. Jos välitämme tulosteena yhdestä etiketistä, valitsisimme suurimman todennäköisyyden, ts c₂ 50 % todennäköisyydellä täällä.

Naiivi Bayes-luokitin yrittää laskea nämä todennäköisyydet suoraan.

Naiivi Bayes

Ok, annettu datapiste x, haluamme laskea p(c|x) kaikille luokille ja tulosta sitten c suurimmalla todennäköisyydellä. Kaavoissa näet tämän usein näin

 

Gaussin naiivi Bayes, selitetty
Kuva tekijältä.

 

Huomautus: max p(c|x) palauttaa suurimman todennäköisyyden, kun taas argmax p(c|x) palauttaa c tällä suurimmalla todennäköisyydellä.

Mutta ennen kuin voimme optimoida p(c|x), meidän on kyettävä laskemaan se. Tätä varten käytämme Bayesin lause:

 

Gaussin naiivi Bayes, selitetty
Bayesin lause. Kuva tekijältä.

 

Tämä on Bayesin osa naiivista Bayesistä. Mutta nyt meillä on seuraava ongelma: Mitä ovat p(x|c) Ja p(c)?

Tästä naiivin Bayes-luokittelijan koulutuksessa on kyse.

Treeni

Kaiken havainnollistamiseksi käytämme lelutietosarjaa kaksi todellista ominaisuutta xXNUMXx₂ja kolme luokkaa c₁c₂c₃ seuraavassa.

 

Gaussin naiivi Bayes, selitetty
Data, visualisoituna. Kuva tekijältä.

 

Voit luoda tämän tarkan tietojoukon kautta

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

Aloitetaan luokan todennäköisyys p(c), todennäköisyys, että jokin luokka c havaitaan merkityssä tietojoukossa. Yksinkertaisin tapa arvioida tämä on vain laskea luokkien suhteelliset taajuudet ja käyttää niitä todennäköisyyksinä. Voimme käyttää tietojoukkoamme nähdäksemme, mitä tämä tarkoittaa tarkalleen.

Luokassa on 7 pistettä 20 pisteestä c₁ (sininen) tietojoukossa, siksi sanomme p(c₁)=7/20. Meillä on 7 pistettä luokasta c₂ (punainen) myös, joten asetamme p(c₂)=7/20. Viimeinen luokka c₃ (keltainen) on siis vain 6 pistettä p(c₃)=6/20.

Tämä yksinkertainen luokan todennäköisyyksien laskenta muistuttaa maksimitodennäköisyyden lähestymistapaa. Voit kuitenkin käyttää myös toista aikaisempi jakelu, jos haluat. Jos esimerkiksi tiedät, että tämä tietojoukko ei edusta todellista populaatiota, koska luokka c₃ pitäisi näkyä 50 %:ssa tapauksista, määrität sitten p(c₁)=0.25, p(c₂)=0.25 ja p(c₃)=0.5. Mikä tahansa auttaa sinua parantamaan testisarjan suorituskykyä.

Nyt käännymme todennäköisyys p(x|c)=p(xXNUMXx₂|c). Yksi tapa laskea tämä todennäköisyys on suodattaa tietojoukko näytteille, joissa on etiketti ja yritä sitten löytää jakauma (esim. 2-ulotteinen Gaussin), joka kaappaa piirteet xXNUMXx₂.

Valitettavasti meillä ei yleensä ole tarpeeksi näytteitä luokkaa kohden tehdäksemme oikean arvion todennäköisyydestä.

Voidaksemme rakentaa kestävämmän mallin teemme naiivi oletus että ominaisuudet xXNUMXx₂ olemme stokastisesti riippumaton, annettu c. Tämä on vain hieno tapa tehdä matematiikasta helpompaa kautta

 

Gaussin naiivi Bayes, selitetty
Kuva tekijältä

 

jokaiselle luokalle c. Tässä on naiivi osa naiiveista Bayesistä tulee, koska tämä yhtälö ei päde yleisesti. Silti naiivi Bayes tuottaa silloinkin hyviä, joskus erinomaisia ​​tuloksia käytännössä. Erityisesti NLP-ongelmissa, joissa on sanapussiominaisuuksia, moninomi-naiivi Bayes loistaa.

Yllä annetut argumentit ovat samat kaikille naiiveille Bayes-luokittelijoille, joita voit löytää. Nyt se riippuu vain mallista p(xXNUMX|cXNUMX), p(xXNUMX|cXNUMX), p(xXNUMX|cXNUMX), p(xXNUMX|c₂), p(xXNUMX|cXNUMX) ja p(x₂|cXNUMX).

Jos ominaisuudet ovat vain 0 ja 1, voit käyttää a Bernoullin jakauma. Jos ne ovat kokonaislukuja, a Multinomiaalinen jakelu. Meillä on kuitenkin todellisia ominaisuusarvoja ja päätämme a Gaussin jakauma, josta nimi Gaussin naiivi Bayes. Otamme seuraavan muodon

 

Gaussin naiivi Bayes, selitetty
Kuva tekijältä.

 

jossa μᵢ,ⱼ on keskiarvo ja σᵢ,ⱼ on keskihajonta, joka meidän on arvioitava tiedoista. Tämä tarkoittaa, että saamme yhden keskiarvon jokaiselle ominaisuudelle i yhdistettynä luokkaan cⱼ, meidän tapauksessamme 2*3=6 tarkoittaa. Sama pätee standardipoikkeamiin. Tämä vaatii esimerkkiä.

Yritetään arvioida μ₂,₁ ja σ₂,₁. Koska j=1, olemme kiinnostuneita vain luokasta c₁, säilytetään vain tällä tarralla varustetut näytteet. Seuraavat näytteet ovat jäljellä:

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

Nyt sen takia i=2 meidän on otettava huomioon vain toinen sarake. μ₂,₁ on keskiarvo ja σ₂,₁ tämän sarakkeen keskihajonta, ts μ₂,₁ = 0.49985176 ja σ₂,₁ = 0.9789976.

Nämä luvut ovat järkeviä, jos katsot sirontadiagrammia uudelleen ylhäältä. Ominaisuudet x₂ luokan näytteistä c₁ ovat noin 0.5, kuten kuvasta näkyy.

Laskemme tämän nyt viidelle muulle yhdistelmälle ja olemme valmiita!

Pythonissa voit tehdä sen seuraavasti:

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

Me vastaanotamme

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

Tämä on tulos Gaussin naiivin Bayes-luokittajan koulutuksesta.

Ennusteiden tekeminen

Täydellinen ennustuskaava on

 

Gaussin naiivi Bayes, selitetty
Kuva tekijältä.

 

Oletetaan uusi datapiste x*=(-2, 5) tulee sisään.

 

Gaussin naiivi Bayes, selitetty
Kuva tekijältä.

 

Lasketaan, mihin luokkaan se kuuluu p(c|x*) kaikille luokille. Kuvasta sen pitäisi kuulua luokkaan c₃ = 2, mutta katsotaanpa. Jätetään huomioimatta nimittäjä p(x) sekunnin ajan. Seuraavaa silmukkaa käyttäen laskettiin nimittäjät kohteelle 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}" )

Me vastaanotamme

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

Tietysti nämä todennäköisyydet (meidän ei pitäisi kutsua niitä sillä tavalla) älä laske yhteen, koska jätimme huomioimatta nimittäjän. Tämä ei kuitenkaan ole ongelma, koska voimme vain ottaa nämä normalisoimattomat todennäköisyydet ja jakaa ne niiden summalla, jolloin ne laskevat yhteen. Joten jakamalla nämä kolme arvoa niiden summalla noin 0.00032569, saamme

 

Gaussin naiivi Bayes, selitetty
Kuva tekijältä.

 

Selkeä voittaja, kuten odotimme. Nyt toteutetaan se!

Tämä toteutus ei ole selvästikään tehokas, ei numeerisesti vakaa, se palvelee vain koulutustarkoitusta. Olemme keskustelleet useimmista asioista, joten sen pitäisi olla nyt helppo seurata. Voit jättää huomioimatta kaikki check toimintoja tai lue artikkelini Rakenna oma mukautettu scikit-learn jos olet kiinnostunut siitä, mitä he tarkalleen tekevät.

Huomaa vain, että toteutin a predict_proba menetelmä ensin todennäköisyyksien laskemiseksi. Menetelmä predict vain kutsuu tätä menetelmää ja palauttaa indeksin (=luokka) suurimmalla todennäköisyydellä käyttämällä argmax-funktiota (se on taas!). Tunti odottaa luokkia 0- k-1, missä k on luokkien lukumäärä.

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)

Toteutuksen testaus

Vaikka koodi on melko lyhyt, se on silti liian pitkä ollaksemme täysin varmoja siitä, ettemme ole tehneet virheitä. Joten tarkistakaamme, kuinka se pärjää scikit-learn GaussianNB-luokitin.

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

lähdöt

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

Ennusteet käyttäen predict menetelmä ovat

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

Nyt käytetään scikit-learniä. Heitä jotain koodia

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

saannot

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

Numerot näyttävät tavallaan samanlaisilta kuin luokittimemme numerot, mutta ne ovat hieman poikkeavia viimeisistä näytetyistä numeroista. Teimmekö jotain väärin? Ei. Scikit-learn-versio vain käyttää toista hyperparametria var_smoothing=1e-09 . Jos asetamme tämän nolla-, saamme tarkalleen numeromme. Täydellinen!

Tutustu luokittimemme päätösalueisiin. Merkkasin myös kolme pistettä, joita käytimme testauksessa. Tällä yhdellä pisteellä lähellä rajaa on vain 56.9 % mahdollisuus kuulua punaiseen luokkaan, kuten voit nähdä predict_proba ulostulot. Kaksi muuta pistettä luokitellaan paljon korkeammalla varmuudella.

 

Gaussin naiivi Bayes, selitetty

Päätösalueet 3 uudella pisteellä. Kuva tekijältä.

 

Tässä artikkelissa olemme oppineet, kuinka Gaussin naiivi Bayes-luokitin toimii, ja antanut intuition siitä, miksi se on suunniteltu sellaiseksi – se on suora lähestymistapa kiinnostavan todennäköisyyden mallintamiseen. Vertaa tätä logistiseen regressioon: siellä todennäköisyys mallinnetaan lineaarisella funktiolla, jonka päällä on sigmoidifunktio. Se on silti helppo malli, mutta se ei tunnu niin luonnolliselta kuin naiivi Bayes-luokittelu.

Jatkoimme laskemalla muutamia esimerkkejä ja keräämällä hyödyllisiä koodinpätkiä matkan varrella. Lopuksi olemme toteuttaneet täydellisen Gaussin naiivin Bayes-luokituksen tavalla, joka toimii hyvin scikit-learnin kanssa. Tämä tarkoittaa, että voit käyttää sitä esimerkiksi putkissa tai ruudukkohaussa.

Lopuksi teimme pienen mielenterveyden tarkistuksen tuomalla scikit-learnsin oman Gaussin naiivin Bayes-luokituksen ja testaamalla, tuottavatko sekä meidän että scikit-learnin luokitin saman tuloksen. Tämä testi onnistui.

 
 
Tri Robert Kübler on tietotutkija Publicis Mediassa ja kirjoittaja Towards Data Science -palvelussa.

 
Alkuperäinen. Postitettu luvalla.
 

Aikaleima:

Lisää aiheesta KDnuggets