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
    

    December 10, 2016

    Neural nets , deep learning, bases en C - part 1

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

    Les reseaux composés de plus d’une couche cachée sont plus difficiles que les autres à entrainer.
    On trouve ça-et-la des articles sur le sujet , bourrés de maths, qui ont de quoi rebuter.

    Les arXiv publiés sont nombreux..

    Pour un réseau type “perceptron” à une couche ( N entrées vers une sortie unique) , partons sur la base suivante ( code C )
    Attention: j’utilise des fonctions ‘maison’ qui peuvent différer des formes ‘canoniques’ si tant est qu’elles existent.
    Je suis un codeur plus qu’un matheux. les programmes originaux sont souvent ‘mathéifiés’ par la suite, et non l’inverse.

    Le calcul du reseau inverse, justement, est , si l’on pousse la reflection sur son mode de calcul, analogue aux phénomènes de mouvements oculaires rapides,et mouvements inconscients pendant les phases de sommeil, lors desquelles il est supposé (ou prouvé?) que le cerveau ‘renforce’ les liens neuronaux en ‘répétant’ des stimulis de la periode d’eveil. les vagues EEG mettent en evidence des mouvements de va-et-vient de l’avant a l’arrière du cortex des signaux electriques dans les reseaux de neurones cérébraux.

    Les fonctions décrites ci-après agissent sur cette structure simple, qui décrit le reseau (désolé pour les ‘mix’ entre anglais,français. mes codes sont tjs commentés en anglais, j’ai étoffé en français pour cet post )  :

    //
    typedef struct {
    	uint32_t synapses;// nombre d'entrées du neurone
    	float* in;	// forward compute inputs
    	float* back;	// backward computed inputs
    	float* w;	// forward/backward weight for each inputs
    	float* part;    // proportion rapportée a 1.0 pour chaque poids entrant
    	float neuron;	// forward computed output, backward compute input
    	float error;    // erreur en sortie
    	float wtot;			// total input weights
    	//
    	uint64_t epoch;	// incrémenté a chaque function d'apprentissage appliquée (modification des poids)
    	uint64_t internal;// incrémenté a chaque calcul de la sortie en fonction des entrées
    } reso;

    Une fonction qui initialise cette structure :

    void RESO_init(reso* n,uint32_t input_count)
    {
    	uint32_t k;
    	float init_w=1.0;// todo: use setup, default var instead
    	n->in=(float*) malloc(sizeof(float)*input_count);
    	n->back=(float*) malloc(sizeof(float)*input_count);
    	n->w=(float*) malloc(sizeof(float)*input_count);
    	n->part=(float*) malloc(sizeof(float)*input_count);
    	//
    	n->synapses=input_count;
    	n->wtot=init_w*input_count;	//prepare startup total weights
    	for(k=0;k<n->synapses;k++)
    	{
    		n->in[k]=0.0;
    		n->w[k]=init_w;	// default...
    		n->part[k]=n->w[k]/n->wtot;// back ready
    	}
    	n->neuron=0.0;
            n->error=0.0;
    	//
    	n->epoch=1;
    	n->internal=1;
    }

    Maintenant, la fonction de calcul ‘FORWARD’ : Calcule la sortie en fonction des entrées

    void RESO_forward(reso* n)
    {
    	uint32_t i;
    	n->neuron=0.0;
    	n->wtot=0.0;
    	for(i=0;i<n->synapses;i++)
    	{
    	  n->neuron+=n->in[i]*n->w[i]; // somme des entrées*poids de cette entrée
    	  n->wtot+=fabs(n->w[i]);      // cumule au passage les poids pour calculer le total des poids entrants
    	}
    	n->internal++;
    }

    On passe a deux fonctions essentielles dans l’apprentissage:

    la fonction ‘BACKWARD’ qui effectue un calcul inverse (de la sortie vers les entrées), mais ne modifie aucun des poids du réseau :

    //
    void RESO_back(reso* n,float expected)
    {
    	uint32_t k;
    	n->error=expected - n->neuron;	// AKA 'expected - what_i_got'
    	//
    	n->neuron=n->error;// prepare for back compute
    	//
    	for(k=0;k<n->synapses;k++)
    	{
    		n->part[k]=fabs(n->w[k])/n->wtot;	 // weight part always positive
    		n->back[k]=n->neuron*n->part[k]*n->in[k];// back[] is the backward equivalent of in[]'s for the net
    	}
    	n->epoch++;
    }

    .. je reviendrai plus tard sur celle ci ainsi que sur la suivante : la fonction de modification du reseau ,qui s’execute après le calcul inverse (fonction précédente) :

    void RESO_apply(reso* n,float rate)
    {
    	uint32_t k;
    	for(k=0;k<n->synapses;k++)
    	{
    		n->w[k]+=n->back[k]*rate;
    	}
    }

    En pratique, on peut tester tout ça avec un programme simple :

    #include "../common/classics.h" // check for a post on the website about it
    // ..les librairies classiques requises
    #include "../common/neuron/RESO_library.c"
    // c'est le nom que j'utilise
    // pour le fichier ou sont les fonctions précédentes et la definition de la structure
    int main(int argc, char **argv)
    {
    	// le reseau
    	reso snet;
    	reso* net=&snet;			// pointeur vers la structure
    	//
    	float expected;				// utile pour stocker ce qu'on attend en reponse
    	float RMSerror,RMScompute;
    	float ang;					// pour un angle, sinus/cosinus cf while()
    
    	//
    	uint32_t i;		// des variables d'utilité..
    	//
    	//
    	RESO_init(net,3);// on initialise le reseau, avec 3 entrées
    	// tous les poids a 1.0 pour tester
    	net->w[0]=1.0;
    	net->w[1]=1.0;
    	net->w[2]=1.0;
    	//
    	RMSerror=0.0;
    	RMScompute=1.0;
    	// on définit ici pour les essais : les 3 entrées,et la sortie attendue par formule
    	#define _INA	3.0
    	#define _INB	0.0+sin(ang)
    	#define _INC	0.0+cos(ang)
    	// on pond une formule qui utilise les 3 entrées, pour tester le reseau
    	#define _REPON	1.0+2.0*cos(ang)+3.0*sin(ang)
    	//
    	ang=0.0;
    	while((RMScompute>0.01)||(net->epoch<2))// jusqu'a RMS erreur total <= 0.01
    	{
    		//
    		net->in[0]=_INA;
    		net->in[1]=_INB;
    		net->in[2]=_INC;
    		//
    		expected=_REPON;
    		ang+=2.6;// incrém. pour le prochain cycle du while, 2.6 est choisi comme ça..
    		// *** CALCUL : FORWARD
    		RESO_forward(net);
    		// *** CALCUL : BACKWARD ( pour obtenir l'erreur de la sortie n->neuron )
    		//  expected contient la valeur attendue, fournie a la fonction
    		RESO_back(net,expected);
    		// au passage, on fait un cumul d'erreur RMS pour superviser l'apprentissage du reseau
    		RMSerror+=fabs(net->error);
    		RMScompute=(RMSerror/(float)net->epoch);
    		// epoque de l'apprentissage, erreur immédiate dela sortie, et erreur RMS total depuis le début
    		printf("\nepoch[%6.6lu] e(%+6.6f) , RMS{%5.5f} ",net->epoch,net->error,RMScompute);
    		printf("wtot[%+3.3f] --",net->wtot);	// au passage, le poids total des entrées a ce cycle
    		// les 'n' entrées (ici 3) , leur part en % dans le résultat que fournit la sortie
    		for(i=0;i<net->synapses;i++) { printf("(%+3.3f%c)",net->part[i]*100.0,'%'); }
    		// final : on modifie le reseau, et rebouclage while()
    		// on fournit la 'learning_rate' a la fonction ( vitesse d'apprentissage ) ,
    		// c'est a dire en quelle quantité elle va venir modifier les poids.
    		// le learning rate agit en dosage sur la modification.
    		RESO_apply(net,0.05); // *** APPLIQUER modification aux poids ***
    
    	}
    	// si le seuil RMS est ok, affiche les infos finales poids/ part dans le résultat
    	printf("\nFinal Parts :\n");
    	for(i=0;i<net->synapses;i++) { printf("(%+3.3f%c)",net->part[i]*100.0,'%'); }
    	printf("\nFinal Weights:\n");
    	for(i=0;i<net->synapses;i++) { printf("(%+3.3f )",net->w[i]);}
    	printf("\n");
    	// on execute un cycle de calcul FORWARD uniquement, pour vérifier que
    	// tout est OK , 20 fois :
    	// 'test run'
    	net->internal=0;
    	while(net->internal<20)
    	{
    		net->in[0]=_INA;
    		net->in[1]=_INB;
    		net->in[2]=_INC;
    		expected=_REPON;// uniquement pour afficher l'attendu VS la sortie du reseau
    		ang+=2.6;//
    		RESO_forward(net);
    		printf("\nCycle[%6.6lu] EXPECTED(%+6.6f) , ACTUAL{%5.5f} ",net->internal,expected,net->neuron);
    
    	}
    // TODO : malloc cleanups !
     return 0;// :)
    }

    Le reseau, dans son état actuel,  converge , et c’est le but.

    On constate que les parts de chacune des entrées est exactement proportionnées tel que nécéssaire , a savoir :

    Final Parts :
    (+6.250%)(+56.250%)(+37.500%)
    Final Weights:
    (+0.333 )(+3.000 )(+2.000 )
    A) On a in[0]=3.0 , poids=0.33333 ce qui fait  3.0*0.33333333 = 1.0
    B) On a in[1]=sin(angle)  , poids = 3.0 ce qui fait 3.0*sin(angle)
    C) On a in[2]=cos(angle) , poids = 2.0  ce qui fait 2.0*cos(angle)
    La valeur du neurone etant la somme des entrées * leur poids respectifs,
    on a donc neuron = A+B+C = 1.0 + 3.0*sin(angle) + 2.0*cos(angle)
    .. c’est ce qu’on voulait, ça tombe bien… (expected : 1.0+2.0*cos(ang)+3.0*sin(ang) )

    Suite au prochain numéro. ( si vous avez VRAIMENT besoin d’aide, ou des remarques constructives, contact : admin(AT)eihis.com)

    314159265358979323846264338327950288
    419716939937510582097494459230781640
    628620899862803482534211706798214808
    

    August 22, 2015

    neuron networks, part 2

    Filed under: Uncategorized — Tags: , , , , , — admin @ 2:53 pm

    Following the previous article, the network was trained with free-running feedback .
    In addition, a second output neuron was created, wich output, instead of following the conway’s game of life rules, was trained to be the sine of the expected normal, ‘game of life ruled’ output.

    The trained network’s output for output 0 is almost the same (used 8 on hidden instead of 5 )

    The screen capture of outputs 0 and 1 :

    Left : output zero (normal, game of life ruled output) , and right : the ’sine’ like output 1 for the same 8 input cells  :

    This networks’ complete weights dump :

    Neuron network array
    numInputs :2
    numHidden :8
    numOutputs :2
    INPUT to HIDDEN weights:
    INPUT[0]:18.4715 -15.7549 -21.2166 -19.4792 2.0692 -2.9851 -14.6416 -17.5079 
    INPUT[1]:-8.0632 -13.0431 4.0268 -12.4184 -7.6292 -8.4492 12.2782 -7.1637 
    HIDDEN to OUTPUT weights:
    OUTPUT[0]:1.8568 1.2939 1.7122 -1.2514 0.8039 -0.7578 1.2156 -0.5770 
    OUTPUT[1]:0.5297 -0.1719 0.1888 -0.7751 0.1120 -0.1462 0.2815 0.4162

    This network uses tanh’d outputs on both hidden and output layer ( tanOutput[n]=tanh(50.0*NormalOut[n]) )

    The output 1 shows groups of cells and highlights some interresting shapes that the normal output[0] does not permit to view :

    ————-

    Now, the same network is trained the same way, but the output[1] with no tanh() function applied is graphed.This renders the subtle values for this output in the range -1.0 / 1.0. ( the supervision’s expected output[1] rule was : output[1] = ( actual_output[0] + the new 8 cell’s sum value ) divided by 2.0

    ————-

    The network is then modified : we add 1 new input, namely the x coordinates of the 2D plane that’s rendering. the actual 2D area is a 64×64 cell array, so the 0-64 value for X will be maped to a -1.0 / 1.0 vector for this new input.
    This time, output[1] is trained in a unmonitored manner again. we want to have output[1] to be the copy of the actual X value.

    So, the new network as 3 inputs :

    1. input[0] is the sum of the 8 surrounding cells at T
    2. input[1] is the actual value of the output[0] at T-1 ( namely, the state of the cell at T )
    3. input[2] is the X coordinates of the cell beeing processed ( 0-64 range mapped to -1.0 / +1.0 )
    The outputs are expected to be :
    1. output[0] is the result of applying the rulesof the Game of life.
    2. output[1] is expected to be the ‘image’ of the X coordinates, with nothing more.
    remarks : the network is trained with 10 neurons. convergence is longer, because the input[2] value is seen as an unwanted value for the output[0] problem. for this reason, convergence takes more time because the weight of input[2] have to be lowered at the maximum, to get output[0] to converge the good value. meanwhile, this input[2] value is absolutely needed for the correct output of output[1]. this makes the overall convergence time longer.
    Here is the snapshot after a 2300000 epochs learning :
    The output[1] values are almost the good one. some errors can be seen at the fringe. remember that our hidden and ouput neurons are tanh’d ( output can’t be linear )
    LR_IH and LR_HO were 0.05 for this training.
    There is the dump of the network’s weights, for information :
    Neuron network array
    numInputs :3
    numHidden :10
    numOutputs :2
    INPUT to HIDDEN weights:
    INPUT[0]:-17.4559 0.0378 0.0916 -1.1608 -2.2167 -15.6072 -14.7210 -16.3537 -1.0468 -25.0423
    INPUT[1]:-15.0747 -1.8141 3.3090 -7.1765 1.9960 -8.8084 6.3518 14.2116 6.8359 4.5404
    INPUT[2]:0.3854 2.4042 2.6311 -0.1052 13.9876 0.0769 0.1231 0.1707 0.6056 -0.0481
    HIDDEN to OUTPUT weights:
    OUTPUT[0]:1.4326 -0.0190 0.1235 -0.9902 -0.0714 -1.4984 -1.5762 1.4177 -1.1382 1.7470
    OUTPUT[1]:0.0721 1.9509 1.7133 -0.4680 0.7523 -0.0126 -0.0061 0.0173 -0.8408 0.0440

    A closer look at the weights shows that input[2] is ‘mainly’ linked to hidden neuron 4 ( weight about 13) and this hidden neuron 4 is then linked to output[1] by a 0.7523 value.

    For information, following output is the one for a trained network with :

    1. output[0] following the game of life rules
    2. output[1] outputs the 8 surrounding cell’s value , multiplied by the X coordinates of the cell into the area :

    ————-

    314159265358979323846264338327950288
    419716939937510582097494459230781640
    628620899862803482534211706798214808
    

    cat{ } { post_744 } { } 2009-2015 EIhIS Powered by WordPress