Semi-Supervised SVM (S3VM)

Le Semi-Supervised Support Vector Machine (S3VM) est une extension du SVM (Support Vector Machine) classique qui permet d’exploiter les données étiquetées et non étiquetées. Alors qu’un SVM standard utilise uniquement des données étiquetées pour créer une hyperplan séparant les différentes classes, un S3VM utilise à la fois des données étiquetées et non étiquetées. L’idée est de trouver une frontière de décision qui non seulement sépare les données étiquetées de manière optimale mais aussi prend en compte la structure des données non étiquetées pour mieux généraliser.

Le S3VM fonctionne en optimisant à la fois la séparation des points étiquetés et l’alignement des points non étiquetés, en cherchant à maximiser la marge entre les classes tout en respectant la structure des données non étiquetées.

Importation : L’implémentation standard de S3VM n’est pas directement disponible dans scikit-learn. Cependant, on peut utiliser des bibliothèques tierces ou implémenter cette méthode nous-mêmes. Une des bibliothèques disponibles est libsvm (une implémentation populaire des SVM), mais elle nécessite un certain travail pour l’adapter au semi-supervisé.

Il existe également des implémentations dans d’autres packages comme scikit-learn via la fonction LabelSpreading ou LabelPropagation, bien que ceux-ci ne soient pas exactement S3VM, mais plutôt des techniques basées sur des graphes.

Voici une approche générale de l’implémentation d’un S3VM avec une bibliothèque tierce telle que pytorch ou libsvm :

Fonctions :

  • Approche semi-supervisée utilisant libsvm

    Voici un exemple simple d'utilisation de la méthode semi-supervisée S3VM avec libsvm. Dans cet exemple, nous supposons que nous avons un jeu de données partiellement étiqueté, où certains labels sont inconnus (étiquetés comme -1).

    Importation :

    from sklearn import datasets
    from sklearn.model_selection import train_test_split
    from libsvm import svm  # Une implémentation de SVM semi-supervisé (besoin d'une implémentation tierce comme `libsvm`)

    Attributs :

    
    Paramètre
    Description
    C Paramètre de régularisation qui contrôle la pénalité appliquée aux erreurs dans le modèle. Un grand C conduit à moins d'erreurs mais plus d'overfitting.
    gamma Paramètre du noyau, qui définit l'influence des échantillons d'entraînement. Il contrôle la "profondeur" du noyau dans le calcul de la frontière de décision.
    kernel Type de noyau utilisé. Les noyaux possibles sont "linear", "polynomial", "radial basis function (rbf)", et "sigmoid".
    nu Paramètre de contrôle pour la fraction de points mal classés, utilisé dans l'implémentation semi-supervisée.
    tol Critère de tolérance pour l'optimisation. Plus il est petit, plus l'algorithme s'exécutera de manière précise mais coûteuse.
    max_iter Limite sur le nombre d'itérations lors de l'entraînement. Peut être défini à -1 pour indiquer aucune limite.

    Exemple de code :

    import numpy as np
    from sklearn.datasets import make_classification
    from sklearn.svm import SVC
    from sklearn.metrics import accuracy_score
    from sklearn.model_selection import train_test_split
    
    # 1. Générer des données
    X, y = make_classification(n_samples=1000, n_features=20, n_classes=2, random_state=42)
    
    # 2. Masquer 70 % des labels
    rng = np.random.RandomState(42)
    unlabeled_mask = rng.rand(len(y)) < 0.7
    y_partial = np.copy(y)
    y_partial[unlabeled_mask] = -1  # données non étiquetées
    
    # 3. Séparer train/test
    X_train, X_test, y_train_partial, _, _, y_test = train_test_split(X, y_partial, y, test_size=0.3, random_state=42)
    
    # 4. Initialiser SVM (libsvm backend)
    svm = SVC(probability=True, kernel='rbf', gamma='scale')
    
    # 5. Itérations de self-labeling
    for iteration in range(10):
        labeled_mask = y_train_partial != -1
        unlabeled_mask = y_train_partial == -1
    
        # Stop si tout est étiqueté
        if not np.any(unlabeled_mask):
            break
    
        # Entraîner sur les données étiquetées
        svm.fit(X_train[labeled_mask], y_train_partial[labeled_mask])
    
        # Prédire sur les données non étiquetées
        probs = svm.predict_proba(X_train[unlabeled_mask])
        preds = svm.predict(X_train[unlabeled_mask])
        confidence = np.max(probs, axis=1)
    
        # Seuil de confiance
        confident_idx = np.where(confidence > 0.9)[0]
    
        if len(confident_idx) == 0:
            print("Aucune prédiction assez confiante à cette itération.")
            break
    
        # Mettre à jour les labels
        unlabeled_indices = np.where(unlabeled_mask)[0]
        confident_indices = unlabeled_indices[confident_idx]
        y_train_partial[confident_indices] = preds[confident_idx]
    
        print(f"Iteration {iteration+1} — Ajout de {len(confident_indices)} exemples avec confiance > 0.9")
    
    # 6. Évaluer sur les données de test
    y_pred = svm.predict(X_test)
    print("Accuracy finale :", accuracy_score(y_test, y_pred))

    Explication du code :

    import numpy as np importe la bibliothèque NumPy, essentielle pour la manipulation efficace des tableaux et le calcul vectoriel.
    
    from sklearn.datasets import make_classification importe la fonction make_classification, utilisée pour créer un jeu de données synthétique adapté à la classification.
    
    from sklearn.svm import SVC importe la classe SVC (Support Vector Classifier), un puissant classificateur à marge maximale basé sur les machines à vecteurs de support.
    
    from sklearn.metrics import accuracy_score importe la fonction accuracy_score, qui permet de mesurer la précision du modèle en comparant les prédictions aux vraies étiquettes.
    
    from sklearn.model_selection import train_test_split importe la fonction train_test_split qui divise le jeu de données en sous-ensembles d’entraînement et de test.
    
    
    1. Générer des données
    X, y = make_classification(n_samples=1000, n_features=20, n_classes=2, random_state=42) génère un jeu de 1000 échantillons avec 20 caractéristiques chacun, répartis en 2 classes. Le paramètre random_state=42 garantit la reproductibilité.
    2. Masquer 70 % des labels
    rng = np.random.RandomState(42) crée un générateur de nombres aléatoires reproductible. unlabeled_mask = rng.rand(len(y)) < 0.7 crée un masque booléen qui désigne 70% des échantillons comme non étiquetés. y_partial = np.copy(y) fait une copie des étiquettes originales pour préserver les données initiales. y_partial[unlabeled_mask] = -1 remplace les étiquettes de 70 % des données par -1, indiquant qu’elles sont non étiquetées.
    3. Séparer train/test
    train_test_split divise les données en ensembles d’entraînement (70%) et de test (30%) en tenant compte à la fois des données étiquetées partiellement (y_partial) et des vraies étiquettes (y).
    4. Initialiser le classificateur SVM
    svm = SVC(probability=True, kernel='rbf', gamma='scale') crée un classificateur SVM avec un noyau RBF (fonction de base radiale) qui peut estimer des probabilités de classe grâce à probability=True. L’option gamma='scale' ajuste automatiquement un paramètre important du noyau.
    5. Itérations de self-labeling
    Un processus itératif est mis en place pour exploiter les données non étiquetées : - À chaque itération, on identifie les données étiquetées (y_train_partial != -1) et non étiquetées (y_train_partial == -1). - Si toutes les données sont étiquetées, la boucle s’arrête. - Le SVM est entraîné sur les données étiquetées uniquement. - Le modèle prédit les probabilités et classes pour les données non étiquetées. - Un seuil de confiance à 0.9 est fixé : seules les prédictions avec une probabilité maximale supérieure à ce seuil sont retenues. - Les étiquettes des données non étiquetées très confiantes sont mises à jour avec ces prédictions. - Le nombre d’exemples ajoutés à chaque étape est affiché. Ce mécanisme permet d’enrichir progressivement le jeu d’entraînement en pseudo-étiquettes fiables.
    6. Évaluer sur les données de test
    y_pred = svm.predict(X_test) génère les prédictions finales sur les données de test. accuracy_score(y_test, y_pred) calcule la précision finale du modèle. print("Accuracy finale :", accuracy_score(y_test, y_pred)) affiche cette précision. Ce code met en œuvre une stratégie d’apprentissage semi-supervisé appelée self-labeling (auto-étiquetage), où un modèle est initialement entraîné sur un sous-ensemble étiqueté, puis utilisé pour prédire et assigner des étiquettes fiables aux données non étiquetées, améliorant ainsi progressivement ses performances.