VI-ME-RÉ-BAR sur vos propres données!
Contenu
VI-ME-RÉ-BAR sur vos propres données!¶
Instructions:
Vous effacerez les instructions au fur et à mesure que vous les aurez suivies. Commencez par effacer celle-ci!
Mettez ici une description de votre jeu de données: lequel avez vous choisi, quel est le défi? Intuitivement quels critères pourraient permettre de distinguer les deux classes d’images?
BEGIN SOLUTION
Nous avons choisir le jeu de données consistant en dix images de poules et dix images de canards.
Les deux espèces ont des
représentants de couleurs variées; il ne paraît donc pas évident
a priori de les séparer sur des critères de couleur. Les canards
pourraient être plus allongés que les poules. L’attribut
elongation
est peut-être encore pertinent.
END SOLUTION
# Load general libraries
import os, re
from glob import glob as ls
import numpy as np # Matrix algebra library
import pandas as pd # Data table (DataFrame) library
import seaborn as sns; sns.set() # Graphs and visualization library
from PIL import Image # Image processing library
import matplotlib.pyplot as plt # Library to make graphs
# Command to insert the graphs in line in the notebook:
%matplotlib inline
# Reload code when changes are made
%load_ext autoreload
%autoreload 2
# Import utilities
from intro_science_donnees import *
from utilities import *
Étape 1: prétraitement et [VI]sualisation¶
Le jeu de données consiste en les images suivantes:
Instruction : Chargez votre jeu de données comme dans la feuille
3_jeux_de_donnees.md
de la semaine dernière, en stockant les
images dans la variables images
et en les affichant.
### BEGIN SOLUTION
from intro_science_donnees import data
dataset_dir = os.path.join(data.dir, 'Farm')
images = load_images(dataset_dir, "*.jpeg")
image_grid(images, titles=images.index)
### END SOLUTION
assert isinstance(images, pd.Series)
assert len(images) == 20
Prétraitement¶
Les données sont très souvent prétraitées c’est-à-dire résumées selon différentes caractéristiques : chaque élément du jeu de données est décrit par un ensemble d’attributs – propriétés ou caractéristiques mesurables de cet élément ; pour un animal, cela peut être sa taille, sa température corporelle, etc.
C’est également le cas dans notre jeu de données : une image est décrite par le couleur de chacun de ses pixels. Cependant les pixels sont trop nombreux pour nos besoins. Nous voulons comme la semaine dernière les remplacer par quelques attributs mesurant quelques propriétés essentielles de l’image, comme sa couleur ou sa forme moyenne: ce sont les données prétraitées.
La semaine dernière, les données prétraitées vous ont été fournies pour les pommes et les bananes. Cette semaine, grâce aux trois feuilles précédentes, vous avez les outils et connaissances nécessaires pour effectuer le prétraitement directement vous-même:
la feuille de rappel sur la gestion de tableaux;
la feuille sur le traitement des images;
la feuille sur l”extraction d’attributs.
Pour commencer, la table prétraitée contient les attributs redness
et elongation
– tels que vous les avez défini dans la feuille
extraction d’attributs – appliqués à
votre jeu de données »:
df = pd.DataFrame({
'redness': images.apply(redness),
'elongation': images.apply(elongation),
'class': images.index.map(lambda name: 1 if name[0] == 'a' else -1),
})
df
Exercice :
Implémentez dans
utilities.py
de nouveaux attributs adaptés à votre jeu de données. Si vous en avez besoin, vous pouvez utiliser les cellules ci-dessous voire en créer de nouvelles; sinon simplement videz les.
Indications: vous pouvez par exemple vous inspirer
des attributes existants comme
redness
;des exemples donnés dans le cours: matched filter, analyse en composantes principales (PCA).
### BEGIN SOLUTION
# Calcule puis affiche une image représentative des images de poules,
# respectivement de canard.
# Ici l'image représentative est calculée par moyennage des images
correlation_average_A = MatchedFilter(images[:10])
correlation_average_B = MatchedFilter(images[10:])
display(correlation_average_A.show())
display(correlation_average_B.show())
# Ici l'image représentative est calculée en prenant la composante principale des images
correlation_PC_A = PCAFilter(images[:10])
correlation_PC_B = PCAFilter(images[10:])
display(correlation_PC_A.show())
display(correlation_PC_B.show())
### END SOLUTION
### BEGIN SOLUTION
# Pour les deux attributs suivants, le principe est de corréler
# l'image en argument avec l'image représentative des poules
# et celle des canards et de faire la différence
def correlation_average(img):
return correlation_average_A.match(img) - correlation_average_B.match(img)
def correlation_PC(img):
return correlation_PC_A.match(img) - correlation_PC_B.match(img)
### END SOLUTION
Comment les avez-vous choisis?
BEGIN SOLUTION
Par curiosité du fonctionnement des deux attributs vus en cours! Et aussi parce que les critères de couleurs n’étant a priori pas trop pertinent, il paraît plausible de jouer plutôt sur la forme.
END SOLUTION
Ajoutez une colonne par attribut dans la table
df
, en conservant les précédents
### BEGIN SOLUTION
df['correlation average'] = images.apply(correlation_average)
df['correlation PC'] = images.apply(correlation_PC)
### END SOLUTION
Vérifications:
la table d’origine est préservée:
assert len(df[df['class'] == 1]) == 10
assert len(df[df['class'] == -1]) == 10
assert 'redness' in df.columns
assert 'elongation' in df.columns
Nouveaux attributs:
assert len(df.columns) > 3, "Ajoutez au moins un attribut!"
assert df.notna().all(axis=None), "Valeurs manquantes!"
for attribute in df.columns[3:]:
assert pd.api.types.is_numeric_dtype(df[attribute]), \
f"L'attribut {attribute} n'est pas numérique"
assert len(df.columns) > 4, "Gagnez un point en ajoutant un autre attribut"
Exercice : Standardisez les colonnes à l’exception de la colonne
class
, afin de calculer les corrélations entre colonnes
### BEGIN SOLUTION
dfstd = (df - df.mean()) / df.std()
dfstd['class'] = df['class']
### END SOLUTION
dfstd
Vérifions :
dfstd.describe()
assert dfstd.shape == df.shape
assert dfstd.index.equals(df.index)
assert dfstd.columns.equals(df.columns)
assert (abs(dfstd.mean()) < 0.01).all()
assert (abs(dfstd.std() - 1) < 0.1).all()
Le prétraitement est terminé!
Visualisation¶
Exercice : Extrayons quelques statistiques de base:
### BEGIN SOLUTION
df.describe()
### END SOLUTION
Exercice :
Visualisez le tableau de données sous forme de carte de chaleur (heat map):
### BEGIN SOLUTION
df.style.background_gradient(cmap='coolwarm')
### END SOLUTION
sa matrice de corrélation:
### BEGIN SOLUTION
df.corr().style.background_gradient(cmap='coolwarm', axis=None)
### END SOLUTION
ainsi que le nuage de points (scatter plot):
### BEGIN SOLUTION
sns.pairplot(dfstd, hue="class", diag_kind="hist", palette='coolwarm');
### END SOLUTION
Observations¶
Exercice : Décrivez ici vos observations: corrélations apparentes ou pas, interprétation de ces corrélations à partir du nuage de points, etc. Est-ce que les attributs choisis semblent suffisants? Quel attribut semble le plus discriminant? Est-ce qu’un seul d’entre eux suffirait?
BEGIN SOLUTION
La rougeur et l’élongation sont finalement assez (anti)-corrélées avec la classe, mais il y a beaucoup de valeurs aberrantes.
La corrélation avec la composante principale est très décevante.
En revanche, l’attribut correlation_average
est complètement
discriminant et suffit à lui tout seul.
END SOLUTION
Étape 2: [ME]sure de performance ([ME]tric)¶
Pour mesurer les performances de ce problème de classification, nous utiliserons la même métrique par taux d’erreur que dans le TP3:
show_source(error_rate)
Partition (split) du jeu de données en ensemble d’entraînement et ensemble de test¶
Extraire, depuis dfstd
, les deux attributs choisis dans X
et la vérité terrain dans
Y
:
### BEGIN SOLUTION
X = dfstd[['correlation average', 'correlation PC']]
Y = dfstd['class']
### END SOLUTION
Ajouter un autotest que les attributs ne sont pas redness/elongation : un nouvel attribut ; deux nouveaux attributs
assert isinstance(X, pd.DataFrame), "X n'est pas une table Pandas"
assert X.shape == (20,2), "X n'est pas de la bonne taille"
assert set(X.columns) != {'redness', 'elongation'}
assert 'redness' not in X.columns and 'elongation' not in X.columns, \
"Pour un point de plus: ne réutiliser ni la rougeur, ni l'élongation"
Exercice : Maintenant partitionnez l’index des images en ensemble
d’entraînement (train_index
) et ensemble de test
(test_index
). Récupérez les attributs et classes de vos images selon
l’ensemble d’entraînement (Xtrain, Ytrain)
et celui de test (Xtest, Ytest)
.
### BEGIN SOLUTION
train_index, test_index = split_data(X, Y, verbose = True, seed=0)
Xtrain, Xtest = X.iloc[train_index], X.iloc[test_index]
Ytrain, Ytest = Y.iloc[train_index], Y.iloc[test_index]
### END SOLUTION
assert train_index.shape == test_index.shape
assert list(sorted(np.concatenate([train_index, test_index]))) == list(range(20))
assert Xtest.shape == Xtrain.shape
assert pd.concat([Xtest, Xtrain]).sort_index().equals(X.sort_index())
assert Ytest.shape == Ytrain.shape
assert pd.concat([Ytest, Ytrain]).sort_index().equals(Y.sort_index())
assert Ytest.value_counts().sort_index().equals(Ytrain.value_counts().sort_index())
Exercice : Affichez les images qui serviront à entraîner notre modèle de prédiction (predictive model):
### BEGIN SOLUTION
image_grid(images.iloc[train_index], titles=train_index)
### END SOLUTION
Exercice : Affichez celles qui permettent de le tester et d’évaluer sa performance:
### BEGIN SOLUTION
image_grid(images.iloc[test_index], titles=test_index)
### END SOLUTION
Exercice : Représentez les images sous forme de nuage de points en fonction de leurs attributs:
### BEGIN SOLUTION
make_scatter_plot(X, images, train_index, test_index, filter=transparent_background_filter, axis='square')
### END SOLUTION
Taux d’erreur¶
Comme la semaine dernière, nous utiliserons le taux d’erreur comme
métrique, d’une part sur l’ensemble d’entraînement, d’autre part sur
l’ensemble de test. Implémentez la fonction error_rate
dans votre
utilities.py. Pour vérifier que c’est correctement fait, nous
affichons son code ci-dessous:
show_source(error_rate)
Étape 3: [RE]férence (base line)¶
Classificateur¶
En Semaine 4: faites la suite de cette feuille avec l’algorithme du plus proche voisin, comme en Semaine 3.
En Semaine 5: faites la feuille sur les classificateurs puis faites la suite de cette feuille avec votre propre classificateur, en notant au préalable votre choix de classificateur ici:
BEGIN SOLUTION
END SOLUTION
Exercice : Ci-dessous, définissez puis entraînez votre classificateur sur l’ensemble d’entraînement.
Indication : Si vous avez besoin de code supplémentaire pour cela, mettez-le dans utilities.py
.
### BEGIN SOLUTION
from sklearn.neighbors import KNeighborsClassifier
neigh = KNeighborsClassifier(n_neighbors=1)
neigh.fit(Xtrain, Ytrain);
### END SOLUTION
Exercice : Calculez les prédictions sur l’ensemble d’entraînement et l’ensemble de test, ainsi que les taux d’erreur dans les deux cas:
### BEGIN SOLUTION
Ytrain_predicted = neigh.predict(Xtrain)
Ytest_predicted = neigh.predict(Xtest)
e_tr = error_rate(Ytrain, Ytrain_predicted)
e_te = error_rate(Ytest, Ytest_predicted)
### END SOLUTION
print("Training error:", e_tr)
print("Test error:", e_te)
assert Ytrain_predicted.shape == Ytrain.shape
assert Ytest_predicted.shape == Ytest.shape
assert 0 <= e_tr and e_tr <= 1
assert 0 <= e_te and e_te <= 1
Visualisons les prédictions obtenues:
# The training examples are shown as white circles and the test examples are black squares.
# The predictions made are shown as letters in the black squares.
make_scatter_plot(X, images.apply(transparent_background_filter),
train_index, test_index,
predicted_labels=Ytest_predicted, axis='square')
Interprétation¶
Exercice : Donnez ici votre interprétation des résultats. La performance des prédictions paraît elle satisfaisante? Avez vous une première intuition de comment l’améliorer?
BEGIN SOLUTION
La prédiction est parfaite sur l’ensemble d’entraînement. Cela est forcément le cas avec un classificateur par plus proche voisin, du moment qu’il n’y a pas deux images de classes distinctes ayant exactement les mêmes attributs.
La prédiction est parfaite aussi sur l’ensemble d’entraînement
que sur l’ensemble de test. Cela est cohérent avec la corrélation
parfaite entre la classe et correlation_average
que nous avions
observée ci-dessus.
END SOLUTION
Étape 4: [BAR]res d’erreur (error bar)¶
Barre d’erreur 1-sigma¶
Exercice : Comme première estimation de la barre d’erreur,
calculez la barre d’erreur 1-sigma pour le taux d’erreur e_te
:
### BEGIN SOLUTION
n_te = len(Ytest)
sigma = np.sqrt(e_te * (1-e_te) / n_te)
### END SOLUTION
print("TEST SET ERROR RATE: {0:.2f}".format(e_te))
print("TEST SET STANDARD ERROR: {0:.2f}".format(sigma))
Barre d’erreur par validation croisée (Cross-Validation)¶
Nous calculons maintenant une autre estimation de la barre d’erreur en répétant l’évaluation de performance pour de multiples partitions entre ensemble d’entraînement et ensemble de test :
n_te = 10
SSS = StratifiedShuffleSplit(n_splits=n_te, test_size=0.5, random_state=5)
E = np.zeros([n_te, 1])
k = 0
for train_index, test_index in SSS.split(X, Y):
print("TRAIN:", train_index, "TEST:", test_index)
Xtrain, Xtest = X.iloc[train_index], X.iloc[test_index]
Ytrain, Ytest = Y.iloc[train_index], Y.iloc[test_index]
neigh.fit(Xtrain, Ytrain.ravel())
Ytrain_predicted = neigh.predict(Xtrain)
Ytest_predicted = neigh.predict(Xtest)
e_tr = error_rate(Ytrain, Ytrain_predicted)
e_te = error_rate(Ytest, Ytest_predicted)
print("TRAIN ERROR RATE:", e_tr)
print("TEST ERROR RATE:", e_te)
E[k] = e_te
k = k+1
e_te_ave = np.mean(E)
# It is bad practice to show too many decimal digits:
print("\n\nCV ERROR RATE: {0:.2f}".format(e_te_ave))
print("CV STANDARD DEVIATION: {0:.2f}".format(np.std(E)))
sigma = np.sqrt(e_te_ave * (1-e_te_ave) / n_te)
print("TEST SET STANDARD ERROR (for comparison): {0:.2f}".format(sigma))
Conclusion¶
Exercice : Résumez ici les performances obtenues, tout d’abord avec votre référence, puis avec les variantes que vous aurez explorées en changeant d’attributs et de classificateur. Puis vous commenterez sur la difficulté du problème ainsi que les pistes possibles pour obtenir de meilleures performances, ou pour généraliser le problème.
BEGIN SOLUTION
Comme attendu avec le classificateur du plus proche voisin, la performance est toujours parfaite sur l’ensemble d’entraînement.
On observe qu’avec certains ensemble de test, il peut tout de même y avoir des erreurs, même avec un des attributs qui discrimine parfaitement. Cela est dû à l’existence de d’images de poule et de canard proches l’une de l’autre. On verra plus tard ce semestre que nous sommes dans une situation de surapprentissage.
En général, sur nos images simples, la forme de l’animal
est très discriminante, et cette forme est bien encodée par
la forme moyenne. Cela rend le problème facile. Mais
il faudrait remplacer le classificateur par plus proche
voisin par un classificateur basé uniquement sur l’attribut
correlation_average
. Sur ce jeu de donnée bien calibré,
la performance devrait alors être parfaite.
END SOLUTION
Exercice : Complétez votre rapport (Semaine 4/Semaine5)