Compare commits

..

No commits in common. 'master' and 'refactorAlgo' have entirely different histories.

@ -80,7 +80,6 @@
display:flex;
justify-content:center;
border:0px;
transform: translate(12.5%);
}
.a {
color:#f00;

@ -7,6 +7,8 @@ def findCoordAround(center, nbAround):
find = []
for i in range(1, nbDimention + 1):
find = getNCoordDifferente(center, find, i, nbAround)
# print(len(find),(nbAround*2+1)*(nbAround*2+1))
# input()
return find
def getNCoordDifferente(center, find, nbDifferanteValue, nbAround):

@ -9,6 +9,7 @@ from knn import *
import matplotlib.pyplot as plt
from random import *
# initialisation du modèle
if (sys.argv[1] == "knn"):
model = Knn()
@ -39,10 +40,10 @@ for i in range(nbInfer):
xt = random()*maxx
yt = model.getValueOfPoint(np.array([xt]),2)
yr = applyFunction(xt,param)
error += abs(100*(yt[0]-yr)/applyFunction(maxx,param))
error += abs(yt[0]-yr)
plt.plot(xt,yt[0],'xr')
print("accuracy: ",100-error/nbInfer)
print("Error: ",(error/nbInfer)/applyFunction(maxx,param))
plt.plot(x,y)
plt.title("f(x) = "+str(param[0])+"*x^2 + "+str(param[1])+"*x + "+str(param[2]))

@ -66,7 +66,7 @@ t = time()
if (sys.argv[1] == "knn"):
model = Knn()
else:
model = Nnnar(2,0,260,125)
model = Nnnar(2,0,260,100)
for y, x in goodPix:
model.addPoint(np.array([x,y]), np.array(image[y,x]))

@ -9,40 +9,38 @@ import numpy as np
import pandas as pd
from time import time
df = pd.read_csv('./data/Iris.csv')
df = df.iloc[:, 1:]
# Normalisation des données
def runOneTest(trainTestRatio,model):
df = pd.read_csv('./data/Iris.csv')
df = df.iloc[:, 1:]
# Normalisation des données
df.iloc[:, 0] = df.iloc[:, 0] - df.iloc[:, 0].min()
df.iloc[:, 1] = df.iloc[:, 1] - df.iloc[:, 1].min()
df.iloc[:, 2] = df.iloc[:, 2] - df.iloc[:, 2].min()
df.iloc[:, 3] = df.iloc[:, 3] - df.iloc[:, 3].min()
df.iloc[:, 0] = df.iloc[:, 0] - df.iloc[:, 0].min()
df.iloc[:, 1] = df.iloc[:, 1] - df.iloc[:, 1].min()
df.iloc[:, 2] = df.iloc[:, 2] - df.iloc[:, 2].min()
df.iloc[:, 3] = df.iloc[:, 3] - df.iloc[:, 3].min()
df.iloc[:, 0] = df.iloc[:, 0] / df.iloc[:, 0].max()
df.iloc[:, 1] = df.iloc[:, 1] / df.iloc[:, 1].max()
df.iloc[:, 2] = df.iloc[:, 2] / df.iloc[:, 2].max()
df.iloc[:, 3] = df.iloc[:, 3] / df.iloc[:, 3].max()
df.iloc[:, 0] = df.iloc[:, 0] / df.iloc[:, 0].max()
df.iloc[:, 1] = df.iloc[:, 1] / df.iloc[:, 1].max()
df.iloc[:, 2] = df.iloc[:, 2] / df.iloc[:, 2].max()
df.iloc[:, 3] = df.iloc[:, 3] / df.iloc[:, 3].max()
df.iloc[:, 0:4] = df.iloc[:, 0:4] * 100
def runOneTest(model,df):
df.iloc[:, 0:4] = df.iloc[:, 0:4] * 100
# Création des données d'entrainement et de test
train = df.sample(frac=0.8)
train = df.sample(frac=trainTestRatio)
test = df.drop(train.index)
# Entrainement du modèle
trainCoord = train.iloc[:, :-1].values
trainValue = train.iloc[:, -1].values
coord = train.iloc[:, :-1].values
value = train.iloc[:, -1].values
for i in range(len(coord)):
model.addPoint(np.array(coord[i]), np.array([value[i]]))
# Test du modèle
coord = test.iloc[:, :-1].values
value = test.iloc[:, -1].values
for i in range(len(trainCoord)):
model.addPoint(np.array(trainCoord[i]), np.array([trainValue[i]]))
nbError = 0
for i in range(len(coord)):
if model.getLabelOfPoint(np.array(coord[i]), 5) != value[i]:
@ -54,7 +52,7 @@ def runOneTest(model,df):
if (sys.argv[1] == "knn"):
model = Knn()
else:
model = Nnnar(4, 0, 100.001, 5)
model = Nnnar(4, 0, 100.001, 10)
t1 = time()
@ -62,7 +60,7 @@ nbRepetition = 100
accuracy = 0
for i in range(nbRepetition):
model.reset()
accuracy += runOneTest(model,df)
accuracy += runOneTest(0.8,model)
t2 = time()
print("accuracy moyenne:",str(accuracy/nbRepetition))

@ -1,85 +0,0 @@
import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/3nar/code")
from nnnar import *
from knn import *
import matplotlib.pyplot as plt
import numpy as np
from time import time
# initialisation du modèle
comp = False
if (sys.argv[1] == "comp"):
model1 = Knn()
model2 = Nnnar(1, 0, 1, 50)
comp = True
if (sys.argv[1] == "knn"):
model = Knn()
else:
model = Nnnar(1, 0, 1, 50)
maxVal = 20_000
nbTest = 100
nbPts = 10
# Création des données d'entrainement
train = []
test = []
for i in range(maxVal):
x = np.random.rand()
y = np.random.rand()
train.append([x,y])
for i in range(nbTest):
x = np.random.rand()
test.append(x)
train = np.array(train)
test = np.array(test)
def testModel(model, train ,test):
model.reset()
t = time()
# Entrainement du modèle
for i in range(len(train)):
model.addPoint(np.array([train[i,0]]), np.array([train[i,1]]))
# Test du modèles
for i in range(len(test)):
model.getValueOfPoint(np.array([test[i]]), 5)[0]
return time() - t
if comp:
res1 = []
res2 = []
idxs = []
for i in range(1,nbPts):
idx = round((i*maxVal)/nbPts)
print(idx)
idxs.append(idx)
res1.append(testModel(model1, train[:idx], test))
res2.append(testModel(model2, train[:idx], test))
plt.xlabel('Number of training points')
plt.ylabel('Time (s)')
plt.xticks(range(len(idxs)), idxs)
plt.plot(res1,label='KNN')
plt.plot(res2,label='3NAR')
plt.legend()
plt.show()
else:
res = []
idxs = []
for i in range(1,nbPts):
idx = round((i*maxVal)/nbPts)
print(idx)
idxs.append(idx)
res.append(testModel(model, train[:round((i*maxVal)/nbPts)], test))
plt.xlabel('Number of training points')
plt.ylabel('Time (s)')
plt.xticks(range(len(idxs)), idxs)
plt.plot(res)
plt.show()

@ -1,71 +0,0 @@
import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/3nar/code")
from nnnar import *
from knn import *
import matplotlib.pyplot as plt
import numpy as np
from time import time
# initialisation du modèle
comp = False
if (sys.argv[1] == "comp"):
model1 = Knn()
model2 = Nnnar(1, 0, 1, 100)
comp = True
if (sys.argv[1] == "knn"):
model = Knn()
else:
model = Nnnar(1, 0, 1, 100)
maxVal = 1_000
nbTest = 1_000
nbPts = 10
# Création des données d'entrainement
train = []
test = []
for i in range(maxVal):
x = np.random.rand()
y = np.random.rand()
train.append([x,y])
for i in range(nbTest):
x = np.random.rand()
test.append(x)
train = np.array(train)
test = np.array(test)
def testModel(model, train ,test):
model.reset()
t = time()
# Entrainement du modèle
for i in range(len(train)):
model.addPoint(np.array([train[i,0]]), np.array([train[i,1]]))
# Test du modèles
for i in range(len(test)):
model.getValueOfPoint(np.array([test[i]]), 5)[0]
return time() - t
if comp:
res1 = []
res2 = []
idxs = []
for i in range(1,nbPts):
idx = round((i*nbTest)/nbPts)
print(idx)
idxs.append(idx)
res1.append(testModel(model1, train, test[:idx]))
res2.append(testModel(model2, train, test[:idx]))
plt.xlabel('Number of points infered')
plt.ylabel('Time (s)')
plt.xticks(range(len(idxs)), idxs)
plt.plot(res1,label='KNN')
plt.plot(res2,label='3NAR')
plt.legend()
plt.show()

@ -1,61 +0,0 @@
import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/3nar/code")
from nnnar import *
import matplotlib.pyplot as plt
import numpy as np
from time import time
maxVal = 1_000
nbTest = 1000
nbPts = 10
nbModel = 10
nbMaxSubDiv = 100
# Création des données d'entrainement
train = []
test = []
for i in range(maxVal):
x = np.random.rand()
y = np.random.rand()
train.append([x,y])
for i in range(nbTest):
x = np.random.rand()
test.append(x)
train = np.array(train)
test = np.array(test)
def testModel(model, train ,test):
model.reset()
t = time()
# Entrainement du modèle
for i in range(len(train)):
model.addPoint(np.array([train[i,0]]), np.array([train[i,1]]))
# Test du modèles
for i in range(len(test)):
model.getValueOfPoint(np.array([test[i]]), 5)[0]
return time() - t
for i in range(1,nbModel):
nbSub = round(i*nbMaxSubDiv/nbModel)
print(nbSub)
model = Nnnar(1,0,1,nbSub)
res = []
idxs = []
for i in range(1,nbPts):
idx = round((i*nbTest)/nbPts)
idxs.append(idx)
res.append(testModel(model, train, test[:idx]))
plt.plot(res,label='NNNAR('+str(nbSub)+')')
plt.xlabel('Number of points infered')
plt.ylabel('Time (s)')
plt.xticks(range(len(idxs)), idxs)
plt.legend()
plt.show()

@ -1,155 +0,0 @@
<section class="titre">
<h1>
<span class="a">3NAR</span>
<span class="b">(n nearest neighbor avrage rapid)</span>
</h1>
<p>
par Ludovic CASTIGLIA
</p>
</section>
<section>
<h2>
Problématique
</h2>
<p>
Le but de ce projet était de trouver un algorithme <b>généraliste</b> (qui puisse être utilisé dans différents cas d'utilisation), <b>rapide</b> (qui n'a pas une grande complexité ou qui trouve des solutions dans un temps raisonnable) et <b>précis</b> (qui trouve des solutions proches de la réalité dans une majorité des cas).<br/></br>
Pour ce faire, j'avais plusieurs outils à ma disposition, mais celui sur lequel je me suis penché est l'algorithme <i>Knn</i> (n plus proche voisin). C'est un algorithme assez simple qui n'a pas réellement besoin d'entraînement contrairement à un réseau de neurones, mais qui à cause de sa complexité algorithmique devient inutilisable dans sa forme naïve pour de larges volumes de données. J'ai donc dû trouver un moyen de modifier l'implémentation de <i>Knn</i> pour réduire sa <b>complexité</b>, tout en gardant si possible l'aspect <b>généraliste</b> et <b>précis</b> de <i>Knn</i>.
</p>
</section>
<section>
<h2>
Algorithme et avantage par rapport à Knn
</h2>
<h3>
Point commun entre <i>Knn</i> et <i>3nar</i> :
</h3>
<p>
Ces deux algorithmes sont capables d'inférer une ou plusieurs valeurs à partir d'une ou plusieurs valeurs en entrée grâce à des exemples fournis au préalable. Ils sont également capables de réaliser de la classification ou de la multi-classification (c'est-à-dire associer une ou plusieurs classes à une donnée ou un groupe de donnée en entrée).
</br></br>
Outre leurs capacités, ils fonctionnent de la même façon. La phase d'entraînement consiste simplement à enregistrer en mémoire les différentes données d'exemples, ainsi que leurs valeurs associées (valeur, groupe de valeurs, classe ou groupe de classe). Ensuite, pour inférer la/les valeur.s/classe.s associée.s à de nouvelles coordonnées, on trouve les n exemples les plus proches et on retourne la moyenne des valeurs des n points multipliés par un poid calculé en fonction de leur distance.
</p>
<h3>
Problème de <i>Knn</i> :
</h3>
<p>
Le problème de l'implémentation naïve de <i>Knn</i> est que pour trouver les n points les plus proches d'un nouveau point A, on calcule la distance de chaque point en mémoire avec A, puis on trouve les n points avec la plus petite distance. Ce qui veut dire que plus le nombre de points d'exemple en mémoire augmente, plus on calcule de distance pour trouver la valeur d'un point. Ainsi, comme nous pouvons le voir sur le graphique ci-dessous, la complexité de l'algorithme est linéaire.
</p>
<image src="./rapport/knn.png"/>
<h3>
Solution de <i>3nar</i> :
</h3>
<p>
Pour trouver les n points les plus proches de A sans avoir à calculer les distances avec tous les autres points, <i>3nar</i> profite de la phase d'ajout des points pour enregistrer des informations</b>.
</br></br>
À l'initialisation, nous allons créer un espace orthonormé avec m dimension (le nombre de coordonnées des exemples). Nous allons ensuite remplir cet espace avec un certain nombre de sous-espaces à déterminer en fonction des cas d'utilisation (ces espaces sont tous de tailles égales et sont eux aussi orthonormés). Ensuite, lors de la phase d'entraînement, il suffit d'ajouter les points dans les différents sous-espaces en fonction de leurs coordonnées. Pour trouver le sous-espace si vos sous-espaces sont dans une liste, vous pouvez calculer l'index avec cette formule:<br/>
Soit t, la taille d'un sous-espace et nb, le nombre de sous-espace dans une dimension:
</br><img src="./rapport/formuleIdx.png"/><br/>
Dans le cas de ce programme, les sous-espaces sont stockés dans un tenseur et les coordonnées du sous-espace d'un point sont données par la division euclidienne de toutes les coordonnées du point par t.
</br></br>
Une fois toutes les données ajoutées dans le modèle, il est temps de lui demander la valeur de nouveau point. Pour cela, l'algorithme va trouver dans quel sous-espace le point serait, s'il existait dans sa mémoire. Puis il vérifie s'il a assez de points dans ce sous-espace dans une distance (dont on parlera plus tard). Si c'est le cas, alors il calcule les distances avec ces points et il retourne les n plus faibles. Sinon on trouve les sous-espaces à proximité et on recommence jusqu'à avoir assez de points. Cela permet de grandement réduire le nombre de calcul de distance entre points pour trouver les n points les plus proches.
<br/></br>
Concernant la distance, elle est calculée en fonction des coordonnées du point que l'on veut deviner, de la taille du sous-espace et en fonction de son centre. Cette distance doit être la plus grande possible (pour capter le plus de point) tout en ne sortant en aucun point du sous-espace (pour être sûr de réellement trouver les points les plus proches). Cette distance peut être calculée de la façon suivante:</br>
<image src="./rapport/rayon.png"/></br>
<i>dist = t/2 - max(ABx,ABy) + t * nbSousEspaceAutour</i></br>
À noter que c'est un exemple en 2d, le même calcule peut-être généralisé pour un nombre m de dimension.<br/>
Dans le cas de ce programme, je n'ai pas utilisé cette distance. A la place, j'ai utilisé cette formule qui me garantit de ne jamais sortir de l'espace et qui est plus simple à calculer:
</br><image src="./rapport/rayon2.png"></br>
<i>dist = t * nbSousEspaceAutour</i>
</br><br/>
Il s'agira maintenant de présenter l'algorithme qui permet de trouver les sous-espaces autour d'un sous-espace. Pour trouver tous les sous-espaces à d de distance d'un sous-espace, il faut dans un premier temps trouver les sous-espaces autour de l'origine, puis appliquer une translation, qui transforme l'origine en notre sous-espace, aux sous-espaces que l'on trouve. Pour ce faire, je vais parcourir les différentes étapes de l'algorithme avec vous en prenant l'exemple d'un espace à 2 dimensions où l'on veut trouver tous les sous-espaces à 1 de distance. Dans une première boucle, on va mettre dans une variable le nombre de valeurs différentes que peut comporter nos coordonnées de sous-espace. Ici, c'est 1 et 2. Puis on va créer un radical de coordonnées (une première partie de coordonnées ordonnées qui contient n valeurs différentes entre -1 et 1, la distance maximum par rapport à zéro). Les radicaux crées vont être: <i>[-1],[0],[1],[-1,0],[-1,1],[0,1]</i>. Une fois qu'on a ces radicaux, nous allons compléter ceux-ci avec les valeurs des radicaux jusqu'à avoir des coordonnées complètes (en conservant seulement les valeurs des radicaux). Nous obtenons ici <i>[-1,-1],[0,0],[1,1],[-1,0],[-1,1],[0,1]</i>. Enfin, les sous-espaces vont être les permutations de coordonnées complètes que l'on vient de trouver. Dans notre cas : <i>[-1,-1],[0,0],[1,1],[-1,0],[0,-1],[-1,1],[1,-1],[0,1],[1,0]</i>. Comme vous pouvez le constater, résultent de l'algorithme les 9 coordonnées que nous souhaitions. Notons également que cet algorithme fonctionne peu importe le nombre de dimensions (positives et entières) et peu importe la distance (elle aussi positive et entière).
</br></br>
Enfin, dernière optimisation de l'algorithme, pour éviter de recalculer en boucle les différents sous-espaces autour d'un sous-espace (ce qui est coûteux à faire dynamiquement pour m dimension), on enregistre en mémoire le résultat de cette opération. Puis, lorsque l'on veut recalculer ce résultat, nous pouvons simplement l'appliquer à notre cas (car les sous-espaces ne seront pas les mêmes, mais leurs sous-espaces autour seront aux mêmes distances et aux mêmes directions).
<br/><br>
Ainsi, toutes ces optimisations permettent de grandement réduire la complexité de <i>3nar</i> par rapport à <i>Knn</i>. Cependant, comme vous pouvez le voir sur les graphiques suivants, la complexité reste linéaire que l'on fasse varier le nombre de points inférés ou le nombre de données d'entraînement:
</p>
<image src="./rapport/compKnn3narTrainPoint.png"/>
<image src="./rapport/compKnn3narPointInfered.png"/>
<p>
Cependant, il y a encore un dernier point intéressant à discuter avec <i>3nar</i>, c'est l'impact d'un paramètre sur la complexité de l'algorithme. Plus on divise l'espace en sous-espace, plus la complexité de l'algorithme se réduit. Vous pouvez voir ce phénomène grâce au graphique suivant:
</p>
<image src="./rapport/comp3narParam.png"/>
</section>
<section>
<h2>
Cas d'utilisations
</h2>
<p>
Comme nous l'avons vu dans la première partie, <i>3nar</i> est utilisable dans pleins de contextes. Ce ne sera pas souvent le meilleur algorithme, mais dans la plupart des cas, il rendra un résultat satisfaisant en un temps lui aussi satisfaisant. Vous pouvez retrouver 4 cas d'utilisations, aussi appelées demo dans ce projet, que vous pouvez lancer au choix avec <i>Knn</i> ou <i>3nar</i>. Pour lancer les demos, placez vous dans le répertoire et appelez les scripts python de demo en ajoutant à votre commande le nom de l'algorithme (knn ou nnnar).<br/><br/>
</p>
<h3>
demo1.py
</h3>
<p>
Dans la demo 1, nous utilisons le csv <i>./data/maison.csv</i>. Nous calculons une valeur en sortie en fonction de 4 valeurs en entrée. Nous calculons le prix des maisons en fonction du nombre de m², du nombre de chambres, du nombre de salles de bain et de l'étage. Sur ces données et avec 80% du dataset en entraînement et 20% en test, on arrive aux alentours de 90% de précision.
</p>
<h3>
demo2.py
</h3>
<p>
Dans cet exemple, nous générons aléatoirement une fonction polynomiale du second degré avec des paramètres étant des réels compris entre -5 et 5. Puis après avoir fourni à <i>3nar</i> quelques exemples (100 dans notre cas), nous utlisons l'algorithme pour prédire de nouvelles valeurs choisit aléatoirement.
</p>
<h3>
demo3.py
</h3>
<p>
Cette demo est plus visuelle et présente un cas d'utilisation ou nous devons calculer plusieurs valeurs en fonction de plusieurs valeurs. Dans cette demo, nous prenons une image (<i>./data/img.jpg</i>), puis de manière aléatoire, on vient remplacer des pixels par des pixels blancs. Ensuite, nous venons retrouver algorithmiquement les pixels qui ont été changés (on pourrait aussi faire cette étape avec <i>3nar</i> mais cela serait plus long).Enfin, on vient calculer la valeur de ces pixels avec <i>3nar</i>. Dans cet exemple, le nombre de données comence à être non négligeable, et on voit l'avantage de <i>3nar</i> par rapport à <i>Knn</i>.
</p>
<h3>
demo4.py
</h3>
<p>
Dans cet exemple, on fait la classification des espèces d'Iris à partir de la taille de la largeur de la tige et des pétales. En quelques secondes, on arrive à avoir environ 95% de précision.
</p>
<p>
À noter que pour les démos, je n'ai pas de dataset d'évaluation, seulement un dataset de test, car je n'ai pas d'entraînement à proprement parler (comme de la backpropagation) donc le dataset de test renvoie forcément le même résultat que le dataset d'évaluation.
</p>
</section>
<style>
.titre {
border-bottom: 1px solid;
padding: 3em;
}
h1 {
text-aligne: center;
display:flex;
justify-content:center;
border:0px;
transform: translate(12.5%);
}
p {
padding: 0px 0px 0px 25px;
}
.a {
color:#f00;
font-weight:900;
font-size:2em;
}
.b {
font-size:0.7em;
}
.titre p {
position:absolute;
right:15px;
}
h2 {
color: #ff7777;
border-bottom: 1px solid #fff;
}
b {
font-weight: 900;
font-size: 1.1em;
}
i {
font-style: normal;
padding: 0px 5px;
border-radius: 5px;
border: 1px solid;
}
h3 {
padding: 0px 0px 0px 10px;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Loading…
Cancel
Save