import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.svm import LinearSVC
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.svm import SVC
Données de travail¶
data = {}
# Définir la taille de la figure
plt.figure(figsize=(12, 10))
for i in [1, 2, 3]:
data['X_trainTp{}'.format(i)] = np.loadtxt('data/data_tp{}_app.txt'.format(i), usecols=(1, 2))
data['y_trainTp{}'.format(i)] = np.loadtxt('data/data_tp{}_app.txt'.format(i), usecols=0, dtype=int)
data['X_testTp{}'.format(i)] = np.loadtxt('data/data_tp{}_dec.txt'.format(i), usecols=(1, 2))
data['y_testTp{}'.format(i)] = np.loadtxt('data/data_tp{}_dec.txt'.format(i), usecols=0, dtype=int)
# Plot training data
plt.subplot(3, 2, 2 * i - 1)
plt.scatter(data['X_trainTp{}'.format(i)][:, 0], data['X_trainTp{}'.format(i)][:, 1],
c=data['y_trainTp{}'.format(i)], marker='o', label='Training Data - Problem {}'.format(i))
plt.title('Distribution des échantillons d\'apprentissage - Problème {}'.format(i))
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
# Plot test data
plt.subplot(3, 2, 2 * i)
plt.scatter(data['X_testTp{}'.format(i)][:, 0], data['X_testTp{}'.format(i)][:, 1],
c=data['y_testTp{}'.format(i)], marker='x', label='Test Data - Problem {}'.format(i))
plt.title('Distribution des échantillons de test - Problème {}'.format(i))
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.tight_layout()
plt.show()
Commençons par examiner comment utiliser l'implémentation par défaut d'un SVM :¶
# Générer des données aléatoires pour l'exemple
X, y = make_classification(n_samples=1000, n_features=20, n_informative=10, n_classes=2, random_state=42)
# Diviser les données en ensembles d'entraînement et de test
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Initialiser le modèle SVM avec les paramètres par défaut
clf = svm.SVC()
# Entraîner le modèle sur l'ensemble d'entraînement
clf.fit(X_train, y_train)
# Prédire les étiquettes de l'ensemble de test
y_pred = clf.predict(X_test)
# Évaluer la performance du modèle
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy}")
Accuracy: 0.96
Testez la solution svm.LinearSVC sur TP1 :¶
# Charger les données TP1
X_train_tp1 = np.loadtxt('data/data_tp1_app.txt', usecols=(1, 2))
y_train_tp1 = np.loadtxt('data/data_tp1_app.txt', usecols=0, dtype=int)
X_test_tp1 = np.loadtxt('data/data_tp1_dec.txt', usecols=(1, 2))
y_test_tp1 = np.loadtxt('data/data_tp1_dec.txt', usecols=0, dtype=int)
# Créer et entraîner le modèle LinearSVC
clf = LinearSVC()
clf.fit(X_train_tp1, y_train_tp1)
# Afficher la frontière de décision et les points d'entraînement/test
h = 0.02 # pas dans le maillage
x_min, x_max = X_train_tp1[:, 0].min() - 1, X_train_tp1[:, 0].max() + 1
y_min, y_max = X_train_tp1[:, 1].min() - 1, X_train_tp1[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.figure(figsize=(8, 6))
# Afficher la frontière de décision
plt.contourf(xx, yy, Z, cmap=plt.cm.coolwarm, alpha=0.8)
# Afficher les points d'entraînement
plt.scatter(X_train_tp1[:, 0], X_train_tp1[:, 1], c=y_train_tp1, marker='o', label='Training Data')
plt.scatter(X_test_tp1[:, 0], X_test_tp1[:, 1], c=y_test_tp1, marker='x', label='Test Data')
plt.title('Frontière de décision avec LinearSVC - TP1')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/sklearn/svm/_base.py:1250: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations. warnings.warn(
Utilisez la classe svm.SVC de scikit-learn sur TP2 et TP3 :¶
# Fonction pour entraîner et visualiser un modèle SVM
def train_and_plot_svm(kernel, C, **kwargs):
# Créer et entraîner le modèle SVM
clf = SVC(kernel=kernel, C=C, **kwargs)
clf.fit(X_train, y_train)
# Afficher la frontière de décision et les points d'entraînement/test
h = 0.02 # pas dans le maillage
x_min, x_max = X_train[:, 0].min() - 1, X_train[:, 0].max() + 1
y_min, y_max = X_train[:, 1].min() - 1, X_train[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.figure(figsize=(8, 6))
# Afficher la frontière de décision
plt.contourf(xx, yy, Z, cmap=plt.cm.coolwarm, alpha=0.8)
# Afficher les points d'entraînement/test
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, marker='o', label='Training Data')
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, marker='x', label='Test Data')
plt.title(f'Frontière de décision avec SVM - Kernel: {kernel}, C: {C}')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()
# Charger les données TP1
X_train = np.loadtxt('data/data_tp1_app.txt', usecols=(1, 2))
y_train = np.loadtxt('data/data_tp1_app.txt', usecols=0, dtype=int)
X_test = np.loadtxt('data/data_tp1_dec.txt', usecols=(1, 2))
y_test = np.loadtxt('data/data_tp1_dec.txt', usecols=0, dtype=int)
# Tester les SVM avec les paramètres spécifiés pour TP1
train_and_plot_svm(kernel='linear', C=1.0)
train_and_plot_svm(kernel='rbf', C=1.0, gamma=0.7)
train_and_plot_svm(kernel='poly', C=1.0, degree=3)
# Charger les données TP2
X_train = np.loadtxt('data/data_tp2_app.txt', usecols=(1, 2))
y_train = np.loadtxt('data/data_tp2_app.txt', usecols=0, dtype=int)
X_test = np.loadtxt('data/data_tp2_dec.txt', usecols=(1, 2))
y_test = np.loadtxt('data/data_tp2_dec.txt', usecols=0, dtype=int)
# Tester les SVM avec les paramètres spécifiés pour TP2
train_and_plot_svm(kernel='linear', C=1.0)
train_and_plot_svm(kernel='rbf', C=1.0, gamma=0.7)
train_and_plot_svm(kernel='poly', C=1.0, degree=3)
Importation des modules et chargement des données :
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier, export_graphviz
Chargement de la base de données Iris :
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.3, random_state=42)
Construction de l'arbre de décision avec les paramètres par défaut de l'algorithme :
clf = DecisionTreeClassifier()
clf.fit(X, y)
DecisionTreeClassifier()In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
DecisionTreeClassifier()
Exportation de la structure de l'arbre au format DOT :
# Exporter la structure de l'arbre au format DOT
export_graphviz(clf, out_file='tree.dot', feature_names=iris.feature_names, class_names=iris.target_names, filled=True, rounded=True)
Visualisation du graphe avec Graphviz
# Visualisation de l'arbre de décision
plt.figure(figsize=(20, 20))
plot_tree(clf, feature_names=iris.feature_names, class_names=iris.target_names, filled=True)
plt.show() # Afficher le graphique
# Chargement de la base de données Iris
iris = load_iris()
X = iris.data
y = iris.target
# Découpage de la base de données en ensemble d'apprentissage et ensemble de test
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=42)
# Apprentissage d'un arbre de décision
dt_classifier = DecisionTreeClassifier()
dt_classifier.fit(X_train, y_train)
DecisionTreeClassifier()In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
DecisionTreeClassifier()
# Apprentissage d'un Gaussian Naive Bayes
nb_classifier = GaussianNB()
nb_classifier.fit(X_train, y_train)
GaussianNB()In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
GaussianNB()
# Apprentissage d'un K-Plus Proches Voisins (KNN)
knn_classifier = KNeighborsClassifier()
knn_classifier.fit(X_train, y_train)
KNeighborsClassifier()In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
KNeighborsClassifier()
# Évaluation des performances sur l'ensemble de test
accuracy_dt = dt_classifier.score(X_test, y_test)
accuracy_nb = nb_classifier.score(X_test, y_test)
accuracy_knn = knn_classifier.score(X_test, y_test)
# Affichage des résultats
print(f"Taux de bonne classification pour l'arbre de décision : {accuracy_dt:.2f}")
print(f"Taux de bonne classification pour Gaussian Naive Bayes : {accuracy_nb:.2f}")
print(f"Taux de bonne classification pour K-Plus Proches Voisins : {accuracy_knn:.2f}")
Taux de bonne classification pour l'arbre de décision : 0.92 Taux de bonne classification pour Gaussian Naive Bayes : 0.99 Taux de bonne classification pour K-Plus Proches Voisins : 0.95
L'évaluation des performances des classifieurs sur l'ensemble de test de la base de données Iris a révélé des résultats significatifs. L'arbre de décision a atteint un taux de bonne classification de 92%, illustrant sa capacité à prendre des décisions précises sur de nouvelles données. En comparaison, le classifieur Gaussian Naive Bayes a démontré une performance remarquable avec un taux de bonne classification de 99%. Cette précision élevée peut être attribuée à son modèle probabiliste qui suppose une indépendance conditionnelle entre les caractéristiques, ce qui semble être bien adapté à la structure de la base de données Iris. Le K-Plus Proches Voisins (KNN) a également affiché des performances solides, atteignant un taux de bonne classification de 95%. Cette méthode, basée sur la proximité des échantillons dans l'espace des caractéristiques, a montré sa capacité à bien généraliser sur de nouvelles observations. Ces résultats suggèrent que le Gaussian Naive Bayes a dépassé les autres méthodes en termes de précision, mais chacun des classifieurs a présenté des performances encourageantes dans la classification des données Iris.
Utilisant une validation croisée:
5 Fold¶
from sklearn.model_selection import cross_val_score, KFold
# Définir les classifieurs
classifiers = {
'Arbre de Décision': DecisionTreeClassifier(),
'Gaussian Naive Bayes': GaussianNB(),
'K-Plus Proches Voisins': KNeighborsClassifier()
}
# Effectuer une validation croisée à 5 folds pour chaque classifieur
for clf_name, clf in classifiers.items():
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(clf, X, y, cv=kfold)
# Afficher les résultats
mean_accuracy = scores.mean()
std_accuracy = scores.std()
print(f"{clf_name} : Accuracy: {mean_accuracy:.2f} (+/- {std_accuracy:.2f})")
Arbre de Décision : Accuracy: 0.95 (+/- 0.03) Gaussian Naive Bayes : Accuracy: 0.96 (+/- 0.02) K-Plus Proches Voisins : Accuracy: 0.97 (+/- 0.02)
Ces valeurs représentent la moyenne des taux de bonne classification sur les cinq folds, accompagnée de l'écart-type qui offre une mesure de la variabilité des performances. L'arbre de décision, le Gaussian Naive Bayes, et le K-Plus Proches Voisins ont tous démontré une stabilité élevée dans leurs performances, avec des taux de classification similaires et des écarts-types limités.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.tree import DecisionTreeClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.neighbors import KNeighborsClassifier
# Générer une base de données synthétique en deux dimensions avec make_moons
X, y = make_moons(n_samples=500, noise=0.2, random_state=42)
# Définir les classifieurs
classifiers = {
'Arbre de Décision': DecisionTreeClassifier(),
'Gaussian Naive Bayes': GaussianNB(),
'K-Plus Proches Voisins': KNeighborsClassifier()
}
# Définir la fonction pour visualiser les frontières de décision
def plot_decision_frontiers(classifier, X, ax):
# Générer une grille de points 2D
h = .02 # pas de la grille
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
# Rassembler les prédictions de l'arbre pour chacun des points de la grille
Z = classifier.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# Afficher les points de la base d'apprentissage
ax.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k', marker='o', s=50, linewidth=1, cmap=plt.cm.Paired)
# Afficher les frontières de décisions
ax.contourf(xx, yy, Z, cmap=plt.cm.Paired, alpha=0.8)
# Créer une figure contenant 3 sous-figures
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# Appeler la fonction plot_decision_frontiers pour chacun des trois classifieurs
for ax, (clf_name, clf) in zip(axes, classifiers.items()):
clf.fit(X, y)
plot_decision_frontiers(clf, X, ax)
ax.set_title(clf_name)
plt.show()
Arbre de Décision : Les frontières de décision générées par l'arbre de décision se manifestent sous la forme de lignes droites ou de combinaisons de celles-ci. Il démontre une adaptation efficace aux formes géométriques simples présentes dans les données. Cependant, il peut éprouver des difficultés à appréhender des structures plus complexes.
Gaussian Naive Bayes : Les frontières de décision de Gaussian Naive Bayes sont de nature probabiliste, s'ajustant aux contours inhérents aux données. Ce modèle excelle lorsqu'il est appliqué à des données suivant des distributions gaussiennes, bien que ce ne soit pas nécessairement le cas dans cette situation particulière.
K-Plus Proches Voisins (KNN) : Les frontières de décision de KNN sont influencées par la proximité entre les points. L'exactitude de KNN dépend significativement du choix approprié du nombre de voisins, noté k. En optant judicieusement pour une valeur de k et en présence d'une structure nette dans les données, KNN peut produire des résultats précis dans ce contexte spécifique. Cependant, il peut manifester une sensibilité face au bruit et aux zones de densité variable.
# Génération de la nouvelle base de données
X, y = make_moons(n_samples=1000, noise=0.3, random_state=42)
# Instancier les classifieurs avec différents critères
clf_gini = DecisionTreeClassifier(criterion='gini')
clf_entropy = DecisionTreeClassifier(criterion='entropy')
# Entraîner les classifieurs sur les données
clf_gini.fit(X, y)
clf_entropy.fit(X, y)
DecisionTreeClassifier(criterion='entropy')In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
DecisionTreeClassifier(criterion='entropy')
plot_decision_frontiers(clf_gini, X, y, title='Decision Tree - Gini Criterion')
plot_decision_frontiers(clf_entropy, X, y, title='Decision Tree - Entropy Criterion')
from sklearn.model_selection import cross_val_score
# Estimer les performances avec validation croisée
scores_gini = cross_val_score(clf_gini, X, y, cv=5)
scores_entropy = cross_val_score(clf_entropy, X, y, cv=5)
# Afficher les résultats
print(f"Scores Gini: {scores_gini.mean()} (+/- {scores_gini.std()})")
print(f"Scores Entropy: {scores_entropy.mean()} (+/- {scores_entropy.std()})")
Scores Gini: 0.8850000000000001 (+/- 0.010488088481701525) Scores Entropy: 0.893 (+/- 0.009273618495495713)
Les résultats de la validation croisée mettent en lumière des performances globalement similaires entre les deux critères de partitionnement, Gini et Entropy, sur la base de données générée. En détaillant les scores moyens obtenus, le critère Gini présente une moyenne de validation croisée d'environ 0.885 avec un écart-type d'environ 0.0105, tandis que le critère Entropy affiche une moyenne légèrement supérieure, soit environ 0.893, avec un écart-type d'environ 0.0093. La proximité des scores moyens et des écarts-types suggère la robustesse des deux critères, leur conférant une stabilité lors de différentes divisions des données. La légère amélioration observée avec le critère Entropy suggère sa capacité à capturer une complexité potentielle dans les données, non prise en compte par le critère Gini. L'examen visuel des frontières de décision générées par les deux critères offre des perspectives supplémentaires, soulignant que des frontières similaires renforceraient l'idée d'une comparabilité des performances.
la profondeur maximum de l’arbre de décision / le nombre minimum de données par feuille¶
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import cross_val_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_moons
# Générer une base de données synthétique en deux dimensions avec la fonction make_moons
X, y = make_moons(n_samples=1000, noise=0.3, random_state=42)
# Définir les valeurs à tester pour la profondeur maximale et le nombre minimum de données par feuille
max_depth_values = np.arange(1, 31)
min_samples_leaf_values = np.arange(0.01, 0.51, 0.01)
# Initialiser des tableaux pour stocker les résultats
mean_scores_depth = []
mean_scores_min_samples_leaf = []
# Faire varier la profondeur maximale
for depth in max_depth_values:
clf = DecisionTreeClassifier(max_depth=depth)
scores = cross_val_score(clf, X, y, cv=5)
mean_scores_depth.append(np.mean(scores))
# Faire varier le nombre minimum de données par feuille
for min_samples_leaf in min_samples_leaf_values:
clf = DecisionTreeClassifier(min_samples_leaf=min_samples_leaf)
scores = cross_val_score(clf, X, y, cv=5)
mean_scores_min_samples_leaf.append(np.mean(scores))
# Tracer les courbes d'évolution
plt.figure(figsize=(8, 10))
plt.subplot(2, 1, 1)
plt.plot(max_depth_values, mean_scores_depth, marker='o')
plt.title('Evolution du taux moyen en fonction de la profondeur maximale')
plt.xlabel('Profondeur maximale')
plt.ylabel('Taux moyen de bonne classification')
plt.subplot(2, 1, 2)
plt.plot(min_samples_leaf_values, mean_scores_min_samples_leaf, marker='o')
plt.title('Evolution du taux moyen en fonction du nombre minimum de données par feuille')
plt.xlabel('Nombre minimum de données par feuille (en pourcentage)')
plt.ylabel('Taux moyen de bonne classification')
plt.tight_layout()
plt.show()
Pour étudier l'impact des paramètres sur les performances, une expérimentation a été menée en faisant varier la profondeur maximale de l'arbre de décision de 1 à 30 et le nombre minimum de données par feuille en pourcentage de 1% à 50%. À chaque valeur, une procédure de validation croisée a été appliquée, en enregistrant le taux moyen de bonne classification dans un tableau. Les résultats ont été synthétisés à travers des courbes représentant l'évolution du taux moyen de bonne classification en fonction des valeurs de chaque paramètre.
En analysant ces courbes, plusieurs observations peuvent être notées. Pour la profondeur maximale, on observe une tendance croissante du taux de bonne classification jusqu'à un certain point, après quoi la performance stagne. Cela suggère qu'une profondeur plus élevée n'améliore pas nécessairement les performances et peut même conduire à un surajustement du modèle. En ce qui concerne le nombre minimum de données par feuille, une augmentation progressive de ce paramètre semble améliorer la performance jusqu'à un certain seuil, au-delà duquel la performance diminue. Ceci souligne l'importance de trouver un équilibre pour optimiser les performances du modèle.