Selbstgemachtes künstliches neuronales Netz

Code-Stücke können hier veröffentlicht werden.
Antworten
Üpsilon
User
Beiträge: 222
Registriert: Samstag 15. September 2012, 19:23

Hallo.

Falls das hier im falschen Unterforum ist, könnt ihr es gerne verschieben, ich poste es mal hier und nicht im "Wissenschaftliches Rechnen"-Bereich weil es eher um den Inhalt und nicht um die Bibliotheken geht.
Habe ein ganz einfaches künstliches neuronales Netz mit Python und Numpy programmiert, mit nur einer versteckten Schicht und nur einem Ausgabeneuron (falls das der richtige Begriff ist). Also für Regression.
Ich habe gehört dass es da ausgereifte Bibliotheken für gibt, aber es geht darum etwas zu lernen. Also dass nicht nur das Netz etwas lernt sondern auch ich. :lol:

Dafür habe ich Kapitel 11.3 dieses Buches benutzt: https://web.stanford.edu/~hastie/ElemStatLearn/

Code: Alles auswählen

import numpy as np

class NeuronalesNetz:
    """Neuronales Netz für Regression.
    Aktivierungsfunktion ist 1/(1+e^-x).
    Es gibt nur ein Ausgabeneuron.
    Die Werte der versteckten Neuronen werden für das Ausgabeneuron nur
    linearkombiniert, und nicht durch eine weitere Funktion durchgedrückt."""

    def __init__(self, anz_eingaben, anz_versteckte):
        self.anz_eingaben = anz_eingaben
        self.anz_versteckte = anz_versteckte
        # gewichte_vorn heißen im Buch alpha
        self.gewichte_vorn = np.random.rand(anz_versteckte, anz_eingaben+1) / 10 - 0.05
        # gewichte_hinten heißen im Buch beta
        self.gewichte_hinten = np.random.rand(1+anz_versteckte) / 10 - 0.05

    def versteckte_werte(self, eingabewerte):
        """Eingabe: eine Matrix, jede Zeile entspricht einem Eingabepunkt,
        jede Spalte entspricht einem Eingabeneuron.
        Berechnet die Werte der Zwischenschicht, im Buch Z genannt"""
        eingabewerte = np.array(eingabewerte)
        assert len(eingabewerte.shape) == 2 and eingabewerte.shape[1] == self.anz_eingaben
        aktivierungsfunktion = lambda x: 1 / (1 + np.exp(-x))
        eingaben_mit_1 = np.hstack([np.ones([eingabewerte.shape[0], 1]), eingabewerte])
        return aktivierungsfunktion(self.gewichte_vorn @ eingaben_mit_1.T)

    def auswerten(self, eingabewerte):
        return self.auswerten_für_training(eingabewerte)[1]

    def auswerten_für_training(self, eingabewerte):
        """Gibt Werte der Zwischenschicht und Endergebnisse"""
        versteckte_werte = self.versteckte_werte(eingabewerte)
        return (versteckte_werte,
                self.gewichte_hinten[1:] @ versteckte_werte + self.gewichte_hinten[0])

    def trainingsschritt(self, eingaben, ausgaben, lernrate, decayparameter=0):
        """´´eingaben´´ soll eine Matrix sein, ´´ausgaben´´ soll ein 1d-Array sein was so viele
        Einträge hat wie ´´eingaben´´ Zeilen hat.
        Batch-Backpropagation mit Weight Decay."""
        versteckte_werte, ergebnisse = self.auswerten_für_training(eingaben)
        # fehler_hinten heißen im Buch delta
        fehler_hinten = -2 * (ausgaben - ergebnisse)
        # fehler_mitte heißen im Buch s
        abgeleitete_aktivierungsfunktion = lambda x: np.exp(-x) / (1 + np.exp(-x)) **2
        fehler_mitte = ((abgeleitete_aktivierungsfunktion(self.gewichte_vorn[:, 1:] @ eingaben.T)
                        * fehler_hinten).T * self.gewichte_hinten[1:]).T
        
        self.gewichte_hinten[1:] -= lernrate * (versteckte_werte @ fehler_hinten
                                                + 2 * decayparameter * self.gewichte_hinten[1:])
        self.gewichte_vorn[:, 1:] -= lernrate * (fehler_mitte @ eingaben
                                                 + 2 * decayparameter * self.gewichte_vorn[:, 1:]) 


if __name__ == "__main__":
    nn = NeuronalesNetz(3, 20)
    #print(nn.auswerten_für_training([[1,2,3],[3,2,1]]))
    for i in range(1000):
        nn.trainingsschritt(np.array([[1,2,3], [2,3,4]]), np.array([1, 3]), .1)
    print(nn.auswerten(np.array([[1,2,3], [2,3,4]])))
Das Ergebnis ist nicht berauschend. Ich frage ja genau nach den Daten, die ich schon beim Training reingesteckt habe. Also verlange ich nur, dass es die Daten auswendig lernt -- das sollte ja wohl möglich sein. Wenn ich nur hundert statt tausend Durchgänge mache, kommen irgendwelche Zahlen zwischen 1 und 3 als Ergebnis raus. Und offenbar ist umso mehr Training erforderlich, je weiter die gewünschten Ausgabewerte auseinanderliegen.
Ist das normal so? Hab ich was falsch gemacht? Soll das die künstliche Intelligenz sein, von der alle reden?

Irgendwie ist das aber auch Quatsch was ich da ausprobiere. Vielleicht wären die Ergebnisse bei einer richtigen Regression auf verrauschten Daten besser. Aber die verschiebe ich auf morgen.

Weitere Rückmeldung ist willkommen.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Hallo Üpsilon,

dein Code macht was er soll, das einzige was nicht stimmt sind deine Erwartungen.
100 Trainingsdurchläufe sind einfach zu wenig!

Erhöhe auf 2000 Trainingsdurchläufe und du bekommst die mathematisch korrekte Ausgabe.

Was man verbessern könnte ist deine Gewichtsinitialisierung, die ist Murks.
Schaust du hier: https://machinelearningmastery.com/weig ... -networks/

Auch die von dir realisierte Sigmoidfunktion ist instabil.
Schaust du hier: https://www.delftstack.com/de/howto/pyt ... on-python/

Wenn du die Ausgabewerte weiter voneinander entfernst, dann musst du die Lernrate verringern.
Hier unten habe ich den zweiten Wert von 3 auf 13 erhöht. Bei gleicher Trainingsanzahl und Lernrate 0.1
kann das NN den gradient descent nicht durchführen.
Schaust du hier: https://www.jeremyjordan.me/nn-learning-rate/
und hier: https://machinelearningmastery.com/unde ... -networks/

Bei einer Lernrate von 0.4 klappt es dann (bei mir).

Code: Alles auswählen

if __name__ == "__main__":
    nn = NeuronalesNetz(3, 20)   
    X = np.array([[1,2,3], [2,3,4]])
    y = np.array([1, 13])
    for i in range(2000):
        nn.trainingsschritt(X, y, .04)
    print(nn.auswerten(X))

Code: Alles auswählen

Ausgabe: [ 1. 13.]
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Üpsilon
User
Beiträge: 222
Registriert: Samstag 15. September 2012, 19:23

Hallo ThomasL,

danke für die Antwort. Ich hab dieses Programm seitdem nicht mehr weiter angefasst, aber ich habe schon vor ein paar Tagen die verlinkten Texte gelesen.
Die waren sehr aufschlussreich. So hatte ich mir das mit den neuronalen Netzen nicht vorgestellt ^^

Allerdings, eine Sache ist mir noch eingefallen, die ich in dem Code oben falsch gemacht habe: ich mache dort den Gradientenabstieg für die "weights", aber die "biases" (also self.gewichte_hinten[0] und self.gewichte_vorn[:,0]) werden gar nicht beachtet. Die werden also am Anfang zufällig festgelegt und den Wert behalten sie dann auch ^^
Das habe ich vergessen, denn in dem Buch aus dem ich abgeschrieben habe steht nicht drin was die Ableitung der Loss-Funktion nach den "biases" ist :D
Die müsste man noch ausrechnen und die trainingsschritt-Methode entsprechend anpassen.

Nebenbei gefragt - kannst Du (oder irgendjemand anderes) ein gutes Buch über neuronale Netze empfehlen, was sowohl praktische (Tensorflow-mäßige) Fähigkeiten vermittelt, aber auch die Mathematik dahinter nicht scheut?

Nette Grüße,
Y.
PS: Die angebotene Summe ist beachtlich.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Hi,

schau dir mal diese Playlist von sentdex an: https://www.youtube.com/watch?v=Wo5dMEP ... OF2tius3V3

Das Buch dazu gibt es hier: https://nnfs.io/

VG Thomas
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Antworten