"La **matrice de confusion** est un tableau où on décompte les valeurs réelle O et prédites S pour chaque observation du jeu de données dans un problème de classification. Par sa conception, on peut valider rapidement le nombre d'observations où la prédiction est égale à la valeur réelle sur la diagonale de la matrice. Par convention, on place les valeurs prédites à la verticale et les valeurs observées à l'horizontale. Lorsque le nombre de modalités de la variable prédite est de deux, on a un problème de classification binaire et on obtient alors le décompte des vrais positifs, faux positifs, faux négatifs et vrais négatifs.\n",
"\n",
"La **précision** correspont à la proportion d'observations où la prédiction et la valeur réelle est dans une même classe par rapport au nombre d'observations où la prédiction est dans cette classe\n",
"Le **rappel** correspond à la proportions d'observations où la prédiction est dans une classe par rapport au nombre d'observations où la valeur réelle est dans cette classe.\n",
"Le project actuel est programmé dans le langage Python 3, et ce rapport est produit à l'aide de Jupyter Notebook. Seule les librairie standard `numpy` et `math` est utilisée, les autres ont été développées dans le cadre de ce projet."
"Ce projet utilise 3 jeux de données: [Iris](https://archive.ics.uci.edu/ml/datasets/Iris), [MONKS](https://archive.ics.uci.edu/ml/datasets/MONK's+Problems) et [Congressional Voting Records Dataset](https://archive.ics.uci.edu/ml/datasets/Congressional+Voting+Records). Pour les jeux qui ne sont pas déjà divisés en entrainement et test, on utilisera un ratio de 70% pour l'entrainement."
"Dans cette section, on présente les résultats obtenus pour l'algorithme des k plus proches voisins. Ce n'est pas un algorithme d'apprentissage à proprement parler, car il n'y a aucun modèle d'entrainé. On prédit la classe d'un nouvel élément en recherchant les $k$ plus proches voisins selon une métrique choisie. Dans notre cas, nous utiliserons la distance de Minkowski, qui est une généralisation de la distance euclidienne et de la distance de Manhattan à l'aide d'un paramètre $p$. Pour nos besoins, on le fixe à $p=2$, soit la distance euclidienne.\n",
"On peut ainsi itérer sur différentes valeurs de ce paramètre pour trouver la mesure la plus adaptée au problème, tout en réutilisant la même fonction. Cette métrique s'utilise aurant pour des domaines de valeurs continus que discrets.\n",
"On effectue l'entrainement du modèle (la copie du jeu d'entrainement et des étiquettes en mémoire) et on obtient les mesures de validation pour ce jeu d'entraînement. Les valeurs des métriques de précision et de rappel sont calculées pour chacune des étiquettes."
"Dans le cas de variables descriptives continues, la probabilité $P(X_f|C)$ prend plutôt la forme d'une distribution de probabilités. On utilisera alors la fonction de densité de probabilités de la loi gaussienne, avec la moyenne et l'écart-type estimés à partir des données.\n",
"\n",
"## Pseudocode\n",
"\n",
"### Initialisation\n",
"\n",
"Il n'y a aucun paramètre à initialiser\n",
"\n",
"### Entrainement\n",
"\n",
"L'entrainement se fait en deux parties. On calcule d'abord les probabilités à priori des classes de l'étiquette $\\mathbf{P}(C)$\n",
"\n",
"```\n",
"T = copie jeu d'entrainement\n",
"L = copie des étiquettes d'entrainement\n",
"Pour c dans unique(L):\n",
" Compteur=0\n",
" Pour l dans L:\n",
" Si l=c:\n",
" Compteur+=1\n",
" P(C=c) = Compteur/N\n",
"```\n",
"\n",
"On calcule ensuite les probabilités conditionnelles des caractéristiques sachant la classe de l'étiquette $\\mathbf{P}(F_1,\\ldots,F_m|C)$\n",
"Pour la prédiction, on ne calcule pas le facteur de régularisation $\\alpha$ pour amener les probabilité à 1, car ce qui nous intéresse, c'est une relation d'ordre et celle-ci est inchangée si on la divise par un nombre positif.\n",
"\n",
"### Prédiction\n",
"\n",
"```\n",
"Pour c dans unique(L):\n",
" Produit=0\n",
" Pour f dans F:\n",
" Pour l dans TS[f]:\n",
" Si Gaussien:\n",
" Si Parametres(C=c,F=f) existe:\n",
" Produit *= Densité_Gaussien(l,Parametres(C=c,F=f))\n",
" Sinon\n",
" Si P(C=c,F=f,V=l) existe:\n",
" Produit *= P(C=c,F=f,V=l)\n",
" P(X|c) = Produit\n",
"Retourner c où P(X|c) = Max(P(X|C))\n",
"```\n",
"\n",
"### Test\n",
"\n",
"```\n",
"Pour chaque observation x_i, i=1,N:\n",
" S_i <- Prédiction(x_i)\n",
"M <- matrice de confusion avec les étiquettes d'entrainement et les étiquettes S\n",
"Calculer les métriques depuis M\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Iris Dataset\n",
"\n",
"Comme ce jeu de données présente des caractéristiques continues, on utilisera la variante gaussienne du Bayes Naif"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"BN = BayesNaif.BayesNaif(True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mesures de performance sur l'échantillon d'entrainement"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"train_iris_nb = BN.train(train1, train_labels1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mesures de performance sur l'échantillon de test"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"test_iris_nb = BN.test(test1,test_labels1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Monks Dataset\n",
"\n",
"Comme ce jeu de données présente des caractéristiques discrètes, on n'utilise pas la variante gaussienne du Bayes Naif"
"Le tableau ci-dessous présente l'exactitude pour les modèles KNN et NB ainsi que le meilleur modèle dans ce cas. On remarque que pour le jeu de données Iris, le résultat obtenu est le même pour les deux modèles. Pour les jeux MONKS, dans deux des trois cas, le modèle KNN est meilleur, mais les deux résultats sont faibles. Dans le troisième cas, le modèle NB est largement plus exact, une situation similaire se reproduit pour les données sur les votes."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(np.array([(i[0],\n",
" i[1][1],\n",
" i[2][1],\n",
" \"KNN\" if i[1][1]>i[2][1] else \"NB\") for i in tous_resultats]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Précision\n",
"\n",
"On obtient les mêmes constats que précédemment au niveau de la précision"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(np.array([(i[0],\n",
" i[1][2],\n",
" i[2][2],\n",
" \"KNN\" if i[1][2]>i[2][2] else \"NB\") for i in tous_resultats]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Rappel\n",
"\n",
"Pour le rappel, on obtient des résultats différents. Pour une même exactitude, le modele KNN a un meilleur rappel pour la seconde classe. Pour le jeu de données MONKS1, l'ordre des valeurs du rappel est inversé. Ceci nous montre qu'il est important de sélectionner la mesure la plus importante pour le problème que nous voulons résoudre, car il est possible d'obtenir un conclusion erronée en utilisant une autre."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(np.array([(i[0],\n",
" i[1][3],\n",
" i[2][3],\n",
" \"KNN\" if i[1][3]>i[2][3] else \"NB\") for i in tous_resultats]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Temps d'exécution\n",
"\n",
"Dans tous les cas, le temps de calcul du Naive Bayes est signicativement plus court."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(np.array([(i[0],\n",
" i[1][4],\n",
" i[2][4],\n",
" \"KNN\" if i[1][4]<i[2][4] else \"NB\") for i in tous_resultats]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Difficultés rencontrées"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Algorithme kNN\n",
"\n",
"Une des difficultés rencontrées a été d'avoir effectivement le bon nombre de voisins dans l'entraînement, car le jeu d'entrainement contient toujours l'exemple et a une distance de 0. J'ai donc du modifier l'ensemble d'entrainement pour enlever l'exemple actif lors du calcul des métriques. Ceci était nécessaire pour pouvoir calculer la meilleure valeur de $k$.\n",
"\n",
"### Algorithme Bayes Naïf\n",
"\n",
"Comme il est possible de rencontrer une classe jamais rencontrée lors de l'entraînement, j'ai du modifier le produite des probabilités conditionnelles indépendantes pour enlever les probabilités qui ne sont pas évaluées durant l'entraînement. J'aurais pu modifier le tableau de probabilités, mais le plus simple a été de leur donner une valeur de 1, ce qui est équivalent à les ignorer dans le produit."