Nach einiger Zeit habe ich mich nun wieder an die guten alten Neuronalen Netzwerke gemacht
Hab nun endlich ein 3-schichtiges Netzwerk hinbekommen (hoffe das alles stimmt)! Um es auf 4 Schichten zu erweitern wäre nun auch nur noch ein Kinderspiel. Konnte noch nicht viel damit rumspielen und hab deshalb nur mal wieder das alte, einfache Problem des Ellipsen- und Boxenunterscheidens geproggt. Das 2-schichtige Netz konnte das zwar auch und musste auch nicht so viel lernen, aber es soll ja nur ein Anfangsbeispiel sein (3-schichtige N. können nun auch nichtlineare Probleme lösen)!
Vorsicht! Braucht ziemlich lange zum berechnen, deshalb vielleicht die #LD-Konstante runter setzen!
So, nun der Code:
Code: Alles auswählen
;/ 3-schichtiges Neuronales Netzwerk
; Eingabeschicht - Verborgene Schicht - Ausgabeschicht
; - Jedes Neuron der Eingabeschicht ist mit jedem Neuron der verborgenen Schicht verbunden
; - Jedes Neuron der verborgenen Schicht ist mit jedem Neuron der Ausgabeschicht verbunden
; - Jedes Neuron der verb.- und der Ausgabeschicht hat ein Verschiebungsgewicht (Gewicht wo immer Input = 1)
; - Weniger fehleranfällig, für nichtlineare Probleme der Form: y = f(x), braucht mehr Training
; als Aktivierungsfunktion habe ich diesmal die nichtlineare sigmoide Funktion gewählt:
; 1 / (1 + e^(-Aktivierung))
; sie bleibt zwischen 0 und 1, wird aber 0 und 1 nie erreichen!
; Zu #LD: für einen Fehler von 10% müsste die Anzahl Lerndurchläufe 10 mal 
; grösser sein, als die Anzahl an Gewichten!!!  (Anz.Gewichte = #v * (#n * #n + #a)
; #Toleranz = 0.01   ;Zum herausfinden, wann genug gelernt wurde (habs auskommentiert)
#LD = 77100 ;Anz. Lerndurchläufe
#TD = 400   ;Anz. Testdurchläufe
#LK = 0.1   ;Lernkonstante
#n  = 16    ;Wurzelvon(Anz. Inputs), Seitenlänge des Bildes
#v  = 300   ;Anz. verb. Neuronen
#a  = 1     ;Anz. Outputs, Achtung, wenn verändert, dann muss auch Generiere_Input() angepasst werden
;Gewichte
Dim Gewichte1.f(#n * #n, #v)   ;Gewichte von Inputs nach verb. Schicht
Dim Gewichte2.f(#v, #a)   ;Gewichte von verb. Schicht nach Outputs
;Outputs der Neuronen
Dim Inputs.f(#n * #n)    ;Inputneuronen
Dim VNeurons.f(#v)  ;Verb. Neuronen
Dim VFehler.f(#v)   ;Um die Fehler zu speichern (Fehler = Abweichung vom berechneten Solloutput)
Dim Outputs.f(#a)   ;Outputneuronen
Dim OFehler.f(#a)   ;Um die Fehler zu speichern (Fehler = Abweichung vom Solloutput)
Dim SollOutputs.f(#a)  ;Array mit den Bildern (1011110000011101)
;- Initialisierung
Procedure Init_Gewichte()  ;Zufällig initialisieren, von -0.3 bis +0.3
  For z1 = 0 To #n * #n  ;von 0, da Verschiebungsgewicht auch initialisiert werden muss
    For z2 = 0 To #v
      Gewichte1(z1, z2) = Random(600) / 1000.0 - 0.3
    Next
  Next
  
  For z1 = 0 To #v
    For z2 = 0 To #a
      Gewichte2(z1, z2) = Random(600) / 1000.0 - 0.3
    Next
  Next
  
  VNeurons(0) = 1
  Inputs(0)  = 1
EndProcedure
;- Input generieren
Global Image.l
Image = CreateImage(#PB_Any, #n, #n)
Procedure Generiere_Input()
  StartDrawing(ImageOutput())
    DrawingMode(0)
    Box(0, 0, #n, #n, 0)  ;Image löschen
    DrawingMode(4)
    zufall = Random(1)
    If zufall = 0         ;Ellipse
      Ellipse(Random(4) + 6, Random(4) + 6, Random(4) + 3, Random(4) + 3, $FFFFFF)
      SollOutputs(1) = 1.0    ;Outputneuron = 1
    ElseIf zufall = 1     ;Box
      Box(Random(6), Random(6), Random(5) + 5, Random(5) + 5, $FFFFFF)
      SollOutputs(1) = 0.0    ;Outputneuron = 0
    EndIf
    
    For z = 1 To #n * #n  ;Inputs()-Array füllen
      x = (z % #n) + 1
      y = z / #n
      farbe = Point(x, y)
      If farbe > 0
        Inputs(z) = 1.0
      Else
        Inputs(z) = 0.0
      EndIf
    Next
  StopDrawing()
EndProcedure
;- Berechnen/Lernen/Anpassen
Procedure Berechne_Outputs()   ;Vorwärts
  For z1 = 1 To #v     ;für jedes Neuron der verb. Schicht
    VNeurons(z1) = 0     ;mit 0 initialisieren
    For z2 = 1 To #n * #n   ;für jedes Neuron der Inputsschicht
      VNeurons(z1) = VNeurons(z1) + Inputs(z2) * Gewichte1(z2, z1)    ;Aktivierung aufsummieren
    Next
    For z3 = 1 To #v    ;und noch die Verschiebungsgewichte
      VNeurons(z1) = VNeurons(z1) + Gewichte1(0, z3)
    Next
    VNeurons(z1) = 1.0 / (1.0 + Pow(2.718, -VNeurons(z1)))  ;Aktivierungsfunktion (sigmoide Funktion) => Output
  Next
  
  For z1 = 1 To #a     ;für jedes Neuron der Ausgabeschicht
    Outputs(z1) = 0    ;mit 0 initialisieren
    For z2 = 1 To #v     ;für jedes Neuron der verb. Schicht
      Outputs(z1) = Outputs(z1) + VNeurons(z2) * Gewichte2(z2, z1)    ;Aktivierung aufsummieren
    Next
    For z3 = 1 To #a    ;und noch die Verschiebungsgewichte
      Outputs(z1) = Outputs(z1) + Gewichte2(0, z3)
    Next
    Outputs(z1) = 1.0 / (1.0 + Pow(2.718, -Outputs(z1)))    ;Aktivierungsfunktion (sigmoide Funktion) => Output
  Next
EndProcedure
Procedure.f Berechne_Fehler()  ;Rückwärts 1, gibt den MaximalFehler zurück
  Protected MaxFehler.f    ;um zu erkennen, wann der maximale Fehler innerhalb der Toleranz #Toleranz liegt
  
  For z = 1 To #a   ;Fehler für 2. Gewichtsschicht
    OFehler(z) = (SollOutputs(z) - Outputs(z)) * Outputs(z) * (1.0 - Outputs(z))    ;fülle Fehler-Array der Outputschicht
    
    ; If Abs(OFehler(z)) > Abs(MaxFehler)   ;Maximaler Fehler herausfinden
      ; MaxFehler = OFehler(z)
    ; EndIf
  Next
  
  For z1 = 1 To #v    ;für jedes Neuron der verb. Schicht
    Fehler1.f = 0     ;mit 0 initialisieren
    For z2 = 1 To #a  ;Fehler 1 berechnen
      Fehler1 = Fehler1 + OFehler(z2) * Gewichte2(z1, z2)  ;Fehler aufsummieren
    Next
    VFehler(z1) = VNeurons(z1) * (1.0 - VNeurons(z1)) * Fehler1    ;fülle Fehlerarray der verb. Schicht
  Next
  
  ProcedureReturn MaxFehler
EndProcedure
Procedure Gewichte_anpassen()  ;Rückwärts 2
  ;2. Gewichteschicht anpassen
  For z1 = 0 To #a    ;für jedes Neuron der Ausgabeschicht   
    For z2 = 0 To #v    ;für jedes Neuron der verb. Schicht
      Gewichte2(z2, z1) = Gewichte2(z2,z1) + (#LK * OFehler(z1)) * VNeurons(z2)    ;die altbekannte Delta-Regel
    Next
  Next
  
  ;1. Gewichteschicht anpassen
  For z1 = 0 To #v    ;für jedes Neuron der verb. Schicht
    For z2 = 0 To #n * #n    ;für jedes Neuron der Eingabeschicht
      Gewichte1(z2, z1) = Gewichte1(z2, z1) + (#LK * VFehler(z1)) * Inputs(z2)     ;die altbekannte Delta-Regel
    Next
  Next
EndProcedure
;- LERNE
Init_Gewichte()   ;Gewichte auf Zufallswerte (nahe beieinander (-0.3 bis +0.3))
; anzInToleranz = 0
For z = 1 To #LD
  Generiere_Input()    ;Inputs()-Array füllen und Solloutput generieren
  Berechne_Outputs()   ;die Outputs der verschiedenen Schichten (verb.- und Ausgabe-) errechnen
  Berechne_Fehler()    ;die Fehler in die Fehlerarrays füllen
  
  ; bereich.f = Berechne_Fehler()
  ; If Abs(bereich) <= #Toleranz
    ; anzInToleranz + 1
  ; EndIf
  ; If anzInToleranz >= 300
    ; Break
  ; EndIf
    
  Gewichte_anpassen()  ;Aus Fehlern lernen!
Next
OpenConsole()
PrintN("Training beendet!")
PrintN("")
;- TESTE
richtige = 0
OpenWindow(0,200,200,200,200,#PB_Window_SystemMenu,"")
For z = 1 To #TD
  Generiere_Input()
  
  StartDrawing(WindowOutput())
    DrawImage(UseImage(Image),0,0,32,32)
  StopDrawing()
  
  Berechne_Outputs()
  If (SollOutputs(1) > 0.8 And Outputs(1) > 0.8) Or (SollOutputs(1) < 0.2 And Outputs(1) < 0.2)  ;hat ers auf 0.2 genau herausgefunden?
    richtige + 1
  EndIf
Next
PrintN("Test:")
PrintN("Richtige von " + Str(#TD) + ": " + Str(richtige))
PrintN("In Prozent: " + StrF(richtige / #TD * 100.0))
Input()Sucht bitte Fehler! (coldarchon?)
greetz
remi
EDIT: Fehler behoben!