Red neuronal como base de datos de activación

Para que la IA en las redes neuronales sea universal, debe comprender lo que la red neuronal no es suficiente para la versatilidad. Para hacer esto, intente implementar la ejecución completa de cualquier programa en la red neuronal.Esto requerirá transiciones condicionales, condiciones, lectura y escritura de estructuras de datos. Después de eso, será posible crear redes neuronales orientadas a objetos. El artículo deberá dividirse en partes.



Considere los diferentes tipos de grupos neuronales. Ya se han mencionado grupos sensoriales y efectores.
Si es Y , se activa solo si todas las condiciones están activas, es decir, la señal ha llegado a todas las sinapsis.
O- Se activa si se ha activado al menos una función. Si este clúster es parte de una cadena, entonces el encadenamiento hacia atrás es obligatorio: está conectado por la condición And. En otras palabras, un grupo se activa solo si el grupo anterior de la cadena estaba activo y alguna de sus propias condiciones también funcionaba. En analogía con los lenguajes de programación, la comunicación en cadena actúa como un puntero de instrucción en el procesador central, una señal que "permite la ejecución de las condiciones restantes del clúster". Veamos un código.

clase NC; // neurocluster
enlace de clase {
	público:
		NC & _de;
		NC & _to;
		...
};
Class LinksO; / * contenedor para enlaces salientes. Conveniente de hacer basado en boost :: intrusivo
 - para ahorrar memoria y mejorar el rendimiento * /
clase LinksI; // también basada en 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    .
	// -     ,   -     ,    .
}

Tenga en cuenta que en _prev generalmente no hay un enlace o un enlace. Esto crea un árbol de prefijos fuera de las cadenas de memoria: en _next puede haber tantos enlaces como desee, y en _prev no puede haber más de uno. Solo en los árboles de prefijos ordinarios solo hay una letra en cada posición, y en la red neuronal hay un número arbitrario de caracteres. Gracias a esto, incluso almacenar el diccionario de Zalizniak no ocupará mucha memoria.

Ahora, por conveniencia, correremos hacia adelante y para que luego no tengamos que volver a escribir dicho código, inmediatamente nos desharemos de las neuronas y las activaciones.
Si los clústeres de alguna manera mantuvieron el historial de activación y no enviaron su activación a otros, podríamos reescribir esta función de esta manera:

bool NC :: allowedToActivateByPrevChain () const {
	para (Enlace y enlace: _prev) {
		NC & nc = link._de;
		if (! nc.wasActivated ()) // verifica el último ciclo
			devuelve falso;
	}
	volver verdadero;
}


Entonces, muchos problemas desaparecerían de inmediato:
1) Después de varios ciclos de pronóstico, no es necesario restaurar el estado de la red neuronal: los grupos almacenan y almacenan información sobre su activación para los ciclos correspondientes. La predicción se puede incluir con mucha más frecuencia y a intervalos más largos por delante.
2) La red neuronal es resistente a los cambios: si una conexión a un clúster se agregó al clúster con retraso, entonces no necesita enviar señales nuevamente para resumir la activación en el clúster de destino; puede verificar de inmediato las condiciones. El código se vuelve más paradigmático funcional: un mínimo de efectos secundarios.
3) Se hace posible introducir demoras de señal arbitrarias: si la caché de activación puede almacenar datos para diferentes ciclos, entonces puede verificar si el grupo de N ciclos hace tiempo estaba activo.
Para hacer esto, agregue un parámetro variable a la conexión: el tiempo de retraso:
enlace de clase {
	...
	int _delay = 1;
};

y luego la función se modifica así:
bool NC :: allowedToActivateByPrevChain () const {
	para (Enlace y enlace: _prev) {
		NC & nc = link._de;
		if (! nc.wasActivated (link._delay)) // verifica N ciclos hacia atrás
			devuelve falso;
	}
	volver verdadero;
}


4) Nos deshacemos del tartamudeo "hierba en el patio, leña en la hierba, ...": las señales de los ciclos más nuevos no sobrescribirán a las viejas, y viceversa.
5) No hay peligro de que la activación se desvanezca (por sí sola, de vez en cuando) cuando aún se requiere. Puede verificar las condiciones muy atrás en el tiempo.
6) Finalmente, no puede escribir una docena de artículos sobre el tema "gestión de redes neuronales a través de la gestión de la actividad rítmica", "métodos de visualización para señales de control de electroencefalogramas", "DSL especial para gestionar electroencefalogramas" y descartar esto es todo:



ahora sobre la implementación de tal caché de activación:
1) ENS nos da tres opciones para colocar el caché de activación: activación actual en el propio neurocluster en sus neuronas, activación (¿en forma de ondas de identificación?) En el hipocampo (aquí se almacena más tiempo que en el propio clúster) y memoria a largo plazo. Resulta un caché de tres niveles, al igual que los procesadores modernos.
2) En el modelo de software, el caché de activación a primera vista está convenientemente ubicado en cada clúster.
3) Más específicamente, ya tenemos esto y aquello: el hipocampo en este modelo crea una cadena de memoria, y los enlaces a todos los grupos que estaban activos y que no se inhibieron en ese momento se ingresan en la cadena de memoria. Y cada conexión se almacena en un clúster como saliente y en otro como entrante. Esto muestra que el "caché" en realidad no es un caché, sino incluso una memoria a largo plazo. Solo las redes neuronales biológicas no pueden extraer información de la memoria a largo plazo directamente, solo a través de la activación, y los ANN pueden. Esta es la ventaja de la IA sobre la ENS, que es una tontería no usar: ¿por qué molestarse con las activaciones si necesitamos información semántica?

Entonces, para verificar si el clúster estaba activo N pasos atrás, puede usar el siguiente pseudocódigo (no optimizado):

NC * Brain :: _ hippo; // grupo actual al que se agregan eventos actuales
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, en lugar de una activación que se ha ido al olvido, es necesario preservar no solo la presencia de la conexión, sino también la fuerza de activación, entonces el campo correspondiente se puede agregar a la conexión misma. Se pueden utilizar otros campos para este propósito, sin introducir otros adicionales: por ejemplo, "importancia", de la que depende la duración de la comunicación.
Pero, ¿qué pasa con los grupos en los que la activación no alcanza el umbral, pero sigue siendo útil, por ejemplo, para el reconocimiento difuso o el cálculo erróneo de las probabilidades, etc.? Una solución no optimizada es utilizar las mismas conexiones. Para hacer esto, cree contenedores adicionales de enlaces dentro del clúster y agréguelos allí (para no mezclarlos con los normales que funcionaron), o incluso interfiera con todo en un montón, y sepárelos solo por la fuerza. Dichas conexiones deberán eliminarse más rápido, ya que son un orden de magnitud mayor que otras. Una solución más optimizada: cada clúster almacena un caché de activación normal, por ejemplo, un búfer circular (anillo) de 16 elementos, donde cada elemento almacena el número de ciclo y la fuerza de activación para ese ciclo. Hay una memoria caché de dos niveles: para señales débiles, subliminales y las más recientes: un búfer en el clúster,de lo contrario, comunicaciones para memoria a largo plazo. No olvide que en estos artículos solo se muestran pseudocódigo y algoritmos ingenuos, y los problemas de optimización pueden ocupar mucho más espacio.

Source: https://habr.com/ru/post/es388797/


All Articles