Files
KI/P4/P4_2.py
2025-11-21 16:48:18 +01:00

348 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)