Formation ML / Apprentissage supervisé - Classification / Statistiques bayésiennes

Naive Bayes

Débutant 35 min 13 sections

L'hypothèse d'indépendance des features est fausse presque partout, et pourtant elle classe vite et bien.

Objectifs d'apprentissage

  • Comprendre le théorème de Bayes et l'hypothèse naïve
  • Connaître les variantes (Gaussian, Multinomial, Bernoulli)
  • Implémenter Naive Bayes avec scikit-learn
  • Savoir quand utiliser cet algorithme

Prérequis

Notions de base en probabilités

Le code de cette page s'exécute dans votre navigateur. Le premier lancement télécharge Python et ses bibliothèques.

Théorie

Le théorème de Bayes

Naive Bayes est basé sur le théorème de Bayes, qui calcule la probabilité d'une classe étant donne les features.

Théorème de Bayes:

$$P(y|X) = \frac{P(X|y) \cdot P(y)}{P(X)}$$

Ou:

  • $P(y|X)$: Probabilité de la classe $y$ étant donne les features $X$ (ce qu'on cherche)
  • $P(X|y)$: Probabilité des features étant donne la classe (vraisemblance)
  • $P(y)$: Probabilité a priori de la classe
  • $P(X)$: Probabilité des features (constante pour toutes les classes)

L'hypothèse "Naive":

On suppose que les features sont indépendantes entre elles:

$$P(X|y) = P(x_1|y) \cdot P(x_2|y) \cdot ... \cdot P(x_n|y)$$

Cette hypothèse est souvent fausse en pratique, mais l'algorithme fonctionne quand même très bien!

Avantages:

  • Très rapide (entraînement et prédiction)
  • Fonctionne bien avec peu de données
  • Pas d'hyperparamètres complexes
  • Interprétable (probabilités)
Théorie

Schéma: Comment Naive Bayes décide

Processus de classification:

flowchart TD X["Nouvelles Features
(x1=0.5, x2=1.2)"] subgraph Calcul ["Calcul pour chaque classe"] C0["P(Classe 0) × P(x1|0) × P(x2|0)
= 0.4 × 0.6 × 0.3 = 0.072"] C1["P(Classe 1) × P(x1|1) × P(x2|1)
= 0.6 × 0.8 × 0.7 = 0.336"] end X --> C0 X --> C1 C0 --> D{"Comparer"} C1 --> D D --> R["Prédiction: Classe 1
(plus probable)"] class R ml-node-accent

Les 3 variantes de Naive Bayes:

flowchart LR NB["Naive Bayes"] G["GaussianNB
Features continues
(distribution normale)"] M["MultinomialNB
Comptages
(ex: texte, TF-IDF)"] B["BernoulliNB
Features binaires
(présence/absence)"] NB --> G NB --> M NB --> B class G ml-node-main class M ml-node-brand class B ml-node-secondary

Exemple concret - Classification d'un email (spam ou non):

Nouvel email avec features: $\textcolor{#3498db}{x_1 = 0.8}$ (mots suspects), $\textcolor{#e67e22}{x_2 = 0.6}$ (liens)

Le modèle a appris:

  • $\textcolor{#9B7AC4}{P(\text{Spam}) = 0.4}$, $\textcolor{#9B7AC4}{P(\text{Normal}) = 0.6}$
  • $\textcolor{#3498db}{P(x_1|\text{Spam}) = 0.9}$, $\textcolor{#3498db}{P(x_1|\text{Normal}) = 0.2}$
  • $\textcolor{#e67e22}{P(x_2|\text{Spam}) = 0.8}$, $\textcolor{#e67e22}{P(x_2|\text{Normal}) = 0.3}$

Calcul pour chaque classe:

$$P(\text{Spam}|X) \propto \textcolor{#9B7AC4}{0.4} \times \textcolor{#3498db}{0.9} \times \textcolor{#e67e22}{0.8} = \textcolor{#e74c3c}{\mathbf{0.288}}$$

$$P(\text{Normal}|X) \propto \textcolor{#9B7AC4}{0.6} \times \textcolor{#3498db}{0.2} \times \textcolor{#e67e22}{0.3} = \textcolor{#27ae60}{\mathbf{0.036}}$$

Décision: $\textcolor{#e74c3c}{0.288}$ > $\textcolor{#27ae60}{0.036}$ → SPAM

Légende des couleurs:

  • $\textcolor{#9B7AC4}{Violet}$ : probabilités a priori P(classe)
  • $\textcolor{#3498db}{Bleu}$ : vraisemblance de $x_1$ (mots suspects)
  • $\textcolor{#e67e22}{Orange}$ : vraisemblance de $x_2$ (liens)
  • $\textcolor{#e74c3c}{Rouge}$ / $\textcolor{#27ae60}{Vert}$ : scores finaux

Interprétation: L'email a beaucoup de mots suspects ($\textcolor{#3498db}{0.9}$ vs $\textcolor{#3498db}{0.2}$) et de liens ($\textcolor{#e67e22}{0.8}$ vs $\textcolor{#e67e22}{0.3}$), ce qui le rend 8x plus probable d'être du spam!

Avancé Exercice manuel: À vous de calculer!

Objectif: Appliquer le théorème de Bayes à la main pour la classification.

CONTEXTE

Classification de spam avec Naive Bayes. On observe le mot "gratuit" dans un email.

Statistiques du corpus :

  • P(SPAM) = 0.3 (30% des emails sont des spams)
  • P(HAM) = 0.7
  • P("gratuit" | SPAM) = 0.8 (80% des spams contiennent "gratuit")
  • P("gratuit" | HAM) = 0.1

Théorème de Bayes :

$$P(SPAM|gratuit) = \frac{P(gratuit|SPAM) \cdot P(SPAM)}{P(gratuit)}$$

PARTIE 1 : Probabilité totale

1.1) Calculez P("gratuit") = P("gratuit"|SPAM)P(SPAM) + P("gratuit"|HAM)P(HAM)

PARTIE 2 : Application de Bayes

2.1) Calculez P(SPAM | "gratuit") 2.2) Calculez P(HAM | "gratuit") 2.3) Quelle est la classification ?

PARTIE 3 : Avec un 2ème mot

L'email contient aussi "urgent". P("urgent"|SPAM) = 0.6, P("urgent"|HAM) = 0.2

3.1) Calculez P(SPAM | "gratuit", "urgent") (hypothèse Naive : indépendance) 3.2) Comment le 2ème mot affecte-t-il la confiance ?

Avancé Solution de l'exercice manuel

SOLUTION DÉTAILLÉE

PARTIE 1 : Probabilité totale

1.1) P("gratuit") :

$$P(gratuit) = 0.8 \times 0.3 + 0.1 \times 0.7$$

$$= 0.24 + 0.07 = \textcolor{#9B7AC4}{\mathbf{0.31}}$$

PARTIE 2 : Application de Bayes

2.1) P(SPAM | "gratuit") :

$$P(SPAM|gratuit) = \frac{0.8 \times 0.3}{0.31} = \frac{0.24}{0.31} = \textcolor{#e74c3c}{\mathbf{0.774}}$$

2.2) P(HAM | "gratuit") :

$$P(HAM|gratuit) = \frac{0.1 \times 0.7}{0.31} = \frac{0.07}{0.31} = \textcolor{#27ae60}{\mathbf{0.226}}$$

Vérification : $0.774 + 0.226 = 1$ ✓

2.3) Classification :

$\boxed{\text{SPAM avec 77.4\% de confiance}}$

PARTIE 3 : Avec un 2ème mot

3.1) Avec "urgent" (hypothèse Naive) :

Numérateur SPAM : $P(g|S) \cdot P(u|S) \cdot P(S) = 0.8 \times 0.6 \times 0.3 = 0.144$

Numérateur HAM : $P(g|H) \cdot P(u|H) \cdot P(H) = 0.1 \times 0.2 \times 0.7 = 0.014$

Total : $0.144 + 0.014 = 0.158$

$$P(SPAM|g,u) = \frac{0.144}{0.158} = \textcolor{#e74c3c}{\mathbf{0.911}}$$

3.2) Le 2ème mot augmente la confiance de 77.4% à 91.1%.

$\boxed{\text{Chaque mot discriminant renforce la classification}}$

Légende : $\textcolor{#e74c3c}{Rouge}$: SPAM, $\textcolor{#27ae60}{Vert}$: HAM, $\textcolor{#9B7AC4}{Violet}$: probabilités totales

Code

Explorer les données

print("=" * 70)
print("       EXPLORATION DES DONNEES POUR NAIVE BAYES")
print("=" * 70)

print("\n" + "=" * 70)
print("1. APERCU DU DATASET")
print("=" * 70)
print("""
Nous avons un dataset de classification binaire.
Naive Bayes va apprendre les distributions de probabilite de chaque
feature pour chaque classe, puis utiliser le theoreme de Bayes
pour classifier de nouvelles observations.
""")
display(df.head(10), title="Dataset de Classification")

print("\n" + "=" * 70)
print("2. DISTRIBUTION DES CLASSES (PROBABILITES A PRIORI)")
print("=" * 70)
print("""
Les probabilites a priori P(classe) representent la frequence
de chaque classe dans les donnees d'entrainement.
""")
class_counts = df['label'].value_counts().sort_index()
total = len(df)
print("Distribution des classes:")
print("-" * 40)
for label, count in class_counts.items():
    prior = count / total
    print(f"  Classe {label}: {count:4d} observations ({prior:.1%})")
    print(f"            → P(Classe {label}) = {prior:.3f}")

# Verifier l'equilibre
balance_ratio = class_counts.min() / class_counts.max()
print(f"\n  Ratio d'equilibre: {balance_ratio:.2f}")
if balance_ratio > 0.8:
    print("  → Classes bien equilibrees ✓")
elif balance_ratio > 0.5:
    print("  → Leger desequilibre (acceptable)")
else:
    print("  → Desequilibre important - attention aux metriques!")

print("\n" + "=" * 70)
print("3. STATISTIQUES PAR CLASSE")
print("=" * 70)
print("""
Naive Bayes Gaussien apprend la moyenne et la variance de chaque
feature pour chaque classe. Ces statistiques sont cruciales!
""")
display(df.groupby('label').describe().round(3), title="Statistiques par classe")

print("\n" + "-" * 40)
print("INTERPRETATION DES STATISTIQUES:")
print("-" * 40)
for feature in ['feature1', 'feature2']:
    mean_0 = df[df['label'] == 0][feature].mean()
    mean_1 = df[df['label'] == 1][feature].mean()
    std_0 = df[df['label'] == 0][feature].std()
    std_1 = df[df['label'] == 1][feature].std()
    separation = abs(mean_1 - mean_0) / ((std_0 + std_1) / 2)

    print(f"\n  {feature}:")
    print(f"    Classe 0: moyenne={mean_0:.2f}, ecart-type={std_0:.2f}")
    print(f"    Classe 1: moyenne={mean_1:.2f}, ecart-type={std_1:.2f}")
    print(f"    Separation des classes: {separation:.2f}")
    if separation > 1.5:
        print(f"    → Bonne separation - feature discriminante!")
    elif separation > 0.5:
        print(f"    → Separation moderee - feature utile")
    else:
        print(f"    → Faible separation - feature peu discriminante")

print("\n" + "=" * 70)
print("RESUME")
print("=" * 70)
print(f"""
Naive Bayes va utiliser:
• Les probabilites a priori (frequence des classes)
• La moyenne et variance de chaque feature par classe

Pour predire, il calcule P(classe|features) avec Bayes!
""")
Ctrl+Entrée

Cliquez sur « Exécuter » pour voir le résultat.

Code

Visualiser la distribution des features

print("=" * 70)
print("    VISUALISATION DES DISTRIBUTIONS PAR CLASSE")
print("=" * 70)
print("""
Naive Bayes Gaussien suppose que chaque feature suit une
distribution normale (gaussienne) pour chaque classe.

Visualisons si cette hypothese est raisonnable!
""")

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# Feature 1 par classe
for label in [0, 1]:
    data = df[df['label'] == label]['feature1']
    axes[0].hist(data, bins=15, alpha=0.6, label=f'Classe {label}',
                color='#9B7AC4' if label == 0 else '#F7E64D')
axes[0].set_xlabel('Feature 1')
axes[0].set_ylabel('Frequence')
axes[0].set_title('Distribution de Feature 1 par classe')
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# Feature 2 par classe
for label in [0, 1]:
    data = df[df['label'] == label]['feature2']
    axes[1].hist(data, bins=15, alpha=0.6, label=f'Classe {label}',
                color='#9B7AC4' if label == 0 else '#F7E64D')
axes[1].set_xlabel('Feature 2')
axes[1].set_ylabel('Frequence')
axes[1].set_title('Distribution de Feature 2 par classe')
axes[1].legend()
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("\n" + "=" * 70)
print("INTERPRETATION DES DISTRIBUTIONS")
print("=" * 70)

print("""
Ce que nous observons:

1. FORME DES DISTRIBUTIONS:
   → Si les histogrammes ressemblent a des cloches, l'hypothese
      gaussienne est raisonnable
   → Des distributions asymetriques peuvent reduire la precision

2. CHEVAUCHEMENT DES CLASSES:
   → Peu de chevauchement = classification plus facile
   → Beaucoup de chevauchement = zone d'incertitude importante

3. CENTRES DES DISTRIBUTIONS:
   → Plus les moyennes sont eloignees, plus les classes sont separables
   → C'est ce que Naive Bayes utilise pour decider!
""")

# Calculer le chevauchement approximatif
for feature in ['feature1', 'feature2']:
    data_0 = df[df['label'] == 0][feature]
    data_1 = df[df['label'] == 1][feature]

    overlap_min = max(data_0.min(), data_1.min())
    overlap_max = min(data_0.max(), data_1.max())

    if overlap_max > overlap_min:
        total_range = max(data_0.max(), data_1.max()) - min(data_0.min(), data_1.min())
        overlap_pct = (overlap_max - overlap_min) / total_range * 100
        print(f"\n  {feature}: {overlap_pct:.0f}% de chevauchement entre classes")
    else:
        print(f"\n  {feature}: Pas de chevauchement - separation parfaite!")

print("\n" + "-" * 40)
print("CONCLUSION")
print("-" * 40)
print("""
Naive Bayes suppose que chaque feature suit une distribution
gaussienne par classe. Meme si cette hypothese n'est pas
parfaitement respectee, l'algorithme fonctionne souvent bien!
""")
Ctrl+Entrée

Cliquez sur « Exécuter » pour voir le résultat.

Contenu verrouillé
6 / 13

Continuez votre apprentissage

Vous avez exploré 6 sections de ce module. Connectez-vous pour débloquer le reste du cours, incluant les exercices pratiques et les solutions.

Ctrl ou Cmd plus Entrée exécute le code. Tab indente ; Échap puis Tab quitte l'éditeur.