Aller au contenu

k plus proches voisins

I. Découverte⚓︎

Mon info

  • Les algorithmes des k plus proches voisins est abrégé kppv en français. En anglais, k nearest neighbors souvent abrégé en knn.
  • L’algorithme des k plus proches voisins appartient à la famille des algorithmes d’apprentissage automatique (machine learning) qui constituent le poumon de l'intelligence artificielle actuellement.
  • Pour simplifier, l'apprentissage automatique part souvent de données (data) et essaye de dire quelque chose des données qui n'ont pas encore été vues : il s'agit de généraliser, de prédire.

banquise

Lancer l'animation sur la banquise. L'animal inconnu est-il un ours ou un phoque ?

La vie sur la banquise

II. Une première approche⚓︎

Des Pokémon

Introduction avec des Pokémon

choix pokemon

Mon info

Ce problème, qui demande à prédire à quelle catégorie, ou classe, appartient ce nouvel élément donné, est appelé problème de classification. L'algorithme des k plus proches voisins permet de trouver les k voisins les plus proches (si k = 5 on cherche les 5 voisins les plus proches) de ce nouvel élément dans le but de lui associer une classe plausible (Psy ou Eau, dans cet exemple).

III. Principe de l'algorithme⚓︎

Que fait l'algorithme des k plus proches voisins ?

A partir d'un jeu de données (par exemple, les données sur nos 34 Pokémons) et d'une donnée cible (le nouveau Pokemon à classifier), l'algorithme des k plus proches voisins détermine les k données les plus proches de la cible. (si k = 3 les 3 plus proches voisins de la cible seront pris en compte, si k = 5 les 5, ...)

Les données

  • une table données de taille n contenant les données et leurs classes
  • une donnée cible : cible
  • un nombre k inférieur à n
  • une formule permettant de calculer la distance entre deux données

Résultat

un tableau contenant les k plus proches voisins de la donnée cible.

Algorithme

  • Créer une table distances_voisins contenant les éléments de la table données et leurs distances avec la donnée cible.
  • Trier les données de la table distances_voisins selon la distance croissante avec la donnée cible
  • Renvoyer les k premiers éléments de cette table triée.
Et notre prédiction alors ?

L'algorithmes des kppv en lui-même n'apporte pas la réponse à notre problème de classification puisqu'il ne fournit que les k plus proches voisins (et leurs classes) de notre donnée cible. Il reste donc une dernière étape pour prédire la classe de notre nouvel élément : pour cela, on choisit la classe majoritaire (la plus présente) dans les k plus proches voisins.

Influence de la valeur de k⚓︎

k impair

On est contents si k est impair car il ne peut pas y avoir d'ex-aequo.

Remarque

La valeur de k est très importante, elle doit être choisie judicieusement car elle a une influence forte sur la prédiction. Regardons le résultat de la prédiction pour différentes valeurs de k sur notre exemple.

k = 1

Si k = 1, cela revient à chercher la donnée la plus proche de notre élément cible.
Ici, on se rend compte que s la classe du plus proche élément est "Eau" (point bleu)
➜ on classerait le nouveau Pokémon comme étant de type "Eau".

k = 3

Si k = 3, on se rend compte que la classe majoritaire dans les 3 plus proches voisins est "Psy" (2 points rouges contre 1 point bleu) donc on classerait le Pokémon inconnu comme étant de type "Psy".

k = 9

La classe majoritaire parmi les 9 plus proches voisin est "Eau" (5 points bleus contre 4 points rouges) donc on classerait le Pokémon inconnu comme étant de type "Eau".

Remarque

Si on choisit k = 34 (le nombre total de données), alors la prédiction serait toujours "Psy" car c'est la classe majoritaire de l'échantillon. Il est donc incorrect de penser que plus la valeur de k augmente meilleure sera la prédiction, c'est plus complexe que cela. Il faudra observer les resultats.

Choix des distances⚓︎

L'algorithme des plus proches voisins repose sur la distance entre deux données. Dans les exemples vus précédemment, c'est la distance "naturelle" qui a été choisie (celle "à vol d'oiseau").

La distance euclidienne

Dans un repère orthonormé, si A et B de coordonnées \((x_{A},y_{A})\) et \((x_{B},y_{B})\), alors la distance entre ces deux points est donnée par la formule :

\(AB=\sqrt{(x_{B}-x_{A})^{2}+(y_{B}-y_{A})^{2}}\)

On parle alors de la distance euclidienne.

IV. 💻 A vous de jouer⚓︎

Dans le TP du II. Nous avons obtenu la figure suivante :

Nous allons mettre en oeuvre l'algorithme knn pour prédire le type des trois Pokémon représentés en vert, qui sont inconnus. Ce sont des "cibles"

Nous avions :

La liste de dictionnaires pokemons

pokemons = [{'Attaque': 105, 'Nom': 'Aligatueur', 'Points de vie': 85, 'Type': 'Eau'},  
{'Attaque': 92, 'Nom': 'Bargantua', 'Points de vie': 70, 'Type': 'Eau'},  
{'Attaque': 63, 'Nom': 'Carabaffe', 'Points de vie': 59, 'Type': 'Eau'},  
... ] 

Nous recherchons les types de :

  • cible_1 : points de vie : 65 et valeur d'attaque : 25
  • cible_2 : points de vie : 75 et valeur d'attaque : 80
  • cible_3 : points de vie : 95 et valeur d'attaque : 125
Mettre en œuvre KNN
  • La liste de dictionnaires pokemons est déjà implémentée, mais cachée pour ne pas alourdir l'exercice.

  • cible est un dictionnaire représentant un pokémon dont on cherche à déterminer le type.

Par exemple cible_1 = {'Attaque': 25, 'Points de vie' : 65}

  • La fonction cree_liste prend en paramètre la liste pokemons et le dictionnaire cible.
    Elle renvoie la liste des listes composées du nom des Pokémon, de leur distance à la cible, et de leur type.

Par exemple

>>> cree_liste(pokemons, cible_1)
[['Aligatueur', 'Eau', 82.46211251235322],
['Bargantua', 'Eau', 67.1863081289633],
 ... ]
  • La fonction cree_liste_triee prend en paramètre la liste pokemons et le dictionnaire cible.
    Elle renvoie la liste des listes composées du nom des Pokémon, de leur type et de leur distance à la cible triée par ordre croissant des distances.

Par exemple

>>> cree_liste_triee(pokemons, cible_1)
[['Spoink', 'Psy', 5.0],
['Munna', 'Psy', 11.0],
 ...]
  • La fonction knn prend en paramètre la liste pokemons et le dictionnaire cible. Elle renvoie la liste des k premiers éléments de la liste créée par la fonction cree_liste_triee

👉 on peut utiliser les syntaxes de la fonction sorted de Python vues dans le chapitre des algorithmes gloutons.

Compléter le script ci-dessous

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

.128013Anco0m/gS-d;fw,72qy+)*e[]h43vblr_:p96kT5 i=8a(sP1tNu050l0x0Y0T0Q0F0V0P0d0F0T0V0V0R010Y0Q0J010406050V0!0g0g0T0G0t040j0e0F0!0^0e0c050h0 1113150}0J04051l1e1o0h1l0}0l0Q0D0-0/0;0?0/0c0i0!0T0i0x0k0J0t0Y0A1c0P0A0Q0i0A0F1Q0A0Y0{050(0E0F0x1x0:0=011P1R1T1R0Y1Z1#1X0Y0G1m1L0-180V0J0T0c0?0r011%1z010n0*0x0c0T0g0x1X1|1~231)261#292b0{0a0P0W0G0e0J0e0V0Q1b0c0P0$1`0G0G0x0d2w1e2e0c1m0h1L2J1?1^1@1Y0l2g1A0Q0c282t1X1u1w0.1(2T2V0c0e2Z1X0J2C1m2H2J2:0~1}2x2#242)0G120F1X0T1O2C0n0?030H0H0d2*0x1T2(0e0k0C0k0X0{0P0X1e0T2;2@0|2?2f2_1)2{2}2 310x33013537393b2W3e0k21040P0r3l3n1~3p2H2S013u0T2~1m300A323436380$3E2)3G0C3i0C3M2G3o0}3Q3s0?3T3V053X3Z3A3#3D2U3F3f0B3i0B3.1f3:3q2^1y3t0e2|3U3w3Y3y3!3C3%403)3f0O3i0O462:3;2@3R3^4g3|3B3$3a4m3d3f0L3i0L4s483=4b3@4d3v3W3x3z4A3 3c3G0q3i0q4J3O4u3r4M3S4O4f4Q4h4S3~4l4V3f0S3i0S4!2I4$4a2$4)4e3_3{4i3}4k4C4;0k0K3i0K4_3P4v3?4~4P3`4R4j4B3(4E3g0f0{0X0f5b4{4w4*505i535k4D3G0X3h045C5s495u4 4y524T4:413g3I0X3L0h3m3/4#5H5e4x4,4z4/555O0X3+5E3-5T3N4`5X4(5Z5h4-5j4U5(435E455-5V5/4L4}5=514.545l5B4p5E4r5~475W612`5v5K655z560X4G5E4I6c4t5:626h5!5L5$673f0X4X5E4Z6q4K5d5;6u5?5#665A6z4?5E4^6E6e6G6t5J6v6j5_4n3g585E5a6R606T6g6V6J6w6L560r5o046;5G6f4c6,645^5N6Z0r5D706^6*6`5g6|5y6Y5m0r3I7b5b1p2.1e2Z2M0l1^2R5e4B2Y1v1m2-0x2/3o5 1m4B7u2f0Q0l0?362H5B3w7B7D6/5(222k0x7J6k7L2J5U6_0?0M0c0{0n2q0g7w6s240o3i7$7U3S7X04121L7+740?7)3J7=4%4}7W0{0Q0g2s0G0Y7w0P7%3t0{0V0s827w0}6d2I5H7I017E2@3G3I5h8h6x6M3H7M2a7O8i7K6 1X5-8d2=3Q8o0H7F3f5*8n7C8v7Q6Z3+0P7N7P793*8y7T7?010M0{0$0n7`4|7(7*8e7z8$3t0n8Y0Q0V0(0c0d0x8#3R0`040U8^5Y0{2s0M0x0g1c8}4(8`0p84863@0{0d0Q1!8@8)9a018`0v0I8c8^8D8F0k5{8I8Q6~5m438O8t9u5%6Z9s5-0P9F857,8X042C0Y0!0G1d8)9H8V7.898b9h7,8`0U8|9W9S9c9e1#954}8`0y9*240V5D02030C0K0m0W0e2U0Y0,2z0D0Q0x9=9@0m9.1)8`0za60?0e0{0kaa3S8 0e91939P8B8V9,af9:0{a39^9`9|9~1$a0a29?9^afa80vafac040w0waf0g0Q0{5S2:0P8A7v8C8J8j1~3G699t8K8R4o8s2b9A6y0kaY9E9G9iaG0u999X0{9!am7{2`9%9faC0{9-9#a`1)aq04as0m0b1/0T0s0!aza4a~04a9b18+abadaf7.909294bi8_a ap9;aAb7b9bbbdaBbr5eaDaF0{aIaKaM6?bfaE8)aR3O8gaU8E8k4F7HbR8L5m4G9ya(a!9v3G6n3MbO8faT7J9q6BaZ8p564Xb!8ub=5Ob:5~9I8Y3yaf7^85bC5;8-040d2C0x0H1T8:9ga_bj9ja@bmahaj1c0Vbf989Q9i7.9da}c49+0{9l9nbr9pbT0k6Ob;8w5m4?b^a)8qcEa-9F9i9J9L9Nal3o9Rb20?9,b0cf4wckbpcTbPa?04cZaS8Vb4b60Z0e0gb6bf0zcpaPcrc$akbfc,c)c.bva40N0t0JbAa5cv24a8c`cUc|041u8:1~8?bfa^c-cWag04boc~daa70{dd3OcVcgcs9(cedncg9kbh6rcAbR9q6#cFbX3G58cJb$9B5mdLcNcOb~040n4da=9$dqaic%d#do0e7^2Ud*dzc}cmc^czc!cBaW6z6=dMa#5na%b_cG5Bd|8z9odJcC5CbVcK6l3hdQb`6Ze97S8*3R9J8Zc18(c!5Yc61G0Y0Hdh8;dkdtcXcieydp0l1c2VdCd1do9k9mbNe6b.e88m308DdN6z21eee2eT8T3Ja.dXcR9Od/c#dgeE0xeGb,anbteBaLaNd?eLdIeNd`3g8HeQbWd~5)e0eb5(8He5e_8v9q0X9se~f3eg9x8PdRa*faeYcPb 8!eBc2bmc6c8e,cb8/0Yca1?a1e-ejbDeAep5;d;0ccneB97e(8~c7dBbLeKdHd^e7e{0XaYfcfh8qfVf2fY6la,3m9Gdye)evdj0x0V37dBfIc{7,aG0RfLfFc7c9fucddlcjd%clfHcof{62a|9)fJcxd@dD0Pd_0c5Bb)fXef5m6mf#glgieYf*dff-8=f/f;9ff:fye,g724f_gC3t0E884dfw0lg1eB7.gu8?f:ct1#f?gefC04dw2If+fM910tbF04f`gN0{eseufvf.bLgdeH2xgg5Bb:gkeW3gb@fggp6zb|f)e!8VcQ0%cSgF9bdgg/gvgRf=0HgAfAb+ejg^6Neaf$5(cIg g|0XcM3mhjbQe`gh6zdLg{eS6!gohsdU8Udoelc0foeogVc50{7Wc(e.eIfDhOg8g3c%gUg?bsgXh9dpgSfA9ifKcq7,7.0MfPg=hTg@fThz3Hd|hCd~6;hFhDh eigsh/0{0D9{2ufHhg0Ga1h!gZa/0{g*f@d$fscaccfwibfzgMfEhXdrd=gbh%h.ikfOiwbMfRgehl3H5Dd}b%3f70i0h~iHdVdf2C0 0F0(83iyd+ihh(aog+04i78/2Ugzicf/c g20Qe@ijhJ7Yd!iWd:04i/i^3Rd,7~hS7_h/gH9K1~1GirhWa{04h;iBdG48f7aVh`7bhnh03HeUhri18miPe#h7e%i|fMiR0!iT0TiViDbP0h7y7f7t7h7q1e0Y7kjJ2P2K0T9f2J7i8d0$0(0*0V04.