Backpropagation Neural Network

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
mmk62
User
Beiträge: 10
Registriert: Freitag 23. September 2022, 13:09

Hallo zusammen,

Ich habe versucht, ein Modell eines neuronalen Netzes zu erstellen, das den ValueError zwischen der erwarteten und der tatsächlichen Ausgabe auf der Konsole ausgibt. Aber immer wenn ich versucht habe, das Programm auszuführen, wird folgender Fehler auf der Konsole angezeigt: ValueError: shapes (5,6) and (5,6) not aligned: 6 (dim 1) != 5 (dim 0).

Ich habe mehrfach versucht das Problem zu lösen aber jedesmal leider ohne Erfolg. Aus diesem Grund wäre ich euch sehr dankbar, falls ihr mir helfen würdet.

Viele Grüße

Das ist der Code:

Code: Alles auswählen

import numpy as np
 X = (Feed rate (fa),Cutting speed (vc),Gangzahl (zo),Heat flux (q),Energy (E)), y = Grinding burn

X = np.array(([0.100,0.300,0.500,0.100,0.500,0.300],  [65.000,65.000,65.000,65.000,65.000,35.000], [1.000,1.000,1.000,3.000,3.000,1.000], [1388.830,1279.338,1635.627,1779.128,5905.937,974.872],[10.032,13.176,21.846,4.284,26.295,18.646]), dtype=float)

y = np.array(([1.000], [1.000], [1.000],[1.000],[3.000],[1.000]), dtype=float)

class Training (object):
    def __init__(self):
        #parameters
        self.inputSize = 5
        self.outputSize = 1
        self.hiddenSize = 6
        
        #weights
        self.W1 = np.random.randn(self.inputSize, self.hiddenSize) #  weight matrix from input to hidden layer
        self.W2 = np.random.randn(self.hiddenSize, self.outputSize) #  weight matrix from hidden to output layer
        
    def feedForward(self, X):
        #forward propogation through the network
        self.z = np.dot(X, self.W1) #dot product of X (input) and first set of weights 
        self.z2 = self.sigmoid(self.z) #activation function
        self.z3 = np.dot(self.z2, self.W2) #dot product of hidden layer (z2) and second set of weights 
        output = self.sigmoid(self.z3)
        return output
        
    def sigmoid(self, s, deriv=False):
        if (deriv == True):
            return s * (1 - s) #derivitation of sigmoid function
        return 1/(1 + np.exp(-s)) #actual sigmoid function
    
    def backward(self, X, y, output):
        #backward propogation through the network
        self.output_error = y - output # error in output
        self.output_delta = self.output_error * self.sigmoid(output, deriv=True)
        
        self.z2_error = self.output_delta.dot(self.W2.T) #z2 error: how much our hidden layer weights contribute to output error
        self.z2_delta = self.z2_error * self.sigmoid(self.z2, deriv=True) #applying derivative of sigmoid to z2 error
        
        self.W1 += X.T.dot(self.z2_delta) # adjusting first set (input -> hidden) weights
        self.W2 += self.z2.T.dot(self.output_delta) # adjusting second set (hidden -> output) weights
        
    def train(self, X, y):
        output = self.feedForward(X)
        self.backward(X, y, output)
        
NN = Training ()

for i in range(1000): #trains the NN 1000 times
    if (i % 100 == 0):
        print("Loss: " + str(np.mean(np.square(y - NN.feedForward(X)))))
    NN.train(X, y)
        
print("Input: " + str(X))
print("Actual Output: " + str(y))
print("Loss: " + str(np.mean(np.square(y - NN.feedForward(X)))))
print("\n")
print("Predicted Output: " + str(NN.feedForward(X)))
Benutzeravatar
__blackjack__
User
Beiträge: 14053
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mmk62: Anmerkungen zum Quelltext: Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Der Name soll dem Leser vermitteln was der Wert dahinter im Programm bedeutet, nicht zum rätseln zwingen.

Das zusammenstückeln von Zeichenketten und Werten mittels ``+`` und `str()` ist eher BASIC als Python. Dafür gibt es die `format()`-Methode auf Zeichenketten und f-Zeichenkettenliterale. Oder in einigen Fällen auch einfach der Umstand das man `print()` auch mehr als ein Argument übergeben kann, und das die Funktion die Argumente selbst in Zeichenketten umwandelt.

Bei den ``if``-Bedingungen sind die Klammern zu viel. Man vergleicht nicht mit literalen Wahrheitswerten. Da kommt ja nur wieder der Wahrheitswert bei heraus den man sowieso schon hatte. Falls man auf dessen Gegenteil testen möchte, gibt es ``not``. Also anstelle von ``if (deriv == True):`` einfach nur ``if deriv:``. Allerdings ist das ein ``if`` das anhand eines Wahrheitswertes entscheided was die Methode tatsächlich macht, also sind das eigentlich zwei Methoden und sollten auch als solche geschrieben werden. Beziehungsweise sind das ja gar keine Methoden sondern Funktionen, wo sich dann die Frage stellt warum die in der Klasse stecken.

In Python 3 erbt eine Klasse automatisch von `object` falls keine andere Basisklasse(n) angegeben werden.

Die Klasse hat zu viele Attribute die zudem noch in grosser Menge in irgendwelchen Methoden ausserhalb von der `__init__()` angelegt werden. Das ist unübersichtlich und fehleranfällig. Nachdem die `__init__()` durchlaufen ist, sollte ein Objekt vollständig initialisiert sein und alle Attribute besitzen.

Der Code für "Loss:" steht zweimal im Quelltext — das wäre dann besser eine Funktion oder Methode.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import numpy as np


def sigmoid(values):
    return 1 / (1 + np.exp(-values))


def sigmoid_deriv(values):
    return values * (1 - values)


class NeuralNetwork:
    def __init__(self, input_size, output_size, hidden_size):
        self.input_to_hidden_weights = np.random.randn(input_size, hidden_size)
        self.hidden_to_output_weights = np.random.randn(
            hidden_size, output_size
        )
        self.hidden_layer = None

    def feed_forward(self, input_data):
        self.hidden_layer = sigmoid(
            np.dot(input_data, self.input_to_hidden_weights)
        )
        return sigmoid(
            np.dot(self.hidden_layer, self.hidden_to_output_weights)
        )

    def get_loss(self, input_data, actual_output):
        return np.mean(
            np.square(actual_output - self.feed_forward(input_data))
        )

    def _propagate_backward(self, input_data, actual_output, output):
        assert self.hidden_layer is not None
        output_delta = (actual_output - output) * sigmoid_deriv(output)
        #
        # how much our hidden layer weights contribute to output error
        #
        hidden_layer_error = output_delta.dot(self.hidden_to_output_weights.T)

        hidden_layer_delta = hidden_layer_error * sigmoid_deriv(
            self.hidden_layer
        )

        self.input_to_hidden_weights += input_data.T.dot(hidden_layer_delta)
        self.hidden_to_output_weights += self.hidden_layer.T.dot(output_delta)

    def train(self, input_data, actual_output):
        self._propagate_backward(
            input_data, actual_output, self.feed_forward(input_data)
        )


def main():
    # X = (Feed rate (fa),Cutting speed (vc),Gangzahl (zo),Heat flux (q),Energy (E)), y = Grinding burn
    input_data = np.array(
        (
            [0.100, 0.300, 0.500, 0.100, 0.500, 0.300],
            [65.000, 65.000, 65.000, 65.000, 65.000, 35.000],
            [1.000, 1.000, 1.000, 3.000, 3.000, 1.000],
            [1388.830, 1279.338, 1635.627, 1779.128, 5905.937, 974.872],
            [10.032, 13.176, 21.846, 4.284, 26.295, 18.646],
        ),
        dtype=float,
    )
    actual_output = np.array(([1], [1], [1], [1], [3], [1]), dtype=float)

    neural_network = NeuralNetwork(5, 1, 6)
    for i in range(1000):
        if i % 100 == 0:
            print("Loss:", neural_network.get_loss(input_data, actual_output))
        neural_network.train(input_data, actual_output)

    print("Input:", input_data)
    print("Actual Output:", actual_output)
    print("Loss:", neural_network.get_loss(input_data, actual_output))
    print("\n\nPredicted Output:", neural_network.feed_forward(input_data))


if __name__ == "__main__":
    main()
Bei den Parametern der Klasse frage ich mich ob die nicht zumindest teilweise von den Eingabe- und Ausgabedaten abhängen und von dort ermittelt werden sollten. Und ob es nicht besser wäre *die* zum Zustand des Objekts hinzuzufügen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
mmk62
User
Beiträge: 10
Registriert: Freitag 23. September 2022, 13:09

@_blackjack_: Erstmal vielen herzlichen Dank für deinen konstruktiven Beitrag. Ich habe nur sehr wenig mit Python zutun und bin quasi noch ein blutiger Amateur. Deine Kritik werde ich mir aufjedenfall zu herzen nehmen, und versuchen diese bestmöglich umzusetzen. Auch vielen Dank für den neu erstellten Code, welchen du mir zur Verfügung gestellt hast.

Diesen habe ich bereits getestet. Jedoch werde mir erneut die gleiche Fehlermeldung angezeigt. Leider kann ich mir immer noch nicht erklären, warum diese Fehlermeldung noch weiterhin angezeigt wird.

Viele Grüße

Mansur
Benutzeravatar
grubenfox
User
Beiträge: 612
Registriert: Freitag 2. Dezember 2022, 15:49

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/operator/privat/entwicklung/pygame/dev_moving/NEAT-Tictactoe-Python/tictactoe/game.py", line 82, in <module>
    main()
  File "/home/operator/privat/entwicklung/pygame/dev_moving/NEAT-Tictactoe-Python/tictactoe/game.py", line 72, in main
    print("Loss:", neural_network.get_loss(input_data, actual_output))
  File "/home/operator/privat/entwicklung/pygame/dev_moving/NEAT-Tictactoe-Python/tictactoe/game.py", line 31, in get_loss
    np.square(actual_output - self.feed_forward(input_data))
  File "/home/operator/privat/entwicklung/pygame/dev_moving/NEAT-Tictactoe-Python/tictactoe/game.py", line 23, in feed_forward
    np.dot(input_data, self.input_to_hidden_weights)
  File "<__array_function__ internals>", line 200, in dot
ValueError: shapes (5,6) and (5,6) not aligned: 6 (dim 1) != 5 (dim 0)
[Finished in 0.1s with exit code 1]
Die vollständige Fehlermeldung (jedenfalls bei mir)....
Es knallt also im ersten `np.dot` von `def feed_forward(self, input_data):`
Benutzeravatar
ThomasL
User
Beiträge: 1379
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Code: Alles auswählen

ValueError: shapes (5,6) and (5,6) not aligned: 6 (dim 1) != 5 (dim 0)
Die erste Dimension des zweiten Arrays (Gewichte) muss 6 sein, also (6,5), ansonsten kann das .dot() nicht berechnet werden.
Wenn ich mich richtig erinnere muss dazu das Array transponiert werden. Ich meine das geht mit .T
Ungetestet so aus der Erinnerung.
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