Formation ML / Apprentissage non supervisé / Deep learning

Autoencoders

Intermédiaire 45 min 13 sections

Ce que le réseau reconstruit mal est ce qu'il n'a jamais vu : la fraude se détecte ainsi.

Objectifs d'apprentissage

  • Comprendre l'architecture encodeur-décodeur
  • Utiliser les autoencoders pour la réduction de dimensionnalité
  • Détecter des anomalies avec les autoencoders
  • Connaître les variantes (VAE, denoising)

Prérequis

Module Réseau de neurones recommandé

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

Théorie

Qu'est-ce qu'un autoencoder?

Un autoencoder est un réseau de neurones qui apprend à reconstruire son entrée après l'avoir comprimée.

Architecture:

  • Encodeur: Comprime l'entrée en une représentation plus petite (latent space)
  • Goulot d'étranglement: Représentation comprimée
  • Décodeur: Reconstruit l'entrée à partir de la représentation comprimée

Objectif:

Minimiser l'erreur de reconstruction: $L = ||x - \hat{x}||^2$

Intuition:

"Si je peux compresser une image et la reconstruire fidèlement, c'est que j'ai capture l'essentiel."

Applications:

  • Compression: Réduire la taille des données
  • Debruitage: Enlever le bruit (denoising autoencoder)
  • Détection d'anomalies: Les anomalies sont mal reconstruites
  • Génération: Variational Autoencoder (VAE)
  • Réduction de dimensionnalité: Alternative à PCA
Théorie

Schéma: Architecture Autoencoder

Structure de l'autoencoder:

flowchart LR I["Entrée
(784 dim)"] subgraph Encoder ["Encodeur"] E1["Dense 256"] E2["Dense 64"] end L["Latent Space
(16 dim)"] subgraph Decoder ["Décodeur"] D1["Dense 64"] D2["Dense 256"] end O["Sortie
(784 dim)"] I --> E1 --> E2 --> L --> D1 --> D2 --> O class L ml-node-accent

Compression et reconstruction:

flowchart TD X["x (entrée)"] Z["z = encode(x)
(comprime)"] X_hat["x^ = décode(z)
(reconstruit)"] L["Loss = ||x - x^||"] X --> Z --> X_hat --> L class Z ml-node-main class L ml-node-accent

Pour la détection d'anomalies:

  • Données normales: Bien reconstruites (faible erreur)
  • Anomalies: Mal reconstruites (forte erreur)

Exemple concret avec code couleur - Reconstruction et détection:

Entrée originale (4 features):

$x = [\textcolor{#3498db}{100}, \textcolor{#e67e22}{45}, \textcolor{#27ae60}{12}, \textcolor{#9B7AC4}{3.5}]$

(montant, heure, âge compte, nb transactions)

Étape 1 - Encodage (compression):

$z = \text{encode}(x) = [\textcolor{#F7E64D}{2.3}, \textcolor{#F7E64D}{-0.8}]$ (2 dimensions)

Étape 2 - Décodage (reconstruction):

$\hat{x} = \text{décode}(z) = [\textcolor{#3498db}{98}, \textcolor{#e67e22}{44}, \textcolor{#27ae60}{13}, \textcolor{#9B7AC4}{3.6}]$

Étape 3 - Erreur de reconstruction:

$\text{Erreur} = ||x - \hat{x}||^2 = (\textcolor{#3498db}{100-98})^2 + (\textcolor{#e67e22}{45-44})^2 + (\textcolor{#27ae60}{12-13})^2 + (\textcolor{#9B7AC4}{3.5-3.6})^2$

$\text{Erreur} = \textcolor{#3498db}{4} + \textcolor{#e67e22}{1} + \textcolor{#27ae60}{1} + \textcolor{#9B7AC4}{0.01} = \textcolor{#27ae60}{\mathbf{6.01}}$ (faible → normal)

Pour une anomalie (fraude):

$x_{fraude} = [5000, 3, 1, 0.1]$ → Erreur = $\textcolor{#e74c3c}{\mathbf{2500}}$ (élevée → anomalie!)

Légende des couleurs:

  • $\textcolor{#3498db}{Bleu}$: Montant de transaction
  • $\textcolor{#e67e22}{Orange}$: Heure de transaction
  • $\textcolor{#27ae60}{Vert}$: Âge du compte / faible erreur (normal)
  • $\textcolor{#9B7AC4}{Violet}$: Nombre de transactions
  • $\textcolor{#F7E64D}{Jaune}$: Espace latent (z)
  • $\textcolor{#e74c3c}{Rouge}$: Erreur élevée (anomalie)
Avancé Exercice manuel: À vous de calculer!

Objectif: Comprendre les autoencoders à la main (encodage, décodage, perte de reconstruction).

CONTEXTE

Autoencoder simple :

  • Entrée : vecteur 3D $x = [4, 6, 8]$
  • Encodeur : réduit en 1D (poids moyens)
  • Décodeur : reconstruit en 3D

Encodeur : $z = \frac{x_1 + x_2 + x_3}{3}$ (espace latent)

Décodeur : $\hat{x}_i = z$ pour tout i (copie de z)

PARTIE 1 : Encodage

1.1) Calculez le code latent z 1.2) Combien d'information a-t-on compressé ?

PARTIE 2 : Décodage

2.1) Reconstruisez $\hat{x}$ 2.2) Comparez $\hat{x}$ avec $x$

PARTIE 3 : Perte de reconstruction

MSE : $L = \frac{1}{n}\sum(x_i - \hat{x}_i)^2$

3.1) Calculez la perte MSE 3.2) Comment améliorer la reconstruction ?

PARTIE 4 : Application

4.1) Que représente z pour les données originales ? 4.2) Pourquoi les autoencoders sont utiles pour la réduction de dimension ?

Avancé Solution de l'exercice manuel

SOLUTION DÉTAILLÉE

$x = [4, 6, 8]$

PARTIE 1 : Encodage

1.1) Code latent :

$$z = \frac{4 + 6 + 8}{3} = \frac{18}{3} = \textcolor{#F7E64D}{\mathbf{6}}$$

1.2) Compression :

  • Entrée : 3 dimensions
  • Code : 1 dimension
  • Ratio : $\textcolor{#e74c3c}{3:1}$ (66% de compression)

PARTIE 2 : Décodage

2.1) Reconstruction :

$$\hat{x} = [z, z, z] = \textcolor{#27ae60}{[6, 6, 6]}$$

2.2) Comparaison :

xx_hatErreur
146-2
2660
386+2

PARTIE 3 : Perte de reconstruction

3.1) MSE :

$$L = \frac{(4-6)^2 + (6-6)^2 + (8-6)^2}{3}$$

$$= \frac{4 + 0 + 4}{3} = \frac{8}{3} = \textcolor{#e74c3c}{\mathbf{2.67}}$$

3.2) Améliorations :

  • Augmenter la dimension latente (z en 2D)
  • Utiliser un encodeur/décodeur non-linéaire (réseau de neurones)
  • Ajouter plus de couches

PARTIE 4 : Application

4.1) z = 6 représente la moyenne des données. C'est une "compression avec perte" qui capture la tendance centrale.

4.2) Utilité :

  • Réduction de dimension (comme PCA mais non-linéaire)
  • Détection d'anomalies (reconstruction élevée = anomalie)
  • Génération de données (VAE)

$\boxed{\text{Autoencoder = Compression + Reconstruction}}$

Légende : $\textcolor{#F7E64D}{Jaune}$: code latent, $\textcolor{#27ae60}{Vert}$: reconstruction, $\textcolor{#e74c3c}{Rouge}$: erreur

Code

Explorer les données (transactions)

print("=" * 70)
print("       EXPLORATION DES DONNEES DE TRANSACTIONS FINANCIERES")
print("=" * 70)
print("""
Les autoencoders excellent dans la DETECTION D'ANOMALIES car ils
apprennent ce qui est "normal" et signalent ce qui est "inhabituel".

Notre dataset simule des transactions bancaires avec:
- La grande majorite: transactions normales
- Une petite minorite: transactions frauduleuses (anomalies)

C'est un probleme de DESEQUILIBRE DE CLASSES tres realiste!
""")

# ========== 1. APERCU DES DONNEES ==========
print("=" * 70)
print("1. APERCU DU DATASET DE TRANSACTIONS")
print("=" * 70)
display(df.head(10), title="Echantillon de Transactions")

print("""
Chaque ligne represente une transaction avec:
- transaction_amount : Montant de la transaction (euros)
- transaction_time   : Heure de la transaction (0-24h)
- account_age        : Anciennete du compte (jours)
- num_transactions   : Nombre de transactions recentes
- is_anomaly         : 0 = normal, 1 = fraude potentielle
""")

# ========== 2. DIMENSIONS ==========
print("-" * 50)
print("2. DIMENSIONS DU DATASET")
print("-" * 50)
n_samples, n_features = df.shape
feature_cols = ['transaction_amount', 'transaction_time', 'account_age', 'num_transactions']
print(f"   Nombre de transactions : {n_samples}")
print(f"   Nombre de features     : {len(feature_cols)}")
print(f"   Features utilisees     : {', '.join(feature_cols)}")

# ========== 3. DISTRIBUTION DES CLASSES ==========
print("-" * 50)
print("3. DISTRIBUTION NORMALES vs ANOMALIES")
print("-" * 50)
counts = df['is_anomaly'].value_counts()
n_normal = counts.get(0, 0)
n_anomaly = counts.get(1, 0)
normal_pct = n_normal / len(df) * 100
anomaly_pct = n_anomaly / len(df) * 100

print(f"""
┌─────────────────────────────────────────────────┐
│  Transactions normales : {n_normal:5} ({normal_pct:5.1f}%)           │
│  Anomalies (fraudes)   : {n_anomaly:5} ({anomaly_pct:5.1f}%)            │
└─────────────────────────────────────────────────┘
""")

# ========== 4. INTERPRETATION DU DESEQUILIBRE ==========
print("-" * 50)
print("4. POURQUOI CE DESEQUILIBRE EST REALISTE?")
print("-" * 50)
ratio = n_normal / max(n_anomaly, 1)
print(f"""
Ratio normal/anomalie: {ratio:.1f}:1

Dans la vraie vie, les fraudes representent typiquement:
- Cartes bancaires : 0.1% des transactions
- Assurance        : 1-5% des reclamations
- Comptabilite     : <1% des ecritures

Notre dataset avec {anomaly_pct:.1f}% d'anomalies est deja
"genereux" en anomalies pour l'apprentissage!

DEFI: Detecter ces rares anomalies sans trop de faux positifs.
""")

# ========== 5. STATISTIQUES PAR GROUPE ==========
print("=" * 70)
print("5. COMPARAISON STATISTIQUE: NORMALES vs ANOMALIES")
print("=" * 70)
display(df.groupby('is_anomaly')[feature_cols].mean().round(2), title="Moyennes par groupe")

print("""
INTERPRETATION DES DIFFERENCES:
""")

# Calcul des differences
normal_stats = df[df['is_anomaly'] == 0][feature_cols].mean()
anomaly_stats = df[df['is_anomaly'] == 1][feature_cols].mean()

for col in feature_cols:
    diff_pct = (anomaly_stats[col] - normal_stats[col]) / max(normal_stats[col], 0.01) * 100
    direction = "↑ plus eleve" if diff_pct > 0 else "↓ plus bas"
    print(f"   {col:25}: {direction} de {abs(diff_pct):.0f}% pour les anomalies")

print("""
Ces differences sont les "signatures" que l'autoencoder apprendra
a reconnaitre comme inhabituelles!
""")
Ctrl+Entrée

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

Code

Visualiser les données

print("=" * 70)
print("       VISUALISATION DES DONNEES: NORMALES vs ANOMALIES")
print("=" * 70)
print("""
Avant de construire un autoencoder, visualisons les donnees pour
comprendre COMMENT les anomalies different des transactions normales.

L'autoencoder apprendra ces differences implicitement, mais nous
pouvons les observer directement avec des graphiques.
""")

# ========== SEPARATION DES DONNEES ==========
normal_df = df[df['is_anomaly'] == 0]
anomaly_df = df[df['is_anomaly'] == 1]

print(f"   Transactions normales a visualiser : {len(normal_df)}")
print(f"   Anomalies a visualiser             : {len(anomaly_df)}")

# ========== CREATION DES GRAPHIQUES ==========
print("\n" + "-" * 50)
print("GRAPHIQUES: Distribution et Separation")
print("-" * 50)

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

# --- Graphique 1: Distribution des montants ---
normal = df[df['is_anomaly'] == 0]['transaction_amount']
anomaly = df[df['is_anomaly'] == 1]['transaction_amount']

axes[0].hist(normal, bins=20, alpha=0.7, label=f'Normal (n={len(normal)})', color='#9B7AC4')
axes[0].hist(anomaly, bins=20, alpha=0.7, label=f'Anomalie (n={len(anomaly)})', color='#F7E64D')
axes[0].set_xlabel('Montant de transaction (euros)', fontsize=11)
axes[0].set_ylabel('Frequence', fontsize=11)
axes[0].set_title('Distribution des Montants', fontsize=12, fontweight='bold')
axes[0].legend(loc='upper right')
axes[0].grid(True, alpha=0.3)

# Ajouter lignes de moyenne
axes[0].axvline(normal.mean(), color='#9B7AC4', linestyle='--', linewidth=2, label='Moy. Normal')
axes[0].axvline(anomaly.mean(), color='#F7E64D', linestyle='--', linewidth=2, label='Moy. Anomalie')

# --- Graphique 2: Scatter 2D ---
axes[1].scatter(normal_df['transaction_amount'], normal_df['account_age'],
               alpha=0.6, label=f'Normal', color='#9B7AC4', s=40)
axes[1].scatter(anomaly_df['transaction_amount'], anomaly_df['account_age'],
               alpha=0.9, label=f'Anomalie', color='#F7E64D', s=120, marker='*', edgecolors='#e74c3c')
axes[1].set_xlabel('Montant (euros)', fontsize=11)
axes[1].set_ylabel('Age du compte (jours)', fontsize=11)
axes[1].set_title('Montant vs Age du Compte', fontsize=12, fontweight='bold')
axes[1].legend(loc='upper right')
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# ========== INTERPRETATION ==========
print("""
INTERPRETATION DES GRAPHIQUES:

Graphique 1 - Distribution des montants:
─────────────────────────────────────────
• Les transactions normales suivent une distribution "typique"
• Les anomalies ont souvent des montants ATYPIQUES (tres hauts ou tres bas)
• Les lignes en pointilles montrent la difference des moyennes

Graphique 2 - Espace 2D:
─────────────────────────────────────────
• Les anomalies (etoiles jaunes) apparaissent souvent dans des zones
  peu peuplees par les transactions normales
• Un autoencoder apprendra cette "zone normale" et signalera
  les points qui en sortent
""")

# ========== STATISTIQUES COMPLEMENTAIRES ==========
print("-" * 50)
print("STATISTIQUES DE SEPARATION")
print("-" * 50)
print(f"""
Montant moyen:
  - Normal  : {normal.mean():.2f} euros
  - Anomalie: {anomaly.mean():.2f} euros
  - Ecart   : {abs(anomaly.mean() - normal.mean()):.2f} euros

Age compte moyen:
  - Normal  : {normal_df['account_age'].mean():.1f} jours
  - Anomalie: {anomaly_df['account_age'].mean():.1f} jours

Observation: Les anomalies ont des patterns differents que
l'autoencoder apprendra a distinguer!
""")
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.