Découpage stratifié apprentissage / test

Lorsqu’une classe est sous-représentée, il y a peu de chances que la répartition apprentissage test conserve la distribution des classes.

[1]:
%matplotlib inline
[1]:
from teachpyx.datasets import load_wines_dataset

df = load_wines_dataset()
X = df.drop(["quality", "color"], axis=1)
y = df["quality"]

On divise en base d’apprentissage et de test avec la fonction train_test_split.

[2]:
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y)
[4]:
import pandas

ys = pandas.DataFrame(dict(y=y_train))
ys["base"] = "train"
ys2 = pandas.DataFrame(dict(y=y_test))
ys2["base"] = "test"
ys = pandas.concat([ys, ys2])
ys["compte"] = 1
piv = (
    ys.groupby(["base", "y"], as_index=False)
    .count()
    .pivot(index="y", columns="base", values="compte")
)
piv["ratio"] = piv["test"] / piv["train"]
piv
[4]:
base test train ratio
y
3 9 21 0.428571
4 45 171 0.263158
5 553 1585 0.348896
6 702 2134 0.328960
7 261 818 0.319071
8 51 142 0.359155
9 4 1 4.000000

On voit le ratio entre les deux classes est à peu près égal à 1/3 sauf pour les notes sous-représentées. On utilise une répartition stratifiée : la distribution d’une variable, les labels, sera la même dans les bases d’apprentissages et de de tests. On s’inspire de l’exemple StratifiedShuffleSplit.

[5]:
from sklearn.model_selection import StratifiedShuffleSplit

split = StratifiedShuffleSplit(n_splits=1, test_size=0.33)
train_index, test_index = list(split.split(X, y))[0]
len(train_index), len(test_index)
[5]:
(4352, 2145)
[6]:
X_train, y_train = X.iloc[train_index, :], y[train_index]
X_test, y_test = X.iloc[test_index, :], y[test_index]
y_train.shape, y_test.shape
[6]:
((4352,), (2145,))
[8]:
ys = pandas.DataFrame(dict(y=y_train))
ys["base"] = "train"
ys2 = pandas.DataFrame(dict(y=y_test))
ys2["base"] = "test"
ys = pandas.concat([ys, ys2])
ys["compte"] = 1
piv = (
    ys.groupby(["base", "y"], as_index=False)
    .count()
    .pivot(index="y", columns="base", values="compte")
)
piv["ratio"] = piv["test"] / piv["train"]
piv
[8]:
base test train ratio
y
3 10 20 0.500000
4 71 145 0.489655
5 706 1432 0.493017
6 936 1900 0.492632
7 356 723 0.492393
8 64 129 0.496124
9 2 3 0.666667

Le ratio entre les classes est identique, la classe test contient deux fois moins d’invidivu et c’est vrai pour toutes les classes excepté pour la classe 9 qui contient si peu d’éléments que c’est impossible.

[9]:
from sklearn.neighbors import KNeighborsRegressor

knn = KNeighborsRegressor(n_neighbors=1)
knn.fit(X_train, y_train)
[9]:
KNeighborsRegressor(n_neighbors=1)
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.
[10]:
prediction = knn.predict(X_test)
[11]:
from sklearn.metrics import r2_score

r2_score(y_test, prediction)
[11]:
-0.15256811411591387

Cela n’améliore pas la qualité du modèle mais on est sûr que les classes sous-représentées sont mieux gérées par cette répartition aléatoire stratifiée.


Notebook on github