Neuronale Netze für die Kleinsten

Hallo, in diesem Beispiel möchte ich zeigen, wie Sie ein Hopfield-Netzwerk für die Mustererkennung implementieren können.

Ich selbst habe mich wie viele andere eines Tages entschlossen, mich für Programmtraining, KI und neuronale Netze zu interessieren. Glücklicherweise gibt es viele Analysen und Beispiele im Netzwerk, aber alle arbeiten mit einer Fülle von Funktionsformeln. Wenn Sie (wie ich) keine mathematischen Kenntnisse haben, werde ich versuchen, ein einfaches Beispiel für das Hopfield-Netzwerk mithilfe der Sprache Golang (GO) zu demonstrieren.

Beschreibung des mathematischen Netzwerks - Hopfield Network

Warum genau das Hopfield-Netzwerk?

Ein ziemlich schnelles und mehr oder weniger klares Beispiel, wenn Sie in der Welt der KI mit Begriffen wie "einfach" und "verständlich" arbeiten können.

In diesem Beispiel werden wir versuchen, Bilder aus einem Schwarzweißbild mit einer Größe von 20 x 20 Pixel zu erkennen.

Versuchen wir, die Schritte zu verstehen, die wir ausführen müssen, bevor wir das gewünschte Ergebnis erzielen:

  1. Bild in Vektor konvertieren
  2. Konvertieren Sie Vektoren in Matrizen
  3. Summiere die Matrizen und erhalte eine einzelne Matrix (W)
  4. Null eine einzelne Matrix diagonal
  5. Multiplizieren Sie die Matrix W mit dem Vektor des Eingabebildes
  6. Leiten Sie den empfangenen Vektor durch die Aktivierungsfunktion für dieses Netzwerk (F).
  7. Ersetzen Sie Schritt 5 durch den neuen Vektor und setzen Sie den Vorgang fort, bis wir einen stabilen Zustand des Netzwerks erhalten (wir erhalten denselben Vektor am Ausgang).

Fahren wir mit dem Code mit einer detaillierten Beschreibung fort. Alle Bibliotheken, die wir brauchen:

package main import ( "github.com/oelmekki/matrix" "fmt" "os" "log" "image" "math" "image/color" "image/png" _ "image/jpeg" ) 

Wir erstellen ein Array von Vektoren mit 3 Elementen (die Anzahl der Samples), konvertieren Bilder in Vektoren und fügen dem Array Samples hinzu. Der 'Y'-Vektor ist das Bild, das wir erkennen möchten.

 vectorArrays := [3][]float64{} x1 := getVectorFromImage("Images/.jpg") x2 := getVectorFromImage("Images/.jpg") x3 := getVectorFromImage("Images/.jpg") y := getVectorFromImage("Images/Income.jpg") // Add images to the array vectorArrays[0] = x1 vectorArrays[1] = x2 vectorArrays[2] = x3 

Erstellen Sie ein Array von Matrizen, konvertieren Sie alle Vektoren in Matrizen und fügen Sie sie dem Array von Matrizen hinzu. Wir erstellen ein Leerzeichen der Matrix W und beginnen mit der Summierung aller Matrizen. Geben Sie das Ergebnis in W ein.

 matrixArray := [len(vectorArrays)]matrix.Matrix{} for i, vInArray := range vectorArrays { matrixArray[i] = vectorToMatrix(vInArray, vInArray) } W := matrix.Matrix{} for i, matrixInArray := range matrixArray { if i == 0 { W = matrixInArray continue } W, _ = W.Add(matrixInArray) } 

Stellen Sie die Matrix diagonal auf Null.

 for i := 0; i < W.Rows(); i++ { W.SetAt(i, i, 0) } 

Wir erstellen ein Leerzeichen des Ausgabevektors, multiplizieren die Matrix mit dem Y-Vektor, setzen das Ergebnis in den Vektor S und ersetzen es durch Multiplikation.

 S := make([]float64, 400) for II := 0; II < 100; II++ { if II == 0 { S, _ = W.VectorMultiply(y) for i, element := range S { // Activation Func "sigmod" S[i] = sigmod(element) } continue } else { S, _ = W.VectorMultiply(S) for i, element := range S { // Activation Func "sigmod" S[i] = sigmod( element) } } } 

Mehr zur Funktion sigmod ().

Dies ist die Aktivierungsfunktion F, und das Prinzip ihrer Funktionsweise (in unserem Beispiel) besteht darin, die Daten in den Ausgabevektor zu konvertieren und sie entweder auf 1 oder -1 zu bringen.

Da wir mit einem bipolaren Netzwerk arbeiten, können die Daten nur 1 und -1 sein.

Das Bild sollte von RGB auf 1 und -1 reduziert werden, wobei die Summe aller durch 3 geteilten Punkte (bedingte Pixelhelligkeit) zu Schwarz oder Weiß tendieren sollte. Da R = 255, G = 255, B = 255 weiß ist und R = 0, G = 0, B = 0 schwarz ist. Ich habe einen Schwellenwert von 150 gewählt, damit mehr als oder gleich 150 weiß ist (1). Alles, was kleiner als schwarz ist (-1), wobei die Wahl zwischen Schwarz bei -1 und Weiß bei 1 bedingt sein kann. Alles, was wir brauchen, ist, Schwarz und Weiß zu zerlegen durch Werte. Weiß kann auch -1 und Schwarz 1 sein, in diesem Fall spielt es keine Rolle. Es ist auch zu bedenken, dass wir mit einer symmetrischen Matrix arbeiten und die Bilder gleichseitig sein sollten.

Um das Bild in einen Vektor umzuwandeln, müssen Sie das Bild als Matrix darstellen, die wir horizontal schneiden, und jede Schnittebene (und wir werden 20 davon haben) am Ende der vorherigen Ebene hinzufügen und einen Vektor mit einer Länge von 400 (20 x 20) erhalten.

Im Beispiel überprüfe ich den Ausgabevektor nicht auf Stabilität, sondern gehe einfach 100 Mal durch den Zyklus, und am Ende überprüfe ich, wie die Stichproben aussehen, nach denen unser Ergebnis aussieht. Das Netzwerk errät oder gibt nicht gleichzeitig die sogenannte Chimäre oder eine freie Interpretation dessen, was sie sehen konnte. Dies ist das Ergebnis, das ich im Bild speichere.

Da wir den synchronen Hopfield-Netzwerkmodus verwenden, ist das Ergebnis schwach. Natürlich können Sie asynchron verwenden, was mehr Zeit und Ressourcen in Anspruch nimmt, aber das Ergebnis ist viel besser.

Arbeitsbeispiel:

Eingabebild -
Die Antwort ist

Quellbilder






Ganzer Code
Paket main

 package main import ( "github.com/oelmekki/matrix" "fmt" "os" "log" "image" "math" "image/color" "image/png" _ "image/jpeg" ) func vectorToMatrix(v1 []float64, v2 []float64) (matrix.Matrix) { m := matrix.GenerateMatrix(len(v1), len(v2)) for i, elem := range v1 { for i2, elem2 := range v1 { m.SetAt(i, i2, elem2*elem) } } return m } func sigmod(v float64) (float64) { if v >= 0 { return 1 } else { return -1 } } func getVectorFromImage(path string) ([] float64) { reader, err := os.Open(path) if err != nil { log.Fatal(err) } defer reader.Close() m, _, err := image.Decode(reader) if err != nil { log.Fatal(err) } v := make([]float64, 400) vectorIteration := 0 for x := 0; x < 20; x++ { for y := 0; y < 20; y++ { r, g, b, _ := m.At(x, y).RGBA() normalVal := float64(r+g+b) / 3 / 257 if normalVal >= 150 { v[vectorIteration] = 1 } else { v[vectorIteration] = -1 } vectorIteration ++ } } return v } func main() { fmt.Println("Memory size ~ ", int(400/(2*math.Log2(400))), " objects") fmt.Println("1 - ") fmt.Println("2 - ") fmt.Println("3 - ") fmt.Println("-----Start------") vectorArrays := [3][]float64{} x1 := getVectorFromImage("Images/.jpg") x2 := getVectorFromImage("Images/.jpg") x3 := getVectorFromImage("Images/.jpg") y := getVectorFromImage("Images/Income.jpg") vectorArrays[0] = x1 vectorArrays[1] = x2 vectorArrays[2] = x3 matrixArray := [len(vectorArrays)]matrix.Matrix{} for i, vInArray := range vectorArrays { matrixArray[i] = vectorToMatrix(vInArray, vInArray) } W := matrix.Matrix{} for i, matrixInArray := range matrixArray { if i == 0 { W = matrixInArray continue } W, _ = W.Add(matrixInArray) } for i := 0; i < W.Rows(); i++ { W.SetAt(i, i, 0) } S := make([]float64, 400) for II := 0; II < 100; II++ { if II == 0 { S, _ = W.VectorMultiply(y) for i, element := range S { S[i] = sigmod(element) } continue } else { S, _ = W.VectorMultiply(S) for i, element := range S { S[i] = sigmod(element) } } } ar := [3]int{1, 1, 1} for vectorI, v := range vectorArrays { for i, elem := range v { if elem != S[i] { ar[vectorI] = 0 break } } } for i, el := range ar { if el == 1 { fmt.Println("Looks like", i+1) } } img := image.NewRGBA(image.Rect(0, 0, 20, 20)) xx := 0 yy := 0 for i := 0; i < 400; i++ { if i%20 == 0 { yy++ xx = 0 } else { xx++ } if S[i] == -1 { img.Set(xx, yy, color.RGBA{0, 0, 0, 255}) } else { img.Set(xx, yy, color.RGBA{255, 255, 255, 255}) } } f, _ := os.OpenFile("Images/out.png", os.O_WRONLY|os.O_CREATE, 0600) png.Encode(f, img) f.Close() var str string fmt.Scanln(&str) } 

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


All Articles