2019-03-16 21:50:53 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*- coding: utf-8 -*
|
|
|
|
"""
|
|
|
|
Vous allez definir une classe pour chaque algorithme que vous allez développer,
|
|
|
|
votre classe doit contenit au moins les 3 methodes definies ici bas,
|
|
|
|
* train : pour entrainer le modèle sur l'ensemble d'entrainement
|
|
|
|
* predict : pour prédire la classe d'un exemple donné
|
|
|
|
* test : pour tester sur l'ensemble de test
|
|
|
|
vous pouvez rajouter d'autres méthodes qui peuvent vous etre utiles, mais moi
|
|
|
|
je vais avoir besoin de tester les méthodes test, predict et test de votre code.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
|
|
|
def minkowski_distance(x,y,p_value):
|
|
|
|
return pow(sum(pow(abs(a-b),p_value) for a,b in zip(x, y)),1/p_value)
|
|
|
|
|
|
|
|
def mode(a):
|
|
|
|
u, c = np.unique(a, return_counts=True)
|
|
|
|
return u[c.argmax()]
|
|
|
|
|
|
|
|
# le nom de votre classe
|
|
|
|
# BayesNaif pour le modèle bayesien naif
|
|
|
|
# Knn pour le modèle des k plus proches voisins
|
|
|
|
|
|
|
|
class Knn: #nom de la class à changer
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
"""
|
|
|
|
c'est un Initializer.
|
|
|
|
Vous pouvez passer d'autre paramètres au besoin,
|
|
|
|
c'est à vous d'utiliser vos propres notations
|
|
|
|
"""
|
|
|
|
self.k=5
|
|
|
|
|
|
|
|
|
|
|
|
def train(self, train, train_labels): #vous pouvez rajouter d'autres attribus au besoin
|
|
|
|
"""
|
|
|
|
c'est la méthode qui va entrainer votre modèle,
|
|
|
|
train est une matrice de type Numpy et de taille nxm, avec
|
|
|
|
n : le nombre d'exemple d'entrainement dans le dataset
|
|
|
|
m : le mobre d'attribus (le nombre de caractéristiques)
|
|
|
|
|
|
|
|
train_labels : est une matrice numpy de taille nx1
|
|
|
|
|
|
|
|
vous pouvez rajouter d'autres arguments, il suffit juste de
|
|
|
|
les expliquer en commentaire
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
------------
|
|
|
|
Après avoir fait l'entrainement, faites maintenant le test sur
|
|
|
|
les données d'entrainement
|
|
|
|
IMPORTANT :
|
|
|
|
Vous devez afficher ici avec la commande print() de python,
|
|
|
|
- la matrice de confision (confusion matrix)
|
|
|
|
- l'accuracy
|
|
|
|
- la précision (precision)
|
|
|
|
- le rappel (recall)
|
|
|
|
|
|
|
|
Bien entendu ces tests doivent etre faits sur les données d'entrainement
|
|
|
|
nous allons faire d'autres tests sur les données de test dans la méthode test()
|
|
|
|
"""
|
|
|
|
|
|
|
|
# on fait seulement utiliser les données du jeu d'entrainement comme paramètre d'un modèle Knn
|
|
|
|
self.train=train
|
|
|
|
self.train_labels=train_labels
|
|
|
|
|
|
|
|
n,m = train.shape
|
|
|
|
nn=np.empty((n,self.k,2))
|
|
|
|
|
2019-03-16 22:40:22 +00:00
|
|
|
self.minkowski_p=m
|
|
|
|
|
2019-03-16 21:50:53 +00:00
|
|
|
# On trouve les k plus proches voisins et leur distance pour chacunes des observations du training set
|
|
|
|
# On enlève la valeur testée de la liste des points pour lesquels on mesure la distance car on sait qu'elle vaut 0.
|
|
|
|
# On veut tester sur les autres points seulement
|
|
|
|
for x in range(n):
|
|
|
|
i_range = [i for i in range(n)]
|
|
|
|
i_range.pop(x)
|
|
|
|
nn[x,:,0]=i_range[0:self.k]
|
2019-03-16 22:40:22 +00:00
|
|
|
nn[x,:,1]=np.apply_along_axis(minkowski_distance,1,self.train[i_range[0:self.k]],train[x],self.minkowski_p)
|
2019-03-16 21:50:53 +00:00
|
|
|
for i in i_range[self.k:n]:
|
2019-03-16 22:40:22 +00:00
|
|
|
dist = minkowski_distance(self.train[i],train[x],self.minkowski_p)
|
2019-03-16 21:50:53 +00:00
|
|
|
nn_dist=nn[x,:,1]
|
|
|
|
distdiff = nn_dist-dist
|
|
|
|
max_distdiff=max(distdiff)
|
|
|
|
if(max_distdiff>0):
|
|
|
|
pos_changement = np.argwhere(nn_dist==max(nn_dist))[0]
|
|
|
|
nn[x,pos_changement,0]=i
|
|
|
|
nn[x,pos_changement,1]=max_distdiff
|
|
|
|
# on retrouve le label modal pour chacunes des observations
|
|
|
|
nn_labels = self.train_labels[nn[:,:,0].astype(np.int)]
|
|
|
|
nn_mode_label = np.apply_along_axis(mode,1,nn_labels)
|
|
|
|
|
|
|
|
# on construit la matrice de confusion
|
|
|
|
cm = self.confusion_matrix(train_labels,nn_mode_label)
|
|
|
|
accuracy, precision, recall = self.prediction_metrics(cm,train_labels,nn_mode_label)
|
|
|
|
self.print_prediction_metrics(cm,accuracy,precision,recall)
|
|
|
|
|
|
|
|
return cm,accuracy,precision,recall
|
|
|
|
|
|
|
|
|
|
|
|
def predict(self, exemple, label):
|
|
|
|
"""
|
|
|
|
Prédire la classe d'un exemple donné en entrée
|
|
|
|
exemple est de taille 1xm
|
|
|
|
|
|
|
|
si la valeur retournée est la meme que la veleur dans label
|
|
|
|
alors l'exemple est bien classifié, si non c'est une missclassification
|
|
|
|
|
|
|
|
"""
|
2019-03-16 22:40:22 +00:00
|
|
|
n,m = self.train.shape
|
|
|
|
nn=np.empty((self.k,2))
|
|
|
|
nn[:,1]=np.apply_along_axis(minkowski_distance,1,self.train[0:self.k],exemple,self.minkowski_p)
|
|
|
|
for i in range(self.k,n):
|
|
|
|
dist = minkowski_distance(self.train[i],exemple,self.minkowski_p)
|
|
|
|
nn_dist=nn[:,1]
|
|
|
|
distdiff = nn_dist-dist
|
|
|
|
max_distdiff=max(distdiff)
|
|
|
|
if(max_distdiff>0):
|
|
|
|
pos_changement = np.argwhere(nn_dist==max(nn_dist))[0]
|
|
|
|
nn[pos_changement,0]=i
|
|
|
|
nn[pos_changement,1]=max_distdiff
|
|
|
|
nn_labels = self.train_labels[nn[:,0].astype(np.int)]
|
|
|
|
nn_mode_label = mode(nn_labels)
|
|
|
|
|
|
|
|
print("Observé:"+str(label)+" Prédit:"+str(nn_mode_label))
|
|
|
|
|
|
|
|
return nn_mode_label
|
2019-03-16 21:50:53 +00:00
|
|
|
|
|
|
|
def test(self, test, test_labels):
|
|
|
|
"""
|
|
|
|
c'est la méthode qui va tester votre modèle sur les données de test
|
|
|
|
l'argument test est une matrice de type Numpy et de taille nxm, avec
|
|
|
|
n : le nombre d'exemple de test dans le dataset
|
|
|
|
m : le mobre d'attribus (le nombre de caractéristiques)
|
|
|
|
|
|
|
|
test_labels : est une matrice numpy de taille nx1
|
|
|
|
|
|
|
|
vous pouvez rajouter d'autres arguments, il suffit juste de
|
|
|
|
les expliquer en commentaire
|
|
|
|
|
|
|
|
Faites le test sur les données de test, et afficher :
|
|
|
|
- la matrice de confision (confusion matrix)
|
|
|
|
- l'accuracy
|
|
|
|
- la précision (precision)
|
|
|
|
- le rappel (recall)
|
|
|
|
|
|
|
|
Bien entendu ces tests doivent etre faits sur les données de test seulement
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
def confusion_matrix(self,obs_labels,pred_labels):
|
|
|
|
"""
|
|
|
|
Retourne la matrice de confusion
|
|
|
|
Prend en entrée deux vecteurs d'étiquettes: observations et prédictions
|
|
|
|
Retourne une matrice NumPy
|
|
|
|
"""
|
|
|
|
unique_obs_labels=np.unique(obs_labels)
|
|
|
|
unique_pred_labels=np.unique(pred_labels)
|
|
|
|
nb_unique_obs_labels=(unique_obs_labels.shape)[0]
|
|
|
|
nb_unique_pred_labels=(unique_pred_labels.shape)[0]
|
|
|
|
|
|
|
|
confusion_matrix = np.zeros((nb_unique_obs_labels,nb_unique_pred_labels))
|
|
|
|
|
|
|
|
for observed,predicted in zip(obs_labels,pred_labels):
|
|
|
|
confusion_matrix[observed][predicted] += 1
|
|
|
|
return confusion_matrix
|
|
|
|
|
|
|
|
def prediction_metrics(self,cm,obs_labels,pred_labels):
|
|
|
|
"""
|
|
|
|
Cette fonction retourne les métriques accuracy, precision et recall
|
|
|
|
Elle prend en entrée la matrice de confusion et les vecteurs d'étiquettes: observations et prédictions
|
|
|
|
accuracy=(tp+tn)/all
|
|
|
|
precision=tp/(tp+fp)
|
|
|
|
recall=tp/(tp+fn)
|
|
|
|
"""
|
|
|
|
|
|
|
|
accuracy = (obs_labels == pred_labels).sum() / float(len(obs_labels))
|
|
|
|
precision=[]
|
|
|
|
recall=[]
|
|
|
|
for label_num in np.unique(obs_labels):
|
|
|
|
precision.append(cm[label_num,label_num] / sum(cm[:,label_num]))
|
|
|
|
recall.append(cm[label_num,label_num] / sum(cm[label_num,:]))
|
|
|
|
|
|
|
|
return accuracy, precision, recall
|
|
|
|
|
|
|
|
def print_prediction_metrics(self,cm,accuracy,precision,recall):
|
|
|
|
"""
|
|
|
|
Cette fonction imprime la matrice de confusion et les métriques
|
|
|
|
"""
|
|
|
|
print("Matrice de confusion:")
|
|
|
|
print(cm)
|
|
|
|
print("\nAccuracy:")
|
|
|
|
print(accuracy)
|
|
|
|
print("\nPrecision:")
|
|
|
|
print(precision)
|
|
|
|
print("\nRecall")
|
|
|
|
print(recall)
|
|
|
|
|
|
|
|
# Vous pouvez rajouter d'autres méthodes et fonctions,
|
|
|
|
# il suffit juste de les commenter.
|