Bayes naïf gaussien, expliqué

Bayes naïf gaussien, expliqué

Nœud source: 2021431

Bayes naïf gaussien, expliqué
La région de décision d'un classificateur Bayes naïf gaussien. Image de l'auteur.

 

Je pense que c'est un classique au début de chaque carrière en science des données : le Classificateur Naive Bayes. Ou devrais-je plutôt dire le famille de classificateurs naïfs de Bayes, car ils se déclinent en plusieurs saveurs. Par exemple, il existe un Bayes naïf multinomial, un Bayes naïf de Bernoulli, et aussi un classificateur Bayes naïf gaussien, chacun différent dans un seul petit détail, comme nous allons le découvrir. Les algorithmes naïfs de Bayes sont assez simples dans leur conception mais se sont avérés utiles dans de nombreuses situations complexes du monde réel.

Dans cet article, vous pouvez apprendre

  • comment fonctionnent les classificateurs naïfs de Bayes,
  • pourquoi il est logique de les définir comme ils sont et
  • comment les implémenter en Python en utilisant NumPy.

Vous pouvez trouver le code sur mon Github.

Cela pourrait aider un peu à consulter mon introduction aux statistiques bayésiennes Une douce introduction à l'inférence bayésienne s'habituer à la formule de Bayes. Comme nous allons implémenter le classificateur d'une manière conforme à scikit learn, il vaut également la peine de consulter mon article Créez votre propre régression scikit-learn personnalisée. Cependant, la surcharge de scikit-learn est assez faible et vous devriez pouvoir suivre de toute façon.

Nous commencerons par explorer la théorie étonnamment simple de la classification naïve de Bayes, puis nous nous tournerons vers la mise en œuvre.

Qu'est-ce qui nous intéresse vraiment lors de la classification ? Que faisons-nous réellement, quelles sont les entrées et les sorties ? La réponse est simple :

Étant donné un point de données x, quelle est la probabilité que x appartienne à une certaine classe c ?

C'est tout ce que nous voulons répondre tous classification. Vous pouvez modéliser directement cet énoncé sous la forme d'une probabilité conditionnelle : p(c|x).

Par exemple, s'il y a

  • les classes 3 c₁c₂c₃et une
  • se compose de 2 fonctions x₁x₂,

le résultat d'un classificateur pourrait être quelque chose comme p(c₁|x₁x₂) = 0.3, p(c₂|x₁x₂)=0.5 et p(c₃|x₁x₂)=0.2. Si nous nous soucions d'une seule étiquette comme sortie, nous choisirons celle avec la probabilité la plus élevée, c'est-à-dire c₂ avec une probabilité de 50% ici.

Le classificateur naïf de Bayes essaie de calculer directement ces probabilités.

Naïf Bayes

Ok, donc étant donné un point de données x, on veut calculer p(c|x) pour toutes les catégories puis sortir le c avec la probabilité la plus élevée. Dans les formules, vous voyez souvent cela comme

 

Bayes naïf gaussien, expliqué
Image de l'auteur.

 

Remarque: max p(c|x) renvoie la probabilité maximale tandis que argmax p(c|x) renvoie le c avec cette probabilité la plus élevée.

Mais avant de pouvoir optimiser p(c|x), nous devons pouvoir le calculer. Pour cela, nous utilisons théorème de Bayes:

 

Bayes naïf gaussien, expliqué
Théorème de Bayes. Image de l'auteur.

 

C'est la partie Bayes du Bayes naïf. Mais maintenant, nous avons le problème suivant : Quels sont p(x|c) et p(c)?

C'est à cela que sert l'entraînement d'un classificateur bayésien naïf.

La formation

Pour illustrer le tout, utilisons un ensemble de données de jouets avec deux vraies caractéristiques x₁x₂et une trois classes c₁c₂c₃ dans ce qui suit.

 

Bayes naïf gaussien, expliqué
Les données, visualisées. Image de l'auteur.

 

Vous pouvez créer cet ensemble de données exact via

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

Commençons par le probabilité de classe p(c), la probabilité qu'une classe c est observé dans le jeu de données étiqueté. Le moyen le plus simple d'estimer cela consiste simplement à calculer les fréquences relatives des classes et à les utiliser comme probabilités. Nous pouvons utiliser notre ensemble de données pour voir ce que cela signifie exactement.

Il y a 7 points sur 20 étiquetés classe c₁ (bleu) dans le jeu de données, donc on dit p(c₁)=7/20. Nous avons 7 points pour la classe c₂ (rouge) également, donc nous fixons p(c₂)=7/20. La dernière classe c₃ (jaune) n'a que 6 points, donc p(c₃)=6/20.

Ce calcul simple des probabilités de classe ressemble à une approche du maximum de vraisemblance. Cependant, vous pouvez également utiliser un autre avant distribution, si vous le souhaitez. Par exemple, si vous savez que cet ensemble de données n'est pas représentatif de la vraie population parce que la classe c₃ doit apparaître dans 50 % des cas, alors vous définissez p(c₁) = 0.25, p(c₂)=0.25 et p(c₃)=0.5. Tout ce qui vous aide à améliorer les performances sur l'ensemble de test.

Nous passons maintenant à la probabilité p(x|c)=p(x₁x₂|c). Une approche pour calculer cette probabilité consiste à filtrer l'ensemble de données pour les échantillons avec étiquette puis essayez de trouver une distribution (par exemple une gaussienne bidimensionnelle) qui capture les caractéristiques x₁x₂.

Malheureusement, généralement, nous n'avons pas assez d'échantillons par classe pour faire une estimation correcte de la vraisemblance.

Pour pouvoir construire un modèle plus robuste, nous faisons le hypothèse naïve que les fonctionnalités x₁x₂  stochastiquement indépendant, donné c. C'est juste une façon élégante de faciliter les calculs via

 

Bayes naïf gaussien, expliqué
Image de l'auteur

 

pour chaque classe c. C'est là que le naïf une partie de Bayes naïf vient du fait que cette équation ne tient pas en général. Pourtant, même dans ce cas, le Bayes naïf donne de bons résultats, parfois exceptionnels, dans la pratique. Surtout pour les problèmes de PNL avec des caractéristiques de sac de mots, le Bayes naïf multinomial brille.

Les arguments donnés ci-dessus sont les mêmes pour tout classificateur Bayes naïf que vous pouvez trouver. Maintenant, cela dépend de la façon dont vous modélisez p(x₁|c₁), p(x₂|c₁), p(x₁|c₂), p(x₂|c₂), p(x₁|c₃) ainsi que le p(x₂|c₃).

Si vos caractéristiques sont 0 et 1 uniquement, vous pouvez utiliser un Distribution de Bernoulli. S'il s'agit d'entiers, un Distribution multinomiale. Cependant, nous avons de vraies valeurs de caractéristiques et décidons pour un Gaussienne distribution, d'où le nom de Bayes naïf gaussien. On suppose la forme suivante

 

Bayes naïf gaussien, expliqué
Image de l'auteur.

 

De μᵢ, ⱼ est la moyenne et σᵢ,ⱼ est l'écart type que nous devons estimer à partir des données. Cela signifie que nous obtenons une moyenne pour chaque caractéristique i couplé à une classe cⱼ, dans notre cas 2*3=6 signifie. Il en va de même pour les écarts-types. Cela appelle un exemple.

Essayons d'estimer µ₂,₁ ainsi que le σ₂,₁. Car j=1, seule la classe nous intéresse c₁, ne gardons que les échantillons portant cette étiquette. Les échantillons suivants restent :

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

Maintenant, à cause de i=2 nous n'avons qu'à considérer la deuxième colonne. µ₂,₁ est la moyenne et σ₂,₁ l'écart type pour cette colonne, c'est-à-dire µ₂,₁ = 0.49985176 et σ₂,₁ = 0.9789976.

Ces chiffres ont du sens si vous regardez à nouveau le nuage de points d'en haut. Les caractéristiques x₂ des échantillons de la classe c₁ sont autour de 0.5, comme vous pouvez le voir sur la photo.

Nous calculons cela maintenant pour les cinq autres combinaisons et nous avons terminé !

En Python, vous pouvez le faire comme ceci :

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

Nous recevons

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

Ceci est le résultat de l'apprentissage d'un classificateur Bayes naïf gaussien.

Faire des prédictions

La formule de prédiction complète est

 

Bayes naïf gaussien, expliqué
Image de l'auteur.

 

Supposons un nouveau point de données x*=(-2, 5) entre.

 

Bayes naïf gaussien, expliqué
Image de l'auteur.

 

Pour voir à quelle classe il appartient, calculons p(c|x*) pour toutes les catégories. D'après la photo, il devrait appartenir à la classe c₃ = 2, mais voyons. Ignorons le dénominateur p(x) pour une seconde. À l'aide de la boucle suivante, calculez les nominateurs pour 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}" )

Nous recevons

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

Bien sûr, ces probabilités (nous ne devrions pas les appeler ainsi) ne totalisez pas un puisque nous avons ignoré le dénominateur. Cependant, ce n'est pas un problème puisque nous pouvons simplement prendre ces probabilités non normalisées et les diviser par leur somme, puis elles totaliseront un. Ainsi, en divisant ces trois valeurs par leur somme d'environ 0.00032569, nous obtenons

 

Bayes naïf gaussien, expliqué
Image de l'auteur.

 

Un gagnant clair, comme nous l'attendions. Maintenant, mettons-le en œuvre !

Cette implémentation n'est de loin pas efficace, pas stable numériquement, elle n'a qu'un but pédagogique. Nous avons discuté de la plupart des choses, il devrait donc être facile de suivre maintenant. Vous pouvez ignorer tous les check fonctions, ou lisez mon article Construisez votre propre scikit-learn personnalisé si vous êtes intéressé par ce qu'ils font exactement.

Notez juste que j'ai implémenté un predict_proba première méthode pour calculer les probabilités. La méthode predict appelle simplement cette méthode et renvoie l'indice (= classe) avec la probabilité la plus élevée à l'aide d'une fonction argmax (le revoilà !). La classe attend les classes de 0 à k-1, où k est le nombre de classes.

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)

Tester la mise en œuvre

Bien que le code soit assez court, il est encore trop long pour être complètement sûr que nous n'avons pas fait d'erreurs. Alors, vérifions comment il se comporte par rapport au classificateur GaussianNB scikit-learn.

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

sorties

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

Les prédictions utilisant le predict méthode sont

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

Maintenant, utilisons scikit-learn. Lancer du code

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

rendements

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

Les chiffres ressemblent un peu à ceux de notre classificateur, mais ils sont un peu décalés dans les derniers chiffres affichés. Avons-nous fait quelque chose de mal ? No. La version scikit-learn utilise simplement un autre hyperparamètre var_smoothing=1e-09 . Si nous réglons celui-ci sur zéro, nous obtenons exactement nos chiffres. Parfait!

Jetez un œil aux régions de décision de notre classifieur. J'ai également marqué les trois points que nous avons utilisés pour les tests. Ce point proche de la frontière n'a que 56.9 % de chances d'appartenir à la classe rouge, comme vous pouvez le voir sur le predict_proba les sorties. Les deux autres points sont classés avec une confiance beaucoup plus élevée.

 

Bayes naïf gaussien, expliqué

Les régions de décision avec les 3 nouveaux points. Image de l'auteur.

 

Dans cet article, nous avons appris comment fonctionne le classificateur Bayes naïf gaussien et donné une intuition sur la raison pour laquelle il a été conçu de cette façon - c'est une approche directe pour modéliser la probabilité d'intérêt. Comparez cela avec la régression logistique : là, la probabilité est modélisée à l'aide d'une fonction linéaire avec une fonction sigmoïde appliquée au-dessus. C'est toujours un modèle facile, mais il ne semble pas aussi naturel qu'un classificateur Bayes naïf.

Nous avons continué en calculant quelques exemples et en collectant quelques morceaux de code utiles en cours de route. Enfin, nous avons implémenté un classificateur Bayes naïf gaussien complet d'une manière qui fonctionne bien avec scikit-learn. Cela signifie que vous pouvez l'utiliser dans les pipelines ou la recherche de grille, par exemple.

En fin de compte, nous avons effectué une petite vérification de cohérence en important scikit-learns propre classificateur Bayes naïf gaussien et en testant si les deux, notre classificateur et celui de scikit-learn donnent le même résultat. Ce test a réussi.

 
 
Dr Robert Kübler est Data Scientist chez Publicis Media et Auteur chez Towards Data Science.

 
ORIGINALE. Republié avec permission.
 

Horodatage:

Plus de KDnuggetsGenericName