EihiS

May 16, 2017

3D photogrammetry on Linux with CUDA + COLMAP

Filed under: linux — Tags: , , , , , — admin @ 6:45 am

Ce post regroupe des notes concernant l’installation de CUDA et du logiciel COLMAP pour photogrammetrie
( Reconstruction de modele 3D en utilisant des photos multiples d’un même élément depuis plusieurs points de vues )

Target config : portable x86_64, 8Go,NVIDIA GT540M , Linux Ubuntu 16.04

Part I : CUDA drivers

  • Nvidia-CUDA drivers,téléchargement (1.8 Go) : http://developer.nvidia.com/cuda-downloads
  • >> choix des OPTIONS du download :  Linux, x86-64, ubuntu, 16.04, deb(local)
  • Installation (depuis le dossier : ‘Downloads’ )

  • sudo dpkg -i ./Downloads/cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64.deb
  • sudo apt-get update
  • sudo apt-get install cuda
    • Note : des warnings “ldconfig.real,   **warnings** sur egl.so ‘not a symbolic link’ ” apparaissent (more on this later )
    • Redémarrer…

    Quick starting guide, PDF : http://developer.download.nvidia.com/compute/DevZone/docs/html/C/doc/CUDA_C_Getting_Started_Linux.pdf

    • Ajouts à PATH & LD_LIBRARY_PATH des librairies CUDA :
    1. $ export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
    2. $ export PATH=/usr/local/cuda/bin:$PATH
    3. (pour ajout permanent, ajouter ces lignes en fin du fichier “.profile” du dossier “home” utilisateur )
    Tester , compiler un exemple :
    • les examples sont localisés dans /usr/local/cuda/samples
    Pour compiler dans un dossir autre ( droits d’accès.. ) , exemple ‘test_cuda’ :
    • recopier le dossier complet ’samples’ de /usr/local/cuda/samples dans l’endroit de votre choix de votre ‘home’
    • Puis, ‘make’ depuis le sous-dossier d’exemple
    • le dossier ./test_cuda/bin/ est créé
    • dans ./test_cuda/bin/x86_64/linux/release/ se trouve l’executable du nom de l’exemple compilé

    Part II : COLMAP software

    • git clone https://github.com/colmap/colmap
    apt-get , dependances :
    sudo apt-get install \
        cmake \
        build-essential \
        libboost-all-dev \
        libeigen3-dev \
        libsuitesparse-dev \
        libfreeimage-dev \
        libgoogle-glog-dev \
        libgflags-dev \
        libglew-dev \
        freeglut3-dev \
        qt5-default \
        libxmu-dev \
        libxi-dev
    executer : $ dpkg -l | grep libeigen
    Si la version n'est pas 3.2.10, voir footnote , réinstaller la version 3.2.10 avant de continuer
    Installer "Ceres Solver" :
    sudo apt-get install libatlas-base-dev libsuitesparse-dev
    git clone https://ceres-solver.googlesource.com/ceres-solver
    cd ceres-solver
    mkdir build
    cd build
    cmake ..
    make -j1 ( <- your taste..)
    sudo make install
    Le cache de ma machine reste plein apres la manip, si c'est votre cas, essayez (flush du cache linux) :
    • sudo sh -c ‘echo 1 >/proc/sys/vm/drop_caches’
    Ensuite, compilation+installation COLMAP :
    • cd repertoire de download colmap
    • mkdir build
    • cd build
    • cmake ..
    • make -j2
    • sudo make install

    FOOTNOTE :

    si problemes de crashes COLMAP en lancant ‘reconstruction’ (SEGFAULTS, EIGEN visible dans le crash resume):
    Vérifier la version de eigen , avec : dpkg -l | grep libeigen
    Il faut la 3.2.10 et non la 3.3(beta) comme c’etait le cas sur ma machine..
    Donc, en résumé :
    • Télécharger Eigen 3.2.10 https://bitbucket.org/eigen/eigen/downloads?tab=tags
    • mkdir build,cd build,cmake ..,make install
    • ->uninstall Eigen 3.3 depuis synaptic package manager
    • Recompiler Ceres ( il FAUT specifier les chemins modifiés pour votre lib EIGEN version 3.2.10 fraichement recompilée )
        dans ceres-solver/ -> cd build, make clean, cmake -D EIGEN_INCLUDE_DIR=/usr/local/include/eigen3 ..
    • make -j2
    • Recompiler Colmap ( il FAUT specifier les chemins modifiés pour votre lib EIGEN version 3.2.10 fraichement recompilée )
    • dans colmap/ -> cd build,make clean,cmake -D EIGEN3_VERSION=3.2.10 -D EIGEN3_INCLUDE_DIRS=/usr/local/include/eigen3 -D EIGEN_INCLUDE_DIR=/usr/local/include/eigen3/ ..
    • make -j2
    314159265358979323846264338327950288
    419716939937510582097494459230781640
    628620899862803482534211706798214808
    

    March 25, 2017

    Using the Data-Folder libraries

    Filed under: linux, neuron — admin @ 11:34 am

    Pour cette fois, le post est en anglais

    Today i released a first bunch of my homebrew libraries , so i’m going to post a first ‘hello world’ example program, wich uses those libraries.
    I have not enough time to expose and explain all of that libraries functions, so the best is to dive into the code if you wish to understand the things.

    In order to continue with the ‘neural net learning ‘ series, i need to use some graphic functions like displaying bitmaps, displaying text and buttons , and so on.. so, i’ll use those libraries in the next séries of post.

    a simple “hello world” program (reminder : it uses the EHS2D_V2 folder from data.eihis.com/common/:

    Opens a sdl2.0 based window, and creates a ‘text window’ inside it, displaying bitmap-based char texts :

    This is a ultra-simple , first program just to use some simple functions of the library.

    You can grab the necessary files from here : (ZIP : data.eihis.com/web_ehs2d_helloworld/web_ehs2d_helloworld.zip )

    • Use the makefile to build the executable
    Here is , for information, the main.c code which is pretty small :
    #include "classics.h"
    // ehs 2d , sdl2.0 etc includes
    #include "local_config.h"
    int main(int argc, char **argv)
    {
    
    	int32_t h_textwindow;
    	char* cb_text;
    	char* cb_temp=malloc(sizeof(char)*256); // some room for sprintf functions
    	uint32_t i_eventreturn;
    	// hello world related variables
    	uint32_t roll;
    	uint32_t quit;
    	//
    	// (uses EHS2D globals)
    	//
    	ehs_main_x=0;
    	ehs_main_y=0;
    	ehs_main_w=900;
    	ehs_main_h=600;
    	// mandatory.. we wont resize the main window in this example
    	ehs_dynamic_main_w=ehs_main_w;
    	ehs_dynamic_main_h=ehs_main_h;
    	//
    	ehs_target_main_w=ehs_main_w;
    	ehs_target_main_h=ehs_main_h;
    	// enables/disables the background image (see local_config.h for the image's definition )
    	ehs_background_name="../common/BITMAP/ARGB8888/helloworld_900x600.bmp";
    	ehs_use_background=1;// if 1, would start with background enabled, displayed
    	// could have been 0, with no background name defined
    	//
    	//
    	// init EHS2D
    	//
    	EHS_init();
    	//
    	cb_text=(char*) malloc(sizeof(char)*32*12); // alloc size for the window's text
    	h_textwindow=EHS_reg_elem(_EHS_SUBWINDOW,50,50,32,12,0,0,_EHS_IDLE,(uint8_t*) cb_text,"Console demo\0",_EHS_DM_CHAR,_EHS_FONTB_SIZE,fontB,fontC);//
    	EHS_set_fonts(h_textwindow,0,2); // this sets the font used , aka 'font A / font C'
    	EHS_SUBWIN_clear(h_textwindow,' '); // clear the window's display buffer ('cb_text') with ' ' (space) chars
    	//
    	EHS_SUBWIN_textAt(h_textwindow,"Hello world!",0,0);// place a text inside, at coordinates 0,0
    	//
    	quit=0;
    	roll=0;
    	while(quit==0)// loop there
    	{
    		// sprintf to a temp buffer for dynamic text content, then displat @ x=0,y=2 inside the ehs window
    		sprintf(cb_temp,"Roll value is:%d \n",roll);
    		EHS_SUBWIN_textAt(h_textwindow,cb_temp,0,2);
    		// render the main window
    		SDL_RenderPresent(sdlRenderer);
    		// update the window's contents
    		EHS_update();
    		// now manage with input events
    		i_eventreturn=EHS_process_events();
    		switch (i_eventreturn)
    		{
    			case _EHS_EVENT_QUIT:
    				quit=1;
    			break;
    			case _EHS_EVENT_KEYINPUT:
    				//
    				switch (input_key)
    				{
    					case SDLK_ESCAPE:
    						quit=1;
    					break;
    					case SDLK_t:
    						//
    					break;
    					//
    					case SDLK_SPACE:
    
    					break;
    					//
    					default:
    					break;
    				}
    			break;
    			case _EHS_MOUSECLICK:
    
    			break;
    
    			default:
    			break;
    		}
    		//	some delay
    	usleep(50000);
    	roll++;
    	}
    	EHS_exit();
    }
    314159265358979323846264338327950288
    419716939937510582097494459230781640
    628620899862803482534211706798214808
    

    December 28, 2016

    Neural nets,bases en C, part 9 : Features

    Filed under: linux, neuron — Tags: , , , , — admin @ 7:42 pm

    ## ajout, le 25-03-2017 : les images de ce post proviennent de programmes compilés avec mes librairies ‘maison’ qui utilisent la sdl2.0. pour faciliter les exemples dans les posts qui vont suivre bientôt, j’ai mis a disposition ces librairies , ‘telles qu’elles sont’ , dans le répertoire ‘DATA’ de ce site ( INDEX )

    Je vous invite a lire cette page pour les utiliser

    Ou on on ré-apprend à faire un dessin :

    Si on devait décomposer l’original ( un carré ) en éléments, on peut rapidement discerner qu’il est composé de :

    • traits verticaux
    • traits horizontaux
    • et éventuellement, d’angles ( notés c, c’ c” c”’ )

    Ce travail de décomposition de l’image, c’est tout l’art de l’artiste peintre qui traduit sur sa toile une scène qu’il a sous les yeux : du peintre débutant au plus expérimenté, les deux sachant décomposer, de facon de plus en plus précise la scène en éléments importants et caractéristiques : géométrie, couleurs …
    Mais la précision du rendu de la scène dépend de l’artiste : un voudra rendre le plus précisement possible la scene, un autre rendra une scene plus abstraite, voire completement abstraite , n’en retenant que l’essence .

    On va prendre l’exemple ci dessus pour un réseau tels que ceux décrits dans les posts précédents, et utiliser le réseau pour analyser une image en entrée .

    Comment faire apprendre au réseau les ‘features’ d’interet d’une image qu’on lui fournit en entrée ? ( et quels sont ces ‘features’ ? )

    La réponse à la question , c’est un reseau de ce type :

    N x Entrées -> R x Hidden -> N x Sorties

    Le réseau à le même nombre d’entrées que de sorties, mais, dans la couche cachée, on limite la quantité de neurones à une valeur inférieure au nombre d’entrées.

    Pour l’apprentissage, on demande au réseau de recomposer exactement l’information fournie en entrée, rien de plus : c’est un apprentissage sans supervision
    Posons N=9 , R=3 :

    Du fait que le nombre de neurones de la couche cachée et inférieur au nombre d’entrées , il va y avoir ‘compression’ des données par le réseau, au niveau de la couche cachée ( réduction d’une information à 9 variables en une information à 3 variables )
    Mais, dans le même temps, l’apprentissage lui demande de restituer, sur ses sorties, la même information qu’a l’entrée ( ce qui équivaut à décompresser maintenant , une information composée de 3 variables vers une information composée de 9 variables , la sortie ) , on effectue l’opération : vecteur(9) vers vecteur(3) vers vecteur(9) pour l’information.

    Ce reseau est auto-encodeur,
    Et une fois l’apprentissage sans supervision terminé, il est capable d’extraire des éléments clé (features) de n’importe qu’elle sortes de données en entrée, et ceci, sans aucune supervision.

    Pour traiter une image bitmap en entrée, un petit schéma descriptif :

    Pour les besoins du post, on pose :

    • le bitmap est composé de valeurs 0.0 a 1.0 (float) , ou bien -1.0 / 1.0 ) , equivalent a la luminosité du pixel.
    • la grille (tile) d’entrée vers le réseau est composée de 3×3pixels = 9 entrées ( a,b,c,d,e,f,g,h,i)
    • La couche cachée est composée de 3 neurones, chacun entièrement connectés aux entrées ( il sont chacun 9 entrées + une de bias )
    • La couche de sortie comporte 9 neurones, entièrement connectés a la couche cachée ( + entrée bias pour chacun ) , notés A,B,C,D,E,F,G,H,I
    • Cette couche de sortie recompose l’entrée de 3×3 pixels , de sorte que, en terme de coordonnées spatiales (x,y)  : a correspond a A , b correspond à B etc
    On fait donc l’apprentissage pour que :
    A B C
    D E F
    G H I
    reflète les valeurs d’entrées :
    a b c
    d e f
    g h i
    Pour mieux comprendre, une capture d’écran , en bas de ce post
    • IN MAP est le bitmap fourni en entrée au réseau. il est parcouru de haut en bas, et de gauche a droite, en faisant des incréments de 3 pixels a chaque itération. a chaque pas, on effectue un cycle d’apprentissage au reseau.
    Ci dessous, IN MAP , zoomé : la grille rouge représent les surfaces de 3×3 pixels qui sont parcourues lors de l’apprentissage.elle ne se recouvrent pas et sont contigues :
    • OUT MAP est la réponse en sortie du réseau, au bout de l’apprentissage (erreur RMS <0.01)
    • La sortie est faite sur le canal rouge
    Pour afficher les éléments clé utilisés (les features), il suffit d’utiliser les 3 neurones de la couche cachée. en effet, leur déclenchement est combiné par la couche de sortie pour refabriquer l’image a l’identique que l’original :
    Il suffit donc de visualiser les poids déclencheurs de ces 3 neurones pour afficher l’aspect du ‘feature’ utilisé pour chacun des 3 neurones.
    • C’est ce qui est visible sur la capture d’écran, noté ‘FEATURE FEATURE FEATURE’
    • Chaque feature a été colorisé Rouge,Vert,Bleu afin de pouvoir le distinguer dans la zone ‘F USAGE’
    La zone ‘FEAT GROUPS’ permet ,grace a une SOM (Self Organizing Map) , de créer des groupes de déclenchements pour ces 3 features et de les visualiser. La carte SOM présente des groupes purs de BLEU , VERT , ROUGE, qui correspondent aux usages d’une feature unique pour fabriquer une sortie determinée, tandis que les autres groupes représentent différents mixages des quantitées des 3 neurones (rouge,vert,bleu) pour certains autres états de sortie a générer : c’est le mixage des features , utilisé par le réseau
    • F USAGE est la carte d’utilisation des features : chaque couleur de cette carte traduit le mixage qui a été fait entre les 3 features (préléve de la carte SOM), pour recréer la sortie le plus correctement possible, grace a l’apprentissage.ils sont repositionnés aux coordonnées x,y correspondantes au bitmap d’entrée, afin de mettre en évidence OU ils ont été utilisés, par rapport a l’image d’entrée :

    Pour faciliter la compréhension, un overlay :
    Les couleurs représentent chaque mixage de features utilisé, en superposition sur le bitmap original.
    (En blanc, les pixels a 1.0 du bitmap d’entrée)

    Une capture sur un bitmap différent :

    314159265358979323846264338327950288
    419716939937510582097494459230781640
    628620899862803482534211706798214808
    

    December 16, 2016

    Neural nets, bases en C, part 8 , généralisation

    Filed under: linux, neuron — Tags: , , , — admin @ 11:41 am

    La fonction backWARD_CASCADE() est transformée afin d’être utilisable dans un réseau qui serait constitué de plusieurs couches, ou d’autres configurations moins conventionelles :

    L’existante :

    void RESO_backWARD_CASCADE(reso* n,float reverse_error)
    {
    	uint32_t k;
    	float xb;
    	xb=tanh(1.0-(n->neuron*n->neuron));
    	xb*=reverse_error;
    	n->error=xb;	//
    	for(k=0;k<n->synapses;k++)
    	{
    		n->part[k]=(fabs(n->w[k])/n->wtot);//
    		n->ward[k]=n->error*n->w[k];//
    		n->back[k]=n->error*n->in[k];//
    	}
    	n->epoch++;
    }

    Un neurone d’une couche ‘cachée’ peut avoir plusieurs connections vers des entrées de la couche suivante :

    ou, plus simplement représenté :

    Il est nécéssaire de modifier la fonction pour cumuler, pour chaque neurone d’une couche ‘N’ du reseau (autre que la dernière couche , ‘de sortie ‘), l’erreur des neurones auquels il est connecté, et ceci dans la proportion à laquelle il aura participé à l’erreur de ces neurones.

    on modifie donc dabord la structure du reseau, pour rendre ‘xb’ élément de la structure :

    //
    typedef struct {
    	uint32_t synapses;
    	float* in;			// forward compute inputs
    	float* back;		// backward computed inputs
    	float* ward;
    	float* w;			// forward/backward weight for each inputs
    	float* part;
    	float neuron;			// forward computed output, backward compute input
    	float error;
    	float wtot;			// total input weights
    	//
    	float xb;
    	//
    	uint64_t epoch;
    	uint64_t internal;
    } reso;

    Puis la fonction backWARD_CASCADE() ,dupliquée et modifiée devient :

    void RESO_backWARD_CASCADE_ADDERROR(reso* n,float reverse_error)
    {
    	uint32_t k;
    	n->xb=tanh(1.0-(n->neuron*n->neuron));
    	n->xb*=reverse_error;
    	n->error+=n->xb;	// on cumule maintenant la part d'erreur entrante
    	for(k=0;k<n->synapses;k++)
    	{
    		n->part[k]=(fabs(n->w[k])/n->wtot);//
    		n->ward[k]=n->xb*n->w[k];//
    		n->back[k]+=n->xb*n->in[k];// on cumule les backs[] générés pour chaque entrées
    	}
    	n->epoch++;
    }

    Ensuite,une fonction pour préparer (initialiser) error et les back[] avant :

    //
    void RESO_prepare_CASCADE(reso* n)
    {
    	uint32_t k;
    	for(k=0;k<n->synapses;k++) { n->back[k]=0.0;}
    	n->error=0.0;
    }

    Avant de tester avec la nouvelle mouture de programme qui suit, quelques détails sur des ajouts :

    • ajout de macros pour modifier facilement le mode d’activation des neurones ( tanh , etc )
    • mise en #define des LR

    Il y précisement 2 macros pour la fonction d’activation :

    #define _OUTPUT_TRANSFORM(x) tanh(x)
    ou bien :
    #define _OUTPUT_TRANSFORM(x) (x>0.0)? tanh(x):0.0

    La deuxième est dérivée de reLU , pour rectifier Linear Unit (google pour plus d’informations), limitée a +1.0 par le tanh, ce qui aurait pu etre fait par une autre méthode.

    On définit 2 sorties , la sortie z_net et la sortie y_net attendues:

    #define _REPON_Z ((ang>3.0)&&(ang<6.0))? +1.0:0.0 /* expected formula, Z output */
    #define _REPON_Y (ang>3.0)?  0.0:1.0 /* expected formula, Y output */

    Je vous invite à tester le programme avec l’une et l’autre de fonctions pour l’activation

    #include "../common/classics.h"
    #include "../common/neuron/RESO_library.c"
    
    int main(int argc, char **argv)
    {
    	// le reseau S (layer1)
    	reso stnet;
    	reso* snet=&stnet;			// pointeur vers la structure
    	//
    	// le reseau T (layer1)
    	reso ttnet;
    	reso* tnet=&ttnet;			// pointeur vers la structure
    	//
    	// le reseau Y (layer 2)
    	reso ytnet;
    	reso* y_net=&ytnet;			// pointeur vers la structure
    	// le reseau Z (layer 2)
    	reso ztnet;
    	reso* z_net=&ztnet;			// pointeur vers la structure
    	//
    	//float s_expected,t_expected,u_expected;				// utile pour stocker ce qu'on attend en reponse
    	float expected_z,expected_y;// commode
    	float RMSerror,RMScompute;
    	float ang;					// pour un angle, sinus/cosinus
    	//
    	uint32_t i;		// une variable d'utilité..
    	//
    	//
    	RESO_init(snet,3);// on initialise le reseau S, avec 3 entrées
    	RESO_init(tnet,3);// on initialise le reseau T, avec 3 entrées
    	//RESO_init(unet,3);// on initialise le reseau U, avec 3 entrées
    	//
    	RESO_init(y_net,3);// on initialise le reseau Z, avec 3 entrées
    	RESO_init(z_net,3);// on initialise le reseau Z, avec 3 entrées
    	//
    	snet->w[0]=0.5;
    	snet->w[1]=0.39;
    	snet->w[2]=0.41;
    	//
    	tnet->w[0]=0.5;
    	tnet->w[1]=0.45;
    	tnet->w[2]=0.43;
    	//
    	//
    	z_net->w[0]=0.49;
    	z_net->w[1]=0.40;
    	z_net->w[2]=0.5;
    	//
    	y_net->w[0]=0.48;
    	y_net->w[1]=0.42;
    	y_net->w[2]=0.5;
    	//
    	RMSerror=0.0;
    	RMScompute=0.0;
    	//
    	#define _OUTPUT_TRANSFORM(x) tanh(x)
    	//#define _OUTPUT_TRANSFORM(x) (x>0.0)? tanh(x):0.0
    	//
    	#define _LR_OUT		0.01
    	#define _LR_HIDDEN	0.01
    	//
    	// on définit ici pour les essais, les 3 entrées, la sortie attendue par formule
    	//
    	#define _INA	1.0 /* un bias */
    	#define _INB 	NN_frand_ab(0.0,2.0) /* inutilisé */
    	#define _INC	ang /* la variable d'entrée */
    	//
    	#define _REPON_Z ((ang>3.0)&&(ang<6.0))? +1.0:0.0 /* expected formula, Z output */
    	#define _REPON_Y (ang>3.0)?  0.0:1.0 /* expected formula, Y output */
    	//
    	//
    	ang=0.0;
    	//
    	while((RMScompute>0.05)||(z_net->epoch<200))// jusqu'a RMS erreur total < 0.01
    	{
    		//
    		snet->in[0]=_INA; //
    		snet->in[1]=_INB;
    		snet->in[2]=_INC;
    
    		tnet->in[0]=_INA;
    		tnet->in[1]=_INB;
    		tnet->in[2]=_INC;
    		//
    		//
    		expected_z=_REPON_Z;
    		expected_y=_REPON_Y;
    		//
    		//	CALCUL : FORWARD
    		RESO_forward(snet);
    		RESO_forward(tnet);
    		//
    		snet->neuron=_OUTPUT_TRANSFORM(snet->neuron);
    		tnet->neuron=_OUTPUT_TRANSFORM(tnet->neuron);
    		// on transmet les 2 sorties de snet,tnet ver z_net, neuron de sortie, qui a 3 entrées :
    		z_net->in[0]=snet->neuron;
    		z_net->in[1]=tnet->neuron;
    		z_net->in[2]=_INA;// entrée BIAS pour ce neurone, est INA pour les 2 neurones de la couche précédente
    		//
    		y_net->in[0]=snet->neuron;
    		y_net->in[1]=tnet->neuron;
    		y_net->in[2]=_INA;// entrée BIAS pour ce neurone, est INA pour les 2 neurones de la couche précédente
    		// on calcule ce dernier noeud:
    		RESO_forward(z_net);
    		z_net->neuron=_OUTPUT_TRANSFORM(z_net->neuron);
    		RESO_forward(y_net);
    		y_net->neuron=_OUTPUT_TRANSFORM(y_net->neuron);
    		//
    		if(z_net->epoch%10000==0)
    		{
    			printf("\n\nepoch[%6.6lu]\nZNET\t e(%+5.5f) , RMS{%8.8f}\n",z_net->epoch,z_net->error,RMScompute);
    			printf("\t\t wtot[%+3.3f] --",z_net->wtot);	// au passage, le poids total des entrées a ce cycle
    			for(i=0;i<z_net->synapses;i++) { printf("(w=%+5.5f / ward=%+7.7f]",z_net->w[i],z_net->ward[i]); }
    			//
    			// affiche snet valeurs
    				printf("\nSNET\t e(%+5.5f) out=[%5.5f]\t",snet->error,snet->neuron);
    				printf("wtot[%+3.3f] --",snet->wtot);	//
    				for(i=0;i<snet->synapses;i++) { printf("(%+3.3f)",snet->w[i]); }
    			// affiche tnet valeurs
    				printf("\nTNET\t e(%+5.5f) out=[%5.5f]\t",tnet->error,tnet->neuron);
    				printf("wtot[%+3.3f] --",tnet->wtot);	//
    				for(i=0;i<tnet->synapses;i++) { printf("(%+3.3f)",tnet->w[i]); }
    		}
    		//  CALCUL : BACKWARD pour Z_NET ( pour obtenir l'erreur de la sortie n->neuron )
    		//  expected contient la valeur attendue, fournie a la fonction
    		RESO_backWARD(z_net,expected_z);
    		RESO_backWARD(y_net,expected_y);
    		//
    		//
    		RESO_prepare_CASCADE(snet);// remise a zéro du cumul d'erreur pour ce neurone
    		RESO_prepare_CASCADE(tnet);// idem
    		// on reporte LES ERREURS sur les reseaux précédents S,T , et ce ,
    		// pour chaque liaison du neurone vers la couche suivante
    		RESO_backWARD_CASCADE_ADDERROR(snet,y_net->ward[0]);// dans quelle 'proportion' snet a participé à l'erreur de znet,A
    		RESO_backWARD_CASCADE_ADDERROR(snet,z_net->ward[0]);//idem pour znet, A
    		// meme chose pour tnet
    		RESO_backWARD_CASCADE_ADDERROR(tnet,y_net->ward[1]);// tnet, pour ynet entrée B
    		RESO_backWARD_CASCADE_ADDERROR(tnet,z_net->ward[1]);//tnet pour znet B
    		// cumul RMS pour controle convergence
    		RMSerror+=(fabs(z_net->error)+fabs(y_net->error))/2.0; // basé sur une moyenne des erreurs RMS des 2 sorties
    		RMScompute=(RMSerror/(float)z_net->epoch);
    		// appliquer correction, inverse : derniere couche vers premiere
    		RESO_apply(z_net,_LR_OUT);
    		RESO_apply(y_net,_LR_OUT);
    		//
    		RESO_apply(snet,_LR_HIDDEN);//
    		RESO_apply(tnet,_LR_HIDDEN);//
    		// .. valeur entre 0/10.0 pour le prochain cycle
    		ang=NN_frand_ab(0.0,10.0);
    		//
    	}
    	// seuil RMS ok, affiche les infos finales poids/ part dans le résultat
    	printf("\n\tZ_NET Final Parts  :");
    	for(i=0;i<z_net->synapses;i++) { printf("(%+3.3f%c)",z_net->part[i]*100.0,'%'); }
    	printf("\n\tZ_NET Final Weights:");
    	for(i=0;i<z_net->synapses;i++) { printf("(%+3.3f )",z_net->w[i]);}
    	printf("\n");
    	printf("\n\tY_NET Final Parts  :");
    	for(i=0;i<y_net->synapses;i++) { printf("(%+3.3f%c)",y_net->part[i]*100.0,'%'); }
    	printf("\n\tY_NET Final Weights:");
    	for(i=0;i<y_net->synapses;i++) { printf("(%+3.3f )",y_net->w[i]);}
    	printf("\n");
    //
    	// infos snet
    	printf("\n\tSNET Final Parts  :");
    	for(i=0;i<snet->synapses;i++) { printf("(%+3.3f%c)",snet->part[i]*100.0,'%'); }
    	printf("\n\tSNET Final Weights:");
    	for(i=0;i<snet->synapses;i++) { printf("(%+3.3f )",snet->w[i]);}
    	printf("\n");
    	// infos tnet
    	printf("\n\tTNET Final Parts  :");
    	for(i=0;i<tnet->synapses;i++) { printf("(%+3.3f%c)",tnet->part[i]*100.0,'%'); }
    	printf("\n\tTNET Final Weights:");
    	for(i=0;i<tnet->synapses;i++) { printf("(%+3.3f )",tnet->w[i]);}
    	printf("\n");
    	// 'test run'
    	z_net->internal=0;
    	ang=0.00;
    	while(z_net->internal<100)
    	{
    		//
    		snet->in[0]=_INA; // diffuses constant 5.0
    		snet->in[1]=_INB;
    		snet->in[2]=ang;
    		//
    		tnet->in[0]=_INA;
    		tnet->in[1]=_INB;
    		tnet->in[2]=ang;
    		//
    		expected_z=_REPON_Z;
    		expected_y=_REPON_Y;
    		//	CALCUL : FORWARD
    		RESO_forward(snet);
    		RESO_forward(tnet);
    		//
    		snet->neuron=_OUTPUT_TRANSFORM(snet->neuron);
    		tnet->neuron=_OUTPUT_TRANSFORM(tnet->neuron);
    		//unet->neuron=tanh(unet->neuron);
    		// on transmet les 3 sorties de snet,tnet,unet ver z_net, neuron de sorties, qui a 3 entrées :
    		z_net->in[0]=snet->neuron;
    		z_net->in[1]=tnet->neuron;
    		z_net->in[2]=_INA;
    		//
    		y_net->in[0]=snet->neuron;
    		y_net->in[1]=tnet->neuron;
    		y_net->in[2]=_INA;
    		// on calcule ce dernier noeud:
    		RESO_forward(z_net);
    		z_net->neuron=_OUTPUT_TRANSFORM(z_net->neuron);
    		RESO_forward(y_net);
    		y_net->neuron=_OUTPUT_TRANSFORM(y_net->neuron);
    		// output version human readable
    		#define _OUT_TEXT 	"cycle[%lu] B[%f] B[%f] A[%f] EXP_Z[%f] OUT_Z[%f] || EXP_Y[%f] OUT_Y[%f]\n"
    		// ou pour export CSV :
    		//#define _OUT_TEXT	"%lu,%f,%f,%f,%f,%f,%f,%f\n"
    		printf(_OUT_TEXT,
    			z_net->internal,
    			_INC,
    			_INB,
    			_INA,
    			_REPON_Z,
    			z_net->neuron,
    			_REPON_Y,
    			y_net->neuron);
    		//
    		ang+=0.10;
    	}
    	//
    	// todo : malloc releases
    }

    La sortie, en version TANH() * seuil atteint en 1100000 epochs

    Le même , avec activation en mode ‘reLU’ , seuil atteint en 140000 epochs :

    En descendant le seuil RMS a 0.01, et reLU en activation :

    Dans cette situation , un résumé des parts de chaque élément :

    Z_NET Final Parts  :(+45.641%)(+46.988%)(+7.372%)
    Z_NET Final Weights:(+4.510 )(-4.643 )(-0.728 )
    
    Y_NET Final Parts  :(+63.674%)(+6.939%)(+29.387%)
    Y_NET Final Weights:(-5.336 )(-0.581 )(+2.463 )
    
    SNET Final Parts  :(+74.550%)(+0.064%)(+25.386%)
    SNET Final Weights:(-16.639 )(+0.014 )(+5.666 )
    
    TNET Final Parts  :(+84.045%)(+0.552%)(+15.403%)
    TNET Final Weights:(-8.278 )(-0.054 )(+1.517 )

    SNET utilise A et C ( BIAS, et la variable ang ) : il détecte un des ’seuils’ ,celui a 3.0 ou celui a 6.0
    TNET utilise A et C egalement : il detecte une des deux seuils , celui a 3.0 ou celui a 6.0
    ZNET utilise majoritairement ses entrées A et B , qui sont les sorties de SNET et TNET, respectivement , pour générer la réponse attendue, ainsi que son entrée de BIAS (comparaison)
    YNET, utilise majoritairement  son entrée A , qui est la sortie de SNET, et son BIAS , hors, comme on sait que ZNET et YNET , en sortie, doivent communément déclencher sur 3.0, on peut en conclure que SNET detecte le passage par 3.0 de la variable en ‘ang’ , puisqu’il est utilisé par YNET ET ZNET.

    Pour finir :

    Même si le reseau et les fonctions sont, pour l’instant, plutot ‘lourdes’, vous avez les bases pour constituer des réseau personnalisés beaucoup plus compacts, et j’ai volontairement découpé les fonctions et phases de l’apprentissage, afin de clarifier chacunes des étapes.
    On va optimiser et rendre synthétiques les choses dans les prochains posts

    314159265358979323846264338327950288
    419716939937510582097494459230781640
    628620899862803482534211706798214808
    
    Older Posts »

    cat{ 96 } { post_1060 } { } 2009-2015 EIhIS Powered by WordPress