Struktur Neuronales Netz



  • Schon vor einiger Zeit habe ich neuronale Struktur in Vererbungsform modelliert.
    Anhand der Beispielanweisungen in main legt das Programm zur Netzstruktur
    gehörende Objekte an und löscht sie bei verlassen des Programms durch [ENTER] wieder.
    Den Programmablauf kann man im Debugger oder mit cout Anweisungen verfolgen.

    Neben Kompiler- und Linkerfehlern bei Erweiterungen scheine ich auch an Grenzen von C++ zu stoßen.
    In der Hoffnung, daß es Möglichkeiten gibt, welche ich noch nicht kenne,
    wende ich mich an das Forum.

    Im Klassentemplate ObjectCare gibt es in vielen Klassen verwendete Setter und Getter Funktionen.
    Kann man ObjectCare statt als Template beispielsweise über Vererbung einbinden ?

    Falls nicht, wie ist die Übergabe einer Parameterliste,
    zur universelleren Verwendung von ObjectCare, möglich (Args...) ?

    Wie kann ein Debuglevel als Komanozeilenparameter (z.B. über *(argv[1])) gelesen werden
    und beispielsweise zum Abprüfen in einer globalen Variable oder Klasse gespeichert werden ?

    Klassendiagramm

    -Layer-  --Hormon--
           n|     X1 |n      1O
            |     |  |        | ----------
           1X     |n O1      n| X0..1   1|
    SetX---NetX---NodeX------Edge---------
       1  n   1  n    1  1..2   |
                  (Position)    |
     (MotorNode)  Voltage       |
          O       O  O          |
          |       |  |          |
         OutputNode  InputNode  |O-----NodeEdge_Edge
                  O  O          |
                  |  |          |
               InnerNode        |O-----EdgeNode_Edge
                   O            |
                   |            |
             PyramidalNode      |O-----NodeEdge_Edge
    

    Weiter gibt es die Klassen
    ObjectCare und DeleteListAndTheirElements

    X -> Gefülltes Pfeilspitzendreieck (Aggregation?)
    O -> Leeres Pfeilspitzendreieck (Komposition?)

    NN.cpp

    #include "NN.h"
    
    int main(int argc, char* argv[])
      {
      Set* p_set = new Set;  // Add set
      // Example network:  Input:      Hidden:      Output:
      //                    (1)          (3)
      //                    (2)          (4)          (6)
      //                                 (5)          (7)
      (*(p_set->HaveNet<Net>(1)))->HaveNode<InputNode>(2);
      (*(p_set->HaveNet<Net>(1)))->HaveNode<PyramidalNode>(5);
      (*(p_set->HaveNet<Net>(1)))->HaveNode<OutputNode>(7);
    
      // Schichten:
      (*(p_set->HaveNet<Net>(1)))->DefineLayer<InputNode>(1,2);
      (*(p_set->HaveNet<Net>(1)))->DefineLayer<PyramidalNode>(3,5);
      (*(p_set->HaveNet<Net>(1)))->DefineLayer<OutputNode>(6,7);
    
      // Gewünschtere Zugriffsart:
      // Instance->Net(1)->Node(1)->ToNode(3)->weight += 1;  // typedef ?
      // Instance->Net(1)->Node(4)->ToAxonBetweenNode(5,7)->weight -= 1;
      // Instance->Net(1)->Node(5)->FromDentrideBetweenNode(2,4)->weight += 1;
    
      cin.get();  // Dummyinput for preventing the console window
                  // from beeing closed automatically in some environments.
      delete p_set;
      return 0;
      }
    

    NN.h

    #ifndef NNH
    #define NNH
    
    #include <iostream>             // cin, cout
    #include "EDataStructure.h"     // Set
    using std::cin;
    
    #endif
    

    EDataStructure.cpp // E für Element

    #include "EDataStructure.h"
    
    // ============================================================================
    
    Edge::Edge(wgt weight, dey delay):mp_weight(new wgt(weight)), mp_delay(new dey(delay))
      {
      }
    
    Edge::~Edge()
      {
      }
    
    wgt Edge::GetWeight() const
      {
      return *mp_weight;
      }
    
    dey Edge::GetDelay() const
      {
      return *mp_delay;
      }
    
    // ----------------------------------------------------------------------------
    
    NodeNode_Edge::NodeNode_Edge(Node* p_source, Node* p_dest, wgt weight, dey delay):Edge(weight,delay), mp_source(p_source), mp_dest(p_dest)
      {
      mp_source = p_source;
      mp_dest = p_dest;
      }
    
    Node* NodeNode_Edge::SourceNode() const
      {
      return mp_source;
      }
    
    Node* NodeNode_Edge::DestNode() const
      {
      return mp_dest;
      }
    
    // ============================================================================
    
    Node::Node()
      {
      mp_voltage = new vtg;
      SetVoltage(-0.7);
      }
    
    Node::~Node()
      {
      delete mp_voltage;
      }
    
    void Node::SetVoltage(vtg voltage)
      {
      *mp_voltage=voltage;
      }
    
    // ----------------------------------------------------------------------------
    
    InputNode::InputNode()  // Sensory neuron
      {
      poplp_destedge = new ObjectCare<Edge>;
      }
    
    InputNode::~InputNode()
      {
      delete poplp_destedge;
      }
    
    // ----------------------------------------------------------------------------
    
    OutputNode::OutputNode()  // Motor neuron, Secretatory neuron
      {
      plp_sourceedge = new list<Edge*>;
      }
    
    OutputNode::~OutputNode()
      {
      delete plp_sourceedge;
      }
    
    //list<Edge*>::iterator OutputNode::HaveSourceEdgeFrom(uli count)
    //  {
    //  }
    
    // ----------------------------------------------------------------------------
    /*
    MotorNode::MotorNode()
      {
      }
    
    MotorNode::~MotorNode()
      {
      }
    */
    // ----------------------------------------------------------------------------
    
    InnerNode::InnerNode()
      {
      plp_sourceedge = new list<Edge*>;
      plp_destedge   = new list<Edge*>;
      }
    
    InnerNode::~InnerNode()
      {
      delete plp_sourceedge;
      delete plp_destedge;
      }
    
    // ----------------------------------------------------------------------------
    
    PyramidalNode::PyramidalNode()
      {
      }
    
    PyramidalNode::~PyramidalNode()
      {
      }
    
    // ============================================================================
    
    Layer::Layer(list<Node*>::iterator first, list<Node*>::iterator last):ilp_first(first), ilp_last(last)
      {
      }
    
    list<Node*>::iterator Layer::HaveNode(uli count)  // count bezeichnet Nummer des Listenelements (1 >= count >= letztes Element)
      {
      ilp_node=ilp_first;  // Iterator initialisieren
      --count;
      do
        {
        ++ilp_node;        // Countschritt bearbeitet
        --count;
        }
      while (count>0);     // Bis count bearbeitet
      return ilp_node;
      }
    
    // ============================================================================
    
    Net::Net()
      {
      poplp_node = new ObjectCare<Node>;
      plp_layer = new list<Layer*>;
      }
    
    Net::~Net()
      {
      delete poplp_node;
      DeleteListAndTheirElements<Layer>(plp_layer, ilp_layer);
      }
    
    // ============================================================================
    
    Set::Set()
      {
      poplp_net = new ObjectCare<Net>;
      }
    
    Set::~Set()
      {
      delete poplp_net;
      }
    

    EDataStructure.h

    #ifndef EDataStructureH
    #define EDataStructureH
    
    #include <iostream>         // cout
    #include <list>
    #include <typeinfo>         // typeid
    //#include <type_traits>    // is_baseof
    //#include <boost/ptr_container/ptr_list.hpp>  // newer version of C++? innerhalb boost: [C++ Fehler] remove_cv.hpp(22): E2238 Bezeichner 'remove_cv<T>' mehrfach deklariert
    #include "TDeleteListAndTheirElements.h"
    #include "EDatatype.h"
    using std::list;
    using std::cout;
    using std::endl;
    //using boost::ptr_list;
    
    // ----------------------------------------------------------------------------
    
    template <typename B>  // B=Base(Class)  // Als Basisklasse ?
    class ObjectCare
      {
      list<B*>* plp;
      typename list<B*>::iterator ilp;
     public:
      ObjectCare()
        {                                                                            cout << "ObjectCare::ObjectCare()\tKonstruktor" << endl;
        plp = new list<B*>;
        }
      ~ObjectCare()
        {                                                                            cout << "ObjectCare::~ObjectCare()\tDestruktor" << endl;
        DeleteListAndTheirElements<B>(plp,ilp);  // Mit g++ not declared in this scope, wenn dieser Header in TDeleteListAndTheirElements.h inkludiert
        }
      template <typename D/*, typename... Args*/>  // D=Data (Type of pointer in list), C=count
      void AddElement(/*Args... args*/)
        {
        plp->push_back(new D/*(args...)*/);                                                       // [C++ Warnung] EDataStructure.h(20): W8030 Temporäre Größe für Parameter '__x' in Aufruf von 'list<Node *,allocator<Node *> >::push_back(Node * const &)' verwendet
        }
      template <typename D, typename C/*, typename... Args*/>  // D=Data (Type of pointer in list), C=count,  // A=Arguments (for Constructor)
      typename list<B*>::iterator HaveElement(C count/*, Args... args*/)
        {                                                                            cout << "ObjC::HavEl(" << count << ". Typ: " << typeid(D).name() << ")" << endl << "ObjC::HavEl(" << count << ". Typ: " << typeid(D).name() << ")\tAnfangsobjektanzahl: " << plp->size() << endl;
        while(plp->size()<count)  // Solange Basisgröße kleiner Auswahl              // Wenn Mehrfachvererbung und zusammenführung InnerNode->InputNode, OutputNode->Node anscheinend keine Typkompatibilität von InnerNode zur Liste mit Zeigern auf die Basisklasse Node:
          AddElement<D/*,Args...*/>(/*args...*/);        // Füge Element hinzu
        ilp=plp->begin();         /* Itterator inititialisieren   */                 cout << "ObjC::HavEl(" << count << ". Typ: " << typeid(D).name() << ")\tilp = plp->begin(): " << *ilp << endl;
        advance(ilp, count-1);    /* Auf Auswahl Zeigen           */                 cout << "ObjC::HavEl(" << count << ". Typ: " << typeid(D).name() << ")\tEndobjektanzahl: " << plp->size() << endl << "ObjC::HavEl(" << count << ". Typ: " << typeid(D).name() << ")\tAdr. 1.\tObjekt: " << *(plp->begin()) << endl << "ObjC::HavEl(" << count << ". Typ: " << typeid(D).name() << ")\tAdr. " << count << ".\tObjekt: " << *ilp << " (Rueckgabe)" << endl;
        return ilp;               // Zeig auf Auswahl zurückgeben
        }
      };
    
    // ----------------------------------------------------------------------------
    
    class Node;
    
    //class Hormon // (Neuro)hormone distribution  // parakrin, neuroendokrin (z.B chemisches rufen nach axonalen synapsen)
    
    class Edge  // Synaptic gap, synapses (weight), axon, dentride
      {
     protected:
      wgt* mp_weight;
      //pos* mp_pos_x, mp_pos_y, mp_pos_z;
      dey* mp_delay;
     public:
      Edge(wgt weight=0.0, dey delay=0.0);
      virtual ~Edge()=0;  // =0: pure: Kein eigenständiges Objekt
      wgt GetWeight() const;
      dey GetDelay() const;
      };
    
    class NodeEdge_Edge : public Edge  // Axo-axonic synapse
      {
     protected:
      Node* mp_source;
      Edge* mp_dest;  // temporary influence(increase, also decrease?) on weight of dest edge (STP)
     public:
      NodeEdge_Edge(Node* p_source, Node* p_dest);
      Node* SourceNode() const;
      Edge* DestEdge() const;
      };
    
    class EdgeNode_Edge : public Edge  // Dendro-dentridic synapse
      {
     protected:
      Edge* mp_source;
      Node* mp_dest;
     public:
      EdgeNode_Edge(Node* p_source, Node* p_dest);
      Edge* SourceEdge() const;
      Node* DestNode() const;
      };
    
    class NodeNode_Edge : public Edge  // Axo-dentritic synapse
      {
     protected:
      Node* mp_source;  // m=membervariable, P=pointer, l=list, i=iterator
      Node* mp_dest;
     public:
      NodeNode_Edge(Node* p_source, Node* p_dest, wgt weight, dey delay);
      Node* SourceNode() const;  // Source=Pre(synaptic)
      Node* DestNode() const;    // Dest(ination)=Post(synaptic)
      };
    
    // ----------------------------------------------------------------------------
    
    class Node  // Neuron: soma
      {
      //pos* mp_pos_x, mp_pos_y, mp_pos_z;
     protected:
      vtg* mp_voltage;
     public:
      Node();
      virtual ~Node()=0;  // =0: pure: Kein eigenständiges Objekt
      void SetVoltage(vtg mp_voltage);
      };
    
    class InputNode : public Node
      {
     protected:
      ObjectCare<Edge>* poplp_destedge;
     public:
      InputNode();
      ~InputNode();
      template <typename D>
      list<Edge*>::iterator HaveDestEdgeTo(uli count)  // virtual  [C++ Fehler] EDataStructure.h(101): E2462 'virtual' kann nur für Nicht-Template-Elementfunktionen verwendet werden
        {
        return poplp_destedge->HaveElement<D>(count);
        }
      };
    
    class OutputNode : public Node
      {
     protected:
      list<Edge*>* plp_sourceedge;
      list<Edge*>::iterator ilp_sourceedge;
     public:
      OutputNode();
      ~OutputNode();
      //virtual list<Edge*>::iterator HaveSourceEdgeFrom(uli count);
      };
    
    /*class MotorNode : public OutputNode  // test
      {
     public:
      MotorNode();
      ~MotorNode();
      };*/
    
    class InnerNode : public Node  // Bei Mehrfachvererbung und Zusammenführung anscheinend nicht typkompatibel zur Liste mit Zeigern auf den Basisklassentyp.
      {                            // Daher Vererbung von public Node statt von public InputNode, public OutputNode
     protected:                    // und Redundanz von deren Listen und Itteratoren in InnerNode.
      list<Edge*>* plp_destedge;
      list<Edge*>::iterator ilp_destedge;
      list<Edge*>* plp_sourceedge;
      list<Edge*>::iterator ilp_sourceedge;
     public:
      InnerNode();
      ~InnerNode();
      };
    
    class PyramidalNode : public InnerNode
      {
     public:
      PyramidalNode();
      ~PyramidalNode();
      };
    
    // ----------------------------------------------------------------------------
    
    class Layer
      {
      list<Node*>::iterator ilp_first;
      list<Node*>::iterator ilp_node;
      list<Node*>::iterator ilp_last;
     public:
      //Layer();
      Layer(list<Node*>::iterator first, list<Node*>::iterator last);
      //list<Node*>::iterator GetStart() const;
      list<Node*>::iterator HaveNode(uli count);
      //list<Node*>::iterator GetEnd() const;
      //list<Node*>::iterator SetStart(list<Node*>::iterator first);
      //list<Node*>::iterator SetEnd(list<Node*>::iterator last);
      };
    
    // ----------------------------------------------------------------------------
    
    class Net
      {
      ObjectCare<Node>* poplp_node;
      list<Layer*>* plp_layer;
      list<Layer*>::iterator ilp_layer;
     public:
      Net();
      ~Net();
      template <typename D>
      list<Node*>::iterator HaveNode(uli count)
        {
        return poplp_node->HaveElement<D>(count);
        }
      template <typename D>
      void DefineLayer(uli first, uli last)  // Erstes vor dem letzten Element oder gleich (first<=last)
        {                                                                              cout << "Net::DefineLayer(first: " << first << ", last: " << last << ")" << endl;
        plp_layer->push_back(new Layer(HaveNode<D>(first),HaveNode<D>(last)));
        }
      };
    
    // ----------------------------------------------------------------------------
    
    class Set
      {
      ObjectCare<Net>* poplp_net;  // poplp(m)=poiter to object containing pointer to list containing pointers (member variable (implyed))
     public:
      Set();
      ~Set();
      template <typename D>                    // Wenn HaveNet=Net Ausdruckssyntaxfehler bei plp_net = new list<Net*>;
      list<Net*>::iterator HaveNet(usi count)  // Wenn typename D in Funktionsparametern: [C++ Fehler] EDataStructure.cpp(165): E2439 'typename' ist nur in Template-Deklarationen zulässig
        {
        return poplp_net->HaveElement<D>(count);
        }
      };
    
    #endif
    

    EDatatype.h

    #ifndef EDatatypeH
    #define EDatatypeH
    
    #include <iostream>  // Operator overloading, (cout)
    #include <limits>
    using std::numeric_limits;
    using std::ostream;
    using std::cout;
    using std::endl;
    
    // ----------------------------------------------------------------------------
    
    const unsigned short int USI_MIN=numeric_limits<unsigned short int>::min();  // C++11 for g++ and BCB
    const unsigned short int USI_MAX=numeric_limits<unsigned short int>::max();
    const unsigned long int ULI_MIN=numeric_limits<unsigned long int>::min();
    const unsigned long int ULI_MAX=numeric_limits<unsigned long int>::max();
    
    // ----------------------------------------------------------------------------
    
    typedef unsigned short int usi;
    typedef unsigned long int uli;
    //typedef signed long int pos;
    typedef double wgt;
    typedef float dey;
    typedef long double vtg;
    
    // ----------------------------------------------------------------------------
    
    #endif
    

    TDeleteListAndTheirElements.h

    #ifndef TDeleteListAndTheirElementsH
    #define TDeleteListAndTheirElementsH
    
    #include <iostream>  // cout
    #include <list>
    #include <typeinfo>  // typeid
    //#include "EDataStructure.h"  // Verwendung von z.B. Net und Node
    #include "EDebug.h"
    //using namespace std;  // list, cout, endl
    using std::list;
    using std::cout;
    using std::endl;
    
    // ----------------------------------------------------------------------------
    
    template <typename D>  // D=Data  // Wenn ausgelagert und Definition im Header: [C++ Warnung] TDeleteListAndTheirElements.cpp(3): W8058 Präcompilierter Header: Header unvollständig kann nicht erzeugt werden
    void DeleteListAndTheirElements(list<D*>* l, typename list<D*>::iterator i)
      {                                                                              cout << "DelLstel(list<" << typeid(D).name() << "*>* l: " << l << ", list<" << typeid(D).name() << "*>::iterator i)\tsize " << l->size() << endl;
      if (l->size()>0)                                                               // Bei Ausgabe von uninitialisiertem *i mit g++ Datenzugriffsfehler.
        {
        i = l->end();
        do
          {
          --i;                                                                       cout << "DelLstel(l,i)\tdelete " << *i << endl;
          delete *i;
          }
        while (i!=l->begin());
        }
      delete l;
      }
    
    #endif
    


  • 1. Warum sind alle deine Member Pointer? Bei denen vom Typ Node* oder Edge* verstehe ich es, aber warum auch weight, delay, voltage und die ganzen Container (list<X>)? Das ist unnötig kompliziert. Weight und Delay sind direkte Eigenschaften von Edge, ohne Verpointerung.

    2. Lies dir mal was über die Smart-Pointer an, die es seit C++11 gibt (std::unique_ptr, std::shared_ptr, std::weak_ptr). Es könnte sein, dass deine Node- und Edge-Verwaltung damit sicherer wird (weniger Probleme mit der Speicherverwaltung).

    3. "Bei Mehrfachvererbung und Zusammenführung anscheinend nicht typkompatibel " - woraus schließt du das? Gibts irgendeine Fehlermeldung, die du hier präsentieren könntest? Das erspart uns viel Herumraten.

    4. Ableitungsbeziehungen (vor allem Mehrfachableitung) haben weitreichende Konsequenzen und sollten daher wohlüberlegt sein. "class Derived : public Base" drückt aus "Derived ist ein Base", und zwar mit Haut und Haar. Das sollte man nur hinschreiben, wenn man sicher ist, eine solche Beziehung gefunden zu haben. Nicht nur, weil es auf den flüchtigen ersten Blick einleuchtet. Dadurch sind schon grausige Designs entstanden. Manchmal ist bei genauerem Hinschauen Komposition oder Aggregation die bessere Wahl.

    5. Das ObjectCare ist doch eigentlich nur ein Container mit AddElement und HaveElement. Und du brauchst es nur zur Implementierung von Methoden wie HaveNet, HaveNode usw. Richtig? Da ist ein Memberverhältnis (ObjectCare als Member der Klasse) genau richtig. Da würde eine Ableitung viel zu weit gehen.


Log in to reply