Bibliothèque et allocation dynamique

Bonsoir
Je me lance dans l’écriture de ma première bibliothèque. Pour (ne pas) faire simple, je veux transformer mon nouveau code de perceptron multicouche sur ESP32 et en faire une bibliothèque.

Bien sûr j’ai plein d’erreurs, mais mon plus gros problème est lié à l’allocation de la mémoire.

Sans la bibliothèque, le code alloue des zones de mémoire pour le réseau et les couches, selon des structures:

typedef struct {                     /* A LAYER OF A NET:                     */
        INT           Units;         /* - number of neurons in this layer     */
        REAL*         Output;        /* - output of ith neuron                */
        REAL*         Error;         /* - error term of ith neuron            */
        REAL**        Weight;        /* - connection weights to ith neuron    */
        REAL**        WeightSave;    /* - saved weights for stopped training  */
        REAL**        dWeight;       /* - last weight deltas for momentum     */
} LAYER;

typedef struct {                     /* A NET:                                */
        LAYER**       Layer;         /* - layers of this net                  */
        LAYER*        InputLayer;    /* - input layer                         */
        LAYER*        OutputLayer;   /* - output layer                        */
        REAL          Alpha;         /* - momentum factor                     */
        REAL          Eta;           /* - learning rate                       */
        REAL          Gain;          /* - gain of sigmoid function            */
        REAL          Error;         /* - total net error                     */
        REAL          EtaSave;       /* - saved learning rate                        */
        REAL          GainSave;      /* - saved gain                        */
} NET;

L’allocation se fait comme suit :

void GenerateNetwork(NET* Net)
{
  INT l, i;

  Net->Layer = (LAYER**) calloc(NUM_LAYERS, sizeof(LAYER*));

  for (l = 0; l < NUM_LAYERS; l++) {
    Net->Layer[l] = (LAYER*) malloc(sizeof(LAYER));

    Net->Layer[l]->Units      = Units[l];
    Net->Layer[l]->Output     = (REAL*)  calloc(Units[l] + 1, sizeof(REAL));
    Net->Layer[l]->Error      = (REAL*)  calloc(Units[l] + 1, sizeof(REAL));
    Net->Layer[l]->Weight     = (REAL**) calloc(Units[l] + 1, sizeof(REAL*));
    Net->Layer[l]->WeightSave = (REAL**) calloc(Units[l] + 1, sizeof(REAL*));
    Net->Layer[l]->dWeight    = (REAL**) calloc(Units[l] + 1, sizeof(REAL*));
    Net->Layer[l]->Output[0]  = BIAS;

    if (l != 0) {
      for (i = 1; i <= Units[l]; i++) {
        Net->Layer[l]->Weight[i]     = (REAL*) calloc(Units[l - 1] + 1, sizeof(REAL));
        Net->Layer[l]->WeightSave[i] = (REAL*) calloc(Units[l - 1] + 1, sizeof(REAL));
        Net->Layer[l]->dWeight[i]    = (REAL*) calloc(Units[l - 1] + 1, sizeof(REAL));
      }
    }
  }
  Net->InputLayer  = Net->Layer[0];
  Net->OutputLayer = Net->Layer[NUM_LAYERS - 1];
}

REAL est un alias pour “float” (ce n’est pas mon code ici)

Il faut ensuite que ces emplacements mémoire soient accessibles aux diverses méthodes de ma bibliothèque.

Dans le code sans bibli, je passe le réseau en argument, par exemple :

void RestoreWeights(NET* Net)

Mais ça c’était avant…
Maintenant, avec la bibliothèque, je déclare une instance d’un réseau. Puis je l’initialise via la méthode GenerateNetwork ci-dessus. Mais comment dois-je écrire la méthode RestoreWeights (et l’appeler) dans la bibliothèque ?

Merci de votre aide…

Déjà, moi j'aurais fait des classes (avec constructeurs/destructeurs) plutot que des struct (inertes)+une fonction qui construit tout "à plat". Tu as adapté un source en C (sans le ++) ?

Il me semble que tu as prévu, pour ta bib., des fonctions qui prennent en arg. un (pointeur sur un) objet.

void RestoreWeights(NET* Net)

Puis, tu décides qu'il n'y aura qu'un seul objet NET, et qu'en plus tu le crées dans la lib (c-à-d que linker avec ta lib importe un NET).

C'est pas un peu contradictoire.

Par ailleurs, si j'ai besoin de travailler sur un objet NET, je ferais plutôt

UnObjetNET.RestoreWeights();

hop, plus de pointeur.

Oui, en effet, j'adapte un code en C. Merci de ta réponse.

Le code sans bibliothèque utilise des pointeurs. Je pensais que ça coulait de source, pour en faire une bibliothèque...

C'est sûr que ce serait mieux de définir des classes plutôt que des struct. Comment faire ?

Bon, j'ai réussi, grâce au mot clé "this". Quand je dis "réussi", c'est que ça compile sans erreur, mais je n'ai pas encore testé si c'est juste.

J'ai un autre problème à régler, de dépendance de bibliothèque.

J'aimerais donner à l'utilisateur (moi pour l'instant) la possibilité d'afficher des choses sur un écran OLED. J'ai donc besoin d'utiliser dans ma bibliothèque des fonctions d'une autre bibliothèque, OLED pour ESP32.

Pour l'instant, j'ai créé une méthode qui permet à l'utilisateur d'indiquer qu'il utilise un écran, grâce à un paramètre booléen. Mais comment puis-je faire passer à ma bibliothèque le nom de l'instance de l'écran choisi par l'utilisateur ?

lesept:
Bon, j'ai réussi, grâce au mot clé "this".

mmmh, c'est louche, fait péter le code, en général on n'a pas besoin du mot-clé this... Enfin en "vrai" C++. Si on adapte du C je comprends mieux.

j'ai créé une méthode qui permet à l'utilisateur d'indiquer qu'il utilise un écran, grâce à un paramètre booléen. Mais comment puis-je faire passer à ma bibliothèque le nom de l'instance de l'écran choisi par l'utilisateur ?

Ta bibliothèque a besoin d'un objet qu'elle ne crée pas elle-même, c'est ça ?
pas d'autre solution que de lui fournir un pointeur (ou une référence) sur l'objet.

biggil:
mmmh, c’est louche, fait péter le code, en général on n’a pas besoin du mot-clé this… Enfin en “vrai” C++. Si on adapte du C je comprends mieux.

Voilou ! Attention les yeux…

biggil:
Ta bibliothèque a besoin d’un objet qu’elle ne crée pas elle-même, c’est ça ?
pas d’autre solution que de lui fournir un pointeur (ou une référence) sur l’objet.

Oui, c’est ce que je cherche à faire, mais je ne trouve pas le type à utiliser…

MLP_bib.ino (985 Bytes)

MLP.h (2.92 KB)

MLP.cpp (16.6 KB)

bon bon j'ai vu le code, c'est pas évident il y a des classes que je ne connait pas (venant de tes includes). En particulier la classe OLEDDisplay.

Donc tu veux dire à te bib. d'utiliser un objet de ce type - ta méthode isOLED() est trop mal nommée, renomme la SetDisplay(), et pas besoin du booléen en paramètre.

Ensuite tu passes cet objet par valeur. Donc tu en fait une copie. Que tu copies ensuite dans un membre de ta classe MLP. Tu as fait 2 copies, une a été détruite, il en reste une dans ton "main" et une dans ton objet MLP. Déjà il faut que la classe OLEDDisplay soit très finement programmée, et puis c'est lourd et casse-gueule. Fait un passage par référence (ou pointeur)

Pour que ta conception fonctionne, il faut que tous les objets display susceptibles d'ètre passés à MLP soient de la classe OLEDDisplay, ou d'une classe dérivée.

Dans le .ino, je vois que tu crées un objet de la classe SSD1306. Est-ce une classe dérivée de OLEDDisplay ? si non ça ne marche pas.

Tu vois le problème ? Si tu veux utiliser un écran X ou Y, il te faut une classe dérivée de OLEDDisplay pour que ça soit utilisable.

Et tu peux supprimer tous les "this->" de ton code. A l'intérieur d'un méthode de classe, ils ne servent à rien.

En prenant un peu de recul, je me dis que demander à un bib. de calcul de RN d'afficher sur un écran, c'est pas du tout une bonne idée. C'est trop disparate, les calculs sont "universels" alors que l'affichage est totalement lié au matériel.

Si tu cherches à faire un bib. propre, c'est peut-être pour que d'autres l'utilisent, il faut que tout soit bien conçu dès le départ, sinon ta bib. ne pourra évoluer (sauf à faire des releases incompatibles).

Donc sépare les fonctions. Ta bib. renvoie des résultats, des valeurs... Arrête toi-là.
Ton "main" récupère ces résultats et, si besoin, les affiche, sur un OLED, dans un fichier, dans la console...

Oui, je pense que tu as raison, ce serait trop compliqué de pouvoir adresser tous les types d'écrans. Je ferai un exemple avec un SSD1306. L'avantage c'est que l'écran permet de voir l'avancement de l'apprentissage de manière graphique. Merci

Je vais ôter les 'this' et voir ce que ça donne.