naive-bayes-gaussian/naive_bayes.ipynb
2022-05-18 18:09:00 +02:00

402 KiB
Raw Blame History

Klasyfikacja za pomocą naiwnej metody bayesowskiej z rozkładem normalnym

Twierdzenie Bayesa

bayes.svg

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

Naiwny klasyfikator bayesowski jest naiwny, ponieważ zakłada, że poszczególne cechy są niezależne od siebie

rozklady.jpg

import pandas as pd
iris_data = pd.read_csv("iris.csv")
iris_data
sepal.length sepal.width petal.length petal.width variety
0 5.1 3.5 1.4 0.2 Setosa
1 4.9 3.0 1.4 0.2 Setosa
2 4.7 3.2 1.3 0.2 Setosa
3 4.6 3.1 1.5 0.2 Setosa
4 5.0 3.6 1.4 0.2 Setosa
... ... ... ... ... ...
145 6.7 3.0 5.2 2.3 Virginica
146 6.3 2.5 5.0 1.9 Virginica
147 6.5 3.0 5.2 2.0 Virginica
148 6.2 3.4 5.4 2.3 Virginica
149 5.9 3.0 5.1 1.8 Virginica

150 rows × 5 columns

import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
setosa = iris_data[:50]
versicolor = iris_data[50:100]
virginica = iris_data[100:150]
# ax.scatter(setosa['sepal.length'],np.arange(50), color='blue', lw=2)
# ax.scatter(versicolor['sepal.length'],np.arange(50),  color='orange', lw=2)
# ax.scatter(virginica['sepal.length'],np.arange(50),  color='green', lw=2) 

ax.scatter(setosa['petal.width'],np.arange(50), color='blue', lw=2)
ax.scatter(versicolor['petal.width'],np.arange(50),  color='orange', lw=2)
ax.scatter(virginica['petal.width'],np.arange(50),  color='green', lw=2)
<matplotlib.collections.PathCollection at 0x7fd5194d9f40>
from scipy import stats
print(stats.shapiro(setosa['sepal.length']))
print(stats.shapiro(versicolor['sepal.length']))
print(stats.shapiro(virginica['sepal.length']))
ShapiroResult(statistic=0.9776989221572876, pvalue=0.4595281183719635)
ShapiroResult(statistic=0.9778355956077576, pvalue=0.46473264694213867)
ShapiroResult(statistic=0.9711798429489136, pvalue=0.25832483172416687)

GaussianNB.png

Funkcja gęstości prawdopodobieństwa rozkładu normalnego

gestosc.svg

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

Pitność wody

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

figure.tight_layout()
plt.show()
from sklearn.decomposition import PCA

pca = PCA(n_components=2)
pca.fit(X_test)
X_pca = pca.transform(X_test)

plt.scatter(X_pca[:, 0], X_pca[:, 1], c=predictions, s=50, cmap='viridis')
<matplotlib.collections.PathCollection at 0x7fd5199264c0>
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y_test, s=50, cmap='viridis')
<matplotlib.collections.PathCollection at 0x7fd51990eaf0>

Irysy

# Preprocessing danych


# Wczytywanie danych
df = pd.read_csv("iris.csv")

# 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.3, stratify=y, random_state=1)

print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)


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


# Wyliczenie accuracy modelu
print(x.accuracy(y_test, predictions))
(105, 4) (105,)
(45, 4) (45,)
0.9333333333333333
f1_score(y_test, predictions, average="macro")
0.9326599326599326
pca = PCA(n_components=2)
pca.fit(X_test)
X_pca = pca.transform(X_test)

df_pred = pd.DataFrame(predictions).replace({'Virginica': 0, 'Versicolor': 1, "Setosa": 2}, regex=True)
df_pred = np.array(df_pred).reshape(1, -1)
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=df_pred[0], s=50, cmap='viridis')
<matplotlib.collections.PathCollection at 0x7fd519886fd0>
sep_len = X_test["sepal.length"]
sep_with = X_test["sepal.width"]
pet_len = X_test["petal.length"]
pet_with = X_test["petal.width"]

figure, axes = plt.subplots(nrows=2, ncols=2)

axes[0, 0].plot(sep_len, predictions, 'bo')
axes[0, 0].set_title("Sepal lenght")

axes[0, 1].plot(sep_with, predictions, 'bo')
axes[0, 1].set_title("Sepal width")

axes[1, 0].plot(pet_len, predictions, 'bo')
axes[1, 0].set_title("Petal length")

axes[1, 1].plot(pet_with, predictions, 'bo')
axes[1, 1].set_title("Petal width")

figure.tight_layout()
plt.show()