L'algorithme k-Nearest Neighbours (kNN) en Python – Real Python

By | avril 7, 2021

Formation gratuite Python

Dans ce didacticiel, vous obtiendrez une introduction complète à l'algorithme k-Nearest Neighbours (kNN) en Python. L'algorithme kNN est l'un des algorithmes d'apprentissage automatique les plus connus et un incontournable absolu dans votre boîte à outils d'apprentissage automatique. Python est le langage de programmation incontournable pour l'apprentissage automatique, alors quel meilleur moyen de découvrir kNN qu'avec les célèbres packages de Python NumPy et scikit-learn!

Ci-dessous, vous explorerez l'algorithme kNN à la fois en théorie et en pratique. Alors que de nombreux didacticiels ignorent la partie théorique et se concentrent uniquement sur l'utilisation des bibliothèques, vous ne voulez pas dépendre de packages automatisés pour votre apprentissage automatique. Il est important de se familiariser avec les mécanismes des algorithmes d'apprentissage automatique pour comprendre leur potentiel et leurs limites.

Dans le même temps, il est essentiel de comprendre comment utiliser un algorithme dans la pratique. Dans cet esprit, dans la deuxième partie de ce didacticiel, vous vous concentrerez sur l'utilisation de kNN dans la bibliothèque Python scikit-learn, avec des conseils avancés pour pousser les performances au maximum.

Dans ce didacticiel, vous apprendrez à:

  • Expliquer le algorithme kNN à la fois intuitivement et mathématiquement
  • Implémenter kNN en Python de zéro utilisant NumPy
  • Utilisez kNN en Python avec scikit-learn
  • Régler hyperparamètres de kNN utilisant GridSearchCV
  • Ajouter ensachage à kNN pour de meilleures performances

Bases de l'apprentissage automatique

Pour vous impliquer, il vaut la peine de prendre du recul et de faire une rapide enquête sur apprentissage automatique en général. Dans cette section, vous découvrirez l'idée fondamentale du machine learning et découvrirez comment l'algorithme kNN est lié aux autres outils de machine learning.

L'idée générale de l'apprentissage automatique est d'obtenir un modèle pour apprendre les tendances à partir de données historiques sur n'importe quel sujet et être capable de reproduire ces tendances sur des données comparables à l'avenir. Voici un diagramme décrivant le processus de base du machine learning:

Idée générale derrière l'apprentissage automatique

Ce graphique est une représentation visuelle d'un modèle d'apprentissage automatique qui est ajusté sur des données historiques. Sur la gauche se trouvent les observations originales avec trois variables: hauteur, largeur et forme. Les formes sont des étoiles, des croix et des triangles.

Les formes sont situées dans différentes zones du graphique. Sur la droite, vous voyez comment ces observations originales ont été traduites en règle de décision. Pour une nouvelle observation, vous devez connaître la largeur et la hauteur pour déterminer dans quelle case elle se situe. Le carré dans lequel il tombe, à son tour, définit la forme qu'il est le plus susceptible d'avoir.

De nombreux modèles différents pourraient être utilisés pour cette tâche. UNE maquette est une formule mathématique qui peut être utilisée pour décrire des points de données. Un exemple est le modèle linéaire, qui utilise une fonction linéaire définie par la formule y = ax + b.

Si vous estimez, ou ajuster, un modèle, vous trouvez les valeurs optimales pour les paramètres fixes en utilisant un algorithme. Dans le modèle linéaire, les paramètres sont une et b. Heureusement, vous n'aurez pas besoin d'inventer de tels algorithmes d'estimation pour commencer. Ils ont déjà été découverts par de grands mathématiciens.

Une fois le modèle estimé, il devient une formule mathématique dans laquelle vous pouvez remplir les valeurs de vos variables indépendantes afin de faire des prédictions pour votre variable cible. D'un point de vue de haut niveau, c'est tout ce qui se passe!

Caractéristiques distinctives de kNN

Maintenant que vous comprenez l'idée de base de l'apprentissage automatique, l'étape suivante consiste à comprendre pourquoi il existe tant de modèles disponibles. Le modèle linéaire que vous venez de voir est appelé régression linéaire.

La régression linéaire fonctionne dans certains cas, mais ne fait pas toujours de prédictions très précises. C'est pourquoi les mathématiciens ont mis au point de nombreux modèles alternatifs d'apprentissage automatique que vous pouvez utiliser. L'algorithme k-Nearest Neighbours est l'un d'entre eux.

Tous ces modèles ont leurs particularités. Si vous travaillez sur l'apprentissage automatique, vous devez avoir une compréhension approfondie de chacun d'eux afin de pouvoir utiliser le bon modèle dans la bonne situation. Pour comprendre pourquoi et quand utiliser kNN, vous examinerez ensuite comment kNN se compare aux autres modèles d'apprentissage automatique.

kNN est un algorithme d'apprentissage automatique supervisé

La première propriété déterminante des algorithmes d'apprentissage automatique est la division entre supervisé et non supervisé des modèles. La différence entre les modèles supervisés et non supervisés est la énoncé du problème.

Dans les modèles supervisés, vous disposez de deux types de variables en même temps:

  1. UNE variable cible, également appelée variable dépendante ou y variable.
  2. Variables indépendantes, également connus sous le nom de X variables ou variables explicatives.

La variable cible est la variable que vous souhaitez prédire. Cela dépend des variables indépendantes et ce n’est pas quelque chose que vous savez à l’avance. Les variables indépendantes sont des variables que vous connaissez à l'avance. Vous pouvez les brancher dans une équation pour prédire la variable cible. De cette façon, il est relativement similaire au y = ax + b Cas.

Dans le graphique que vous avez vu précédemment et dans les graphiques suivants de cette section, la variable cible est la forme du point de données, et les variables indépendantes sont la hauteur et la largeur. Vous pouvez voir l'idée derrière l'apprentissage supervisé dans le graphique suivant:

Apprentissage automatique supervisé

Dans ce graphique, les points de données ont chacun une hauteur, une largeur et une forme. Il y a des croix, des étoiles et des triangles. Sur la droite se trouve une règle de décision qu'un modèle d'apprentissage automatique aurait pu apprendre.

Dans ce cas, les observations marquées d'une croix sont hautes mais pas larges. Les étoiles sont à la fois grandes et larges. Les triangles sont courts mais peuvent être larges ou étroits. Essentiellement, le modèle a appris une règle de décision pour décider si une observation est plus susceptible d'être une croix, une étoile ou un triangle en se basant uniquement sur sa hauteur et sa largeur.

Dans les modèles non supervisés, vous n'avez pas de division entre les variables cibles et les variables indépendantes. L'apprentissage non supervisé tente de regrouper les points de données en évaluant leur similitude.

Comme vous pouvez le voir dans l'exemple, vous ne pouvez jamais être certain que les points de données groupés appartiennent fondamentalement ensemble, mais tant que le regroupement a du sens, il peut être très utile en pratique. Vous pouvez voir l'idée derrière l'apprentissage non supervisé dans le graphique suivant:

Apprentissage automatique non supervisé

Dans ce graphique, les observations n’ont plus de formes différentes. Ce sont tous des cercles. Pourtant, ils peuvent toujours être regroupés en trois groupes en fonction de la distance entre les points. Dans cet exemple particulier, il existe trois groupes de points qui peuvent être séparés en fonction de l'espace vide entre eux.

L'algorithme kNN est un modèle d'apprentissage automatique supervisé. Cela signifie qu'il prédit une variable cible en utilisant une ou plusieurs variables indépendantes.

Pour en savoir plus sur les modèles d'apprentissage automatique non supervisés, consultez Clustering K-Means en Python: un guide pratique.

kNN est un algorithme d'apprentissage non linéaire

Une deuxième propriété qui fait une grande différence dans les algorithmes d'apprentissage automatique est de savoir si les modèles peuvent estimer ou non relations non linéaires.

Modèles linéaires sont des modèles qui prédisent à l'aide de lignes ou d'hyperplans. Dans l'image, le modèle est représenté par une ligne tracée entre les points. Le modèle y = ax + b est l'exemple classique d'un modèle linéaire. Vous pouvez voir comment un modèle linéaire peut s'adapter aux données d'exemple dans le dessin schématique suivant:

Modèles d'apprentissage automatique linéaire

Dans cette image, les points de données sont représentés sur la gauche avec des étoiles, des triangles et des croix. Sur la droite se trouve un modèle linéaire qui peut séparer les triangles des non-triangles. La décision est une ligne. Chaque point au-dessus de la ligne est un non-triangle et tout ce qui se trouve en dessous de la ligne est un triangle.

Si vous souhaitez ajouter une autre variable indépendante au graphique précédent, vous devez la dessiner en tant que dimension supplémentaire, créant ainsi un cube avec les formes à l'intérieur. Pourtant, une ligne ne pourrait pas couper un cube en deux parties. Le pendant multidimensionnel de la ligne est le hyperplan. Un modèle linéaire est donc représenté par un hyperplan, qui dans le cas d'un espace bidimensionnel se trouve être une ligne.

Modèles non linéaires sont des modèles qui utilisent toute approche autre qu'une ligne pour séparer leurs cas. Un exemple bien connu est le arbre de décision, qui est essentiellement une longue liste d'instructions if… else. Dans le graphe non linéaire, les instructions if… else vous permettraient de dessiner des carrés ou toute autre forme que vous souhaitiez dessiner. Le graphique suivant illustre un modèle non linéaire appliqué aux données d'exemple:

Modèles d'apprentissage automatique non linéaires

Ce graphique montre comment une décision peut être non linéaire. La règle de décision est composée de trois carrés. La zone dans laquelle se trouve un nouveau point de données définira sa forme prédite. Notez qu'il n'est pas possible d'ajuster cela à la fois en utilisant une ligne: deux lignes sont nécessaires. Ce modèle peut être recréé avec des instructions if… else comme suit:

  • Si la hauteur du point de données est faible, il s’agit d’un triangle.
  • Sinon, si la largeur du point de données est faible, il s’agit d’une croix.
  • Sinon, si rien de ce qui précède n’est vrai, c’est une étoile.

kNN est un exemple de modèle non linéaire. Plus loin dans ce didacticiel, vous reviendrez à la manière exacte dont le modèle est calculé.

kNN est un apprenant supervisé pour la classification et la régression

Les algorithmes d'apprentissage automatique supervisés peuvent être divisés en deux groupes en fonction du type de variable cible qu'ils peuvent prédire:

  1. Classification est une tâche de prédiction avec un catégorique variable cible. Les modèles de classification apprennent à classer toute nouvelle observation. Cette classe attribuée peut être bonne ou mauvaise, pas entre les deux. Un exemple classique de classification est le jeu de données sur l'iris, dans lequel vous utilisez des mesures physiques de plantes pour prédire leurs espèces. Un algorithme célèbre qui peut être utilisé pour la classification est la régression logistique.

  2. Régression est une tâche de prédiction dans laquelle la variable cible est numérique. Le défi des prix du logement sur Kaggle est un exemple célèbre de régression. Dans ce concours d'apprentissage automatique, les participants tentent de prédire les prix de vente des maisons en fonction de nombreuses variables indépendantes.

Dans le graphique suivant, vous pouvez voir à quoi ressembleraient une régression et une classification en utilisant l'exemple précédent:

Classification vs régression

La partie gauche de cette image est une classification. La variable cible est la forme de l'observation, qui est une variable catégorielle. La partie droite est une régression. La variable cible est numérique. Les règles de décision pourraient être exactement les mêmes pour les deux exemples, mais leurs interprétations sont différentes.

Pour une seule prédiction, les classifications sont correctes ou erronées, tandis que les régressions ont une erreur sur une échelle continue. Avoir une mesure d'erreur numérique est plus pratique, de sorte que de nombreux modèles de classification prédisent non seulement la classe mais aussi la probabilité d'appartenir à l'une ou l'autre des classes.

Certains modèles ne peuvent faire que de la régression, certains ne peuvent faire que de la classification et certains peuvent faire les deux. L'algorithme kNN s'adapte de manière transparente à la fois à la classification et à la régression. Vous apprendrez exactement comment cela fonctionne dans la prochaine partie de ce didacticiel.

kNN est rapide et interprétable

En tant que critère final pour caractériser les modèles de machine learning, vous devez prendre en compte complexité du modèle. L'apprentissage automatique, et en particulier l'intelligence artificielle, est actuellement en plein essor et est utilisé dans de nombreuses tâches complexes, telles que la compréhension du texte, des images et de la parole, ou pour les voitures autonomes.

Des modèles plus avancés et complexes comme les réseaux de neurones peuvent probablement apprendre tout ce qu'un modèle k-Nearest Neighbours peut. Après tout, ces modèles avancés sont de très bons apprenants. Cependant, sachez que cette complexité a aussi son prix. Afin d'adapter les modèles à votre prédiction, vous passerez généralement beaucoup plus de temps sur le développement.

Vous aurez également besoin de beaucoup plus de données pour adapter un modèle plus complexe, et les données ne sont pas toujours disponibles. Enfin, les modèles plus complexes sont plus difficiles à interpréter pour nous, humains, et parfois cette interprétation peut être très précieuse.

C'est là que réside la force du modèle kNN. Il permet à ses utilisateurs de comprendre et d’interpréter ce qui se passe à l’intérieur du modèle, et son développement est très rapide. Cela fait de kNN un excellent modèle pour de nombreux cas d'utilisation d'apprentissage automatique qui ne nécessitent pas de techniques très complexes.

Inconvénients de kNN

Il est juste d’être également honnête sur les inconvénients de l’algorithme kNN. Comme évoqué précédemment, le véritable inconvénient de kNN est sa capacité à s'adapter à des relations très complexes entre variables indépendantes et dépendantes. kNN est moins susceptible de bien fonctionner sur des tâches avancées telles que la vision par ordinateur et le traitement du langage naturel.

Vous pouvez essayer de pousser les performances de kNN aussi loin que possible, éventuellement en ajoutant d'autres techniques issues de l'apprentissage automatique. Dans la dernière partie du didacticiel, vous examinerez une technique appelée ensachage, qui est un moyen d'améliorer les performances prédictives. À un certain point de complexité, cependant, kNN sera probablement moins efficace que les autres modèles, quelle que soit la façon dont il a été réglé.

Utilisez kNN pour prédire l'âge des limaces de mer

Pour suivre la partie de codage, vous examinerez un exemple de jeu de données pour le reste de ce didacticiel: le jeu de données Abalone. Cet ensemble de données contient des mesures d'âge sur un grand nombre d'ormeaux. Juste pour information, voici à quoi ressemble un ormeau:

Un ormeau
Source de l'image

Les ormeaux sont de petits escargots de mer qui ressemblent un peu à des moules. Si vous souhaitez en savoir plus à leur sujet, vous pouvez consulter la page Wikipédia de l'ormeau pour plus d'informations.

L'énoncé du problème de l'ormeau

L'âge d'un ormeau peut être trouvé en coupant sa coquille et en comptant le nombre d'anneaux sur la coquille. Dans l'ensemble de données Abalone, vous pouvez trouver les mesures d'âge d'un grand nombre d'ormeaux ainsi que de nombreuses autres mesures physiques.

Le but du projet est de développer un modèle permettant de prédire l'âge d'un ormeau en se basant uniquement sur les autres mesures physiques. Cela permettrait aux chercheurs d'estimer l'âge de l'ormeau sans avoir à couper sa coquille et à compter les anneaux.

Vous allez appliquer un kNN pour trouver le score de prédiction le plus proche possible.

Importation du jeu de données Abalone

Dans ce didacticiel, vous travaillerez avec l'ensemble de données Abalone. Vous pouvez le télécharger et utiliser des pandas pour importer les données dans Python, mais il est encore plus rapide de laisser les pandas importer les données directement pour vous.

Pour suivre le code de ce didacticiel, il est recommandé d'installer Python avec Anaconda. La distribution Anaconda est fournie avec de nombreux packages importants pour la science des données. Pour plus d'aide sur la configuration de votre environnement, vous pouvez consulter Configuration de Python pour l'apprentissage automatique sur Windows.

Vous pouvez importer les données à l'aide de pandas comme suit:

>>>

>>> importer pandas comme pd
>>> URL = (
...     "https://archive.ics.uci.edu/ml/machine-learning-databases"
...     "/abalone/abalone.data"
... )
>>> ormeau = pd.read_csv(URL, entête=Rien)

Dans ce code, vous importez d'abord des pandas, puis vous l'utilisez pour lire les données. Vous spécifiez le chemin comme URL afin que le fichier soit récupéré directement sur Internet.

Pour vous assurer que vous avez correctement importé les données, vous pouvez effectuer une vérification rapide comme suit:

>>>

>>> ormeau.diriger()
            0 1 2 3 4 5 6 7 8
0 M 0,455 0,365 0,095 0,5140 0,2245 0,1010 0,150 15
1 M 0,350 0,265 0,090 0,2255 0,0995 0,0485 0,070 7
2 F 0,530 0,420 0,135 0,6770 0,2565 0,1415 0,210 9
3 M 0,440 0,365 0,125 0,5160 0,2155 0,1140 0,155 10
4 I 0,330 0,255 0,080 0,2050 0,0895 0,0395 0,055 7

Cela devrait vous montrer les cinq premières lignes du jeu de données Abalone, importé en Python en tant que DataFrame pandas. Vous pouvez voir que les noms de colonne sont toujours manquants. Vous pouvez trouver ces noms dans le abalone.names fichier sur le référentiel UCI Machine Learning. Vous pouvez les ajouter à votre Trame de données comme suit:

>>>

>>> ormeau.Colonnes = [[[[
...     "Sexe",
...     "Longueur",
...     "Diamètre",
...     "Hauteur",
...     "Poids total",
...     "Poids écaillé",
...     "Poids des viscères",
...     "Poids de la coque",
...     "Anneaux",
... ]

Les données importées devraient maintenant être plus compréhensibles. Mais il y a une autre chose à faire: vous devez supprimer le Sexe colonne. Le but de l'exercice actuel est d'utiliser des mesures physiques pour prédire l'âge de l'ormeau. Étant donné que le sexe n'est pas une mesure purement physique, vous devez le supprimer de l'ensemble de données. Vous pouvez supprimer le Sexe colonne utilisant .tomber:

>>>

>>> ormeau = ormeau.tomber("Sexe", axe=1)

Avec ce code, vous supprimez le Sexe colonne, car elle n'aura aucune valeur ajoutée dans la modélisation.

Statistiques descriptives de l'ensemble de données Abalone

Lorsque vous travaillez sur l'apprentissage automatique, vous devez avoir une idée des données avec lesquelles vous travaillez. Sans aller trop loin, voici quelques statistiques et graphiques exploratoires.

La variable cible de cet exercice est Anneaux, vous pouvez donc commencer par cela. Un histogramme vous donnera un aperçu rapide et utile des tranches d'âge auxquelles vous pouvez vous attendre:

>>>

>>> importer matplotlib.pyplot comme plt
>>> ormeau[[[["Anneaux"].hist(bacs=15)
>>> plt.Afficher()

Ce code utilise la fonctionnalité de traçage des pandas pour générer un histogramme avec quinze bacs. La décision d'utiliser quinze bacs est basée sur quelques essais. Lors de la définition du nombre de casiers, on essaie généralement de n'avoir ni trop d'observations par casier ni trop peu. Trop peu de cases peuvent masquer certains modèles, tandis que trop de cases peuvent rendre l'histogramme peu lisse. Vous pouvez voir l'histogramme dans le graphique suivant:

Répartition de l'âge des ormeaux

L'histogramme montre que la plupart des ormeaux de l'ensemble de données ont entre cinq et quinze anneaux, mais qu'il est possible d'en obtenir jusqu'à vingt-cinq. Les ormeaux plus âgés sont sous-représentés dans cet ensemble de données. Cela semble intuitif, car les distributions par âge sont généralement biaisées comme celle-ci en raison de processus naturels.

Une deuxième exploration pertinente consiste à déterminer quelles variables, le cas échéant, ont une forte corrélation avec l'âge. Une forte corrélation entre une variable indépendante et votre variable d'objectif serait un bon signe, car cela confirmerait que les mesures physiques et l'âge sont liés.

Vous pouvez observer la matrice de corrélation complète dans corrélation_matrix. Les corrélations les plus importantes sont celles avec la variable cible Anneaux. Vous pouvez obtenir ces corrélations comme ceci:

>>>

>>> corrélation_matrix = ormeau.corr()
>>> corrélation_matrix[[[["Anneaux"]
Longueur 0,556720
Diamètre 0,574660
Hauteur 0,557467
Poids total 0.540390
Poids écaillé 0.420884
Poids des viscères 0.503819
Poids de la coque 0,627574
Anneaux 1.000000
Nom: Anneaux, type: float64

Regardez maintenant les coefficients de corrélation pour Anneaux avec les autres variables. Plus ils sont proches de 1, plus il y a de corrélation.

Vous pouvez en conclure qu’il existe au moins une corrélation entre les mesures physiques des ormeaux adultes et leur âge, mais ce n’est pas non plus très élevé. Des corrélations très élevées signifient que vous pouvez vous attendre à un processus de modélisation simple. Dans ce cas, vous devrez essayer de voir les résultats que vous pouvez obtenir à l’aide de l’algorithme kNN.

Il existe de nombreuses autres possibilités d'exploration de données à l'aide de pandas. Pour en savoir plus sur l'exploration de données avec les pandas, consultez Utilisation de pandas et de Python pour explorer votre ensemble de données.

Un kNN pas à pas à partir de zéro en Python

Dans cette partie du didacticiel, vous allez découvrir comment l'algorithme kNN fonctionne en profondeur. L’algorithme comprend deux composants mathématiques principaux que vous devez comprendre. Pour vous échauffer, vous commencerez par une présentation en anglais simple de l'algorithme kNN.

Procédure pas à pas en anglais simple de l'algorithme kNN

L'algorithme kNN est un peu atypique par rapport aux autres algorithmes d'apprentissage automatique. Comme vous l'avez vu précédemment, chaque modèle d'apprentissage automatique a sa formule spécifique qui doit être estimée. La spécificité de l'algorithme k-Nearest Neighbours est que cette formule n'est pas calculée au moment de l'ajustement mais plutôt au moment de la prédiction. Ce n’est pas le cas de la plupart des autres modèles.

Lorsqu'un nouveau point de données arrive, l'algorithme kNN, comme son nom l'indique, commencera par trouver les voisins les plus proches de ce nouveau point de données. Ensuite, il prend les valeurs de ces voisins et les utilise comme prédiction pour le nouveau point de données.

Pour illustrer intuitivement pourquoi cela fonctionne, pensez à vos voisins. Vos voisins sont souvent relativement semblables à vous. Ils appartiennent probablement à la même classe socio-économique que vous. Peut-être qu'ils ont le même type de travail que vous, peut-être que leurs enfants vont à la même école que la vôtre, etc. Mais pour certaines tâches, ce type d'approche n'est pas aussi utile. Par exemple, il n’aurait aucun sens de regarder la couleur préférée de votre voisin pour prédire la vôtre.

L'algorithme kNN est basé sur la notion que vous pouvez prédire les caractéristiques d'un point de données en fonction des caractéristiques de ses voisins. Dans certains cas, cette méthode de prédiction peut réussir, alors que dans d'autres cas, ce n'est pas le cas. Ensuite, vous examinerez la description mathématique de «le plus proche» pour les points de données et les méthodes permettant de combiner plusieurs voisins en une seule prédiction.

Définir «le plus proche» à l'aide d'une définition mathématique de la distance

Pour trouver les points de données les plus proches du point que vous devez prédire, vous pouvez utiliser une définition mathématique de la distance appelée distance euclidienne.

Pour arriver à cette définition, vous devez d'abord comprendre ce que l'on entend par différence de deux vecteurs. Voici un exemple:

Théorème de Pythagore pour la distance euclidienne 2D

Sur cette image, vous voyez deux points de données: bleu à (2,2) et vert à (4,4). Pour calculer la distance entre eux, vous pouvez commencer par ajouter deux vecteurs. Vecteur une va du point (4,2) au point (4,4), et le vecteur b va du point (4,2) au point (2,2). Leurs têtes sont indiquées par les points colorés. Notez qu'ils sont à un angle de 90 degrés.

La différence entre ces vecteurs est le vecteur c, qui va de la tête de vecteur une à la tête du vecteur b. La longueur du vecteur c représente la distance entre vos deux points de données.

La longueur d'un vecteur s'appelle le norme. La norme est une valeur positive qui indique la magnitude du vecteur. Vous pouvez calculer la norme d'un vecteur en utilisant la formule euclidienne:

Distance euclidienne

Dans cette formule, la distance est calculée en prenant les différences au carré dans chaque dimension, puis en prenant la racine carrée de la somme de ces valeurs. Dans ce cas, vous devez calculer la norme du vecteur de différence c pour obtenir la distance entre les points de données.

Maintenant, pour appliquer cela à vos données, vous devez comprendre que vos points de données sont en fait des vecteurs. Vous pouvez ensuite calculer la distance entre eux en calculant la norme du vecteur de différence.

Vous pouvez calculer cela en Python en utilisant linalg.norm () de NumPy. Voici un exemple:

>>>

>>> importer engourdi comme np
>>> une = np.déployer([[[[2, 2])
>>> b = np.déployer([[[[4, 4])
>>> np.linalg.norme(une - b)
2,8284271247461903

Dans ce bloc de code, vous définissez vos points de données comme des vecteurs. Vous calculez ensuite norme() sur la différence entre deux points de données. De cette façon, vous obtenez directement la distance entre deux points multidimensionnels. Même si les points sont multidimensionnels, la distance entre eux reste un scalaire ou une valeur unique.

Si vous souhaitez obtenir plus de détails sur les mathématiques, vous pouvez consulter le théorème de Pythagore pour comprendre comment la formule de distance euclidienne est dérivée.

Trouvez le k Voisins les plus proches

Maintenant que vous avez un moyen de calculer la distance de n'importe quel point à n'importe quel point, vous pouvez l'utiliser pour trouver les voisins les plus proches d'un point sur lequel vous voulez faire une prédiction.

Vous devez trouver un certain nombre de voisins, et ce nombre est donné par k. La valeur minimale de k est 1. Cela signifie n'utiliser qu'un seul voisin pour la prédiction. Le maximum correspond au nombre de points de données dont vous disposez. Cela signifie utiliser tous les voisins. La valeur de k est quelque chose que l'utilisateur définit. Les outils d'optimisation peuvent vous y aider, comme vous le verrez dans la dernière partie de ce didacticiel.

Maintenant, pour trouver les voisins les plus proches dans NumPy, revenez à l'ensemble de données Abalone. Comme vous l'avez vu, vous devez définir des distances sur les vecteurs des variables indépendantes, vous devez donc d'abord obtenir vos pandas DataFrame dans un tableau NumPy en utilisant le .valeurs attribut:

>>>

>>> X = ormeau.tomber("Anneaux", axe=1)
>>> X = X.valeurs
>>> y = ormeau[[[["Anneaux"]
>>> y = y.valeurs

Ce bloc de code génère deux objets qui contiennent désormais vos données: X et y. X est les variables indépendantes et y est la variable dépendante de votre modèle. Notez que vous utilisez une majuscule pour X mais une lettre minuscule pour y. Cela se fait souvent dans le code d'apprentissage automatique, car la notation mathématique utilise généralement une lettre majuscule pour les matrices et une lettre minuscule pour les vecteurs.

Vous pouvez maintenant appliquer un kNN avec k = 3 sur un nouvel ormeau qui a les mesures physiques suivantes:

Variable Valeur
Longueur 0,569552
Diamètre 0,446407
Hauteur 0,154437
Poids total 1,016849
Poids écaillé 0,439051
Poids des viscères 0,222526
Poids de la coque 0,291208

Vous pouvez créer le tableau NumPy pour ce point de données comme suit:

>>>

>>> new_data_point = np.déployer([[[[
...     0,569552,
...     0,446407,
...     0,154437,
...     1,016849,
...     0,439051,
...     0,222526,
...     0,291208,
... ])

L'étape suivante consiste à calculer les distances entre ce nouveau point de données et chacun des points de données dans l'ensemble de données Abalone à l'aide du code suivant:

>>>

>>> les distances = np.linalg.norme(X - new_data_point, axe=1)

Vous avez maintenant un vecteur de distances et vous devez savoir quels sont les trois voisins les plus proches. Pour ce faire, vous devez trouver les identifiants des distances minimales. Vous pouvez utiliser une méthode appelée .argsort () pour trier le tableau du plus bas au plus élevé, et vous pouvez prendre le premier k éléments pour obtenir les indices de k voisins les plus proches:

>>>

>>> k = 3
>>> plus proche_nequart_ids = les distances.argsort()[:[:[:[:k]
>>> plus proche_nequart_ids
déployer([4045, 1902, 1644], dtype = int32)

Cela vous indique quels sont les trois voisins les plus proches de votre new_data_point. Dans le paragraphe suivant, vous verrez comment convertir ces voisins dans une estimation.

Vote ou moyenne de plusieurs voisins

Après avoir identifié les indices des trois voisins les plus proches de votre ormeau d'âge inconnu, vous devez maintenant combiner ces voisins dans une prédiction pour votre nouveau point de données.

Dans un premier temps, vous devez trouver les vérités fondamentales pour ces trois voisins:

>>>

>>> les anneaux les plus proches du voisinage = y[[[[plus proche_nequart_ids]
>>> les anneaux les plus proches du voisinage
déployer([ 9, 11, 10])

Maintenant que vous avez les valeurs de ces trois voisins, vous allez les combiner dans une prédiction pour votre nouveau point de données. La combinaison des voisins dans une prédiction fonctionne différemment pour la régression et la classification.

Moyenne pour la régression

Dans les problèmes de régression, la variable cible est numérique. Vous combinez plusieurs voisins en une seule prédiction en prenant la moyenne de leurs valeurs de la variable cible. Vous pouvez le faire comme suit:

>>>

>>> prédiction = les anneaux les plus proches du voisinage.signifier()

Vous obtiendrez une valeur de dix pour prédiction. Cela signifie que la prédiction 3-Nearest Neighbor pour votre nouveau point de données est dix. Vous pouvez faire de même pour n'importe quel nombre de nouveaux ormeaux que vous souhaitez.

Mode de classification

Dans les problèmes de classification, la variable cible est catégorique. Comme indiqué précédemment, vous ne pouvez pas calculer de moyennes sur des variables catégorielles. Par exemple, quelle serait la moyenne de trois marques de voitures prévues? Ce serait impossible à dire. Vous ne pouvez pas appliquer une moyenne aux prédictions de classe.

Au lieu de cela, dans le cas du classement, vous prenez le mode. Le mode est la valeur qui se produit le plus souvent. Cela signifie que vous comptez les classes de tous les voisins et que vous conservez la classe la plus courante. La prédiction est la valeur qui survient le plus souvent chez les voisins.

S'il existe plusieurs modes, il existe plusieurs solutions possibles. Vous pouvez sélectionner un gagnant final au hasard parmi les gagnants. Vous pouvez également prendre la décision finale en fonction des distances des voisins, auquel cas le mode des voisins les plus proches serait conservé.

Vous pouvez calculer le mode à l'aide du SciPy mode() une fonction. Comme l'exemple d'ormeau n'est pas un cas de classification, le code suivant montre comment vous pouvez calculer le mode pour un exemple de jouet:

>>>

>>> importer scipy.stats
>>> class_neighbours = np.déployer([[[["UNE", "B", "B", "C"])
>>> scipy.Statistiques.mode(class_neighbours)
ModeResult (mode = tableau (['B'], dtype = '<U1'), count = tableau ([2]))

Comme vous pouvez le voir, le mode dans cet exemple est "B" car c'est la valeur qui apparaît le plus souvent dans les données d'entrée.

Ajuster kNN en Python à l'aide de scikit-learn

Bien que le codage d'un algorithme à partir de zéro soit idéal à des fins d'apprentissage, ce n'est généralement pas très pratique lorsque vous travaillez sur une tâche d'apprentissage automatique. Dans cette section, vous explorerez la mise en œuvre de l'algorithme kNN utilisé dans scikit-learn, l'un des packages d'apprentissage automatique les plus complets de Python.

Fractionnement des données en ensembles de formation et de test pour l'évaluation du modèle

Dans cette section, vous évaluerez la qualité de votre modèle kNN en ormeau. Dans les sections précédentes, vous aviez une orientation technique, mais vous allez maintenant avoir un point de vue plus pragmatique et axé sur les résultats.

Il existe plusieurs façons d'évaluer les modèles, mais la plus courante est la division train-test. Lorsque vous utilisez une division train-test pour l'évaluation du modèle, vous divisez le jeu de données en deux parties:

  1. Données d'entraînement est utilisé pour s'adapter au modèle. Pour kNN, cela signifie que les données d'apprentissage seront utilisées comme voisines.
  2. Données de test est utilisé pour évaluer le modèle. Cela signifie que vous ferez des prédictions pour le nombre de sonneries de chacun des ormeaux dans les données de test et comparerez ces résultats au nombre réel de sonneries connu.

Vous pouvez diviser les données en ensembles d'entraînement et de test en Python à l'aide de la fonction intégrée de scikit-learn train_test_split ():

>>>

>>> de sklearn.model_selection importer train_test_split
>>> X_train, X_test, y_train, y_test = train_test_split(
...     X, y, test_size=0,2, random_state=12345
... )

le test_size fait référence au nombre d'observations que vous souhaitez insérer dans les données d'entraînement et les données de test. Si vous spécifiez un test_size de 0,2, ton test_size sera 20 pour cent des données originales, laissant donc les 80 pour cent restants comme données de formation.

le random_state est un paramètre qui vous permet d'obtenir les mêmes résultats à chaque exécution du code. train_test_split () effectue une division aléatoire des données, ce qui est problématique pour reproduire les résultats. Par conséquent, il est courant d’utiliser random_state. Le choix de la valeur random_state est arbitraire.

Dans le code ci-dessus, vous séparez les données en données d'entraînement et de test. Cela est nécessaire pour une évaluation objective du modèle. Vous pouvez maintenant procéder à l'ajustement d'un modèle kNN sur les données d'entraînement à l'aide de scikit-learn.

Ajustement d'une régression kNN dans scikit-learn à l'ensemble de données Abalone

Pour adapter un modèle à partir de scikit-learn, vous commencez par créer un modèle de la classe appropriée. À ce stade, vous devez également choisir les valeurs de vos hyperparamètres. Pour l'algorithme kNN, vous devez choisir la valeur de k, qui est appelée n_neighbours dans l'implémentation scikit-learn. Voici comment procéder en Python:

>>>

>>> de sklearn.neighbors importer KNeighboursRegressor
>>> knn_model = KNeighboursRegressor(n_neighbours=3)

Vous créez un modèle non ajusté avec knn_model. Ce modèle utilisera les trois voisins les plus proches pour prédire la valeur d'un futur point de données. Pour obtenir les données dans le modèle, vous pouvez ensuite ajuster le modèle sur l'ensemble de données d'entraînement:

>>>

>>> knn_model.ajuster(X_train, y_train)

Utilisant .ajuster(), vous laissez le modèle apprendre des données. À ce point, knn_model contient tout ce qui est nécessaire pour faire des prédictions sur les nouveaux points de données des ormeaux. C’est tout le code dont vous avez besoin pour ajuster une régression kNN à l’aide de Python!

Utilisation de scikit-learn pour inspecter l'ajustement du modèle

Cependant, adapter un modèle ne suffit pas. Dans cette section, vous examinerez certaines fonctions que vous pouvez utiliser pour évaluer l'ajustement.

De nombreuses mesures d'évaluation sont disponibles pour la régression, mais vous utiliserez l'une des plus courantes, l'erreur quadratique moyenne (RMSE). Le RMSE d'une prédiction est calculé comme suit:

  1. Compute the difference between each data point’s actual value and predicted value.
  2. For each difference, take the square of this difference.
  3. Sum all the squared differences.
  4. Take the square root of the summed value.

To start, you can evaluate the prediction error on the training data. This means that you use the training data for prediction, so you know that the result should be relatively good. You can use the following code to obtain the RMSE:

>>>

>>> de sklearn.metrics import mean_squared_error
>>> de math import sqrt
>>> train_preds = knn_model.predict(X_train)
>>> mse = mean_squared_error(y_train, train_preds)
>>> rmse = sqrt(mse)
>>> rmse
1.65

In this code, you compute the RMSE using the knn_model that you fitted in the previous code block. You compute the RMSE on the training data for now. For a more realistic result, you should evaluate the performances on data that aren’t included in the model. This is why you kept the test set separate for now. You can evaluate the predictive performances on the test set with the same function as before:

>>>

>>> test_preds = knn_model.predict(X_test)
>>> mse = mean_squared_error(y_test, test_preds)
>>> rmse = sqrt(mse)
>>> rmse
2.37

In this code block, you evaluate the error on data that wasn’t yet known by the model. This more-realistic RMSE is slightly higher than before. The RMSE measures the average error of the predicted age, so you can interpret this as having, on average, an error of 1.65 years. Whether an improvement from 2.37 years to 1.65 years is good is case specific. At least you’re getting closer to correctly estimating the age.

Until now, you’ve only used the scikit-learn kNN algorithm out of the box. You haven’t yet done any tuning of hyperparameters and a random choice for k. You can observe a relatively large difference between the RMSE on the training data and the RMSE on the test data. This means that the model suffers from overfitting on the training data: It does not generalize well.

This is nothing to worry about at this point. In the next part, you’ll see how to optimize the prediction error or test error using various tuning methods.

Plotting the Fit of Your Model

A last thing to look at before starting to improve the model is the actual fit of your model. To understand what the model has learned, you can visualize how your predictions have been made using Matplotlib:

>>>

>>> import seaborn comme sns
>>> cmap = sns.cubehelix_palette(as_cmap=Vrai)
>>> F, ax = plt.subplots()
>>> points = ax.scatter(
...     X_test[:[:[:[: 0], X_test[:[:[:[: 1], c=test_preds, s=50, cmap=cmap
... )
>>> F.colorbar(points)
>>> plt.show()

In this code block, you use Seaborn to create a scatter plot of the first and second columns of X_test by subsetting the arrays X_test[:,0] et X_test[:,1]. Remember from before that the first two columns are Length et Diameter. They are strongly correlated, as you’ve seen in the correlations table.

You use c to specify that the predicted values (test_preds) should be used as a colorbar. The argument s is used to specify the size of the points in the scatter plot. You use cmap to specify the cubehelix_palette color map. To learn more about plotting with Matplotlib, check out Python Plotting With Matplotlib.

With the above code, you’ll get the following graph:

Visualize kNN Predictions using Python

On this graph, each point is an abalone from the test set, with its actual length and actual diameter on the X- and Y-axis, respectively. The color of the point reflects the predicted age. You can see that the longer and larger an abalone is, the higher its predicted age. This is logical, and it’s a positive sign. It means that your model is learning something that seems correct.

To confirm whether this trend exists in actual abalone data, you can do the same for the actual values by simply replacing the variable that is used for c:

>>>

>>> cmap = sns.cubehelix_palette(as_cmap=Vrai)
>>> F, ax = plt.subplots()
>>> points = ax.scatter(
...     X_test[:[:[:[: 0], X_test[:[:[:[: 1], c=y_test, s=50, cmap=cmap
>>> )
>>> F.colorbar(points)
>>> plt.show()

This code uses Seaborn to create a scatterplot with a colorbar. It produces the following graph:

Visualizing kNN fit with Python

This confirms that the trend your model is learning does indeed make sense.

You could extract a visualization for each combination of the seven independent variables. For this tutorial, that would be too long, but don’t hesitate to try it out. The only thing to change is the columns that are specified in the scatter.

These visualizations are two-dimensional views of a seven-dimensional dataset. If you play around with them, it will give you a great understanding of what the model is learning and, maybe, what it’s not learning or is learning wrong.

Tune and Optimize kNN in Python Using scikit-learn

There are numerous ways you can improve your predictive score. Some improvements could be made by working on the input data using data wrangling, but in this tutorial, the focus is on the kNN algorithm. Next, you’ll look at ways to improve the algorithm part of the modeling pipeline.

Improving kNN Performances in scikit-learn Using GridSearchCV

Until now, you’ve always worked with k=3 in the kNN algorithm, but the best value for k is something that you need to find empirically for each dataset.

When you use few neighbors, you have a prediction that will be much more variable than when you use more neighbors:

  • If you use one neighbor only, the prediction can strongly change from one point to the other. When you think about your own neighbors, one may be quite different from the others. If you lived next to an outlier, your 1-NN prediction would be wrong.

  • If you have multiple data points, the impact of one extremely different neighbor will be much less.

  • If you use too many neighbors, the prediction of each point risks being very close. Let’s say that you use all neighbors for a prediction. In that case, every prediction would be the same.

To find the best value for k, you’re going to use a tool called GridSearchCV. This is a tool that is often used for tuning hyperparameters of machine learning models. In your case, it will help by automatically finding the best value of k for your dataset.

GridSearchCV is available in scikit-learn, and it has the benefit of being used in almost the exact same way as the scikit-learn models:

>>>

>>> de sklearn.model_selection import GridSearchCV
>>> parameters = "n_neighbors": range(1, 50)
>>> gridsearch = GridSearchCV(KNeighborsRegressor(), parameters)
>>> gridsearch.fit(X_train, y_train)
GridSearchCV(estimator=KNeighborsRegressor(),
                                                    param_grid='n_neighbors': range(1, 50),
                                                                                                    'weights': ['uniform', 'distance'])

Here, you use GridSearchCV to fit the model. In short, GridSearchCV repeatedly fits kNN regressors on a part of the data and tests the performances on the remaining part of the data. Doing this repeatedly will yield a reliable estimate of the predictive performance of each of the values for k. In this example, you test the values from 1 à 50.

In the end, it will retain the best performing value of k, which you can access with .best_params_:

>>>

>>> gridsearch.best_params_
'n_neighbors': 25, 'weights': 'distance'

In this code, you print the parameters that have the lowest error score. With .best_params_, you can see that choosing 25 as value for k will yield the best predictive performance. Now that you know what the best value of k is, you can see how it affects your train and test performances:

>>>

>>> train_preds_grid = gridsearch.predict(X_train)
>>> train_mse = mean_squared_error(y_train, train_preds_grid)
>>> train_rmse = sqrt(train_mse)
>>> test_preds_grid = gridsearch.predict(X_test)
>>> test_mse = mean_squared_error(y_test, test_preds_grid)
>>> test_rmse = sqrt(test_mse)
>>> train_rmse
2.0731294674202143
>>> test_rmse
2.1700197339962175

With this code, you fit the model on the training data and evaluate the test data. You can see that the training error is worse than before, but the test error is better than before. This means that your model fits less closely to the training data. Using GridSearchCV to find a value for k has reduced the problem of overfitting on the training data.

Adding Weighted Average of Neighbors Based on Distance

Using GridSearchCV, you reduced the test RMSE from 2.37 à 2.17. In this section, you’ll see how to improve the performances even more.

Below, you’ll test whether the performance of your model will be any better when predicting using a weighted average instead of a regular average. This means that neighbors that are further away will less strongly influence the prediction.

You can do this by setting the weights hyperparameter to the value of "distance". However, setting this weighted average could have an impact on the optimal value of k. Therefore, you’ll again use GridSearchCV to tell you which type of averaging you should use:

>>>

>>> parameters = 
...     "n_neighbors": range(1, 50),
...     "weights": [[[["uniform", "distance"],
... 
>>> gridsearch = GridSearchCV(KNeighborsRegressor(), parameters)
>>> gridsearch.fit(X_train, y_train)
GridSearchCV(estimator=KNeighborsRegressor(),
                                                    param_grid='n_neighbors': range(1, 50),
                                                                                                    'weights': ['uniform', 'distance'])
>>> gridsearch.best_params_
'n_neighbors': 25, 'weights': 'distance'
>>> test_preds_grid = gridsearch.predict(X_test)
>>> test_mse = mean_squared_error(y_test, test_preds_grid)
>>> test_rmse = sqrt(test_mse)
>>> test_rmse
2.163426558494748

Here, you test whether it makes sense to use a different weighing using your GridSearchCV. Applying a weighted average rather than a regular average has reduced the prediction error from 2.17 à 2.1634. Although this isn’t a huge improvement, it’s still better, which makes it worth it.

Further Improving on kNN in scikit-learn With Bagging

As a third step for kNN tuning, you can use bagging. Bagging is an ensemble method, or a method that takes a relatively straightforward machine learning model and fits a large number of those models with slight variations in each fit. Bagging often uses decision trees, but kNN works perfectly as well.

Ensemble methods are often more performant than single models. One model can be wrong from time to time, but the average of a hundred models should be wrong less often. The errors of different individual models are likely to average each other out, and the resulting prediction will be less variable.

You can use scikit-learn to apply bagging to your kNN regression using the following steps. First, create the KNeighborsRegressor with the best choices for k et weights that you got from GridSearchCV:

>>>

>>> best_k = gridsearch.best_params_[[[["n_neighbors"]
>>> best_weights = gridsearch.best_params_[[[["weights"]
>>> bagged_knn = KNeighborsRegressor(
...     n_neighbors=best_k, weights=best_weights
... )

Then import the BaggingRegressor class from scikit-learn and create a new instance with 100 estimators using the bagged_knn model:

>>>

>>> de sklearn.ensemble import BaggingRegressor
>>> bagging_model = BaggingRegressor(bagged_knn, n_estimators=100)

Now you can make a prediction and calculate the RMSE to see if it improved:

>>>

>>> test_preds_grid = bagging_model.predict(X_test)
>>> test_mse = mean_squared_error(y_test, test_preds_grid)
>>> test_rmse = sqrt(test_mse)
>>> test_rmse
2.1616

The prediction error on the bagged kNN is 2.1616, which is slightly smaller than the previous error that you obtained. It does take a little more time to execute, but for this example, that’s not problematic.

Comparison of the Four Models

In three incremental steps, you’ve pushed the predictive performance of the algorithm. The following table shows a recap of the different models and their performances:

Model Error
Arbitrary k 2.37
GridSearchCV for k 2.17
GridSearchCV for k et weights 2.1634
Bagging and GridSearchCV 2.1616

In this table, you see the four models from simplest to most complex. The order of complexity corresponds with the order of the error metrics. The model with a random k performed the worst, and the model with the bagging and GridSearchCV performed the best.

More improvement may be possible for abalone predictions. For instance, it would be possible to look for ways to wrangle the data differently or to find other external data sources.

Conclusion

Now that you know all about the kNN algorithm, you’re ready to start building performant predictive models in Python. It takes a few steps to move from a basic kNN model to a fully tuned model, but the performance increase is totally worth it!

In this tutorial you learned how to:

  • Understand the mathematical foundations behind the kNN algorithm
  • Code the kNN algorithm from scratch dans NumPy
  • Use the scikit-learn implementation to fit a kNN with a minimal amount of code
  • Use GridSearchCV to find the best kNN hyperparameters
  • Push kNN to its maximum performance using bagging

A great thing about model-tuning tools is that many of them are not only applicable to the kNN algorithm, but they also apply to many other machine learning algorithms. To continue your machine learning journey, check out the Machine Learning Learning Path, and feel free to leave a comment to share any questions or remarks that you may have.