Introduction au Machine Learning

Quiz sur les techniques d'entraînement de modèles ML

Quiz P2

10h du matin, -30 degrés et un vent à 140km/h sur l’île Biscoe dans l’archipel Palmer en Antarctique. Vous n’avez pas vu le soleil depuis 3 mois. Bien camouflé.e dans la neige, vous observez à la jumelle un groupe de manchots tout en notant rapidement leurs mensurations sur votre tablette.

Vous faites partie de l’équipe du Pr Kristen Gorman https://www.uaf.edu/cfos/people/faculty/detail/kristen-gorman.php de la station Palmer https://pallter.marine.rutgers.edu/ et vous adorez les manchots!

Votre travail de recherche porte sur

“l’impact de la variabilité de la glace de mer hivernale sur la recherche de nourriture pré-nuptiale des mâles et des femelles manchots”

De retour à la station, vous chargez le dataset que vous avez compilé avec vos collègues et vous vous mettez au travail.

Note sur la traduction penguins => manchots: en francais, les pingouins vivent dans l’hémisphère nord et ils peuvent voler ! Quant aux manchots, ils ne peuvent pas voler et ils vivent dans l’hémisphère sud.

Le dataset

insert art

Artwork by @allison_horst

Le dataset a bien été compilé sur le terrain par l’équipe du Dr Gorman. Il est analysé par Allison Horst en détail (avec R) sur https://allisonhorst.github.io/palmerpenguins/

Il est composé de 344 échantillons

des variables categoriques

Des variables numériques

insert art bec

Pour charger le dataset en python

pip install palmerpenguins from palmerpenguins import load_penguins data = load_penguins()

Le dataset est aussi disponible sur le github du cours filename = “https://raw.githubusercontent.com/OpenClassrooms-Student-Center/8063076-Initiez-vous-au-Machine-Learning/master/data/palmer_penguins.csv” data = pd.read_csv(filename)

Dans la suite nous utilisons cette version du dataset qui a été mélangée et dont les échantillons avec des valeurs manquantes ont été expurgés

Les données sont ordonnées par espèce et par island. Nous allons mélanger le dataset avec

data = data.sample(frac = 1, random_state = 808)

enlever les échantillons avec des valeurs manquantes. Il reste 333 manchots. data.dropna(inplace = True)

et reset l’index de la Dataframe data.reset_index(inplace = True, drop = True)

Partie 1 régression linéaire

Vous allez explorer le dataset en utilisant une régression linéaire. Vous cherchez à comprendre la relation entre les mensurations des animaux (bill_length etc ) et leur poids

Q1) Dans un premier temps vous réalisez 3 régressions linéaires univariées

body_mass_g ~ bill_length_mm body_mass_g ~ bill_depth_mm body_mass_g ~ flipper_length_mm

le code pour la première régression sera donc

from sklearn.linear_model import LinearRegression reg = LinearRegression() X = data[‘bill_length_mm’].values.reshape(-1, 1) y = data[‘body_mass_g’]

reg.fit(X, y)

En regardant le score donnée par le modèle soit le R^2, reg.score(), quelle est la mensuration la plus prédictive du poids des animaux

y = data[‘body_mass_g’] reg = LinearRegression() for col in [‘bill_length_mm’,’bill_depth_mm’,’flipper_length_mm’]: X = data[col].values.reshape(-1, 1) reg.fit(X, y) print(“\n”,col, reg.score(X, y))

y_pred = reg.predict(X)
print(f"RMSE: {mean_squared_error(y, y_pred)}")
print(f"MAPE: {mean_absolute_percentage_error(y, y_pred)}")

bill_depth_mm bill_length_mm and bill_depth_mm flipper_length_mm species and island

Sol:

le code donne un R^2

Comme R^2 mesure le pouvoir de prédiction des variables de la régression sur la variable cible, une valeur élevée indique une meilleure régression. C’est donc la longueur des nageoires flipper_length_mm qui est de loin la plus prédictive du poids de la bête.

Q2) Considérons maintenant la régression du poids sur toutes les variables numériques

body_mass_g ~ bill_length_mm + bill_depth_mm + flipper_length_mm

et faisons en sorte de standardiser les variables prédictrices avec le MinMax Scaler

from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler()

X = scaler.fit_transform(data[[‘bill_length_mm’,’bill_depth_mm’,’flipper_length_mm’]])

Entraînez la regression body_mass_g ~ bill_length_mm + bill_depth_mm + flipper_length_mm

et calculez le RMSE et le MAPE (il vous faut d’abord obtenir les predictions du modele avec la fonction reg.predict(…) )

from sklearn.metrics import mean_squared_error, mean_absolute_percentage_error

y = data[‘body_mass_g’] from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler()

X = scaler.fit_transform(data[[‘bill_length_mm’,’bill_depth_mm’,’flipper_length_mm’]]) reg = LinearRegression() reg.fit(X, y)

y_pred = reg.predict(X) from sklearn.metrics import mean_squared_error, mean_absolute_percentage_error print(f”RMSE: {mean_squared_error(y, y_pred)}”) print(f”MAPE: {mean_absolute_percentage_error(y, y_pred)}”)

– question

Q3) par espèce

On va entraîner la même régression mais cette fois en séparant les espèces on aura donc 3 modèles différents entraînés sur des données différentes

scaler = MinMaxScaler() reg = LinearRegression() for espece in [‘Adelie’, ‘Gentoo’, ‘Chinstrap’]: df = data[data.species == espece].copy() y = df[‘body_mass_g’] X = scaler.fit_transform(df[[‘bill_length_mm’,’bill_depth_mm’,’flipper_length_mm’]]) reg.fit(X, y) print(“–\n”,espece, reg.score(X, y))

y_pred = reg.predict(X)
print(f"RMSE: {mean_squared_error(y, y_pred)}")
print(f"MAPE: {mean_absolute_percentage_error(y, y_pred)}")

– Adelie 0.5064249444393831 RMSE: 103103.77927187756 MAPE: 0.0688582880470465 – Gentoo 0.6280736371428204 RMSE: 92745.44334404985 MAPE: 0.04719861757681761 – Chinstrap 0.5038143982587502 RMSE: 72215.44695584572 MAPE: 0.05660976676978926

Sol 1 et 3 R^2 est une mesure absolu donc si 2 modèle ont le même R^2 ils sont aussi performant l’un que l’autre RMSE est une mesure relative qui va dépendre de la valeur de la variable cible. 2 RMSE différentes pour 2 set de données et donc 2 modèles différents ne permet pas de conclure MAPE est une mesure relative mais indépendant de l’amplitude de la variable cible donc une MAPE plus petite veut bien dire un modèle meilleur

Q4) Entraînement et validation

On va maintenant scinder le dataset en 2 sous ensembles: test et entraînement la fonction train_test_split permet de régler la taille de l’ensemble de test test_size et de fixer l’aspect aléatoire du split avec le paramètre random_state

Nous allons regarder l’influence du paramètre random_state sur le R^2 de la régression

On reprend donc nos données:

scaler = MinMaxScaler() y = data[‘body_mass_g’] X = scaler.fit_transform(data[[‘bill_length_mm’,’bill_depth_mm’,’flipper_length_mm’]]) reg = LinearRegression()

et on fait varier le random_state de 0 à 19: np.arange(20)

Le code donne

import seaborn as sns for test_size in [0.2, 0.5, 0.8, 0.9]:

iqrs = [] for test_size in np.arange(0.1,1,0.1): score = [] for random_state in np.arange(200): X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state) reg.fit(X_train, y_train) score.append(reg.score(X_test, y_test)) # calcul du IQR iqr = np.quantile(score, q=[0.25, 0.75]) print(“IQR”, test_size, iqr[1] - iqr[0])

iqrs.append(iqr[1] - iqr[0])

fig = plt.figure(figsize=(6, 6))
sns.boxplot(score)
plt.title(f"test_size {test_size}")
plt.show()

Maintenant recommencez avec test_size = 0.5 et test_size = 0.8

qu’est ce que vous observez a partir des boxplots?

Régression logistique

Dans cette deuxième partie du quiz vous allez travailler sur la classification avec la régression logistique.

Mais tout d’abord chargez à nouveau le dataset pour être sûr de travailler sur les bonnes données

filename = “https://raw.githubusercontent.com/OpenClassrooms-Student-Center/8063076-Initiez-vous-au-Machine-Learning/master/data/palmer_penguins_openclassrooms.csv” data = pd.read_csv(filename) data = data.sample(frac = 1, random_state = 808) data.dropna(inplace = True) data.reset_index(inplace = True, drop = True)

La variable sex a pour valeur male, female, nous devons d’abord la numériser.

data.loc[data.sex == ‘male’, ‘sex’] = 0 data.loc[data.sex == ‘female’, ‘sex’] = 1 data[‘sex’] = data.sex.astype(‘int’) Maintenant le décompte des valeurs de la variable ‘sex’ doit donner

sex 0 168 1 165 Name: count, dtype: int64

Enfin scindez le dataset en 2 sous ensembles test et train après avoir contraint l’amplitude des variables

scaler = MinMaxScaler() y = data[‘sex’].values X = scaler.fit_transform(data[[‘bill_length_mm’,’bill_depth_mm’,’flipper_length_mm’, ‘body_mass_g’]]) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=42)

q1) Peut-on prédire le sexe des manchots à partir de leurs mensurations ?

from sklearn.linear_model import LogisticRegression from sklearn.metrics import confusion_matrix from sklearn.metrics import accuracy_score

Entraînez une régression logistique et regardez la matrice de confusion

La colonne 0 est pour les mâles, la colonne 1 est pour les femelles

clf = LogisticRegression(random_state = 42) clf.fit(X_train, y_train)

y_pred = clf.predict(X_test) y_proba = clf.predict_proba(X_test)

confusion_matrix(y_test, y_pred) vous pouvez vous aider en regardant directement le nombre de true positive, true negative etc … avec tn, fp, fn, tp = confusion_matrix(y_test, y_pred).ravel()

Q5) Quelle affirmation est vraie?

le modèle est parfait le modèle a mal classé 9 mâles en femelles le modèle a correctement classé 32 femelles le nombre de faux négatifs est de 9

Solution on obtient tn, fp, fn, tp (23, 9, 3, 32)

donc

Q6) recall vs precision

Regardez les scores de recall et de précision

recall_score(y_test, y_pred) precision_score(y_test, y_pred)

En gardant en tête que les positifs sont les 1 et les négatifs les 0 et que notre encodage de la variable cible est male = 0 ; female = 1,

Quelle affirmation est vrai

Le recall est supérieur à la précision donc Le modèle est meilleur pour minimiser l’erreur : male => female les faux positifs Le modèle est meilleur pour maximiser les vrai positifs Le modèle est meilleur pour minimiser l’erreur : vrai valeur female mais valeur prédite: male (faux négatifs) Le modèle est meilleur pour maximiser les vrai négatifs

solution 1 c’est dans le cours, le recall est supérieur à la précision 2 et 4 sont fausses car les définition de recall et précision ne permettent pas conclure sur la maximisation des TN et TP 3 est fausse. Ce serait la bonne réponse si la précision est supérieure au recall

Q7) regardons maintenant ce qui se passe quand on fait varier le seuil de classification

Calculez les probabilités d’appartenance à la catégorie female (0 et 1) avec y_proba = clf.predict_proba(X_test)[:,1]

y_pred_03 = [ 0 if value < 0.3 else 1 for value in y_proba ] y_pred_07 = [ 0 if value < 0.7 else 1 for value in y_proba ]

Comparez les matrices de confusion pour y_pred_03 et y_pred_07 En gardant en tête que la matrice de confusion est pour scikit-learn

TN FP
FN TP

Que peut on affirmer

confusion_matrix(y_test, y_pred_03) Out[303]: array([[14, 18], [ 1, 34]])

confusion_matrix(y_test, y_pred_07) Out[304]: array([[32, 0], [13, 22]])

[T] Baisser le seuil de classification influence le modèle pour qu’il identifie plus souvent les femelles que les mâles [F] Augmenter le seuil de classification influence le modèle pour qu’il identifie plus souvent les femelles que les mâles [F] Baisser le seuil de classification influence le modèle pour qu’il identifie plus souvent les mâles que les femelles [T] Augmenter le seuil de classification influence le modèle pour qu’il identifie plus souvent les mâles que les femelles

Solution

confusion_matrix(y_test, y_pred_03) Out[303]: array([[14, 18], [ 1, 34]])

donc baisser le seuil à 0.3 fait que presque toutes les femelles sont correctement identifiées 34 sur 35. par contre les mâles ont plus d’erreurs: 18 sont identifiés comme femelles et seulement 14 comme mâles

confusion_matrix(y_test, y_pred_07) Out[304]: array([[32, 0], [13, 22]])

donc augmenter le seuil fait que tous les mâles sont correctement identifiés 32 sur 32 alors que 13 femelles sur 35 sont identifiés comme mâles

– prédiction de l’espèce Q8) changeons maintenant de variable cible et créons un modele de prediction de l’espèce des manchots

pour cela il nous faut numériser la variable cible de la façon suivante

data.loc[data.species == ‘Adelie’, ‘species’] = 3 data.loc[data.species == ‘Gentoo’, ‘species’] = 2 data.loc[data.species == ‘Chinstrap’, ‘species’] = 1 data[‘species’] = data.species.astype(‘int’)

ce qui donne la répartition suivante

3 146 2 119 1 68

Construisant la matrice de prédiction et les sous ensembles de train et de test

scaler = MinMaxScaler() y = data[‘species’].values

on va comparer 2 modèles, le premier avec les variables de mensurations sans le poids X1 = scaler.fit_transform(data[[‘bill_length_mm’,’bill_depth_mm’,’flipper_length_mm’]]) X1_train, X1_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=42) le deuxième auquel on ajoute la variable de poids X2 = scaler.fit_transform(data[[‘bill_length_mm’,’bill_depth_mm’,’flipper_length_mm’, ‘body_mass_g’]]) X2_train, X2_test, y_train, y_test = train_test_split(X2, y, test_size=0.20, random_state=42)

X2 = scaler.fit_transform(data[[‘bill_length_mm’,’bill_depth_mm’,’flipper_length_mm’, ‘body_mass_g’,’sex’]]) X2_train, X2_test, y_train, y_test = train_test_split(X2, y, test_size=0.20, random_state=42)

X2 = scaler.fit_transform(data[[‘bill_length_mm’,’bill_depth_mm’,’flipper_length_mm’,’sex’]]) X2_train, X2_test, y_train, y_test = train_test_split(X2, y, test_size=0.20, random_state=42)

enfin entraînons 2 nouveau modèles clf1 = LogisticRegression(random_state = 42) clf1.fit(X1_train, y_train) clf2 = LogisticRegression(random_state = 42) clf2.fit(X2_train, y_train)

y1_pred = clf1.predict(X1_test) y2_pred = clf2.predict(X2_test)

Regardez la matrice de confusion des 2 modèles, qu’en concluez vous

En effet la seule différence entre les matrices de confusion est que dans le premier modèle sans poid, un manchot de type Chinstrap est confondu avec un manchot Adelie.

Donc les réponses … sont vraies. nous n’avions pas forcément besoin du poids.

Partitionnement

Nous allons maintenant voir si le kmeans donne un partitionnement exacte des espèces des manchots

Q9) supposons que l’on ne sache pas qu’il y a 3 espèces

Utilisez le score de silhouette pour déterminer le nombre de partition optimum des manchots

sur tout le dataset en utilisant les variables numériques

X = data[[‘bill_length_mm’,’bill_depth_mm’,’flipper_length_mm’, ‘body_mass_g’,’sex’]]

from sklearn.cluster import KMeans km = KMeans( n_clusters=3, random_state = 808, n_init = 10) km.fit(X) y_pred = km.labels_ data[‘labels’] = km.labels_

data[[‘species’, ‘labels’, ‘island’]].groupby(by = [‘species’, ‘labels’]).count().reset_index().rename(columns = {‘island’: ‘count_’})

donne le tableau suivant qui indique par espèce et par label, le nombre de manchot species labels count_ 0 1 0 48 1 1 2 20 2 2 1 74 3 2 2 45 4 3 0 102 5 3 2 44

A partir de ce tableau, que pouvez vous conclure

Si le partitionnement recoupent parfaitement les espèces nous aurions un count_ plus important par groupe et espèce or ce n’est pas le cas En particulier, le label 0 est a la fois principale pour l’espece 3 (Adelie) et l’espece 1 (Chinstrap) on ne peut donc pas associer la bonne espèce au label 0 Par contre l’espèce 2 (Gentoo) est principalement associée au groupe 1 qui lui n’est pas considéré pour une autre espèce.

Q10) combien de groupe

utilisez maintenant le score de silhouette pour déterminer le nombre optimal de partitions

from sklearn.metrics import silhouette_score

scores = [] for n in range(2, 11, 1): km = KMeans( n_clusters=n, random_state = 808, n_init = 10) km.fit(X) labels_ = km.predict(X) scores.append(silhouette_score(X,labels_ ))

plt.plot(range(2, 11, 1), scores)

Que pouvez vous conclure