Ein Framework für eventbasierte Programmierung

Es ist schon eine Weile her, als sich Ralf Westphal das letzte Mal hier Gedanken über eine EBC Infrastruktur gemacht hat. Allerdings hab ich einen gewissen Abscheu gegenüber LabView und derartigem grafischen Rumspielen. Man nehme doch einfach mal an man hätte 40 funktionale Abhängigkeiten als Methoden und Delegates und möchte die jetzt zusammenstecken. Da ist 40 noch gar nichts. Bei größeren Projekten hat man doch locker allein 40 Interfaces im UnityContainer ganz zu schweigen von den Methoden noch… Als XML Datei geh ich da schon mal noch mit(zum Beispiel so):

Ok, also das geht, ist aber natürlich nicht so schön wie nur die Klassen registrieren, weil man wesentlich mehr mit der Hand tippen muss, sieht aber noch ganz übersichtlich aus, weniger das hier:

Je mehr Abhängigkeiten hinzukommen, umso unübersichtlicher wird diese grafische Darstellung. Man stelle sich nur mal vor einen Fehler in einem Baum zu finden der 4 – oder noch mehr mals größer als der obige ist. Ehrlich, irgendwie ist das für mich nicht DIE Lösung. Aber: müssen wir denn alle Abhängigkeiten auf Methodenebene definieren? Womit ich sagen will: Müssen wir jede Zuordnung definieren? Ich glaube nein!

Wenn wir im UnityContainer Klassen registrieren und mit Injection Based Components arbeiten, dann registrieren wir doch auch nur die Service-Klassen, und nicht ihre Verbindungen zu den Clients. Sie werden dann von einem, oder mehreren Clients über eine Kontraktspezifikation das Interface gefunden.

Ok, aber das geht bei EBC´s doch auch,  nur nicht so offensichtlich. Der Kontrakt sind die Parameter auf der Methode oder dem Delegate. Wenn ein Client eine Action<string> verlangt passt dazu eben nur void(string parameter) und nicht zum Beispiel int(bool parameter, double parameter). Logisch.

Nur leider ist dieser Kontrakt noch nicht ganz eindeutig, es ist ja auch möglich mehrere Methoden mit gleichen Parametern und Rückgabetypen zu definieren. Also: Mein Vorschlag, und damit schlagen wir gleich zwei Fliegen mit einer Klappe: im Unity Container werden wieder ganz normal Klassen registriert(Service Klassen), diesmal eben als object, weil ja kein Interface bekannt ist, und die passenden Service-Methoden werden per Reflection ermittelt. Vorteile:

1. Die Methoden sind mit einer höheren Wahrscheinlichkeit eindeutig, weil sie jetzt auch noch durch den Klassen-schlüssel im Unity Container beschrieben werden, der auf dem Client optional mit angegeben werden kann

2. Der Konfigurationsaufwand verringert sich immens, weil Klassen und keine Methoden registriert werden

Super, das hört sich ja schon ganz gut an, aber: auch eine Klasse kann ja zwei Methoden mit gleichen Parametertypen definieren, nun nichts einfacher als das, wir geben einfach der Methode noch einen optionalen Schlüssel, der auch beim Client wieder mit angegeben wird. Die Implementierung, mach ich jetzt einfach mal ganz spontan über Attribute, weil die sich schön einfach handhaben lassen, also sieht ein Client jetzt zum Beispiel so aus:

[EBCImport]
public Func<DigitTable, NumberSystem, NumberSystem, DigitTable> ConvertDigitTable
{
    get;
    set;
}

[EBCImport]
public Func<int, DigitTable> IntToDigitTable
{
    get;
    set;
}

[EBCImport]
public Func<DigitTable, NumberSystem, int> DigitTableToInt
{
    get;
    set;
}

[EBCImport]
public Func<string, NumberSystem, DigitTable> StringToDigitTable
{
    get;
    set;
}

[EBCImport]
public Func<DigitTable, NumberSystem, string> DigitTableToString
{
    get;
    set;
}

Und ein Service könnte so aussehen:

[EBCExport]
public DigitTable BuildGivenFormattedNumber(string formattedNumber, NumberSystem format)
{
    ...
    return result;
}

[EBCExport]
public DigitTable BuildGivenInt(int value)
{
    ...
    return result;
}

Dann steht der Service normal im UnityContainer, einfach als object:

this.container.RegisterType<object, DigitTableBuilder>("DigitTableBuilder", new ContainerControlledLifetimeManager());

Was natürlich viel einfacher ist als Methode zu registrieren, es gäbe da ja allein an der Tatsache das Action<> und Func<> generisch sind Umständlichkeiten. So ist jetzt alles wie gewohnt, mit der Ausnahme, dass bei  einigen Fällen eben noch auf [EBCImport(string containerKey, string key)] und [EBCExport(string key)] zurückgegriffen werden muss, um Dopplungen zu vermeiden, ich denke aber das ist recht akzeptabel und auch überschaubar, weil normalerweise nicht viel Dopplungen vorkommen.
Um die Infrastruktur und Reflection braucht sich keiner (außer ich jedenfalls) mehr zu kümmern, weil ich die schon fertig hab und in Kürze zu Download zur Verfügung stellen werde(einfach mal hier vorbeischauen).
Zu beachten ist, dass eine Bootstrapper Klasse, die von EBCBootstrapper erbt, und einen Service Locator zurückgibt(der für den Unity Container liegt unter CDT.Infrastructure.EBC.Connections.Unity) zu erstellen ist, dessen Run Methode ganz am Anfang, bevor die EBC´s kommunizieren können aufgerufen werden muss. Diese Klasse muss in der überschriebenen Methode CreateContainer() alle Services und Clients im ServiceLocator registrieren, und natürlich:
Bitte die Attribute nicht vergessen sonst funktioniert das nicht(Achtung über die Klassen und nicht über die Interfaces setzen!). Natürlich ist das noch nicht die Endlösung weil sie im Moment nur Delegates und Methoden unterstützt(und keine eigenen Channels oder so), aber das lässt sich noch nachrüsten, und soweit erstmal viel Spaß mit den EBC´s!

Von Event- und Network Based Components – Softwarearchitektur mit Flow

Diesen Monat hat Ralf Westphal in der dotnetpro einen Artikel über Event Based Components veröffentlicht. Dazu mal ein bisschen auf seinem Blog rumgestöbert und das hier gefunden. Es geht mir dabei nicht um irgendeine Geschäftsanwendung oder sowas in der Art, sondern vielmehr um die Logik für ein Computerspiel. Die Leute, die sich in der Spieleentwicklung auskennen werden wissen, dass es da immer einen Game-Loop gibt. Also eine Update Methode, die immer aufgerufen wird. Jetzt mal weitergedacht: Was wenn ich an dieses Update eine Sammlung oder eine Schicht von EBC´s ranhänge, bei denen direkt irgendwelche Updates immer ausgeführt werden. Also ganz kleine Einheiten, wie zum Beispiel prüfen ob eine Taste gedrückt ist, und dann ein Event feuern. Das sind meine Inputs, also meine Update Einheiten. Dann aktivieren die logische Einheiten, wie zum Beispiel dann irgendeine Funktion, dass sich Spielfiguren bewegen und so weiter. Das ganze kann sich ja auch auf sehr viele Schichten von EBC`s aufteilen, die wieder miteinander verknüpft sind. Irgendwie erinnert mich das an neuronale Netze. Oder zumindest einfach  an ein Netz. Es werden inputs definiert, und dann steuert sich der Ablauf nur noch über Events, und dann? Ja dann könnte es zum Beispiel Draw Einheiten im Netz geben, die bei der Draw Methode abgefragt werden, und deren Zustand dann eben gezeichnet wird. Es war mir schon ewig ein Problem wie man eigentlich diese zwei Sachen Update und Draw verbindet, und dabei noch eine anständige Architektur hat. Eine Möglichkeit sind Game Objects oder Entities, die einfach alles vom Baum bis zum Schwertkämpfer darstellen als universelle Basisklasse mit universeller Funktionalität. Das lässt sich aber mit so einem Netz deke ich besser hinbekommen, man kann solche Sachen ja als größere zusammengefasste Funktionseinheiten implementieren, kein Problem… Ich stelle mit das ungefähr so vor:

So schön wandert jetzt der Flow hier durch, und auf ein Event können sich beliebig viele, oder auch gar niemand registrieren, man muss dann eben vor dem Aufruf prüfen ob eine Registrierung vorliegt. Ich nenne meine „neuen“ Bausteine jetzt Network Based Components(NBC`s). Hier bietet sich natürlich(wie von Ralf Westphal auch erkannt) eine schöne Möglichkeit asynchron anzusetzen. Warum denn nicht die Events asynchron verschicken, was heißt verschicken und Events, dann nehmen wir doch gleich Ports von der CCR(Concurrency and Coordination Runtime) und schicken uns die Nachrichten so, oder noch besser: AppSpace(auch von Ralf Westphal). Spiele einfach übers ganze Intenet laufen zu lassen mit dem einen Teil der Logik auf dem einen dem anderen Teil auf dem anderen Rechner. Mal sehen was draus wird, auf jeden Fall sehr spannend das Thema! Wahrscheinlich nur bedingt einsetzbar wenn es um Geschäftsanwendungen geht, aber hier schaut das richtig gut aus.

EDIT: Bitte mal hier nachschauen, ich möchte nicht sagen es ist die optimale Lösung, aber es macht vieles einfacher(zum Beispiel bei Geschäftslogik)

Viele Layer im deep belief network

In einem der letzten Posts war bereits  eine ganz einfache Restricted Boltzmann Machine das Thema, jetzt soll es darum gehen, wie mehrere Layer aussehen, und wie das Lernen implementiert wird.

1. Wie funktioniert eine RBM?

Wir müssen uns bei dem Umstieg von einem feed-forward Netz zur RBM zunächst mit einigen grundsätzlichen Dingen vertraut machen. Ein feed-forward Netz hat Eingabe- und Ausgabeunits, und beliebig viele versteckte zwischengeschaltete Schichten. Von der Optik her sieht die RBM ganz ähnlich aus, mit dem Unterschied, dass es prinzipiell nur 2 Schichten gibt, wobei die Units allerdings nicht nur in eine Richtung, sondern in beide Richtungen miteinander verkettet sind:

Eine RBM verwendet ausschließlich binäre Aktivierungsfunktionen, wobei beachtet werden muss, dass das ganze mit Wahrscheinlichkeiten rechnet, dazu aber später mehr. Das Netz berechnet sich aber jetzt nicht mehr so einfach wie feed forward(logisch), sondern die erste Schicht dient sowohl als Eingabe-, als auch als Ausgabeschicht, wobei wenn sich diese ändert, sich wieder die nachgeschaltete Schicht ändert, die wieder die erste Schicht beeinflusst u.s.w. Ein bestimmter Zustand des Netzes bezeichnet eine sogenannte Energie des Zustandes. Je geringer diese Energie umso wahrscheinlicher, dass das Netz in der Stellung verharrt, oder je größer die Energie umso größer die nachfolgende Änderung, ähnlich wie beim Hopfield – Netz. Die Energie berechnet sich aus den bestimmten Zuständen der Units und den Gewichten nach der Formel:

v = ist der Zustand aller input(visible) Units, also erste Schicht(normalerweise Vektor oder im Programm eine Liste),
h = der Zustand aller hidden Units(zweite Schicht),
w = Gewichtsmatrix
b = die bias Parameter der Units(könnte auch als eigenständige Bias Unit entwickelt werden, dann entspricht dieser Wert den Bias – Gewichten auf die Unit)

Es gibt also nicht mehr input und target Werte, sondern einfach nur noch input Werte, was die ganze Sache vereinfacht, das Netz lernt sich selbst ein. Der Lernvorgang bezeichnet also eigentlich das Vermindern der Energie für einen bestimmten Zustand, damit die Wahrscheinlichkeit höher ist, dass dieser am Ende herauskommt. Zu der Verknüpfung der gelernten Daten mit anderen Werten als den inputs kommen wir später mal. Jetzt ist unser Ziel erst mal die exakte Reproduktion gelernter Daten(d.h. ich lerne einen Zustand ein, und wenn der dann wieder kommt ändert sich das Netz nicht, d.h. der Zustand hat eine zu geringe Energie für eine Änderung)

Das tatsächliche Lernverfahren ist relativ einfach:

  • Als erstes werden neben den input und hidden Schichten noch zwei weitere Schichten erstellt, die wir nur zum Lernen brauchen. Wir erstellen also ein Netz was das Netz trainiert. Die neuen Schichten entsprechen den ersten Schichten, und auch die Gewichte sind immer dieselben. Dieses Kopieren hat den einzigen Grund, dass die Werte zwar reproduziert werden sollen, aber der Eingangsvektor unverändert bleiben soll, weil der dann später noch gebraucht wird. Man kann sich das auch so vorstellen, dass das Netz nur einmal ausgeführt wird, also die input Werte genau einmal auf die hidden, dann wieder auf die inputs, und dann noch mal auf die hidden propagiert werden, und zwischendurch alle Werte und Zwischenstellungen gemerkt werden. Sieht dann so aus:
  • Dann werden die Gewichte zufällig initialisiert(oder eben bereits vorhandene Gewichte von anderen Lernvogängen sind schon da), wobei alle drei Gewichtsmatrizen exakt gleich sind. Man schaue sich zur Verdeutlichung noch mal  das Bild weiter oben an. Man sieht, dass die Gewichte nach oben und unten übereinstimmen, und da der letzte sowieso  nur eine Kopie vom ersten ist sind die natürlich auch gleich.
  • Jetzt kommt das eigentliche Lernen(simpel): Der Eingabevektor wird auf die ganz linke untere Schicht draufgegeben, dann werden alle anderen Schichten berechnet. Wenn in den beiden linken Schichten zwei Units gleichzeitig aktiv(eine oben, eine unten) sind wird deren Gewicht um eine Lernrate erhöht, wenn dasselbe in der rechten der Fall ist wird das Gewicht um die Rate vermindert.

Fertig mit Lernen!

2. Wie funktioniert ein deep belief network?

Was ist der Unterschied zwischen einer RBM und einem deep belief network? Ganz einfach ein deep belief network ist eine Stapelung von RMB´s, das heißt ich nehme einfach die hidden Schicht von meiner RBM als input für die nächste RBM, die dann oben darüber gebaut wird. Dann nehm ich deren hidden wieder als input für die nächste, und so weiter… Das macht das Ganze wesentlich effizienter, weil wir viel mehr Regelmäßigkeiten/Features oder Muster oder wie man es auch immer nennen will erkennen können, weilwir in den Mustern wieder nach Mustern suchen. Klingt vielleicht kompliziert, ist es aber nicht. Am Lernen ändert sich überhaupt nichts. Es wird einfach erst die unterste RBM eingelernt, dann wird mit deren hidden die nächste trainiert, und die nächste und nächste…

Wäre natürlich langweilig wenn das alles über deep belief networks wäre, man kann die noch tunen, da kommt dann wieder mal die Backprop, aber das schreib ich jetzt nicht mehr hier rein. Also: Auch wenn Deutschland(leider) jetzt nicht ins WM Finale kommt: Das war erst der Anfang(und nicht das Ende, wie bei der WM)

Version 3 Neuron Net Builder – Performance

Die Ausführung biologischer Netze erfolgt über einen größeren Zeitrahmen, das heißt ein Neuron hat eine bestimmte Feuerfrequenz und muss sich erst wieder laden, ehe es erneut feuern kann. Der Vorteil dieser Vorgehensweise liegt auf der Hand. Wenn alle Neuronen sofort wenn sie sich ändern selbst feuern würden käme es sehr schnell zu einer Überlastung des Netzes bei vielen Neuronen.

Beispiel: 3 Neuronen in der ersten Schicht, 3 in der zweiten und 3 in der dritten, feed – forward netz

1. Das erste Input-Neuron feuert(bei asynchroner Ausführung ist die Wahrscheinlichkeit, dass zwei Neuronen exakt gleichzeitig feuern nahezu gleich null), es wirkt auf die nächsten drei Neuronen ein die ihrerseits wieder feuern, und jedes von diesen zieht eine Aktualisierung der letzten drei Neuronen mit  sich(das heißt es finden 1 + 3 + 3*3 Aktualisierungsvorgänge statt, erstes Neuron, alle in der nächsten Schicht, 3 mal alle in der letzten Schicht)

2. Die beiden anderen Input-Neuronen feuern ebenfalls

3. Die Anzahl der Aktualisierungen beträgt also 3*(1 + 3 + 3*3) = 39, obwohl eigentlich nur für jedes Neuron eine Aktualisierung nötig wäre(also insgesamt 9)

man kann sich sicherlich vorstellen, dass bei einer höheren Anzahl der Neuronen die Anzahl der Aktualisierungen ins Unermessliche ansteigt, die Formel für diese Aktualisierungen wäre nämlich(ganz einfach) wenn a die Anzahl der Neuronen in der ersten ,b die in der zweiten und c die in der dritten ist:

ges = a + ab + abc

Und mit einer Aktualisierung nur: ges = a + b + c

Allgemein für eine bestimmte Anzahl von n Schichten, wenn u ein n dimensionaler Vektor ist, der die entsprechenden Anzahlen der Neuronen in den Schichten beschreibt:

Es gilt also einen Algorithmus zu finden, der ohne das zusätzliche Produkt auskommt, also ledeglich alle Werte einmal aktualisiert werden, und so sehr viel Rechenzeit gespart wird. In der Version 3 wird es nun so sein, dass  das Netz nicht von vorn sondern von hinten her aktualisiert wird. Das heißt im Klartext: Ich such mir eine Unit aus deren geauen Wert ich wissen will, und ruf auf der eine Methode auf, die asynchron ausgeführt wird, und irgendwann das OutputChanged Ereignis aufruft, dann ist der Outputwert dieser Unit, und der aller Units von denen diese abhängt aktualisiert. Ich brauch also  auch nicht immer den ganzen Baum zu aktualisieren sondern  kann eine selektive Aktualisierung durchführen, wenn ich das möchte. Jetzt passiert intern folgendes:

Die Unit, die aktualisiert werden soll ruft auf allen Units von denen sie abhängt auch die Aktualisierungsmethode auf, registriert sich auf deren OutputChanged Event, und wenn die alle fertig sind berechnet sie ihren eigenen Ausgabewert und ruft ihr OutputChanged Ereignis auf. Die Input Units geben dann einfach konstant ihren Wert aus und schicken die Nachricht an keine weiteren Units weiter.

Problem: Wenn zwei Units von derselben Unit abhängen wird diese doppelt aktualisiert(also das gleiche Problem wie oben). Die Lösung ist sehr simpel. In der Aktualisierungsmethode wird einfach noch eine eindeutige ID als Parameter mitgegeben, und die Unit merkt sich diese. Wenn dann der Aufruf noch einmal mit der gleichen ID ankommt gibt sie einfach den Wert vom letzten Pass zurück. Ich muss also immer eine neue ID zum Aktualisieren nehmen, wenn sich irgendwas geändert hat. Ansonsten ist das Verfahren sehr performant und einfach zu handhaben. Es entspricht im Prinzip der Umkehrung der biologischen Aktualisierung.

Aktualisierung einer Augabeunit:

Selektive Aktualisierung: