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ètrerandom_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’optiongamma='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.