Nous allons voir dans cette partie une classe d'algorithmes permettant de classer des objets (au sens large) dans différentes catégories.
La décision prise par cet algorithme évolue au cours du temps, en fonction des choix qu'il a déjà effectué. C'est pourquoi il est classé dans un des algorithme les plus simples mettant en œuvre l'intelligence artificielle.
L'exemple historique des Iris
En 1936, le biologiste Edgar Anderson cherche à étudier la classification des Iris. Il s'intéresse plus particulièrement à 3 espèces :
iris setosa
iris virginica
iris versocolor
Iris setosa
Iris versicolor
Iris virginica
Comme on peut le constater sur les photos, il n'est sûrement pas évident pour un non initié de déterminer l'espèce d'un Iris croisé dans la nature au hasard d'une promenade.
Les données
Edgar Anderson décide donc de mettre au point un test systématique permettant de différencier ces trois espèces.
Pour cela il réalise des mesures sur les pétales et les sépales des Iris :
Il mesure :
La largeur et la longueur des sépales
La largeur et la longueur des pétales
Pour un ensemble d'Iris à la même saison, connaissant l'espèce de chaque fleur.
Pour simplifier le traitement des données nous ne prendrons en compte que les mesures des pétales.
Vous trouverez un extrait de ces données ici. Dans ce fichier les deux premières colonnes correspondent à la largeur et la longueur du pétale, la troisième colonne contient 0, 1 ou 2 en suivant le code suivant :
0 : iris setosa
1 : iris virginica
2 : iris versocolor
Récupérez le fichier de donnée présenté ci-dessus.
Visualisez ces données grâce à ce script :
import matplotlib.pyplot as plt
import csv
iris=[]
x0,x1,x2=[],[],[]
y0,y1,y2=[],[],[]
esp=[]
with open("iris.csv",'r',encoding='utf-8') as f:
lecteur=csv.reader(f,delimiter=',')
for row in lecteur:
iris.append(row)
for i in range (1,len(iris)):
if iris[i][2]=='0':
x0.append(float(iris[i][0]))
y0.append(float(iris[i][1]))
if iris[i][2]=='1':
x1.append(float(iris[i][0]))
y1.append(float(iris[i][1]))
if iris[i][2]=='2':
x2.append(float(iris[i][0]))
y2.append(float(iris[i][1]))
plt.axis('equal')
plt.scatter(x0, y0, color='r', label='iris setosa')
plt.scatter(x1, y1, color='g', label='iris versicolor')
plt.scatter(x2, y2, color='b', label='iris virginica')
plt.legend()
axes = plt.gca()
axes.set_xlabel('longueur des pétales')
axes.set_ylabel('largeur des pétales')
axes.legend()
plt.show()
Le problème est de déterminer l'espèce d'un Iris inconnu, ne connaissant que la largeur et la longueur de ses pétales.
L'algorithme
Pour résoudre ce problème nous allons utiliser l'algorithme des k plus proches voisins.
Pour cela nous allons
Placer l'iris inconnu sur le plan
Chercher ses k plus proches voisins dans ce plan
Chercher l'espèce majoritaire parmi ces k voisins
Ajouter l'iris inconnu à l'espèce déterminée
Cet algorithme met en œuvre l'apprentissage automatique ou "machine learning". En effet Chaque nouvel élément va être associé à une espèce et donc enrichir les connaissances de l'ordinateur, qui pourra ainsi affiner ses choix par la suite.
On commence à voir apparaître une forme d'intelligence artificielle
Pour bien comprendre illustrons l'algorithme que l'on vient de décrire.
Voici trois iris inconnus ajoutés dans le plan sous la forme d'un point noir.
Iris inconnu 1
Iris inconnu 2
Iris inconnu 3
Pour les inconnus 1 et 2, nous pouvons déterminer "à l’œil" l'espèce de l'iris. Pour l'inconnu 3 cela va être plus compliqué, il va falloir calculer les distances aux iris voisins pour choisir l'espèce de l'inconnu 3.
Mise en œuvre de l'algorithme
Vous n'allez pas coder cet algorithme complètement, voici donc la structure du code que nous vous proposons et les structures de données associées :
Structures de données
Deux structures de données seront mises en oeuvre dans ce code.
Les données extraites du fichier csv sont stockées dans un tableau de tableau du type :
que l'on appellera par la suite "tableau de données".
Lors du calcul des distances entre l'iris inconnu et ses voisins, un nouveau tableau de tableaux sera créé, en ajoutant au tableau précédent la distance correspondante :
que l'on appellera par la suite "tableau de données et distances".
Structure du code
Le code est structuré en fonctions indépendantes :
recup_donnees(nomDeFichier)->list permet de stocker les données d'un fichier csv dans une structure du type "tableau de données"
affichage_iris(tab:list) permet l'affichage dans un plan à partir d'une structure de données du type "tableau de données" (voir ci-dessus).
distance(x1:float, y1:float, x2:float, y2:float)->float calcule la distance entre un iris de coordonnées (x1, y1) et un autre iris de coordonnées (x2, y2)
mini_iris(tab:list) recherche dans une structure de données du type "tableau de données et distances", la plus petite distance et renvoie le sous tableau correspondant (par exemple [1.4, 0.2, 0, 1.4836666666]) ainsi que la structure de données "tableau de données et distances" auquel ce sous tableau a été enlevé)
kppv_iris(iris:list, larg:float, haut:float,nbvoisins:int)->list: C'est bien sur ici que tout se passe...La fonction recherche les nbvoisins plus proches voisins, recherche l'espèce majoritaire puis ajoute au tableau de données le nouvel iris avec son espèces, et renvoie ce tableau.
Travail à réaliser
Vous pouvez télécharger le programme kppv_iris_eleve.py ou le visualiser ci-dessous. Répondez aux questions sous le code.
import matplotlib.pyplot as plt
import csv
from math import sqrt
def affichage_iris(tab:list):
'''
ne rien toucher à cette fonction
'''
x0,x1,x2=[],[],[]
y0,y1,y2=[],[],[]
esp=[]
for i in range (1,len(tab)):
if tab[i][2]==0:
x0.append(tab[i][0])
y0.append(tab[i][1])
if tab[i][2]==1:
x1.append(tab[i][0])
y1.append(tab[i][1])
if tab[i][2]==2:
x2.append(tab[i][0])
y2.append(tab[i][1])
plt.axis('equal')
plt.scatter(x0, y0, color='r', label='iris setosa')
plt.scatter(x1, y1, color='g', label='iris versicolor')
plt.scatter(x2, y2, color='b', label='iris virginica')
plt.legend()
axes = plt.gca()
axes.set_xlabel('longueur des pétales')
axes.set_ylabel('largeur des pétales')
axes.legend()
plt.show()
def recup_donnees(nomDeFichier)->list:
'''
à partir du nom de fichier, cette fonction récupère les données et les renvoie sous forme de tableau de tableaux
ne rien toucher à cette fonction
'''
iris=[]
with open(nomDeFichier,'r',encoding='utf-8') as f:
lecteur=csv.reader(f,delimiter=',')
for row in lecteur:
iris.append(row)
for i in range (1,len(iris)):
for j in range(2):
iris[i][j]=float(iris[i][j])
iris[i][2]=int(iris[i][j])
return iris
def mini_iris(tab:list):
'''
recherche le minimu et enlève l'élément du tableau de tableaux
renvoie le minimum et le tableau de tableaux modifié
'''
min=100000
indicemin=0
for i in range (len(tab))
if tab[i][3] < min:
min=tab[i][3]
indicemin=i
min=tab.pop(indicemin)
return min,tab
def distance(x1:float,y1:float,x2:float,y2:float)->float
'''
calcule la "distance" entre deux iris
'''
return sqrt((x2-x1)**2+(y2-y1)**2)
def kppv_iris(iris:list, larg:float,haut:float,nbvoisins:int)-> list:
'''
recherche des nbvoisins les plus proches pour déterminer l'espèce de l'iris donc les pétales mesurent larg x haut.
Une fois l'espèce déterminée, le nouvel iris est ajouté au tableau iris qui est renvoyé modifié en fin de fonction.
'''
#copie du tableau de tableaux iris dans le tableau de tableaux iris_kppv
iris_kppv=[[iris[i][j] for j in range(3)]for i in range(1,len(iris))]
#calcul des distances à toutes les autres fleurs et stockage dans le tableau de tableaux iris_kppv
for i in range (0,len(iris_kppv)):
iris_kppv[i].append(distance(larg,haut,iris_kppv[i][0],iris_kppv[i][1]))
#recherche des k voisins les plus proches
voisins=[]
for i in range (nbvoisins):
voisin_proche,iris_kppv=mini_iris(iris_kppv)
voisins.append(voisin_proche)
#recherche de l'espèce majoritaire des k voisins
#le tableau espece va contenir l'effectif de chaque espèce : espece[0]->setosa ; espece[1]->versicolor ; espece[2]->virginica
espece=[0,0,0]
#on parcourt le tableau de tableaux contenant les proches voisins
for i in range (len(voisins)):
if voisins[i][2]==0:
pass # ... supprimer le pass puis compléter la ligne
if voisins[i][2]==1:
pass # ... supprimer le pass puis compléter la ligne
if voisins[i][2]==2:
pass # ... supprimer le pass puis compléter la ligne
#ajout de l'iris inconnu au tableaua de taableaux "iris"
iris.append([larg,haut,espece.index(max(espece))])
return iris
#####################################################
#récupération des données
fleurs=recup_donnees('iris.csv')
#application de l'algorithme des k plus proches voisins
#affichage des données
affichage_iris(fleurs)
Le code ci-dessus contient des erreurs et est incomplet.
Téléchargez le code et exécutez le (en le plaçant dans le même répertoire que le fichier iris.csv. Une erreur de syntaxe apparaît, corrigez là
Une fois le première erreur corrigée, une deuxième erreur de syntaxe apparaît, corrigez là (attention c'est un peu plus subtil ici))
Une fois les deux erreurs de syntaxe corrigées, le programme doit s'exécuter normalement et afficher le graphique des données des iris.
Complétez le programme principal pour ajouter au tableau de tableaux fleursl'iris inconnu dont le pétale mesure 5.5 cm de longueur et 1.95 cm de largeur. On s'appuiera pour cela sur ses 5 plus proches voisins.
Le point correspondant à l'iris est ajouté dans le plan mais vous pouvez constater qu'ils n'est pas classé dans la bonne espèce.
C'est logique, il manque trois lignes au programme (lignes 96, 98 et 100).
Complétez ces trois lignes et testez à nouveau le programme, si vous avez bien compris, cela doit fonctionner.