Pour que l'IA sur les réseaux neuronaux soit universelle, vous devez comprendre ce que le réseau neuronal n'est pas suffisant pour la polyvalence. Pour ce faire, essayez d'implémenter l'exécution complète de tous les programmes sur le réseau neuronal.Cela nécessitera des transitions conditionnelles, des conditions, la lecture et l'écriture de structures de données. Après cela, il sera possible de créer des réseaux de neurones orientés objet. L'article devra être divisé en plusieurs parties.
Considérez les différents types de clusters neuronaux. Des grappes sensorielles et effectrices ont déjà été mentionnées.Si c'est Et , il n'est activé que si toutes les conditions sont actives - c'est-à-dire que le signal est arrivé à toutes les synapses.Ou- déclenché si au moins une fonction a été activée. Si ce cluster fait partie d'une chaîne, le chaînage arrière est obligatoire - il est connecté par la condition Et. En d'autres termes, un cluster n'est activé que si le cluster précédent de la chaîne était actif et si l'une de ses propres conditions fonctionnait également. Par analogie avec les langages de programmation, la communication en chaîne agit comme un pointeur d'instructions dans le processeur central - un signal «Je permets l'exécution des conditions restantes du cluster». Regardons un peu de code.classe NC; // neurocluster
Lien de classe {
public:
NC & _de;
NC & _to;
...
};
Class LinksO; / * conteneur pour les liens sortants. Pratique à faire basé sur boost :: intrusif
- pour économiser de la mémoire et améliorer les performances * /
class LinksI; // également basé sur 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 .
// - , - , .
}
Notez que dans _prev il n'y a généralement pas de lien, ou un lien. Cela crée un arbre de préfixe à partir des chaînes de mémoire: dans _next il peut y avoir autant de liens que vous le souhaitez, et dans _prev il ne peut y en avoir plus d'un. Seulement dans les arbres de préfixe ordinaires, il n'y a qu'une seule lettre à chaque position, et dans le réseau neuronal il y a un nombre arbitraire de caractères. Grâce à cela, même le stockage du dictionnaire de Zalizniak ne prendra pas beaucoup de mémoire.Maintenant, pour des raisons de commodité, nous allons aller de l'avant et pour que plus tard nous ne devions pas réécrire un tel code, nous nous débarrasserons immédiatement des neurones et des activations.Si les clusters conservaient en quelque sorte l'historique d'activation et n'envoyaient pas leur activation à d'autres, nous pourrions réécrire cette fonction comme ceci:bool NC :: allowedToActivateByPrevChain () const {
pour (Link & link: _prev) {
NC & nc = link._from;
if (! nc.wasActivated ()) // recherche le dernier cycle
retour faux;
}
return true;
}
Ensuite, de nombreux problèmes disparaissent immédiatement:1) Après plusieurs cycles de prévision, il n'est pas nécessaire de restaurer l'état du réseau neuronal - les clusters stockent et stockent des informations sur leur activation pour les cycles correspondants. La prédiction peut être incluse beaucoup plus souvent et à des intervalles plus longs.2) Le réseau neuronal est résistant aux changements: si une connexion au cluster a été ajoutée au cluster tardivement, vous n'avez pas besoin d'envoyer à nouveau des signaux pour résumer l'activation sur le cluster de destination - vous pouvez immédiatement vérifier les conditions. Le code devient plus fonctionnel-paradigmatique - un minimum d'effets secondaires.3) Il devient possible d'introduire des retards de signaux arbitraires: si le cache d'activation peut stocker des données pour différents cycles, alors vous pouvez vérifier si le cluster de N cycles auparavant était actif.Pour ce faire, ajoutez un paramètre variable à la connexion - le temps de retard:Lien de classe {
...
int _delay = 1;
};
puis la fonction est modifiée comme ceci:bool NC :: allowedToActivateByPrevChain () const {
pour (Link & link: _prev) {
NC & nc = link._from;
if (! nc.wasActivated (link._delay)) // retourne N cycles
retour faux;
}
return true;
}
4) Nous nous débarrassons du bégaiement «herbe dans la cour, bois de chauffage sur l'herbe,…»: les signaux des cycles plus récents n'écraseront pas les anciens, et vice versa.5) Il n'y a aucun danger que l'activation disparaisse (de lui-même, de temps en temps) lorsqu'elle est toujours requise. Vous pouvez vérifier les conditions très loin dans le temps.6) Enfin, vous ne pouvez pas écrire une douzaine d'articles sur le sujet "gestion des réseaux neuronaux par la gestion de l'activité rythmique", "méthodes de visualisation des signaux de contrôle des électroencéphalogrammes", "DSL spécial pour contrôler les électroencéphalogrammes" et jeter tout cela:
Maintenant, à propos de l'implémentation d'un tel cache d'activation:1) L'ENS nous donne trois options pour placer le cache d'activation: l'activation actuelle dans le neurocluster lui-même dans ses neurones, l'activation (sous forme d'ondes d'identification?) Dans l'hippocampe (ici, il est stocké plus longtemps que sur le cluster lui-même) et la mémoire à long terme. Il se révèle un cache à trois niveaux, tout comme les processeurs modernes.2) Dans le modèle logiciel, le cache d'activation à première vue est idéalement situé dans chaque cluster.3) Plus précisément, nous avons déjà ceci et cela: l'hippocampe de ce modèle crée une chaîne de mémoire, et des liens vers tous les clusters qui étaient actifs et qui n'étaient pas inhibés à ce moment sont entrés dans la chaîne de mémoire. Et chaque connexion est stockée dans un cluster comme sortant et dans un autre comme entrant. Cela montre que le "cache" n'est en fait pas un cache, mais même une mémoire à long terme. Seuls les réseaux de neurones biologiques ne peuvent pas extraire directement des informations de la mémoire à long terme, uniquement par activation, et les RNA le peuvent. C'est l'avantage de l'IA sur l'ENS, ce qui est idiot de ne pas l'utiliser - pourquoi s'embêter avec des activations si nous avons besoin d'informations sémantiques?Donc, pour vérifier si le cluster était actif N pas en arrière, vous pouvez utiliser le pseudocode suivant (non optimisé):NC * Brain :: _ hippo; // cluster actuel auquel les événements actuels sont ajoutés
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
}
Si au lieu d'une activation tombée dans l'oubli, il est nécessaire de préserver non seulement la présence de la connexion, mais également la force d'activation, le champ correspondant peut être ajouté à la connexion elle-même. D'autres champs peuvent être utilisés à cet effet, sans en introduire d'autres: par exemple, «importance», dont dépend la durée de vie de la communication.Mais qu'en est-il des clusters dans lesquels l'activation n'atteint pas le seuil, mais est toujours utile, par exemple, pour la reconnaissance floue, ou le mauvais calcul des probabilités, etc.? Une solution non optimisée consiste à utiliser toutes les mêmes connexions. Pour ce faire, créez des conteneurs de liens supplémentaires dans le cluster et ajoutez-les là (afin de ne pas les mélanger avec ceux qui fonctionnaient normalement), ou même interférer avec tout dans un tas et ne les séparer que par la force. Ces connexions devront être supprimées plus rapidement, car elles sont d'un ordre de grandeur plus grandes que les autres. Une solution plus optimisée: chaque cluster stocke un cache d'activation normal - par exemple, un tampon circulaire (anneau) de 16 éléments, où chaque élément stocke un numéro de cycle et une force d'activation pour ce cycle. Il existe un cache à deux niveaux: pour les signaux faibles, le sous-seuil et le plus récent - un tampon dans le cluster,sinon, communications pour la mémoire à long terme. N'oubliez pas que dans ces articles, seuls les pseudocodes et les algorithmes naïfs sont présentés, et les problèmes d'optimisation peuvent prendre beaucoup plus de place.