250 KiB
250 KiB
Klasyfikacja za pomocą naiwnej metody bayesowskiej z rozkładem normalnym
Twierdzenie Bayesa
P(A) -- oznacza prawdopodobieństwo a-priori wystąpienia klasy A (tj. prawdopodobieństwo, że dowolny przykład należy do klasy A)
P(B|A) -- oznacza prawdopodobieństwo a-posteriori, że B należy do klasy A
P(B) -- znacza prawdopodobieństwo a-priori wystąpienia przykładu B
Funkcja gęstości prawdopodobieństwa rozkładu normalnego
import numpy as np
import pandas as pd
import scipy.stats as stats
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
sns.set(style="whitegrid")
class NaiveBayesClassifier():
def calc_prior(self, features, target):
'''
Wyliczenie prawdopodobieństwa a priori
'''
self.prior = (features.groupby(target).apply(lambda x: len(x)) / self.rows).to_numpy()
return self.prior
def calc_statistics(self, features, target):
'''
Wyliczenie średnich i wariancji dla danych
'''
self.mean = features.groupby(target).apply(np.mean).to_numpy()
self.var = features.groupby(target).apply(np.var).to_numpy()
return self.mean, self.var
def gaussian_density(self, class_idx, x):
'''
Wyliczenie prawdopodobieństwa z rozkładu normalnego
(1/√2pi*σ) * exp((-1/2)*((x-μ)^2)/(2*σ²))
μ -średnia
σ² - wariancja
σ - odchylenie standardowe
'''
mean = self.mean[class_idx]
var = self.var[class_idx]
numerator = np.exp((-1/2)*((x-mean)**2) / (2 * var)) # Licznik wzoru na gęstość rozkładu normalnego
denominator = np.sqrt(2 * np.pi * var) # Mianownik wzoru na gęstość rozkładu normalnego
prob = numerator / denominator
return prob
def classify(self, x):
'''
Wyliczenie prawdopodobieństwa a posteriori i zwrócenie klasy, dla której prawdopodobieństwo jest najwyższe
'''
posteriors = []
posteriors_no_log = []
# calculate posterior probability for each class
for i in range(self.count):
prior = np.log(self.prior[i]) # Do predykcji używane jest prawodopodobieństwo logarytmiczne
prior_no_log = self.prior[i] # Zwykłe prawdopodobieństwo liczymy, żeby zwrócić je z predykcjami
conditional = np.sum(np.log(self.gaussian_density(i, x)))
conditional_no_log = np.prod(self.gaussian_density(i, x))
posterior = prior + conditional
posterior_no_log = prior_no_log * conditional_no_log
posteriors.append(posterior)
posteriors_no_log.append(posterior_no_log)
# Zwracamy klasę o największym prawdopodobieństwie
return self.classes[np.argmax(posteriors)], np.max(posteriors_no_log)
def fit(self, features, target):
'''
Główna metoda trenująca model
'''
self.classes = np.unique(target)
self.count = len(self.classes)
self.feature_nums = features.shape[1]
self.rows = features.shape[0]
self.calc_statistics(features, target)
self.calc_prior(features, target)
def predict(self, features):
'''
Predykcja wartości dla każdego wiersza
'''
preds = [self.classify(f) for f in features.to_numpy()]
return preds
def accuracy(self, y_test, y_pred):
'''
Wyliczenie accuracy modelu
'''
accuracy = np.sum(y_test == y_pred) / len(y_test)
return accuracy
def visualize(self, y_true, y_pred, target):
'''
Narysowanie wykresu porównującego rozkład klas prawdziwych i przewidzianych
'''
tr = pd.DataFrame(data=y_true, columns=[target])
pr = pd.DataFrame(data=y_pred, columns=[target])
fig, ax = plt.subplots(1, 2, sharex='col', sharey='row', figsize=(15,6))
sns.countplot(x=target, data=tr, ax=ax[0], alpha=0.7, hue=target, dodge=False)
sns.countplot(x=target, data=pr, ax=ax[1], alpha=0.7, hue=target, dodge=False)
ax[0].tick_params(labelsize=12)
ax[1].tick_params(labelsize=12)
ax[0].set_title("Prawdziwe wartości", fontsize=18)
ax[1].set_title("Predykcje", fontsize=18)
plt.show()
# Preprocessing danych
# Uzupełnienie pustych wartości w kolumnach
def fill_nan(df):
for index, column in enumerate(df.columns[:9]):
df[column] = df[column].fillna(df.groupby('Potability')[column].transform('mean'))
return df
# Wczytywanie danych
df = pd.read_csv("water_potability.csv")
df = fill_nan(df)
# Zrandomizowanie kolejności danych w datasecie
df = df.sample(frac=1, random_state=10).reset_index(drop=True)
# Podział na atrybuty i przewidywane wartości
X, y = df.iloc[:, :-1], df.iloc[:, -1]
# Normalizacja i skalowanie danych
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X = sc.fit_transform(X.to_numpy())
X = pd.DataFrame(X, columns=df.columns.values.tolist()[:-1])
# Podział na dane trenujące i testowe, z uwzględnieniem równego rozłożenia danych
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, stratify=y, random_state=1)
print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)
(2948, 9) (2948,) (328, 9) (328,)
X_train
ph | Hardness | Solids | Chloramines | Sulfate | Conductivity | Organic_carbon | Trihalomethanes | Turbidity | |
---|---|---|---|---|---|---|---|---|---|
1022 | 0.003078 | 0.688791 | 0.846257 | 1.428934 | -0.858263 | 0.002792 | 0.913790 | 0.232417 | 2.319505 |
3191 | -0.587365 | 0.223203 | -0.731867 | 0.397503 | 0.759893 | 0.330607 | 0.094379 | 0.282563 | 0.235024 |
13 | 0.003078 | -0.241037 | 0.773051 | 0.580019 | 1.334369 | -0.049130 | -1.121422 | -0.200432 | -0.946356 |
2068 | -2.176058 | 1.443006 | -1.626771 | -4.164610 | -0.033706 | -1.050763 | -0.391328 | -0.398649 | -0.298341 |
1484 | 0.213047 | 0.403036 | -0.464729 | 0.070417 | 0.021560 | -0.952776 | -0.213330 | 0.111419 | -0.235893 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
691 | 0.003078 | 1.199106 | -0.003483 | -0.670308 | -0.069513 | 0.185754 | -0.466010 | 0.031975 | 0.676276 |
1283 | -2.034004 | -1.508135 | 0.255310 | 0.083839 | -1.413707 | 0.694074 | -1.110579 | 0.232996 | 2.544703 |
2818 | -0.702987 | -0.575677 | 0.755056 | 0.664695 | 0.021560 | -0.489334 | 0.371852 | -2.272990 | -1.764684 |
1330 | 1.525943 | 0.497074 | -0.714355 | -1.024237 | -1.022037 | -0.327074 | -1.107341 | 0.517432 | -1.230528 |
1926 | -0.043558 | -0.882359 | -0.456141 | -0.770271 | 0.795189 | 0.560306 | -1.086081 | -1.356820 | 0.172521 |
2948 rows × 9 columns
# Trenowanie modelu klasyfikatora
x = NaiveBayesClassifier()
x.fit(X_train, y_train)
# Predykcja wartości dla danych testowych
predictions = x.predict(X_test)
# Prawdopodobieństwa kolejnych predykcji
probabilities = [p[1] for p in predictions]
# Przewidziana wartość
predictions = [p[0] for p in predictions]
predictions[0]
0
# Wyliczenie accuracy modelu
x.accuracy(y_test, predictions)
0.6280487804878049
from sklearn.metrics import f1_score
f1_score(y_test, predictions)
0.14084507042253522
y_test.value_counts(normalize=True)
0 0.609756 1 0.390244 Name: Potability, dtype: float64
x.visualize(y_test, predictions, 'Potability')
ph_val = X_test["ph"]
sulfate_val = X_test["Sulfate"]
hard_val = X_test["Hardness"]
carb_val = X_test["Organic_carbon"]
turb_val = X_test["Turbidity"]
ch_val = X_test["Chloramines"]
figure, axes = plt.subplots(nrows=3, ncols=2)
axes[0, 0].plot(ph_val, predictions, 'bo')
axes[0, 0].set_title("pH")
axes[0, 1].plot(sulfate_val, predictions, 'bo')
axes[0, 1].set_title("Sulfate")
axes[1, 0].plot(hard_val, predictions, 'bo')
axes[1, 0].set_title("Hardness")
axes[1, 1].plot(carb_val, predictions, 'bo')
axes[1, 1].set_title("Organic carbon")
axes[2, 0].plot(turb_val, predictions, 'bo')
axes[2, 0].set_title("Turbidity")
axes[2, 1].plot(ch_val, predictions, 'bo')
axes[2, 1].set_title("Chloramines")
plt.show()
/usr/lib/python3/dist-packages/matplotlib/cbook/__init__.py:1377: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version. Convert to a numpy array before indexing instead. x[:, None] /usr/lib/python3/dist-packages/matplotlib/axes/_base.py:237: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version. Convert to a numpy array before indexing instead. x = x[:, np.newaxis]