Beleg Neuroinformationsverarbeitung

 

 

 

 

 

 

 

Analyse von Mausgesten

mittels eines neuronalen Netzes

 

 

 

 

 

 

 

 

Name:

Robert Hahn

Matrikelnummer:

8136

Seminargruppe:

96/041/02

Email:

htw8136@htw-dresden.de

1      Benutzerdokumentation

 

1.1   Überblick

 

Die Aufgabe des Programms Mouse Gesture Recognizer ist das Erkennen von speziellen Mausbewegungen (sogenannten Gesten) des Benutzers und deren Zuordnung zu bekannten Mustern.

 

Diese Technik, die vorallem mit dem Webbrowser Opera populär wurde, inzwischen aber auch in einigen anderen Applikationen verwendet wird, eignet sich für die Entwicklung von intuitiv bedienbaren Benutzerschnittstellen.

 

Da es sich bei Mausbewegungen um relativ ungenaue Eingabedaten handelt bietet sich die Verwendung eines neuronalen Netzes an zu ihrer Erkennung an.

 

Die Funktionalität des neuronalen Netzes sowie der Aufzeichnung von Bewegungsmustern der Maus wurden in eigene Bibliotheken gekapselt und können auch in anderen Anwendungen verwendet werden. Zu Demonstrations- und Testzwecken wird eine Beispielanwendung mitgeliefert die auch als Basis für eigene Entwicklungen verwendet werden kann.

 

 

1.2   Arbeitsweise

1.2.1   Erfassung und Aufbereitung der Eingabedaten

 

Der Anwender zeichnet, meist bei gedrückter rechter Maustaste, Figuren direkt auf die Programmoberfläche. Diese Muster werden anschliessend analysiert und können in Aktionen umgesetzt werden. So kann im einfachsten Fall, durch eine Bewegung nach links oder rechts in einem Browser vorwärts bzw. rückwärts geblättert werden. Natürlich ist auch die Verwendung komplexerer Muster wie Buchstaben möglich, um eine Vielzahl verschiedener Programmfunktionen zu steuern.

 

Zu beachten ist dabei allerdings, dass die zu lernenden Gesten ausreichende Unterscheidungsmerkmale haben sollten um gute Ergebnisse zu erzielen, da Mausbewegungen in der Regel sehr schnell und nicht sonderlich exakt durchgeführt werden.

 

Aus diesem Grund kann in echten Anwendungen oft auch auf eine grafische Darstellung der Gesten auf dem Bildschirm verzichtet werden. Im vorliegenden Programm ist eine Visualisierung aber natürlich vorhanden.

 

 

 

Im Gegensatz zu OCR Texterkennungssystemen, werden die Mausgesten nicht als ein aus Pixeln bestehendes Bild analysiert. Stattdessen wird eine aufgezeichnete Geste in eine Menge von Teilstrecken zwischen einer konfigurierbaren Anzahl von Stützpunkten zerlegt. Die Ausrichtungen der Teilstrecken im Koordinatensystem ergeben das charakteristische Muster der Geste.

 

Die Winkel werden nach der Formel  mit  und berechnet.

 

Eine Bewegung senkrecht nach oben ergibt einen Winkel von 0°, nach rechts 90° und eine Bewegung nach unten 180°.

 

Aufgrund dieser Vorgehensweise ist es wichtig zu beachten, dass zum Beispiel ein im Uhrzeigersinn gezeichneter Kreis eine völlig andere Geste darstellt als ein entgegengesetzt gezeichneter Kreis, obwohl beide geometrisch die selbe Figur darstellen.

 

Auch der relative Start- und Endpunkt der Bewegung sind von Bedeutung (z.B. ob man die Kreisbewegung oben oder unten beginnt). Die absolute Position der Geste innerhalb des Fensters hat aber keinen Einfluss auf das Ergebnis. Ein großer Vorteil der Verwendung von Winkeln als Eingabedaten ist auch die Unabhängigkeit von der Skalierung. Es ist also für die Erkennung der Geste völlig egal ob ein Kreis mit einem Durchmesser von 5 oder 10 cm gezeichnet wurde.

 

Beispiel:

 

Für einen oben begonnenen, entgegen dem Urzeigersinn gezeichneten Kreis könnten sich bei 15 Stützpunkten folgende Winkel (in Grad) ergeben:

280, 259, 226, 209, 193, 147, 99, 78, 57, 18, 355, 330, 302, 270,

 

ein Kreis im Urzeigersinn dagegen könnte folgende Werte ergeben:

98, 120, 154, 193, 228, 259, 273, 319, 340, 5, 35, 47, 75, 90.

 

Bei der Aufzeichnung einer Bewegung darf die Maus nicht abgesetzt werden, zwischen aufeinanderfolgenden Punkten besteht  also immer eine Verbindung. Bei einigen Figuren wie dem Buchstaben X kann dies zu Problemen führen so dass solche Gesten entweder vermieden oder durch eine alternative Schreibweise ersetzt werden müssen.

 

 

1.2.2   Mustererkennung mit neuronalen Netzen

 

Unter Verwendung eines neuronalen Netzes sollen die aufgezeichneten Bewegungen zuvor gelernten Mustern zugeordnet werden. Neuronale Netze eignen sich gut für die Lösung dieser Problemstellung, da es relativ schwierig ist mit einer Maus Muster exakt zu reproduzieren und daher der Algorithmus zur Erkennung fehlertolerant und lernfähig sein muss.

 

Es wird ein Backpropagation Netz mit einer Eingabeschicht, einer oder mehreren verdeckten Schichten und einer Ausgabeschicht verwendet. Um einen extremen Sprung der Eingabewerte zwischen 0° und 360° zu verhindern wird statt des Winkels in Grad jeweils seine Sinus und Consinus Komponente verwendet. Die Anzahl der Eingabeneuronen ergibt sich also aus (Anzahl der Stützpunkte – 1) * 2, wobei die Menge der Stützpunkte konfigurierbar ist.

 

Die Anzahl der Ausgabeneuronen ergibt sich aus der Anzahl der bekannten Gesten. So hätte ein Netz welches in der Lage ist die Gesten Hoch, Runter, Links, Rechts und einen Kreis zu erkennen 5 Ausgabeneuronen. Anhand der Ausgabewerte dieser Neuronen lässt sich die Wahrscheinlichkeit bestimmen mit der ein Muster erkannt wurde.

 

In diesem Beispiel wurde eine Bewegung nach links sehr eindeutig erkannt:

 

 

1.3   Neuronale Netze

1.3.1   Grundlagen

Unter neuronalen Netzen versteht man Algorithmen der Informatik die sich an der biologischen Funktionsweise des menschlichen Gehirns orientieren. Das Ziel bei der Verwendung von neuronalen Netzen ist es meist auf eine fehlertolerante Weise aus ungenauen Eingabedaten die gewünschten Ausgabewerte zu erhalten. Für die zu lösenden Probleme sind meist keine mathematischen Algorithmen bekannt oder diese lassen sich aufgrund der ungenauen Eingabedaten nicht anwenden. Beispiele für den Einsatz von neuronalen Netzen sind die Texterkennung oder biometrische Verfahren zum Erkennen menschlicher Gesichter oder Fingerabdrücke.

 

Das Hauptmerkmal von neuronalen Netzen ist ihre Lernfähigkeit. Ohne explizite Programmierung des benötigten Algorithmus können neuronale Netze diesen anhand von Trainingsdatensätzen erlernen.

 

Das Grundelement eines neuronalen Netzes ist das künstliche Neuron, ein einfaches Prozessorelement welches dem biologischen Neuron nachempfunden wurde. Die Neuronen sind über gerichtete Verbindungen, die den biologischen Synapsen entsprechen, verbunden und können sich so untereinander aktivieren. Während des Lernvorgangs werden die Gewichte der Verbindungen angepasst.

 

 

 

 

Die Aktivierung eines Neurons ergibt sich indem die Aktivierungsfunktion auf die Netto-Eingabe (welche sich aus der Summe der Produkte der Ausgabewerte der Vorgängerschicht mit den jeweiligen Verbindungsgewichten errechnet) angewendet wird.

 

Während des Lernvorgangs modifiziert sich ein neuronales Netz selbst. Die Veränderungen erfolgen anhand einer vorgegebenen Lernregel. Meist werden dabei die Gewichte der Verbindungen angepasst. Aber auch strukturelle Veränderungen wie das Hinzufügen oder Entfernen von Neuronen und Verbindungen sind möglich.

 

1.3.2   Verwendete Algorithmen

 

Für Mouse Gesture Recognizer wurde ein ebenenweise verbundenes Feedforward-Netz verwendet. Dabei besteht das Netz aus mehreren Schichten wobei es nur Verbindungen von Schicht zu Schicht in einer Richtung gibt. Jedes Neuron ist mit allen Neuronen der darauffolgenden Schicht verbunden.

 

 

 

Das Netz besteht aus einer Eingabeschicht (Input), einer oder mehreren verdeckten Schichten (Hidden) und einer Ausgabeschicht (Output). Aufgrund der Mehrschichtigkeit ist es möglich auch komplexere Zusammenhänge zwischen Ein- und Ausgängen zu lernen. In den meisten Fällen sollte die Verwendung von nur einer verdeckten Schicht ausreichend sein.

 

Die Eingabeschicht besitzt keine Eingangsgewichte und reicht die Eingabe an die darauffolgenden Schichten weiter.

 

Das verwendete Netz besitzt ein konstantes Bias Neuron (Wert 1.0) das mit allen Neuronen der verdeckten Schicht und der Ausgabeschicht verbunden ist. Die Verbindungen verfügen ebenfalls über veränderliche Gewichte und fungieren als Aktivierungsschwellwerte der Neuronen in den verbundenen Schichten.

 

In der Hidden- und der Outputschicht errechnen sich die Eingabewerte der Neuronen aus der Ausgabe aller verbundenen Neuronen in der vorherigen Schicht multipliziert mit den Verbindungsgewichten.

 

Die Gewichte der Verbindungen werden beim Erstellen des Netzes mit zufälligen Werten zwischen -1.0 und 1.0 initialisiert und werden während des Lernvorgangs anhand einer Lernregel modifiziert.

 

Als Lernregel wird der Backpropagation Algorithmus eingesetzt. Dabei wird iterativ die Konfiguration des Netzes mit der minimalen Fehlersumme über alle Trainingsmuster ermittelt.

 

Dem Netz werden während des Lernvorgangs in mehreren Schritten die Trainingseingabemuster präsentiert. In Abhängigkeit vom auftretenden Fehler (d.h. der Falschheit der errechneten Netzausgabe im Vergleich zu den erwarteten Werten) werden die Gewichte der Verbindungen über eine rückwärtsgerichtete Fehlerweitergabe modifiert. Da die erwarteten Ausgabewerte bekannt sind spricht man von überwachtem Lernen.

 

Durch die Anpassung der Gewichte sollte bei erfolgreichem Lernen der Fehler im nächsten Iterationsschritt geringer ausfallen.

 

Ziel ist es das globale Minimum der Fehlerfunktion zu finden, was allerdings nicht immer möglich ist da die Gefahr besteht dass der Fehlerwert um ein lokales Minimum oszilliert oder ein Tal der Fehlerfunktion nicht mehr in Richtung des globalen Minimums verlassen werden kann.

 

Ablauf des Lernvorgangs:

 

  1. Im Forward Pass wird dem Netz ein beliebiger Trainingsvektor präsentiert. Schicht für Schicht durchlaufen die Daten nun das Netz indem in jedem Neuron die Aktivierung mittels der Aktivierungsfunktion und die Ausgabe mittels der Ausgabefunktion ermittelt werden.

    Als Aktivierungsfunktion wird hierbei die sigmoide Funktion verwendet.

    Die Ausgabefunktion entspricht der Aktivierungsfunktion.

 

  1. Unter Verwendung der bekannten Ausgabemuster wird nun durch die Fehlerfunktion der Fehler des Netzes (der quadratischer Fehler über alle Musterpaare) bestimmt. Unterschreitet dieser eine festgelegte Schwelle wird das Training erfolgreich beendet.

Der Fehler der einzelnen Neuronen berechnet sich abhängig von der jeweiligen Schicht mittels:

         für die Outputschicht

   für Input- und Hiddenschicht

Die zweite Abbruchbedingung ist meist die maximale Anzahl von Lernschritten, nach deren Erreichen allerdings nicht immer ein akzeptables Ergebnis vorliegen muss.

 

 

  1. Im Backward Pass werden ausgehend von der Ausgabeschicht über die Hiddenschicht bis hin zur Eingabeschicht die Gewichte anhand der Lernregel verändert. Als Lernregel wird die generalisierte Form der Delta Regel eingesetzt.


Lernrate

Fehler des Elements i

Output des Elements j

Momentum

Gewichtsänderung des vorherigen Schrittes

 

Ein möglichst großer Wert der Lernrate resultiert in einem schnellen Absinken des Fehlerwertes, wodurch allerdings die Gefahr steigt dass der Fehlerwert über das Minimum der Fehlerfunktion hinausläuft.

 

Der Momentumfaktor bestimmt wie stark der Wert der alten Gewichtsänderung in das neue Ergebnis einfliesst.

 

1.4   Installation

1.4.1   Voraussetzungen

Mouse Gesture Recognizer ist auf allen Windows Systemen ab Windows 98 mit installiertem .NET Framework lauffähig.

 

Vor der Installation des eigentlichen Programms muss zunächst die .NET Laufzeitumgebung auf dem Zielsystem installiert werden falls dies nicht bereits zuvor geschehen ist. Das .NET Framework enthält die Laufzeitumgebung sowie Kommandozeilencompiler und wird von Microsoft kostenlos angeboten (z.B. unter http://www.microsoft.com/net).

 

Zum Ausführen und Compilieren von Programmen ist das .NET Framework ausreichend. Zum Entwickeln eigener Applikationen empfiehlt es sich allerdings das ebenfalls kostenlose .NET SDK (zusätzliche Dokumentationen und Tools) oder das kommerzielle Visual Studio .NET zu verwenden.

 

Sollte Visual Studio .NET nicht verfügbar sein kann auch die kostenlose Entwicklungsumbegung Sharp Develop (http://www.icsharpcode.net) verwendet werden, die sich allerdings zur Zeit noch in der Entwicklung befindet.

 

1.4.2   Installation des Programms

Die Installation Anwendung erfolgt menügesteuert durch den Aufruf von Setup.exe. Die Dateien werden in das ausgewählte Verzeichnis kopiert, ansonsten werden keinerlei Änderungen wie z.B. Registry-Einträge am System vorgenommen.

 

Zum Starten der Anwendung muss nach erfolgreicher Installation die Datei GestureDemo.exe aus dem Zielverzeichnis gestartet werden.

 

Alternativ kann auch der Quelltext selbst kompiliert und das Programm direkt aus Visual Studio .NET gestartet werden.

 

1.4.3   Deinstallation des Programms

Zur Deinstallation genügt es den erstellten Ordner zu löschen, es ist aber auch möglich den Software Dialog der Windows Systemsteuerung zu verwenden.

 

1.5   Bedienung

1.5.1   Übersicht

Die Oberfläche des Programms gliedert sich in eine Menüleiste, ein Property Grid zur Konfiguration und Datendarstellung, eine Combobox zur Auswahl der aktuellen Geste mit einem Button zum Hinzufügen neuer Patterns, eine Statuszeile sowie einer freien Fläche in der die Gesten gezeichnet werden können.

 

 

1.5.2   Die Menüs

1.5.2.1     Das File Menü

Das File Menü dient neben dem Beenden des Programms dem Laden und Speichern von Daten-Dateien, sowie dem Importieren oder Exportieren von Gesten-Dateien.

 

Die Dateien mit der Endung .data enthalten neben den aufgezeichneten Gesten auch das komplette Neuronale Netz inklusive der aktuellen Gewichte sowie die Konfigurationsoptionen, während die Dateien mit der Endung .gesture nur die aufgezeichneten Gesten enthalten. Zum Speichern und Laden wird der Serialisierungsmechanismus von .NET verwendet.

 

1.5.2.2     Das Neural Network Menü

Dieses Menü enthält die Kommandos zum Starten und Beenden des Lernvorgangs, zum Erkennen des aktuellen Musters, sowie zum Zurücksetzen der Gewichte des Netzes auf Zufallswerte.

 

1.5.2.3     Das Gestures Menü

Über dieses Menü können die aufgezeichneten Gesten komplett gelöscht werden, wobei wahlweise nur die Patterns oder auch die Einträge für den Namen der Geste gelöscht werden.

 

1.5.2.4     Das Help Menü

Dieses Menü enthält momentan nur einen About Dialog.

 

1.5.3   Die Konfiguration

Für die Konfiguration des Programms wird das sehr praktische PropertyGrid Control verwendet, welches es ermöglicht auch umfangreiche Eigenschaften von Objekten darzustellen und zu editieren ohne spezielle Dialoge zu entwerfen die bei einer Änderung des Objekts auch entsprechend angepasst werden müssten.

 

Folgende Eigenschaften sind verfügbar:

 

Eigenschaft

Beschreibung

KnownGestures

die Liste mit bekannten Gesten. Ermöglicht das Hinzufügen und Löschen von Gesten und den Zugriff auf  die jeweiligen Patternlisten

LastPattern

das zuletzt aufgezeichnete Mausbewegungsmuster

NeuralNetwork

die Optionen des Neuronalen Netzes

AnchorPointNumber

die Anzahl der Stützpunkte die eine Geste repräsentieren

MouseButton

die für das Aufzeichnen von Gesten verwendete Maustaste

ShowAnchorPoints

bestimmt ob die Knotenpunkte der Geste nach dem Aufzeichnen angezeigt werden sollen

AnchorPen

die Visualisierungsoptionen der Stützpunkte

DrawingPen

die Visualisierungsoptionen für das Zeichnen der Gesten

 

1.5.4   Beispiel

Anhand der folgenden Beispielsession soll die Verwendung der Programms demonstriert werden.

 

  1. Anlegen der zu erlernenden Gesten über die Eigenschaft KnownGestures im PropertyGrid. Zum Aufklappen des Editors muss auf den [...] Button gedrückt werden. Dadurch wird ein neuer Konfigurationsdialog geöffent:



    Die Anzahl der Gesten ist beliebig. In diesem Beispiel soll mit den Gesten Hoch, Runter, Rechts, Links, Kreis und Dreieck gearbeitet werden.

 

  1. Anschliessend müssen für jede Geste einige Trainingsdaten erstellt werden. Dazu wählt man in der Combobox die gewünschte Geste aus, zeichnet mit gedrückter rechter Maustaste eine Bewegung auf und fügt sie über den Add Pattern Button (oder Alt+A) der Patternliste der jeweiligen Geste hinzu.


    Sollte versehentlich ein falsches Muster hinzugefügt worden sein kann dieses auch wieder gelöscht werden über die Patterns-Collection der jeweiligen Geste im Editor von KnownGestures.

 

  1. Nachdem eine ausreichende Menge von Trainingsdaten aufgezeichnet wurde kann mit dem Training begonnen werden.

 

  1. Das Training kann über den Menüpunkt Neural Network / Training oder über die Tastenkombination Control+T  gestartet werden. Der Verlauf des Trainings lässt sich in der Statuszeile beobachten.

 

  1. Nach Abschluss des Trainings kann das Ergebnis getestet werden indem Gesten gezeichnet werden und versucht wird diese über den Menüeintrag Neural Network / Recall oder die Tastenkombination Control+R erkennen zu lassen.

 

 

2      Entwicklerdokumentation

2.1   Das .NET Framework und C#

2.1.1   Übersicht

Für die Entwicklung von Mouse Gesture Recognizer wurde die Programmiersprache C# (gesprochen als C Sharp), die im Rahmen von Microsofts .NET Initiative entstanden ist, verwendet.

 

2.1.2   Microsoft .NET

Bei .NET handelt es sich um eine umfassende Strategie von Microsoft zur modernen objektorientierten Softwareentwicklung mit der auf längere Sicht bisherige Technologien wie COM und ActiveX bei der Entwicklung von Windows Anwendungen abgelöst werden sollen.

 

Neben einigen Serverprodukten und Dienstleistungen versteht man unter der .NET Plattform vorallem das .NET Framework welches die Laufzeitumgebung CLR (Common Language Runtime) sowie die umfangreiche Klassenbibliothek FCL (Framework Class Library) umfasst.

 

Das Konzept ist vergleichbar mit Java,  ist aber im Gegensatz dazu nicht an eine bestimmte Programmiersprache gebunden. Alle Compiler erzeugen eine, Intermediate Language (IL oder MSIL) genannte Zwischensprache, die zur Laufzeit durch einen Just In Time Compiler übersetzt wird. Microsoft liefert die Sprachen C#, VB.NET und Managed C++ mit, inzwischen ist aber eine Vielzahl anderer Programmiersprachen für die .NET Platform umgesetzt worden. Innerhalb eines Projektes können mehrere Sprachen auf Assembly-Ebene gemischt werden. Unter einer Assembly versteht man eine eigentständige .NET Komponente die entweder ein ausführbares Programm oder eine Bibliothek sein kann.

 

Teile von .NET sowie die Programmiersprache C# sind inzwischen ein ECMA Standard wodurch alternative Implementierungen z.B. für Linux durch das Mono Projekt (http://www.go-mono.com) möglich wurden.

 

2.1.3   C#

Die wahrscheinlich bedeutendste, und für dieses Projekt vewendete, Programmiersprache für die .NET Platform ist C#. Die Sprache C# wurde eigens für .NET entwickelt und erinnert von der Syntax her stark an Java und teilweise an C++ wurde aber um viele sinnvolle Features wie Properties, Indexer, typsichere Enums und ein gutes Event-Handling erweitert.

 

2.2   Aufbau des Projekts

2.2.1   Grundlegender Aufbau

Die Grundlage des Projekts bildet die Bibliothek NeuralNetworkLibrary, welche die Funktionalität eines neuronalen Netzes kapselt. Es wurde Wert auf die Erweiterbarkeit gelegt, so dass es ohne größeren Aufwand möglich sein sollte andere Netztypen oder Lernalgorithmen neben dem mitgelieferten Backpropagation Algorithmus zu implementieren.

 

Aufbauend auf dieser Funktionalität kümmert sich die Bibliothek GestureRecognition um das Aufzeichnen von Mausbewegungen sowie das Aufbereiten der Daten für das neuronale Netz. Durch Einbindung dieser Bibliothek können eigene Anwendungen mit der Gestenerkennung ausgestattet werden.

 

Zur Demonstration der Funktionalität dieser Bibliothken wird das Programm GestureDemo mitgeliefert welches dem Anwender umfangreiche Konfigurationsmöglichkeiten für eigene Experimente zur Verfügung stellt.

 

 

2.2.2   Auszüge aus dem Quelltext

2.2.2.1     Berechnung der Ausgabe eines Neurons

 

Die Methode ComputeOutput der Klasse Neuron berechet die Ausgabe eines Neurons in Abhängigkeit von den eingehenden Verbindungen unter Verwendung von konfigurierbaren Funktionen für Eingabe, Aktivierung und Ausgabe.

 

/// <summary>

/// Computes the output of the neuron.

/// </summary>

public void ComputeOutput()

{

if (this.inputFunction != null && this.inputConnections.Count > 0)

       {

              // call the input function to compute the net input from

              // the incoming connections

              this.netInput = this.inputFunction.Compute(this.inputConnections);

       }

       else

       {

              // input layer (no input connections, use external input instead)

              this.netInput = this.input;

       }

       // add bias

       if (this.isUsingBias)

{

              this.netInput += this.biasWeight;

       }

                    

       // compute activation

       if (this.activationFunction != null) 

       {

              this.activation = this.activationFunction.Compute(this.netInput);

       }

       else

       {

              this.activation = this.netInput;

       }

                    

       // compute output

       if (this.outputFunction != null) 

       {

              this.output = this.outputFunction.Compute(this.activation);

       }

       else

       {

              this.output = this.activation;

       }

}

 

 

 

 

 

Um eine flexible Wahl der Funktionen zu ermöglichen wurde mit Interfaces gearbeitet, so dass verschiedene Implementierungen ohne größere Änderungen am Programm verwendet werden können.

 

/// <summary>

/// Provides an interface of an input function.

/// </summary>

public interface IInputFunction

{     

       double Compute(ConnectionList input);

}

 

 

/// <summary>

/// Provides an interface for activation functions.

/// </summary>

public interface IActivationFunction

{

       string Description { get; }      

       double Compute(double input);

       double ComputeDerivation(double input);

}

 

 

Implementierung der sigmoiden Funktion:

 

/// <summary>

/// Provides a sigmoid activation function.

/// </summary>

[Serializable]

public class SigmoidFunction : IActivationFunction

{

       #region Fields

       private double gain;

       #endregion

      

       #region Properties

       /// <summary>

       /// Gets the description of the function.

       /// </summary>

       public string Description

       {

              get { return "Sigmoid"; }

       }

 

       /// <summary>

       /// Gets or sets the gain factor of the sigmoid function.

       /// </summary>

       public double Gain

{

              get { return this.gain; }

              set { this.gain = value; }

       }

       #endregion

             

       #region Methods

       public double Compute(double input)

       {

              return (double) (1.0 / (1.0 + Math.Exp(-this.gain * input)));

       }

             

       public double ComputeDerivation(double input)

       {

              double y = Compute(input);

              return (y * (1 - y));

       }

       #endregion

}

 

 

2.2.2.2     Feedback Pass

 

In diesem Schritt wird ausgehen von der Ausgabeschicht der Fehler durch das Netz propagiert. Die aktuelle Gewichtsänderung mittels der Delta Regel errechnet. Die Gewichtsänderungen des aktuellen Schritts werden jeweils gespeichert da sie in die Berechnung der nächsten Schritts mit eingehen.

 

/// <summary>

/// Runs a feeds backward pass through the network.

/// </summary>

/// <param name="targetOutput">The target output vector.</param>

public void FeedBackward(double[] targetOutput)

{

       for (int l = layers.Count - 1; l > 0; l--)

       {

              // output layer

              if (l == layers.Count - 1)

              {

                     // calculate errors for output layer

                     for (int n = 0; n < this.layers[l].Count; n++)

                     {                                                            

this.layers[l][n].ComputeError(targetOutput[n]);

                     }

              }

              else

              {

                     foreach (Neuron n in this.layers[l])

                     {

                           n.ComputeError();

                     }

              }

                          

              // adjust weights for connections to each neuron in the

              // current layer

              foreach (Neuron n in this.layers[l])

              {

                     double weightChange;

                     foreach (Connection c in n.InputConnections)

                     {

                           // delta rule

                           weightChange = this.learnRate * n.OutputError *

c.OtherNeuron.Output + this.momentum *

c.LastWeightChange;

 

                           c.Weight += weightChange;

                           c.LastWeightChange = weightChange;                                   }

                     if (n.IsUsingBias)

                     {

                           // adjust bias

                           weightChange = this.learnRate * n.OutputError +

                                  this.momentum * n.LastBiasChange;

                           n.BiasWeight += weightChange;

                           n.LastBiasChange = weightChange;

                     }

              }

       }

}

 

 

2.2.2.3     Training

 

Die Methode Train startet das Training des neuronalen Netzes mit den übergebenen Trainingsmustern. Der Lernvorgang wird abgebrochen wenn ein akzeptables Fehlerminimum oder die maximale Anzahl von Lernschritten erreicht ist. In jedem Durchlauf werden dem Netz jeweils alle Trainingsmuster präsentiert die das Netz vorwärts und rückwärts durchlaufen. Anschliessend wird das Fehler berechnet.

 

Über die Events StepFinished und LearningFinished wird die Aussenwelt über den Verlauf des Lernvorgang informiert.

 

/// <summary>

/// Trains the neural network using the given patterns.

/// </summary>

/// <param name="patterns">An array of training patterns.</param>

public void Train(ITrainingPattern[] patterns)

{

       this.currentError = 100;

       while ((this.currentIteration < this.maximumIterations) &&

              (this.currentError > this.errorLimit))

       {

              this.currentIteration++;

              double error = 0.0;

              foreach (ITrainingPattern p in patterns)

              {

                     FeedForward(p.Input);

                     FeedBackward(p.Output);

                     foreach (Neuron n in this.layers[this.layers.Count - 1])

                     {

                           error += Math.Pow(n.OutputError, 2);

                     }

              }

              this.currentError = error;

              // trigger event

              if (this.StepFinished != null)

              {

                     this.StepFinished(this, new NeuralNetworkEventArgs(

                            this.currentIteration, this.currentError));

              }

       }

       this.currentIteration = 0;

       if (this.LearningFinished != null)

       {

              this.LearningFinished(this, null);

       }

}

 

 

2.2.2.4     Berechnung der Stützpunkte der Geste aus der aufgezeichneten Mausbewegung

 

Bei der Aufzeichnung von Mausbewegungen erhält keine feste Anzahl von Punkten da diese abhängig von der Geschwindigkeit der Bewegung ist.

Da das neuronale Netz aber Muster von einheitlicher Größe erwartet müssen die Trainingsdaten von der Methode UnifyPath der MouseMovement Klasse zuvor aufbereitet werden.

 

Sind mehr Punkte als benötigt vorhanden werden solange die beiden am nähesten beieinanderliegenden Punkte durch einen einzelnen neuen Punkt ersetzt bis die gewünschte Maximalzahl erreicht wurde.

 

Sind zuwenig Punkte vorhanden werden solange neue Punkte eingefügt bis der geforderte Mindeswert erreicht wurde.

 

/// <summary>

/// Adds or removes points from the path until the wanted number of

/// points is reached, so all paths have the same number of anchor

/// points.

/// </summary>

public void UnifyPath(int anchorPointNumber)

{

       Point   p1  = Point.Empty;

       Point   p2  = Point.Empty;

       double  d   = 0.0; // distance

 

       if (anchorPointNumber < 1) return; // abort

             

       // there are too much points so the number has to be reduced

       while (this.path.Count > anchorPointNumber)

{

              Point   p1min  = Point.Empty;

              Point   p2min  = Point.Empty;

              double  dmin   = Double.MaxValue;

 

              // search for the 2 points with the shortest line, calculate

              // the mean and replace the 2 points with the new one

              // (always keep the first and the last point).

              for (int i = 1; i < this.path.Count - 2; i++)

              {

                     p1 = (Point) this.path[i];

                     p2 = (Point) this.path[i + 1];

                     d = Math.Sqrt(Math.Pow(p1.X - p2.X, 2) +

                     Math.Pow(p1.Y - p2.Y, 2));

                     if (d < dmin)

                     {

                           dmin = d;

                           p1min = p1;

                           p2min = p2;

                     }

              }

              p2min.X = (p1min.X + p2min.X) / 2;

              p2min.Y = (p1min.Y + p2min.Y) / 2;

              // System.Console.WriteLine("Removing point " + i);

              this.path.Remove(p1min);

       }

 

       // there are too few points so the number has to be increased

       while (this.path.Count < anchorPointNumber)

       {

              Point   p1max        = Point.Empty;

Point   p2max        = Point.Empty;

              int     insertIndex  = 0;

              double  dmax         = 0.0;

 

              // search for the longest distance between 2 points and add

              // a new point between them

              for (int i = 0; i < this.path.Count - 1; i++)

              {

                     p1 = (Point) this.path[i];

                     p2 = (Point) this.path[i + 1];

                     d = Math.Sqrt(Math.Pow(p1.X - p2.X, 2) +

                           Math.Pow(p1.Y - p2.Y, 2));

                     if (d > dmax)

                     {

                           dmax = d;

                           p1max = p1;

                           p2max = p2;

                           insertIndex = i + 1;

                     }

              }

              Point pnew = new Point(

                     (p1max.X + p2max.X) / 2, (p1max.Y + p2max.Y) / 2);

 

              this.path.Insert(insertIndex, pnew);

       }

}

 

2.2.2.5     Berechnung der Winkel

 

Um die Unabhängigkeit von der Skalierung und Position der Gesten zu erreichen wird nicht direkt mit den Punkten sondern mit zuvor berechneten Winkeln gearbeitet. Die Methode CalculateAngels errechnet die Ausrichtungen der Teilstrecken zwischen 2 Punkten im Koordinatensystem. Diese werden anschliessend zum Training oder Recall verwendet.

 

/// <summary>

/// Calculates the angles between the lines of the path.

/// </summary>

public void CalculateAngles()

{

       this.angles = new double[this.path.Count - 1];

       for (int i = 0; i < this.path.Count - 1; i++)

       {

              Point p1 = this.path[i];

              Point p2 = this.path[i + 1];

              int dx = p1.X - p2.X;  // y-axis delta

              int dy = p1.Y - p2.Y;  // x-axis delta

              double angle;

             

              if (dx != 0 || dy != 0)

              {

                     double cos = dy / Math.Sqrt(Math.Pow(dx, 2) +

Math.Pow(dy, 2));

                                 

                     if (dx > 0)

                     {

                           angle = 360.0 - (Math.Acos(cos) * 180.0 / Math.PI);

                     }

                     else

                     {

                           angle = Math.Acos(cos) * 180.0 / Math.PI;

                     }

                     this.angles[i] = angle;

              }

       }

}