Choisissez votre style : colorisé, impression

Série 16
Exercice de révision et de « remise en jambe » (Niveau 1 à 3).

Le but de cet exercice est de continuer à vous faire réviser les acquis du premier semestre (fonctions, structures, alias de type, types énumérés) et de vous faire pratiquer les nouveautés de la semaine passée : gestion des exceptions, espaces de nommage et arguments de main

Motivation

Certains neurones du cerveau (dans le cortex cérébral par exemple) sont très réceptifs à des stimuli spécifiques. Ceci signifie que ces neurones ne sont "activés" que s'ils reçoivent un signal correspondant au stimuli auquel ils sont sensibles. Supposons que le signal envoyé par le monde extérieur soit caractérisé par une séquence de 20 données binaires, avec par exemple:
signal  = 11111000000000011111 (en l'absence du stimulus, signal négatif)
signal  = 11111000001111100000 (en présence du stimulus, signal positif)

Supposons que de façon aléatoire, de nombreux neurones soient réceptifs à un stimulus donné :

Modele

Nous nous intéressons alors à trouver parmi ces neurones lesquels sont les plus réceptifs à ce stimulus, y compris dans le cas où la transmission du signal correspondant est bruitée. En clair, nous souhaitons trouver les neurones dont l'activité est hautement corrélée à la présence/absence du stimulus.

Modele

Modèle

Nous partons ici du modèle de détection d'un objet dans le champ visuel suivant : soit ζ un stimulus binaire correspondant à la présence ou non d'un objet dans le champ de vision d'un individu. Le stimulus ζ déclenche l'émission d'un signal nerveux s, codé sur vingt bits, correspondant à la valeur prise par le stimulus :

ζ = 0 → s  = 11111000000000011111 (signal négatif)
ζ = 1 → s  = 11111000001111100000 (signal positifs)

La transmission de ce signal est effectuée par le nerf optique.

Des erreurs de transmission peuvent avoir lieu, ce que l'on modélise par un bruitage inversant chaque bit avec une probabilité de 0.1. Notons s le signal bruité.

Le dernier chaînon dans le processus de détection est un neurone modélisé par vingt poids synaptiques ω. En notant φ le produit scalaire euclidien, on pose que l'activation ξ du neurone ayant reçu un signal s; est donnée par :

 ξ = 1   si  φ(ω,s) ≥ 0 
 ξ = 0   sinon
On dira que l'activation ξ du neurone est corrélée avec le stimulus initial ζ si ξ = ζ.

Le but de cet exercice est de construire un neurone (par détermination de ses poids synaptiques) dont l'activation est corrélée, au mieux, avec des stimuli initiaux.

On procédera pour cela comme suit 

Implémentation

Commencez par créer un sous-répertoire neurones dans votre répertoire cpp/serie15.

Copiez l'archive neurones.zip dans ce sous-répertoire puis dezzipez-la :

cd 
cd cpp/serie15/neurones
unzip neurones.zip 

Complétez ensuite le fichier fourni neurones.cc selon les indications ci-dessous :

Définition de types utiles

Dans le fichier fourni neurones.cc, définissez les alias de types suivants : Inclusions utiles :
#include <array>

Toutes ces définitions seront placées dans un espace de nommage appelé impl.

Représentation d'un signal nerveux

Les signaux nerveux seront représentés par une structure Signal. Cette structure sera caractérisée fondamentalement par  :

Vous écrirez ensuite les fonctions suivantes :

Pour tester cette partie, vous pouvez ajouter au début du programme principal fourni, les instructions suivantes :

Signal s1(init_signal(true));
cout << "Affichage d'un signal avec présence de l'objet,";
cout << "après bruitage : " << endl;
display_signal(cout, s1);
cout << endl;

Signal s2(init_signal(false));
cout << "Affichage d'un signal sans présence de l'objet,";
cout << "après bruitage : " << endl;
display_signal(cout, s2);
cout << endl;

Représentation d'un neurone

Les neurones seront représentés par une structure Neuron. Cette structure sera caractérisée essentiellement par  :

Vous coderez ensuite les fonctions suivantes relatives aux neurones :

Pour le codage de la fonction process vous vous référerez aux choix de modélisation et pourrez utiliser la fonction STL std::inner_product (cette référence, les modalités d'utilisation de cette fonction deviendront plus claires lorsque nous aurons abordé la STL), de la façon suivante :

        // produit scalaire des ensembles sig.s_ et neuron.w_ :
	double prod(std::inner_product(sig.s_.begin(), sig.s_.end(), neuron.w_.begin(), 0.0)); 

sig est de type Signal, s_ est l'ensemble des bits du signal, neuron est de type Neuron et w_ est l'ensemble de poids synaptiques du neurone.

Pour tester cette partie, vous pouvez compléter le programme principal fourni par les instructions suivantes :

	Neuron n(init_neuron());
	display_neuron(cout,n);
	cout << endl;
	bool activated(process(n,s1));
	if (activated)
	{
		cout << "Le neurone a été activé par le signal" << endl;
	}
	else
	{
		cout << "Le neurone n'a pas été activé par le signal" << endl;
	}

Si vous lancez plusieurs fois le programme vous devriez voir s'afficher tantôt le message Le neurone a été activé par le signal tantôt le message Le neurone n'a pas été activé par le signal

Du stimulus au neurone (algorithme)

L'algorithme simulant la création, le bruitage et le traitement d'un signal avec, en fin de code, la détermination de la corrélation entre le stimulus de départ ζ et l'activation ξ du neurone aura donc l'allure suivante :
Neuron n;
// le stimulus aléatoire engendre un signal qui est ensuite bruité
Signal s(rand_range(0, 1));

// retourne le signal bruité
S = s.getSignal();

// traitement du signal (bruité) par le neurone
n.process(S);

// détermination de la corrélation
bool ζ = s.objectPresent();
bool ξ = n.activated();
if (ξ == ζ) {
     // corrélation
} else {
     // pas de corrélation

Programme principal

Le programme principal devra : Votre programme, pourra être invoqué en ligne de commande selon les options suivantes :
./neurones -d [-s [val] -n [val]]
ou
./neurones -b [-s [val] -n [val]]
Les options -d et -b s'écrasent l'une l'autre :

Pour parvenir à ce résultat, il vous est demandé  :

  1. Vous pourrez reprendre les fonctions starts_with et read_integer déjà codé dans l'exercice précédent.
  2. La fonction init_parameters lancera des exceptions prédéfinies de type invalid_argument ou logic_error pour toutes les appels invalides au programme en ligne de commande (inspirez-vous aussi de l'exercice précédent).

Pour vérifier que votre programme fonctionne correctement, vous pouvez le lancer en mode "affichage de tous les scores" avec 1000 neurones et 1000 signaux (décommentez les instructions fournies une fois codés les éléments manquants).

Vous pouvez ensuite générer un graphique de la distribution des scores.

Si vous êtes sur une VM de l'EPFL ou si vous êtes sur Linux/ MacOS, vous pouvez utiliser pour cela le script fourni make_graph.gp (le script histogram.awk doit être présent dans le dossier), selon les modalités suivantes :

gnuplot make_graph.gp
Sur vos propres machines, il faut avoir installé l'outil gnuplot.
L'histogramme est produit sous la forme d'une image stockée dans un fichier graph.png. Vous devriez obtenir une distribution de ce type:
Modele
(meilleur score culminant à près de 100% de signaux correctement interprétés et moyenne des score autour des 50%). Alternativement, et en particulier si vous êtes sous Windows, vous pouvez générer le graphique en ayant recours à une feuille de calcul. Notez à ce propos que vous pouvez rediriger les affichages de votre programme vers un fichier. Par exemple dans une invite de commande :
neurones > data.txt
data.txt est un nom de votre choix. Au lieu de s'afficher dans le terminal, le résultat de l'exécution du programme neurones se trouvera dans le fichier data.txt.
Retour à la série
Dernière mise à jour : 2024/02/22 16:47