Naive
Päädyimme edellisessä luvussa Bayesin teemassa muotoon, joka näkyy alla. Tällä kertaa olemme tosin korvanneet A
:n sanalla belief ja B
:n sanalla evidence.
Tämän voisi kirjoittaa myös seuraavalla tavalla:
Kuten scikit learn:n dokumentaatio sanoo, jakaja on datasetin suhteen vakio, joten sen voi jättää pois. Jakajan hylkääminen tekee lopputuloksesta normalisoimattoman, mutta se ei ole ongelma, koska meitä kiinnostaa vain suhteellinen todennäköisyys. Alla olevan kaavan kalan näköinen symboli luetaan "proportional to". Tämä tarkoittaa, että vasen puoli on suoraan verrannollinen oikean puolen tulon kanssa.
Voimme myös korvata kaavasta sanan belief
sanalla spam
ja sanan evidence
useita datasetissä olevia sanoja korvaavalla x_1, x_2, ..., x_n
.
Naive Bayes luokittelija
Naive Bayes on Bayesin teoreeman implementaatio, jossa tehdään naiivi oletus, että yksittäiset datapisteet eivät ole keskenään riippuvaisia. Eli sanan Viagra
esiintyminen viestissä lisää todennäköisyyttä, että viesti on roskapostia täysin riippumatta siitä, mitä muita sanoja sähköposti sisältää. Kukin sana osallistuu todennäköisyyteen itsenäisesti. Tämän naiivin oletuksen kanssa kaava on:
Saman voisi kirjoittaa Pythonina näin:
from math import prod
DATASET = [
("Free Viagra now", "Spam"),
("A game of golf tomorrow?", "Not spam"),
]
def probability_of_word_being_in_class(word, y_val):
""" P(evidence_i | y) """
# N(x_i | y): (1)
n_docs = sum(
[
1 for sentence, y
in DATASET
if y == y_val and word in sentence.lower()
]
)
# N(y): (2)
n_class = len([1 for x, y in DATASET if y == y_val])
# Laplace smoothing (3)
n_docs += 0.5
n_class += 1
return n_docs / n_class
def predict_class_probability(evidence, y_val):
# P(y) (4)
prior = sum(
[
1 for _, y
in DATASET
if y == y_val
]
) / len(DATASET)
# product[ P(evidence_i | y) ]
likelihood = prod(
[
probability_of_word_being_in_class(word, y_val)
for word in evidence.lower().split()
]
)
return likelihood * prior
############ TESTILAUSE #############
evidence = "Free Viagra of something"
probabilities = [
predict_class_probability(evidence, "Not spam")
predict_class_probability(evidence, "Spam"),
]
y_hat_prob, other_prob = max(probabilities), min(probabilities)
y_hat = probabilities.index(y_hat_prob)
print(f"Predicted class: {y_hat} (with probability {y_hat_prob} > {other_prob})")
- Kyseisen sanan sisältävien viestien määrä luokassa [spam/no spam]
- Lauseiden määrä datasetissä, jotka eduustavat luokkaa [spam/no spam]
- Smoothing estää tilanteen, jossa jokin sana ei esiinny yhdessäkään viestissä. Tällöin todennäköisyys olisi 0, ja koko lasku menisi nollalla, koska mikä tahansa kertaa 0 on 0.
- Luokan todennäköisyys [spam/no spam]. Eli mikä osuus viesteistä on esimerkiksi roskapostia.
Tip
Huomaa, että toteutus on aivan äärimmäisen yksinkertaistettu ja tarkoitettu pienten datasettien testailuun. Kestävämmän toteutuksen löydät kirjasta "Data Science from Scratch". Vielä paremman toteutuksen löydät scikit-learn kirjastosta.