Apprentissage semi-supervisé: Cybersécurité

L’apprentissage semi-supervisé en cybersécurité est une méthode qui exploite à la fois des données étiquetées, souvent rares et coûteuses à obtenir, et un grand volume de données non étiquetées pour améliorer la détection des menaces et des comportements malveillants. Cette approche permet de renforcer la capacité des systèmes à identifier les attaques, les logiciels malveillants ou les anomalies réseau, même lorsque les exemples connus sont limités, en tirant parti des informations présentes dans les données non annotées. Ainsi, l’apprentissage semi-supervisé contribue à rendre les solutions de cybersécurité plus efficaces et adaptatives face à des menaces en constante évolution.

Fonctions :

  • Cybersécurité (exemple)

    La cybersécurité désigne l’ensemble des techniques et pratiques visant à protéger les systèmes informatiques, les réseaux et les données contre les attaques, les intrusions ou les accès non autorisés. Par exemple, un système de détection d’intrusion analyse le trafic réseau en temps réel pour repérer des comportements suspects et prévenir des tentatives de piratage. Demander à ChatGPT

    Exemple de code :

    import tensorflow as tf
    import numpy as np
    
    # Simulation de données réseau (features numériques)
    n_features = 20
    n_labeled = 500    # peu de données étiquetées
    n_unlabeled = 4500 # beaucoup de données non étiquetées
    n_val = 1000       # données de validation
    
    def generate_data(n, labeled=True):
        X = np.random.randn(n, n_features).astype(np.float32)
        if labeled:
            # 0 = trafic normal, 1 = attaque, labels binaires aléatoires
            y = np.random.randint(0, 2, size=(n, 1)).astype(np.float32)
            return X, y
        else:
            return X
    
    X_labeled, y_labeled = generate_data(n_labeled)
    X_unlabeled = generate_data(n_unlabeled, labeled=False)
    X_val, y_val = generate_data(n_val)
    
    # Modèle simple réseau dense
    model = tf.keras.Sequential([
        tf.keras.layers.Dense(64, activation='relu', input_shape=(n_features,)),
        tf.keras.layers.Dense(32, activation='relu'),
        tf.keras.layers.Dense(1)  # sortie logits pour classification binaire
    ])
    
    optimizer = tf.keras.optimizers.Adam()
    loss_fn = tf.keras.losses.BinaryCrossentropy(from_logits=True)
    batch_size = 64
    consistency_weight = 1.0
    epochs = 10
    
    # Fonction d'augmentation simple (ajoute du bruit gaussien sur features non étiquetées)
    def augment_features(x):
        noise = tf.random.normal(shape=tf.shape(x), mean=0.0, stddev=0.1)
        return x + noise
    
    # Création des datasets TensorFlow
    labeled_ds = tf.data.Dataset.from_tensor_slices((X_labeled, y_labeled)).shuffle(n_labeled).batch(batch_size).repeat()
    unlabeled_ds = tf.data.Dataset.from_tensor_slices(X_unlabeled).shuffle(n_unlabeled).batch(batch_size).repeat()
    val_ds = tf.data.Dataset.from_tensor_slices((X_val, y_val)).batch(batch_size)
    
    labeled_iter = iter(labeled_ds)
    unlabeled_iter = iter(unlabeled_ds)
    
    for epoch in range(epochs):
        print(f"Epoch {epoch+1}/{epochs}")
        total_loss = 0
        steps_per_epoch = n_labeled // batch_size
    
        for step in range(steps_per_epoch):
            x_lab, y_lab = next(labeled_iter)
            x_unlab = next(unlabeled_iter)
            x_unlab_aug = augment_features(x_unlab)
    
            with tf.GradientTape() as tape:
                # Perte supervision sur données étiquetées
                logits_lab = model(x_lab, training=True)
                loss_sup = loss_fn(y_lab, logits_lab)
    
                # Consistency regularization sur données non étiquetées
                logits_unlab = model(x_unlab, training=True)
                logits_unlab_aug = model(x_unlab_aug, training=True)
                prob = tf.sigmoid(logits_unlab)
                prob_aug = tf.sigmoid(logits_unlab_aug)
                loss_consistency = tf.reduce_mean(tf.square(prob - prob_aug))
    
                loss = loss_sup + consistency_weight * loss_consistency
    
            grads = tape.gradient(loss, model.trainable_variables)
            optimizer.apply_gradients(zip(grads, model.trainable_variables))
            total_loss += loss.numpy()
    
        print(f"Loss moyenne: {total_loss/steps_per_epoch:.4f}")
    
    # Évaluation finale
    acc_metric = tf.keras.metrics.BinaryAccuracy()
    for x_batch, y_batch in val_ds:
        logits = model(x_batch, training=False)
        preds = tf.sigmoid(logits)
        acc_metric.update_state(y_batch, preds)
    print(f"Accuracy validation: {acc_metric.result().numpy():.4f}")

    Explication du code :

    1. Simulation et préparation des données
    - Les données simulées contiennent 20 caractéristiques numériques par échantillon. - On crée 3 ensembles : - 500 exemples étiquetés (`X_labeled`, `y_labeled`) avec labels binaires aléatoires (0 = normal, 1 = attaque). - 4500 exemples non étiquetés (`X_unlabeled`). - 1000 exemples de validation (`X_val`, `y_val`).
    2. Définition du modèle dense simple pour classification binaire
    Le modèle est un réseau séquentiel composé de : - une couche dense de 64 neurones avec activation ReLU, prenant en entrée les 20 features, - une couche dense de 32 neurones ReLU, - une couche dense finale avec 1 neurone produisant un logit (sortie avant sigmoid).
    3. Configuration de l’entraînement
    - optimizer = tf.keras.optimizers.Adam() : optimiseur Adam pour la mise à jour des poids. - loss_fn = tf.keras.losses.BinaryCrossentropy(from_logits=True) : fonction de perte binaire adaptée aux logits. - batch_size = 64, consistency_weight = 1.0 (poids de la perte de consistance), epochs = 10 définissent les paramètres d’entraînement.
    4. Fonction d’augmentation des features
    augment_features(x) ajoute un bruit gaussien (bruit normal centré réduit) aux données non étiquetées, simulant de petites perturbations pour rendre le modèle plus robuste.
    5. Création des datasets TensorFlow
    - labeled_ds contient les données étiquetées, mélangées, découpées en batchs, et répétées indéfiniment. - unlabeled_ds contient les données non étiquetées, également mélangées, batchées et répétées. - val_ds contient les données de validation batchées.
    6. Boucle d’entraînement semi-supervisé
    Pour chaque époque et batch : - On récupère un batch étiqueté (x_lab, y_lab) et un batch non étiqueté (x_unlab). - On crée une version augmentée du batch non étiqueté (x_unlab_aug) via augment_features. - Calcul des logits sur données étiquetées et calcul de la perte supervisée (loss_sup) avec les vraies étiquettes. - Calcul des logits sur données non étiquetées normales et augmentées. - Conversion des logits en probabilités (sigmoid). - Calcul d’une perte de consistance (loss_consistency) : moyenne des carrés des différences entre les probabilités des données normales et augmentées, pour encourager la stabilité des prédictions malgré l’augmentation. - La perte totale est la somme : loss = loss_sup + consistency_weight * loss_consistency. - Les gradients sont calculés puis appliqués pour mettre à jour les poids du modèle. - Affichage de la perte moyenne par époque.
    7. Évaluation finale
    - Calcul de la précision sur le jeu de validation en comparant les prédictions (après sigmoid) aux vraies étiquettes avec BinaryAccuracy(). - Affichage de la précision finale sur les données de validation. Ce code met en œuvre un entraînement semi-supervisé combinant une supervision classique sur un petit ensemble étiqueté et une régularisation par consistance sur un grand ensemble non étiqueté, ce qui aide à améliorer la robustesse et la performance du modèle sur des données numériques de classification binaire.