.. _winesknnevalrst: ================================= Plus proches voisins - évaluation ================================= .. only:: html **Links:** :download:`notebook `, :downloadlink:`html `, :download:`PDF `, :download:`python `, :downloadlink:`slides `, :githublink:`GitHub|_doc/notebooks/lectures/wines_knn_eval.ipynb|*` Comment évaluer la pertinence d’un modèle des plus proches voisins. .. code:: ipython3 %matplotlib inline .. code:: ipython3 from papierstat.datasets import load_wines_dataset df = load_wines_dataset() X = df.drop(['quality', 'color'], axis=1) y = df['quality'] .. code:: ipython3 from sklearn.neighbors import KNeighborsRegressor knn = KNeighborsRegressor(n_neighbors=1) knn.fit(X, y) .. parsed-literal:: KNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=1, n_neighbors=1, p=2, weights='uniform') .. code:: ipython3 prediction = knn.predict(X) Le modèle ne fait pas d’erreur sur tous les exemples de la base de vins. C’est normal puisque le plus proche voisin d’un vin est nécessairement lui-même, la note prédite et la sienne. .. code:: ipython3 min(prediction - y), max(prediction - y) .. parsed-literal:: (0.0, 0.0) Il est difficile dans ces conditions de dire si la prédiction et de bonne qualité. On pourrait estimer la qualité de la prédiction sur un vin nouveau mais il n’y en a aucun pour le moment et ce n’est pas l’ordinateur qui va les fabriquer. On peut peut-être regarder combien de fois le plus proche voisin d’un vin autre que le vin lui-même partage la même note. .. code:: ipython3 from sklearn.neighbors import NearestNeighbors nn = NearestNeighbors(n_neighbors=2) nn.fit(X) .. parsed-literal:: NearestNeighbors(algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=1, n_neighbors=2, p=2, radius=1.0) .. code:: ipython3 distance, index = nn.kneighbors(X) proche = index[:, 1].ravel() note_proche = [y[i] for i in proche] Il ne reste plus qu’à calculer la différence entre la note d’un vin et celle de son plus proche voisin autre que lui-même. .. code:: ipython3 diff = y - note_proche ax = diff.hist(bins=20, figsize=(3,3)) ax.set_title('Histogramme des différences\nde prédiction') .. parsed-literal:: Text(0.5,1,'Histogramme des différences\nde prédiction') .. image:: wines_knn_eval_11_1.png Ca marche pour les deux tiers de la base, pour le tiers restant, les notes diffèrent. On peut maintenant regarder si la distance entre ces deux voisins pourrait être corrélée à cette différence. .. code:: ipython3 import pandas dif = pandas.DataFrame(dict(dist=distance[:,1], diff=diff)) ax = dif.plot(x="dist", y="diff", kind='scatter', figsize=(3,3)) ax.set_title('Graphe XY - distance / différence'); .. image:: wines_knn_eval_13_0.png Ce n’est pas très lisible. Essayons un autre type de graphique. .. code:: ipython3 from seaborn import violinplot, boxplot import matplotlib.pyplot as plt fig, ax = plt.subplots(1, 2, figsize=(8,3)) violinplot(x="diff", y="dist", data=dif, ax=ax[0]) ax[0].set_ylim([0,25]) ax[0].set_title('Violons distribution\ndifférence / distance') boxplot(x="diff", y="dist", data=dif, ax=ax[1]) ax[1].set_title('Boxplots distribution\ndifférence / distance') ax[1].set_ylim([0,25]); .. image:: wines_knn_eval_15_0.png A priori le modèle n’est pas si mauvais, les voisins partageant la même note ont l’air plus proches que ceux qui ont des notes différentes. .. code:: ipython3 import numpy dif['abs_diff'] = numpy.abs(dif['diff']) .. code:: ipython3 from seaborn import jointplot ax = jointplot("dist", "abs_diff", data=dif[dif.dist <= 10], kind="kde", space=0, color="g", size=4) ax.ax_marg_y.set_title('Heatmap distribution distance / différence'); .. image:: wines_knn_eval_18_0.png Les vins proches se ressemblent pour la plupart. C’est rassurant pour la suite. 61% des vins ont un voisin proche partageant la même note. .. code:: ipython3 len(dif[dif['abs_diff'] == 0]) / dif.shape[0] .. parsed-literal:: 0.6152070186239803