Les méthodes d'apprentissage supervisé de scikit-learn permettent de définir un objet, doté de différents attributs et méthodes, dont fit
(pour procéder à l'apprentissage), predict
(pour prédire les classes des éléments d'une base de test), ou score
pour calculer la proportion d'observations bien classées dans la base de test, lorsqu'on connaît par ailleurs la "vraie" classe.
Ci-dessous, un exemple d'utilisation, dans un scénario où on suppose disposer d'une base d'apprentissage $(X_{train},y_{train})$, et d'une base de test $X_{test}$ pour laquelle on connaît $y_{test}$.
# (le code suivant ne peut pas être exécuté "tel quel"...
# classifieur au plus proche voisin (on peut changer le paramètre n_neighbors):
knn = neighbors.KNeighborsClassifier(n_neighbors=1)
# on cherchera le p.p.v. dans X_train, la prédiction sera la classe de ce p.p.v., donnée par y_train:
knn.fit(X_train,y_train) # (il n'y a pas d'apprentissage à proprement parler pour les p.p.v.)
# on stocke dans y_pred les classes prédites sur un ensemble de test:
y_pred = knn.predict(X_test)
# calcul d'un score lorsqu'on connaît les vraies classes des observations de X_test:
# (proportion d'observations pour lesquelles y_test==y_pred)
score = knn.score(X_test,y_test)
Implantez une classification aux $K$ plus proches voisins de la base des chiffres manuscrits extraite de MNIST (cf cellule ci-dessous): vous utiliserez comme base d'apprentissage 50% des données de digits
, et vous calculerez le score de classification sur les 50% restants, lorsque $K$ varie entre 1 et 15.
Quelle valeur de $K$ semble la plus adaptée à ce problème?
from sklearn import datasets, neighbors, linear_model
%matplotlib inline
# dataset natif sklearn: (lignes à "décommenter" pour utiliser ce jeu de données)
# size_images=(8,8)
# digits = datasets.load_digits()
# X_digits = digits.data
# y_digits = digits.target
# Mnist database: (il faut quelques dizaines de secondes pour charger la base)
# les données sont décrites ici: https://www.openml.org/d/554
size_images=(28,28)
X_digits, y_digits = datasets.fetch_openml('mnist_784', version=1, return_X_y=True)
X_digits=X_digits[:2000,:]
y_digits=y_digits[:2000]
n_samples = len(X_digits)
print("nombre total d'observations (apprentissage + test): %d" % n_samples)
n_features = len(X_digits[0])
print("nombre de caractéristiques par observation: %d" % n_features)
X_train = X_digits[: int(.5*n_samples)]
y_train = y_digits[: int(.5*n_samples)]
X_test = X_digits[int(.5*n_samples) :]
y_test = y_digits[int(.5*n_samples) :]
# votre code ici:
Lorsqu'on fixe la base d'apprentissage et la base de test a priori, on introduit des fluctuations d'échantillonnage: une autre partition de la base de données originale entre apprentissage et test donnerait des scores légèrement différents. D'autre part, la partie de la base initiale réservée au test ne sert jamais pour l'apprentissage, ce qui est dommage.
Une manière de dépasser cette limitation est la validation croisée décrite sur wikipedia. Constatez que l'on vient d'implémenter la holdout method.
En vous inspirant de la syntaxe décrite dans la documentation sklearn, adaptez le code de la question précédente pour calculer une 5-fold cross validation ("validation croisée à 5 plis") et une 10-fold cross validation (ce sont les deux approches traditionnelles) sur toute la base digits
. Vous utiliserez la fonction cross_val_score
. On peut difficilement trop augmenter le nombre de "plis" car le temps de calcul grandit.
Constatez que le temps de calcul devient handicapant lorsque le nombre de plis augmente: ceci motive les approches n-fold cross validation par rapport à la validation leave-one-out.
from sklearn.model_selection import cross_val_score
# votre code ici:
La fonction suivante permet d'afficher les 150 premières images d'une base de test, ainsi que la classe déterminée par l'algorithme de classification et la classe véritable, qui est connue dans cet exercice.
import numpy as np
import matplotlib.pyplot as plt
def affichage_150_images(X_test,y_test,y_pred):
plt.figure(figsize=[15,12])
for n in range(150):
plt.subplot(10,15,n+1,xticks=[],yticks=[])
plt.imshow(np.reshape(X_test[n,:],size_images),cmap='gray_r',vmin=0,vmax=16)
if y_pred[n]==y_test[n]:
plt.text(0.1,0.1,str(y_pred[n])+' / '+str(y_test[n]),fontsize=6,bbox=dict(facecolor='white', alpha=1))
else:
plt.text(0.1,0.1,str(y_pred[n])+' / '+str(y_test[n]),fontsize=6,bbox=dict(facecolor='red', alpha=1))
plt.suptitle('classe predite / classe réelle');
# exemple d'utilisation: classification au plus proche voisin et bases train / test de la première question:
knn = neighbors.KNeighborsClassifier(n_neighbors=1) # essayez n_neighbors=10
%time knn.fit(X_train, y_train)
print('KNN score: %f' % knn.score(X_test, y_test))
%time y_pred_nn = knn.predict(X_test)
affichage_150_images(X_test,y_test,y_pred_nn)
Quelles sont les informations fournies par classification_report
et confusion_matrix
du module metrics
?
Voir la documentation
from sklearn import metrics
print(metrics.classification_report(y_test,y_pred_nn))
# cf http://scikit-learn.org/stable/modules/generated/sklearn.metrics.precision_recall_fscore_support.html#sklearn.metrics.precision_recall_fscore_support
print(metrics.confusion_matrix(y_test,y_pred_nn))
Comparez aux résultats obtenus par
la classification naïve bayésienne gaussienne décrite dans la documentation scikit-learn
à la régression logistique, décrite dans la documentation scikit-learn
Comparez également les temps de calcul.
# votre code ici: