TP 9 : Apprentissage automatique
- Télécharger tp_apprentissage.zip et le charger dans le Codespace avec clic droit -> charger.
- Le décompresser avec
unzip tp_apprentissage.zip
dans le terminal.
Fonctions utilitaires
Dans cette partie, les fonctions seront écrites dans utils.c
et déclarées dans utils.h
.
Le dossier data
contient les données d'entraînement et de test pour deux problèmes : classification de chiffres manuscrits (digits) et de vêtements (fashion).
Pour chaque fichier :
- la première ligne est le nombre N de données
- chacune des N lignes suivantes contient valeurs : le label de la donnée (0 à 9 pour les chiffres et les vêtements) et les 28x28 pixels de l'image (de 0 à 255).


utils.h
contient plusieurs définitions, dont un type sample
permettant de représenter une donnée :
#define LEN (28 * 28) // taille d'une image
#define NB_CLASSES 10
typedef struct data {
int class;
float pixels[LEN]; // pixels[i*28+j] est le pixel (i, j)
} data;
utils.c
contient une fonction data* read_dataset(char* path, int n)
renvoyant un tableau contenant n
données du fichier path
.
-
Écrire une fonction
float d(data x1, data x2)
calculant la distance euclidienne au carré entre deux données (il est inutile de calculer la racine carrée car on ne cherche qu'à comparer les distances). -
Écrire une fonction
int nearest(data x, data* X, bool* T, int n)
renvoyant uni
tel queT[i]
est vrai etdist(x, X[i])
est minimum, oùn
est la taille deX
et deT
. -
Écrire une fonction
int majority(int* T, int n, int p)
renvoyant un élément majoritaire d'un tableauT
de taillen
dont les éléments sont entre0
etp - 1
, en complexité O().
KNN
Dans cette partie, les fonctions seront écrites dans un nouveau fichier knn.c
.
-
Écrire une fonction
int* k_nearest(data x, data* X, int n, int k)
renvoyant les indices desk
plus proches voisins dex
dansX
, oùn
est la taille deX
. -
Écrire une fonction
int knn(data x, data* X, int n, int k)
renvoyant la classe majoritaire desk
plus proches voisins dex
dansX
, oùn
est la taille deX
. -
Tester avec le code suivant, avec
gcc -fsanitize=address -Wall knn.c utils.c && ./a.out
:
int main() {
int n_train = 10000;
int n_test = 200;
int k = 3;
data *train = read_dataset("data/digit-train.txt", n_train);
printf("Read %d training data\n", n_train);
data *test = read_dataset("data/digit-test.txt", n_test);
printf("Read %d testing data\n", n_test);
int c = knn(test[0], train, n_train, k);
printf("Predicted class: %d (class is %d)\n", c, test[0].class); // doit afficher 7
free(train);
free(test);
return 0;
}
- Écrire une fonction
precision(data* test, int n_test, data* train, int n_train, int k)
renvoyant la précision du classifieur KNN surtest
en utilisanttrain
pour l'entraînement. Essayer avec différentes valeurs dek
,n_train
etn_test
.
-moyennes
Dans cette partie, les fonctions seront écrites dans dans un nouveau fichier kmeans.c
.
-
Écrire une fonction
data* compute_centers(data* X, int n, int* classes, int k)
renvoyant les centres des classes obtenues par l'algorithme des -moyennes, oùclasses[i]
est la classe deX[i]
,n
est la taille deX
etk
est la taille de classes. -
Écrire une fonction
int* compute_classes(data* X, int n, data* centers, int k, bool* has_changed)
renvoyant les classes obtenues par l'algorithme des -moyennes, oùn
est la taille deX
etk
est la taille decenters
.has_changed
doit être mis à vrai si une donnée change de classe, faux sinon. -
Écrire une fonction
data* init_centers(data* X, int n, int k)
renvoyantk
centres aléatoires parmi les données deX
, oùn
est la taille deX
. On utiliserasrand(time(NULL));
dans lemain
etrand() % n
pour générer un nombre aléatoire entre0
etn - 1
. -
Écrire une fonction
int* kmeans(data* X, int n, int k)
renvoyant les classes obtenues par l'algorithme des -moyennes, oùn
est la taille deX
. -
Tester sur les données de chiffres.