221 lines
8.9 KiB
Python
221 lines
8.9 KiB
Python
#!/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
|
|
import metrics
|
|
|
|
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, k=5, **kwargs):
|
|
"""
|
|
c'est un Initializer.
|
|
Vous pouvez passer d'autre paramètres au besoin,
|
|
c'est à vous d'utiliser vos propres notations
|
|
"""
|
|
self.k=k
|
|
|
|
def set_best_k(self, train, train_labels, nb_split, k_potentiel):
|
|
## Création des échantillons de validation croisée
|
|
shuffle_indices = np.random.permutation([i for i in range(train.shape[0])])
|
|
validation_index = np.array_split(shuffle_indices, nb_split)
|
|
|
|
train_index = []
|
|
for i in range(nb_split):
|
|
train_index.append(list(set(shuffle_indices)-set(validation_index[i])))
|
|
|
|
## Itération sur les valeurs de K et sur les échantillons de validation croisée
|
|
accuracy_cv = np.empty((len(k_potentiel),nb_split))
|
|
|
|
for k_index in range(len(k_potentiel)):
|
|
for i in range(nb_split):
|
|
self.k=k_potentiel[k_index]
|
|
self.train(train[train_index[i]], train_labels[train_index[i]], verbose=False)
|
|
cm,accuracy,precision,recall = self.test(train[validation_index[i]], train_labels[validation_index[i]], verbose=False)
|
|
accuracy_cv[k_index][i] = accuracy
|
|
|
|
## Calcul de la moyenne
|
|
mean_accuracy = accuracy_cv.mean(axis=1)
|
|
|
|
## Extraction du meilleur K
|
|
best_k = k_potentiel[int(min(np.argwhere(mean_accuracy==max(mean_accuracy))))]
|
|
|
|
## Assignation
|
|
self.k=best_k
|
|
|
|
print("Le meilleur k est: "+str(best_k))
|
|
|
|
## Retourner la valeur
|
|
return best_k
|
|
|
|
|
|
|
|
def train(self, train, train_labels, verbose=True): #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_set=train
|
|
self.train_labels=train_labels
|
|
|
|
n,m = train.shape
|
|
nn=np.empty((n,self.k,2))
|
|
|
|
self.minkowski_p=m
|
|
|
|
# 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]
|
|
nn[x,:,1]=np.apply_along_axis(minkowski_distance,1,self.train_set[i_range[0:self.k]],train[x],self.minkowski_p)
|
|
for i in i_range[self.k:n]:
|
|
dist = minkowski_distance(self.train_set[i],train[x],self.minkowski_p)
|
|
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 = metrics.confusion_matrix(train_labels,nn_mode_label)
|
|
accuracy, precision, recall = metrics.prediction_metrics(cm,train_labels,nn_mode_label)
|
|
if (verbose):
|
|
metrics.print_prediction_metrics(cm,accuracy,precision,recall)
|
|
|
|
return cm,accuracy,precision,recall
|
|
|
|
|
|
def predict(self, exemple, label, verbose=True):
|
|
"""
|
|
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
|
|
|
|
"""
|
|
n,m = self.train_set.shape
|
|
nn=np.empty((self.k,2))
|
|
nn[:,0]=[i for i in range(self.k)]
|
|
nn[:,1]=np.apply_along_axis(minkowski_distance,1,self.train_set[0:self.k],exemple,self.minkowski_p)
|
|
for i in range(self.k,n):
|
|
dist = minkowski_distance(self.train_set[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)
|
|
|
|
if (verbose):
|
|
print("Observé:"+str(label)+" Prédit:"+str(nn_mode_label))
|
|
|
|
return (nn_mode_label,label)
|
|
|
|
def test(self, test, test_labels, verbose=True):
|
|
"""
|
|
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
|
|
|
|
"""
|
|
|
|
n,m = test.shape
|
|
n_ex,m_ex = self.train_set.shape
|
|
nn=np.empty((n,self.k,2))
|
|
# Boucle sur chaque ligne du jeu de test
|
|
for x in range(n):
|
|
nn[x,:,0]=[i for i in range(self.k)]
|
|
nn[x,:,1]=np.apply_along_axis(minkowski_distance,1,self.train_set[0:self.k],test[x],self.minkowski_p)
|
|
for i in range(self.k,n_ex):
|
|
dist = minkowski_distance(self.train_set[i],test[x],self.minkowski_p)
|
|
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
|
|
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 = metrics.confusion_matrix(test_labels,nn_mode_label)
|
|
accuracy, precision, recall = metrics.prediction_metrics(cm,test_labels,nn_mode_label)
|
|
if (verbose):
|
|
metrics.print_prediction_metrics(cm,accuracy,precision,recall)
|
|
|
|
return cm,accuracy,precision,recall
|
|
|
|
|
|
|
|
|
|
# Vous pouvez rajouter d'autres méthodes et fonctions,
|
|
# il suffit juste de les commenter.
|