import numpy as np import matplotlib.pyplot as plt from PIL import Image class FeedForwardNetwork: def __init__(self, input_nodes=784, hidden_nodes=200, output_nodes=10, learning_rate=0.1): """ Initialisiert das Feed-Forward-Netzwerk """ self.input_nodes = input_nodes self.hidden_nodes = hidden_nodes self.output_nodes = output_nodes self.learning_rate = learning_rate # Gewichtsmatrizen initialisieren (normalverteilt um 0, std = 1/sqrt(n)) # W_ih: Verbindungen von Input zu Hidden (200x784) self.W_ih = np.random.normal(0.0, pow(self.input_nodes, -0.5), (self.hidden_nodes, self.input_nodes)) # W_ho: Verbindungen von Hidden zu Output (10x200) self.W_ho = np.random.normal(0.0, pow(self.hidden_nodes, -0.5), (self.output_nodes, self.hidden_nodes)) def sigmoid(self, x): """ Sigmoid-Aktivierungsfunktion: S(x) = 1/(1+e^(-x)) """ # Overflow-Schutz für große negative Werte return 1.0 / (1.0 + np.exp(-np.clip(x, -500, 500))) def sigmoid_derivative(self, x): """ Ableitung der Sigmoid-Funktion: S'(x) = S(x) * (1 - S(x)) """ return self.sigmoid(x) * (1.0 - self.sigmoid(x)) def think(self, inputs): """ Ein Vorwärtsdurchlauf durch das Netzwerk Args: inputs: Eingabedaten als numpy array (784,) Returns: final_outputs: Ausgabe des Netzwerks (10,) """ # Eingaben in 2D-Array umwandeln inputs = np.array(inputs, ndmin=2).T # Input -> Hidden Layer # H = S(W_ih × I) hidden_inputs = np.dot(self.W_ih, inputs) hidden_outputs = self.sigmoid(hidden_inputs) # Hidden -> Output Layer # O = S(W_ho × H) final_inputs = np.dot(self.W_ho, hidden_outputs) final_outputs = self.sigmoid(final_inputs) return final_outputs def train(self, inputs, targets): """ Trainiert das Netzwerk mit einem Eingabe-Ziel-Paar Args: inputs: Eingabedaten (784,) targets: Zielwerte (10,) """ # Eingaben und Ziele in 2D-Arrays umwandeln inputs = np.array(inputs, ndmin=2).T targets = np.array(targets, ndmin=2).T # Forward Pass # Input -> Hidden hidden_inputs = np.dot(self.W_ih, inputs) hidden_outputs = self.sigmoid(hidden_inputs) # Hidden -> Output final_inputs = np.dot(self.W_ho, hidden_outputs) final_outputs = self.sigmoid(final_inputs) # Backpropagation # Ausgabefehler berechnen: E_out = T - O output_errors = targets - final_outputs # Hidden Layer Fehler berechnen: E_hidden = W_ho^T × E_out hidden_errors = np.dot(self.W_ho.T, output_errors) # Gewichte von Hidden zu Output aktualisieren # ΔW_ho = (E_out * S'(O)) × H^T delta_who = np.dot((output_errors * final_outputs * (1.0 - final_outputs)), hidden_outputs.T) self.W_ho += self.learning_rate * delta_who # Gewichte von Input zu Hidden aktualisieren # ΔW_ih = (E_hidden * S'(H)) × I^T delta_wih = np.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), inputs.T) self.W_ih += self.learning_rate * delta_wih def plot_image(image_data, label=None): """ Hilfsfunktion zum Plotten von MNIST-Bildern """ image_array = np.asarray(image_data, dtype=float).reshape((28, 28)) plt.imshow(image_array, cmap='Greys', interpolation='None') if label is not None: plt.title(f"Label: {label}") plt.show(block=False) def load_and_preprocess_data(filename): """ Lädt und verarbeitet MNIST-Daten aus CSV-Datei """ import os # Debug: Aktuelles Arbeitsverzeichnis anzeigen current_dir = os.getcwd() print(f"Aktuelles Arbeitsverzeichnis: {current_dir}") # Debug: Dateien im aktuellen Verzeichnis auflisten files_in_dir = [f for f in os.listdir(current_dir) if f.endswith('.csv')] print(f"CSV-Dateien im Verzeichnis: {files_in_dir}") # Versuche Datei im gleichen Verzeichnis wie das Skript zu finden script_dir = os.path.dirname(os.path.abspath(__file__)) full_path = os.path.join(script_dir, filename) print(f"Lade Daten aus {filename}...") print(f"Vollständiger Pfad: {full_path}") # Versuche zuerst im Skript-Verzeichnis try: with open(full_path, 'r') as f: data_list = f.readlines() print(f"✅ Datei erfolgreich geladen: {len(data_list)} Zeilen") except FileNotFoundError: # Versuche im aktuellen Arbeitsverzeichnis try: with open(filename, 'r') as f: data_list = f.readlines() print(f"✅ Datei erfolgreich geladen: {len(data_list)} Zeilen") except FileNotFoundError: print(f"❌ Datei {filename} nicht gefunden!") print(f" Überprüfe, ob die Datei existiert in:") print(f" - {script_dir}") print(f" - {current_dir}") return None, None inputs = [] targets = [] for record in data_list: values = record.split(',') # Label (erste Spalte) label = int(values[0]) # Pixel-Werte normalisieren: [0,255] -> [0.01,0.99] pixel_values = (np.asarray(values[1:], dtype=float) / 255.0 * 0.98) + 0.01 inputs.append(pixel_values) # Target-Array erstellen (One-Hot-Encoding) target = np.zeros(10) + 0.01 # Alle auf 0.01 target[label] = 0.99 # Korrekte Klasse auf 0.99 targets.append(target) return inputs, targets def load_custom_handwriting(image_path, true_label): """ Lädt ein eigenes Handschrift-Bild und bereitet es für das Netzwerk vor Args: image_path: Pfad zum Bild true_label: Die tatsächliche Ziffer (0-9) Returns: processed_image: Normalisierte Pixelwerte für das Netzwerk true_label: Die Ziffer als Zahl """ try: # Bild laden img = Image.open(image_path) # In Graustufen konvertieren img = img.convert('L') # Auf 28x28 skalieren falls nötig img = img.resize((28, 28), Image.Resampling.LANCZOS) # In numpy array konvertieren img_array = np.array(img) # Normalisierung wie bei MNIST: [0,255] -> [0.01,0.99] processed_image = (img_array / 255.0 * 0.98) + 0.01 # In 1D-Array umwandeln (784 Pixel) processed_image = processed_image.flatten() return processed_image, true_label except Exception as e: print(f"Fehler beim Laden von {image_path}: {e}") return None, None def test_custom_handwriting(network, image_paths_and_labels): """ Testet das Netzwerk mit eigenen Handschrift-Bildern Args: network: Trainiertes FeedForwardNetwork image_paths_and_labels: Liste von (pfad, label) Tupeln """ print("Teste eigene Handschrift...") correct = 0 total = len(image_paths_and_labels) for i, (image_path, true_label) in enumerate(image_paths_and_labels): # Bild laden und verarbeiten processed_image, true_label = load_custom_handwriting(image_path, true_label) if processed_image is None: continue # Vorhersage outputs = network.think(processed_image) predicted_label = np.argmax(outputs) confidence = np.max(outputs) * 100 # Ergebnis is_correct = predicted_label == true_label if is_correct: correct += 1 print(f"\nBild {i}: {image_path}") print(f"Tatsächlich: {true_label}") print(f"Vorhergesagt: {predicted_label} (Konfidenz: {confidence:.1f}%)") print(f"✅ Richtig" if is_correct else "❌ Falsch") # Bild anzeigen (optional) show_image_with_prediction(processed_image, true_label, predicted_label, confidence) accuracy = (correct / total) * 100 if total > 0 else 0 print(f"\nEigene Handschrift - Ergebnisse:") print(f"Korrekt: {correct}/{total}") print(f"Genauigkeit: {accuracy:.1f}%") def show_image_with_prediction(image_data, true_label, predicted_label, confidence): """ Zeigt das Bild mit Vorhersage an """ # Zurück in 28x28 umformen image_2d = image_data.reshape(28, 28) plt.figure(figsize=(4, 4)) plt.imshow(image_2d, cmap='gray') plt.title(f"Wahr: {true_label}, Vorhergesagt: {predicted_label} ({confidence:.1f}%)") plt.axis('off') plt.show() if __name__ == "__main__": # Netzwerk-Parameter input_nodes = 784 # 28*28 Pixel hidden_nodes = 200 # Anzahl Hidden-Neuronen output_nodes = 10 # Zahlen 0-9 learning_rate = 0.1 # Lernrate # Netzwerk erstellen print("Erstelle Feed-Forward-Netzwerk...") network = FeedForwardNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate) # Trainingsdaten laden training_inputs, training_targets = load_and_preprocess_data("mnist_train_full.csv") if training_inputs is None: print("Konnte Trainingsdaten nicht laden. Stelle sicher, dass mnist_train_100.csv existiert.") exit(1) # Training print(f"Starte Training mit {len(training_inputs)} Beispielen...") epochs = 5 # Anzahl der Trainingsdurchläufe for epoch in range(epochs): # print(f"Epoche {epoch + 1}/{epochs}") for i, (inputs, targets) in enumerate(zip(training_inputs, training_targets)): network.train(inputs, targets) # Fortschritt anzeigen # if (i + 1) % 20 == 0: # print(f" Trainiert: {i + 1}/{len(training_inputs)} Beispiele") print("Training abgeschlossen!") # Testdaten laden und testen test_inputs, test_targets = load_and_preprocess_data("mnist_test_full.csv") if test_inputs is None: print("Konnte Testdaten nicht laden. Teste mit Trainingsdaten...") test_inputs, test_targets = training_inputs[:10], training_targets[:10] # Performance testen print("\nTeste Netzwerk...") correct = 0 total = len(test_inputs) for i, (inputs, targets) in enumerate(zip(test_inputs, test_targets)): # Prediction outputs = network.think(inputs) # Vorhergesagte und tatsächliche Klasse predicted_label = np.argmax(outputs) actual_label = np.argmax(targets) if predicted_label == actual_label: correct += 1 # Genauigkeit berechnen accuracy = (correct / total) * 100 print(f"\nErgebnisse:") print(f"Korrekte Vorhersagen: {correct}/{total}") print(f"Genauigkeit: {accuracy:.2f}%") my_handwriting = [ ("meine_0.png", 0), ("meine_1.png", 1), ("meine_2.png", 2), ("meine_3.png", 3), ("meine_4.png", 4), ("meine_5.png", 5), ("meine_6.png", 6), ("meine_7.png", 7), ("meine_8.png", 8), ("meine_9.png", 9), ] test_custom_handwriting(network, my_handwriting)