Damit KI in neuronalen Netzen universell ist, müssen Sie verstehen, dass das neuronale Netz für die Vielseitigkeit nicht ausreicht. Versuchen Sie dazu, die vollständige Ausführung aller Programme im neuronalen Netzwerk zu implementieren.Dies erfordert bedingte Übergänge, Bedingungen, Lesen und Schreiben von Datenstrukturen. Danach können objektorientierte neuronale Netze erstellt werden. Der Artikel muss in Teile geteilt werden.
Betrachten Sie die verschiedenen Arten von neuronalen Clustern. Sensorische und Effektorcluster wurden bereits erwähnt.Wenn es Und ist , wird es nur aktiviert, wenn alle Bedingungen aktiv sind - das heißt, das Signal ist an allen Synapsen angekommen.Oder- Wird ausgelöst, wenn mindestens eine Funktion aktiviert wurde. Wenn dieser Cluster Teil einer Kette ist, ist eine Rückwärtsverkettung obligatorisch - er ist durch die Und-Bedingung verbunden. Mit anderen Worten, ein Cluster wird nur aktiviert, wenn der vorherige Cluster der Kette aktiv war und auch eine seiner eigenen Bedingungen funktioniert hat. In Analogie zu Programmiersprachen fungiert die Kettenkommunikation als Befehlszeiger im Zentralprozessor - ein Signal „Ich erlaube die Ausführung der verbleibenden Bedingungen des Clusters“. Schauen wir uns einen Code an.Klasse NC; // Neurocluster
Klasse Link {
Öffentlichkeit:
NC & _from;
NC & _to;
...
};
Klasse LinksO; / * Container für ausgehende Links. Praktisch basierend auf boost :: intrusive
- um Speicherplatz zu sparen und die Leistung zu verbessern * /
Klasse LinksI; // basiert auch auf boost :: intrusive
struct NeuronA1 {
qreal _activation = 0;
static const qreal _threashold = 1;// , .
bool activated()const {
return _activation >= _threshold;
}
};
struct NeuronAT {
qreal _activation = 0;
qreal _threashold = 1;//
bool activated()const {
return _activation >= _threshold;
}
};
class NC {
public:
LinksO _next;
LinksO _down;
LinksI _prev;
LinksI _up;
NeuronA1 _nrnSumPrev;
NeuronAT _nrnSumFromBottom;
...
}
// , _nrnSumPrev:
void NC::sendActivationToNext() {
for(Link& link: _next) {
link._to._nrnSumPrev._activation += 1;
}
}
// - and/or/not :
bool NC::allowedToActivateByPrevChain()const {
if(_prev.isEmpty())// , , .
return true;// , .
return _nrnSumPrev.activated();
// , .
// 0 .
// - , - , .
}
Beachten Sie, dass es in _prev normalerweise entweder keinen oder nur einen Link gibt. Dies macht einen Präfixbaum aus Speicherketten: In _next kann es so viele Links geben, wie Sie möchten, und in _prev kann es nicht mehr als einen geben. Nur in gewöhnlichen Präfixbäumen gibt es an jeder Position nur einen Buchstaben, und im neuronalen Netzwerk gibt es eine beliebige Anzahl von Zeichen. Dank dessen wird selbst das Speichern des Zalizniak-Wörterbuchs nicht viel Speicherplatz beanspruchen.Der Einfachheit halber werden wir jetzt weitermachen und damit wir später keinen solchen Code neu schreiben müssen, werden wir Neuronen und Aktivierungen sofort los.Wenn die Cluster den Aktivierungsverlauf irgendwie beibehalten und ihre Aktivierung nicht an andere senden würden, könnten wir diese Funktion folgendermaßen umschreiben:bool NC :: allowToActivateByPrevChain () const {
für (Link & Link: _prev) {
NC & nc = link._from;
if (! nc.wasActivated ()) // auf den letzten Zyklus prüfen
return false;
}}
return true;
}}
Dann würden viele Probleme sofort verschwinden:1) Nach mehreren Prognosezyklen ist es nicht erforderlich, den Zustand des neuronalen Netzwerks wiederherzustellen - die Cluster speichern und speichern Informationen über ihre Aktivierung für die entsprechenden Zyklen. Die Vorhersage kann viel häufiger und in längeren Abständen erfolgen.2) Das neuronale Netzwerk ist resistent gegen Änderungen: Wenn dem Cluster verspätet eine Verbindung zum Cluster hinzugefügt wurde, müssen Sie keine erneuten Signale senden, um die Aktivierung im Zielcluster zusammenzufassen. Sie können die Bedingungen sofort überprüfen. Der Code wird funktional-paradigmatischer - ein Minimum an Nebenwirkungen.3) Es wird möglich, beliebige Signalverzögerungen einzuführen: Wenn der Aktivierungscache Daten für verschiedene Zyklen speichern kann, können Sie überprüfen, ob der Cluster von N Zyklen vor aktiv war.Fügen Sie dazu der Verbindung einen variablen Parameter hinzu - die Verzögerungszeit:Klasse Link {
...
int _delay = 1;
};
und dann wird die Funktion wie folgt geändert:bool NC :: allowToActivateByPrevChain () const {
für (Link & Link: _prev) {
NC & nc = link._from;
if (! nc.wasActivated (link._delay)) // N Zyklen zurück prüfen
return false;
}}
return true;
}}
4) Wir werden das stotternde „Gras im Hof, Brennholz im Gras, ...“ los: Signale von neueren Zyklen überschreiben alte nicht und umgekehrt.5) Es besteht keine Gefahr, dass die Aktivierung (von Zeit zu Zeit von selbst) verschwindet, wenn sie noch benötigt wird. Sie können Bedingungen weit in der Zeit überprüfen.6) Schließlich können Sie nicht ein Dutzend Artikel zum Thema "Management neuronaler Netze durch rhythmisches Aktivitätsmanagement", "Visualisierungsmethoden zur Steuerung von Signalen von Elektroenzephalogrammen", "spezielles DSL zur Steuerung von Elektroenzephalogrammen" schreiben und dies alles wegwerfen:
Nun zur Implementierung eines solchen Aktivierungscaches:1) ENS gibt uns drei Möglichkeiten, den Aktivierungscache zu platzieren: die aktuelle Aktivierung im Neurocluster selbst in seinen Neuronen, die Aktivierung (in Form von Identifikationswellen?) Im Hippocampus (hier wird sie länger gespeichert als im Cluster selbst) und das Langzeitgedächtnis. Es stellt sich ein dreistufiger Cache heraus, genau wie moderne Prozessoren.2) Im Softwaremodell befindet sich der Aktivierungscache auf den ersten Blick bequem in jedem Cluster.3) Genauer gesagt, wir haben dies und das bereits: Der Hippocampus in diesem Modell erstellt eine Speicherkette, und Verknüpfungen zu allen Clustern, die zu diesem Zeitpunkt aktiv waren und nicht gesperrt wurden, werden in die Speicherkette eingegeben. Und jede Verbindung wird in einem Cluster als ausgehend und in einem anderen als eingehend gespeichert. Dies zeigt, dass der "Cache" eigentlich kein Cache ist, sondern sogar ein Langzeitgedächtnis. Nur biologische neuronale Netze können Informationen nicht direkt aus dem Langzeitgedächtnis extrahieren, sondern nur durch Aktivierung, und ANNs können dies. Dies ist der Vorteil der KI gegenüber der ENS, die nicht zu verwenden ist - warum sollten wir uns mit Aktivierungen beschäftigen, wenn wir semantische Informationen benötigen?Um zu überprüfen, ob der Cluster N Schritte zurück aktiv war, können Sie den folgenden (nicht optimierten) Pseudocode verwenden:NC * Brain :: _ hippo; // aktueller Cluster, zu dem aktuelle Ereignisse hinzugefügt werden
NC* NC::prevNC(int stepsBack)const {
// _prev
// link._delay, .
// , ()
}
bool NC::wasActivated(int stepsAgo)const {
NC* timeStamp = _brain._hippo->prevNC(stepsAgo);
if(!timeStamp)//
return false;
return linkExists(timeStamp, this);
// , boost intrusive ,
// , node 2 3
}
Wenn anstelle der in Vergessenheit geratenen Aktivierung nicht nur das Vorhandensein der Verbindung, sondern auch die Aktivierungskraft erhalten bleiben muss, kann das entsprechende Feld zur Verbindung selbst hinzugefügt werden. Zu diesem Zweck können andere Felder verwendet werden, ohne zusätzliche einzuführen: zum Beispiel „Wichtigkeit“, von der die Kommunikationslebensdauer abhängt.Aber was ist mit Clustern, in denen die Aktivierung die Schwelle nicht erreicht, aber dennoch nützlich ist, beispielsweise für die Fuzzy-Erkennung oder die Fehleinschätzung von Wahrscheinlichkeiten usw.? Eine nicht optimierte Lösung besteht darin, dieselben Verbindungen zu verwenden. Erstellen Sie dazu entweder zusätzliche Container mit Links innerhalb des Clusters und fügen Sie sie dort hinzu (um sich nicht mit den normalen zu mischen, die funktionierten), oder stören Sie sogar alles in einem Heap und trennen Sie sie nur mit Gewalt. Solche Verbindungen müssen schneller entfernt werden, da sie eine Größenordnung größer sind als andere. Eine optimierte Lösung: Jeder Cluster speichert einen normalen Aktivierungscache - beispielsweise einen kreisförmigen Puffer (Ring) mit 16 Elementen, in dem jedes Element eine Zyklusnummer und eine Aktivierungskraft für diesen Zyklus speichert. Es gibt einen zweistufigen Cache: für schwache Signale, Unterschwellenwert und den neuesten - einen Puffer im Cluster,ansonsten Kommunikation für das Langzeitgedächtnis. Vergessen Sie nicht, dass in diesen Artikeln nur Pseudocode- und naive Algorithmen gezeigt werden und Optimierungsprobleme viel mehr Platz beanspruchen können.