Liste der Anhänge anzeigen (Anzahl: 1)
Moin Forum,
der Blog ist mir zu weit ab vom Schuss. Den lesen zu wenige. Ich bleib erstmal hier im Thread. Soooo umfangreich ist meine Erkenntnis noch nicht. Eher simpel und naheliegend.
Viele Grüße
Wolfgang
************************
Generic Intelligence
GI vs. KI
------------------------------------------------------------
Vorwort
Ein wichtiges Gebiet der AI bzw. KI (artificial intelligence bwz. künstlichen Intelligenz) befasst sich mit der Erstellung künstlicher neuronaler Netze (KNN).
Das Vorgehen bei den KNN ist simpel. Es wird die Funktionalität eines einzigen Neurons programmiert. Und es wird eine große Anzahl von Speicherstrukturen angelegt, die der Anzahl von Nervenzellen entspricht.
Nun wird in endlosen Schleifen das Neuron mit Daten aus der Umwelt oder den internen Speicherstrukturen gefüttert und seine Rechenergebnisse abgespeichert. Ob Daten aus der Umwelt oder internen Speichern verrechnet werden, entscheidet die jeweilige Neuronenschicht, die gerade berechnet wird.
Viele KNN bestehen aus drei Schichten. Einer äußeren Schicht, die mit den Umweltsensoren direkt gekoppelt ist. Einer versteckten inneren Schicht. Und einer Ausgabeschicht, die Aktoren ansteuert.
Die Emulation eines Neurons ist eine wahre Wissenschaft. Die Eingangssignale können gewichtet, summmiert und mit unlinearen Funktionen multipliziert werden. Typischerweise entsteht so ein einziges Ausgangssignal, was alle Eingänge miteinander verrechnet hat.
Formell ist ein KNN eine Matrix-Berechnung. Besonders schnelle Multiplikationen und Additionen sind eine Domäne von Grafikprozessoren, weswegen diese Bauteile gerne in der KI verwendet werden.
Die die natürlichen Intelligenz beherrschenden Merkmale der Vernetzung und Parallelität werden durch mathematische Korrelation und enorme Rechengeschwindigkeiten ersetzt.
------------------------------------------------------------
Motivation
Statt immer größere Rechenleistungen einzusetzen, will ich das andere Ende der Skala beleuchten.
Kleine Controller (z.B. Tiny85 etc.) verfügen nicht über die Ressourcen zur Abarbeitung der geschilderten Dampframmen-KI.
Diese Dampframmen-KI erscheint mir sowieso eher lustlos. Die Entwickler wollen eine informelle Blackbox nutzen, deren Innereien sie eigentlich gar nicht interessiert. Die sie vielleicht nicht mal verstehen. So eine Art KI-Betriebssystem, um das man sich nicht kümmern muss und was es schon richtig machen wird. Konsumer-Mentalität auf höchstem Level.
Es soll stattdessen eine zur Controller-Klasse passende künstliche Intelligenz entwickelt werden, die wegen ihrer universellen Skalierbarkeit als GI bezeichnet wird.
------------------------------------------------------------
Modell
Die GI eines Kleincontrollers besteht aus drei pyramidenförmigen Schichten.
Anhang 34461
In der obersten Schicht, dem „RAM“ wird z.B. Bildverstehen auf höchster Abstraktion betrieben. Hier wird auch geschlafen, um das RAM für einen neuen Tag freizuschaufeln. Die Speicherzeit ist kurz.
In der mittleren „EEPROM“-Schicht wird Gerlerntes gespeichert. Also z.B. Laufen, Sprechen und Bilderkennung. Hier wachsen Verknüpfungen und werden Anpassungen an Umwelten gespeichert. Die Speicherzeit ist lang.
Die unterste „Flash“-Schicht wird von mir programmiert. Hier befinden sich Reflexe, Instinkte, Triebe und die Treiber für Sensoren und Aktoren. Das Stammhin sozusagen mit ewiger Speicherzeit.
Die Aufgabe des Gebildes ist das Zurechtfinden in einer unbekannten Umwelt.
Vom Prinzip her ist ein GI-System schon mit der Flash-Schicht alleine - in Grenzen -lebensfähig. Es schreit und zappelt zum Beispiel, wenn die Bordnetzspannung zu weit sinkt und es Hunger hat.
Interessanter wird’s aber, wenn die EEPROM-Schicht sich durch Interaktion mit der Flash-Schicht und der daran angekoppelten Umwelt zu organisieren beginnt. Zur Erlangung von Mobilität muss die EEPROM-Schicht rumprobieren. Sie muss selbsttätig versuchen, ein Ablaufprogramm zum „Laufen“ zu entwickeln. Um einen Schritt zu machen, mussen die Unterprogramme und Treiber der Flash-Schicht in einer gewissen Chronologie angesteuert werden. Ein gutes GI-System lernt und optimiert Fortbewegung ganz von alleine.
Sobald die EEPROM-Schicht ausgebildet ist. Kann sich das System fortbewegen, weglaufen, Futter suchen und hat sich an seine Umwelt adaptiert. Man könnte jetzt zufrieden sein und sich am Rumwuseln des Roboters erfreuen.
Spannend wird’s aber, wenn die oberste Schicht freigeschaltet wird. Hier treffen mit höchstem Abstraktionslevel Ereignisse ein. Hier findet „Kombinieren“ und „Denken“ statt. Hier wird im kybernetischen Sinne gespielt und im regelungstechnischen Sinne optimiert. Hier kommt es auch nicht mehr auf die Geschwindigkeit an.
---------------------
Realisierung
Die GI unterstützt eine generische Vorgehensweise. Zuerst werden die Eigenschaften der Flash-Schicht kodiert. Es entsteht eine Art Betriebssystem des Robotors. Dieses Subsystem kann Befehle mit uniformierten Messages empfangen und Events an die höheren Schichten asynchron senden.
Die EEPROM-Schicht nutzt das Flash-Betriebssystem und dessen Rückmeldungen. Es wird zum Beispiel mit einem leckeren Happen gelockt und muss nun irgendwie seine Betriebsystem-Befehle in geeigneter Weise chronologisch kaskadieren, dass eine Bewegung hin zum Futterhappen stattfindet.
Die Arbeitsweise der EEPROM-Schicht erinnert mehr an einen Homöostaten als an ein intelligentes Wesen. Es optimiert sich selbst und passt sich selbst auch an die Umwelt an.
Auch die EEPROM-Schicht kann Messages empfangen und Events senden. Allerdings schon auf sehr hohem Abstraktionsleveln. Ein typischer Befehl an die EEPROM-Schicht wäre: „gehe drei Schritte die Treppe runter“ oder „fahre mit dem Rad immer geradeaus“.
Die übergeordnete RAM-Schicht ist noch nicht konkreter durchdacht worden. Die kann generisch draufgesatteltwerden, wenn die anderen Schichten laufen.
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,
so langsam wächst und gedeiht das System:
Zitat:
CC5X Version 3.5, Copyright (c) B Knudsen Data, Norway 1992-2014
--> FREE edition, 8-16 bit int, 24 bit float, 32k code, reduced optim.
Warning : Main file extension should be '.c'
gi.cp:
Chip = 12F629
RAM: 00h : -------- -------- -------- --------
RAM: 20h : =====7.. ........ ........ ........
RAM: 40h : ....**** ******** ******** ********
RAM usage: 36 bytes (5 local), 28 bytes free
Optimizing - removed 1 instructions (-0 %)
File 'gi.pic'
File 'gi.lst'
File 'gi.hex'
Total of 135 code words (13 %)
Der 12F629 hat 1 kWorte Flash, wovon ich zur Zeit 13% benutze. Bis aufs "Verlernen", "Reflexe" und "Schlafen" (= Zellen-Erregungen wieder normalisieren) ist alles schon programmiert.
Der PIC hat 64 Byte RAM, von denen ich 36 nutze. Damit verwalte ich 30 Neuronen ("Zellen"). Die 128 Byte des EEPROMs werden für Dendriten benutzt, die ich "Links" nenne.
Die Anzahl der Zellen wird vorgegeben. Die Links organisieren sich vollständig alleine. Sie bilden Korrelations-Cluster. Man kann beim GI-System auch nicht von Zell-"Schichten", starren Matrizen oder Deep Learning sprechen. Jede Zelle kann potentiell mit jeder Zelle verlinkt sein. Links können verstärkt werden. Das entspricht der klassischen Gewichtung oder auch vollständig verkümmern. Propagation ist in alle Richtungen möglich. Das System kann sich selbst organisieren, optimieren und anpassen, muss also nicht einem Training unterzogen werden. Kann aber.
Enthalten ist auch schon das "Gehirnknistern" (Stochastik), "Hunger", vier Sensoren und zwei Aktoren.
Neu ist auch die Art des Feuerns. Bei mir wird gefeuert, wenn Deltas auftreten. Dadurch und durch die dynamische Anzahl von Links hab ich auch bei starken System-Erregungen nur eine relativ geringe Anzahl von Operationen abzuleisten.
Das flutscht schon alles ganz gut. Morgen mach ich das Gegenstück dazu: den Umwelt-PIC.
Es wird, es wird.... ich tippe auf ersten laufenden Prototypen am WE.
Viele Grüße
Wolfgang
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo,
es lebt...
Anhang 34478
Zu sehen sind EEPROM-Bytes, die die 3-Byte-"Links" (Dentriten) beinhalten. Die führenden Nullen sind der Galep-Anzeige geschuldet. Und die letzten beiden Bytes werden wegen der 3-Byte-boundaries nicht verwendet.
Spannend ist, dass sich ein Dendrit gebildet hat, der die Sinneszelle "x00" mit einem Neuron "x0D" verbunden hat und noch "00" Nutzen brachte. Dass der Dendrit sich am Ende des Speichers gebildet hat, hat den Grund, dass der PIC das ganze EEPROM komplett durchsucht hatte und keinen mit der Sinneszelle verbundenen Dendriten finden konnte.
Nach einem Durchlauf hab ich das System angehalten, damit ich das EEPROM analysieren kann. Die Zellen kann man nicht sehen, weil die im RAM versteckt sind.
Aber mir zeigen die drei Byte an, dass alles komplett erfolgreich durchgelaufen ist. Jetzt nehm ich alle Bremsen raus und lass das System sich komplett vernetzen.
Ich rechne mit Sekundenbruchteilen.
Viele Grüße
Wolfgang
----------------------------------
Tatsache: 256-mal alles komplett durchgerechnet. Gesamtlaufzeit dafür vilelleicht 1/4 Sekunde.
Anhang 34479
Nun ist die Sinneszelle 0x00 (nur die war aktiv) mit acht Links an andere Zellen x19, x11 x09 x07 x17 x0F x15 x0D angeschlossen. Alle Verbindungen konnten ihre Nützlichkeit noch nicht beweisen, weil es nur einen einzigen Lichtblitz gab.
Sobald weitere Lichtblitze erscheinen, werden die zur Zeit noch verkümmerten Links wachsen. Es können sich auch gänzlich neue Links anlegen. Das Dingens sollte sich selbst belohnen können und deep-learning machen. Schon deshalb, weil die Zellen nicht in festen Schichten, sondern sich in dynamischen Clustern organisieren.
Das alles ohne jegliche Matrizen oder Multiplikationen.
Die Weichware sieht auch schon richtig hübsch aus. Zwar spezieller PIC-C-Slang. Aber kurz und knackig. Mit Debug-Code beleg ich zur Zeit 29% des 1 k Flashs.
So richtig große Dinge hab ich wohl nicht vergessen. Da kommt fast nichts mehr dazu. Sensor- und Aktoren-Treiber sind auch schon drin. Es fehlt nur noch die Logik der beiden Zellen für "Hunger" und "Gehirnknistern". Aber das ist beides trivial.
GI-Sourcenvorstellung am WE könnte was werden.
Viele Grüße
Wolfgang
- - - Aktualisiert - - -
Jepp,
Motoren laufen. Sieht schrecklich intelligent aus. Ein zuckeln und ruckeln. Er hat ja noch kein Feedback. Die Umwelt erscheint ihm völlig statisch. Singularität.
Aber das System hat sich in Clustern organisiert. Der Nutzen in den Links läuft hoch (ich hab nen Endanschlag vergessen). Außerdem wird er noch nicht verrechnet, nur geschriebnen. Zellen feuren synchron
Anhang 34480
Ein Beispiel: die beiden Motorzellen heißen x1C und x1D.
x1D hat sich zweimal direkt mit dem einzigen aktiven Sensor verkoppelt. Offensichtlich war die Motor-Zelle gerade nicht feuerbereit, als der Sensor wieder einen Lichtblitz erzeugte. Welcher der beiden Links sich schließlich durchsetzen kann, ist noch nicht klar.
x1C hatte am Anfang auch geruckelt. Wurde dann aber immer schwächer und seine Dendriten verkümmerten. Zur Zeit ist er taub. Mit nur einer Sinneszelle wird sich daran auch erst dann was ändern können, wenn die Stochastik hilt (Gehirnknistern) oder wenn x1D nicht alle Ereignisse vom Sensor mitbekommt.
Die restlichen Zellen haben sich untereinander vernetzt. Worüber die wohl nachdenken? Vielleicht weden sich die die nächste Zeit mit dem tauben Motor verkoppeln?
Es hat was. Streckenweise läuft es mir etwas kalt den Rücken runter. Ich könnte ein Programm gebrauchen, was mir in einem grafischen 3D-Netz die Zellen als Kugeln und die Links als Verbindungen darstellt. Feuerbereite Kugeln müssten größer sein. Und nützliche Links dicker sein.
Da das ganze aber atmet (lebt) und schon in den nächsten Millisekunden ganz neue Verbindungen schlagen kann, müsste man sowas eigentlich kontinierlich darstellen.
Ich glaub, ich hab was ganz Spannendes erdacht. Die Interpretation ist so komplex, dass man geneigt ist, dem Ding zuzutrauen, dass es schon eine Lösung finden wird. Früher oder später. Aber in jedem Fall in wenigen Millisekunden.
Soll ich die Source mal zeigen. Noch ohne Abfabgen des Nutzen-Überlaufs und ohne Logik für Hunger und Knistern.
Ich machs mal einfach:
Code:
#include "allpic.h"
#pragma config = 0b000110000100 // hex-datei: fuses des 12F629
#define EE_ROM // eeprom nutzen
#include "pic_mem.h"
#include "timeloop.h" // timer-routinen
#define IN_0 0 // GPIO-Eingänge
#define IN_1 1 //
#define IN_2 2 //
#define IN_3 3 //
#define OUT_0 4 // GPIO-Ausgänge
#define OUT_1 5 //
//** alle sensoren, aktoren, triebe und neuronen nach abstraktion gestaffelt ******
uns8 zellen[30]; // zellen im RAM anlegen
#define FIRE_REQ 7 // fire_req bit in der zelle
#define FIRE_LIMIT 10 // fire_limit
#define SENS_0 0 // spezialzellen definieren
#define SENS_1 1 // sensoren
#define SENS_2 2
#define SENS_3 3
#define TIMER 4 // Timer-Zelle
#define HUNGER 5 // Hunger-Zelle
#define FIRST_NEURON 6 // davor nur read-only-zellen
#define AKT_0 (sizeof(zellen) - 2)
#define AKT_1 (sizeof(zellen) - 1)
//******* verbunden werden zellen mit vielen links (dendriten) *********
#define NO_LINK -1
#define MAX_LINKS (128 / sizeof(struct _link))// viele links im EEPROM
struct _link { // struktur eines EEPROM-links
uns8 src; // leerer link: src_zelle == NO_LINK
uns8 dst; // verbindet source- mit dest-zelle
uns8 use; // nützlichkeit dieses links
};
// LINK-Zugriffe
#define LINK_RD(a,m) ee_read((a) + offsetof(struct _link,m))
#define LINK_WR(a,m,v) ee_write((a) + offsetof(struct _link,m),v)
static uns8 rand_link; // randomize link
static void gi_denke(void) // die genesis
{
uns8 zell_ind = 0, link, free, buf;
BOOL hit;
do { // suche feuernde zellen
rand_link++; // randomize link
FSR = zellen + zell_ind; // pointer auf zelle
/**** sensoren, aktoren und triebe sind besondere zellen *****/
if(zell_ind <= SENS_3) { // sensoren abfragen
buf = _BV(zell_ind); // bit-maske entwickeln
if(GPIO & buf) { if(!(INDF.0)) INDF = FIRE_LIMIT + _BV(FIRE_REQ);}
else INDF.0 = 0 // FIRE_REQ bearbeiten
} // nun die aktoren
else if(zell_ind == AKT_0) GPIO.OUT_0 = INDF.FIRE_REQ;
else if(zell_ind == AKT_1) GPIO.OUT_1 = INDF.FIRE_REQ;
if(INDF.FIRE_REQ == FALSE) continue;// zelle möchte nicht feuern
/***** wenn zelle feuern will, verfolge die links *************/
INDF.FIRE_REQ = FALSE; // zelle möchte feuern
free = NO_LINK; link = 0; hit = FALSE;
do { // alle links durchsuchen
rand_link++; // randomize link
buf = LINK_RD(link,src); // linkbyte lesen
if(buf == NO_LINK) free = link;// leeren link merken
else if(buf == zell_ind) {// link gefunden
FSR = zellen + LINK_RD(link,dst);// pointer auf dst-zelle lesen
if((++INDF == FIRE_LIMIT)&&(!(INDF.FIRE_REQ))) {// will dst feuern?
INDF.FIRE_REQ = TRUE; // feuer_req schon mal vormerken
buf = LINK_RD(link,use);// zwei zellen feuern synchron
LINK_WR(link,use,++buf);// dadurch wird der link nuetzlicher
hit = TRUE; // zumindest ein nützlicher link
}
}
link += sizeof(struct _link);// nächsten link adressieren
} while(link < MAX_LINKS * sizeof(struct _link));
/** wenn kein nützlicher link gefunden und platz ist: erzeuge neuen link **/
if((!hit)&&(free != NO_LINK)) {
LINK_WR(free,src,zell_ind);// link neu erzeugen
buf = rand_link % sizeof(zellen); // randomize verlinken, aber eingrenzen
if(buf < FIRST_NEURON) buf += FIRST_NEURON; // keine read-only-zellen
if(buf == zell_ind) buf++; // zellen nicht direkt rückkoppeln
LINK_WR(free,dst,buf);
LINK_WR(free,use,0); // ob der link nützlich wird, weiß keiner
}
} while(++zell_ind < sizeof(zellen)); // nächste zelle
}
#define DELETE 0
#define REDUCE 1
#define SLEEP 2
static void gi_links(uns8 steuer) // links abschwächen
{
uns8 link = 0, buf;
do { // alle links durchsuchen
if(LINK_RD(link,src) != NO_LINK) { // gelöschte ignorieren
if(steuer == DELETE) buf = 0;
else {
buf = LINK_RD(link,use); // nuetzlichkeit lesen
if(buf) LINK_WR(link,use,--buf);// langsam verlernen
}
if(!buf) LINK_WR(link,src,NO_LINK);// link ganz löschen
}
link += sizeof(struct _link); // nächster link
} while(link < MAX_LINKS * sizeof(struct _link));
if(steuer == DELETE) rand_link = 0;
}
static void gi_zellen(uns8 steuer)// zell-erregungen abschwächen
{
uns8 zell_ind = 0;
do { // suche zellen
FSR = zellen + zell_ind; // zellerregung lesen
if(steuer == DELETE) INDF = 0;
if(INDF) INDF--;
if((steuer == SLEEP)&&(INDF > FIRE_LIMIT)) INDF = FIRE_LIMIT - 1;
} while(++zell_ind < sizeof(zellen));// nächste zelle
}
void main(void)
{
RP0 = 1; // erstmal alle Spezialregister...
#asm
DW /*CALL*/ 0x2000 + /*ADRESSE*/ 0x03FF // oscal abholen
#endasm
OSCCAL = W; // und Oszillatorkalibrierung speichern
OPTION = 0; // global weak-pullup ein (je 20kOhm)
WPU = _BV(IN_0) | _BV(IN_1) | _BV(IN_2) /*| _BV(IN_3)*/;
TRISIO = _BV(IN_0) | _BV(IN_1) | _BV(IN_2) | _BV(IN_3);// eingänge
RP0 = 0; // normale register und ram
CMCON = 0x07; // komparator aus
timeloop_init(); // timer initialisieren
gi_links(DELETE); // alle links löschen
gi_zellen(DELETE); // zellerregungen löschen
uns8 loop = 0;
FOREVER {
gi_denke();
if(!++loop) {
gi_links(REDUCE); // links langsam verlernen
gi_zellen(SLEEP); // schlafen
}
else if(!(loop & 0x1F)) // alle 32 durchläufe
gi_zellen(REDUCE); // zellerregungen vermindern
}
}
/* ENDE */
Wie gesagt: noch Kleinkram dran zu machen.
Viele Grüße
Wolfgang