8. Unterrichtsblock

Kursinhalte
- Effiziente Replikation durch Prompting
- Robuste Programmierung und Fehlerbehandlung
- Code-Validierung und Debugging mit LLMs
Replikation & Qualitätssicherung mit Ki am Beispiel des Perzeptrons
In den vorherigen Blöcken dieses Semesters habt ihr die Grundlagen des Maschinellen Lernens erarbeitet. Dazu gehört die Theorie hinter dem Perzeptron – einem zentralen Baustein neuronaler Netze – sowie die manuelle Implementierung seiner Funktionsweise. Ihr wisst nun, wie ein Perzeptron lernt, warum Vektorisierung wichtig ist und wie man es als robuste Python-Klasse strukturiert. Dieses Verständnis bildet die Basis eurer Kompetenz als KI-Entwickler.
Moderne Softwareentwicklung im Bereich der Künstlichen Intelligenz erfordert neben diesem Grundlagenwissen auch Effizienz und Qualitätssicherung. Hier setzen generative KI-Tools wie Large Language Models (LLMs) an. Der 8. Unterrichtsblock verbindet euer manuelles Programmierwissen mit den Möglichkeiten moderner KI-Assistenz.
Ihr lernt, wie ihr euer Verständnis des Perzeptrons gezielt einsetzen könnt, um LLMs zur Codegenerierung zu nutzen und dabei qualitativ hochwertige, robuste und testbare Ergebnisse zu erzielen. Ziel ist nicht, die KI unkritisch zu übernehmen, sondern sie als unterstützendes Werkzeug einzusetzen, dessen Output ihr durch euer Fachwissen steuern und überprüfen könnt. Damit beschleunigt ihr eure Entwicklungsprozesse und erreicht den nächsten Schritt professioneller KI-Entwicklung – vom Verstehen zur effizienten und qualitätsgesicherten Umsetzung.
1. Effiziente Replikation durch Prompting
In diesem ersten Schritt nutzen wir unser umfassendes Verständnis des Perzeptrons, um generative KI-Tools gezielt für die schnelle Replikation komplexer Code-Strukturen einzusetzen. Das Ziel ist es, euch zu zeigen, wie ihr mit präzisem Prompting einen vollständigen und funktionsfähigen Perzeptron-Code in kürzester Zeit erzeugen könnt – ein echter „Speedrun“ nach der detaillierten manuellen Implementierung.
Aufgabe: Der „One-Shot“-Prompt
Das Ziel ist es, ein LLM wie Ollama dazu zu bringen, eine vollständige Implementierung der Perceptron-Klasse in einem einzigen, umfassenden Prompt zu generieren. Dies ist der „Speedrun“ – das schnelle Erreichen eines funktionierenden Ergebnisses.
Deine Aufgabe:
Formuliert einen einzigen, komplexen Prompt, der euer lokal laufendes LLM (z.B. über Ollama) anweist, eine vollständige, vektorisierte Python-Klasse namens Perceptron zu generieren.
- Euer Prompt muss sicherstellen, dass die generierte Klasse mindestens die folgenden essenziellen Methoden enthält und dabei vektorisierte Operationen (idealerweise mit NumPy) verwendet:
__init__(self, num_features, learning_rate=0.01, epochs=100): Initialisiert das Perzeptron mit der Anzahl der Features, Lernrate und Epochen.predict(self, features): Berechnet die Ausgabe des Perzeptrons für gegebene Eingabefeatures (oft mit einer Aktivierungsfunktion wie dem Step-Funktion).train(self, training_features, labels): Implementiert die Perzeptron-Lernregel, um die Gewichte basierend auf den Trainingsdaten und Labels anzupassen.
Ziel: Der vom LLM generierte Code sollte sofort lauffähig sein und die Kernfunktionalität eines Perzeptrons abbilden, so wie ihr es in den vorherigen Blöcken gelernt habt.
- Beispiel-Prompt-Struktur (für euch zum Weiterentwickeln):
"Generiere eine Python-Klasse namens 'Perceptron'. Die Klasse soll im Konstruktor 'num_features', 'learning_rate' und 'epochs' entgegennehmen und Gewichte sowie Bias initialisieren. Implementiere eine 'predict'-Methode, die für gegebene Features die Ausgabe des Perzeptrons berechnet. Implementiere eine 'train'-Methode, die die Gewichte und den Bias mithilfe der Perzeptron-Lernregel über mehrere Epochen anpasst. Stelle sicher, dass alle Operationen vektorisiert mit NumPy durchgeführt werden."
Prompt-Tuning für Code-Qualität
Ein von einem LLM generierter Code muss nicht nur funktionieren, sondern auch den Standards guter Softwareentwicklung entsprechen. Funktionalität ist die Basis, aber Lesbarkeit, Wartbarkeit und Einhaltung von Konventionen sind entscheidend, besonders wenn man im Team arbeitet oder der Code später erweitert werden soll. Hier setzen wir Techniken des Prompt-Tunings ein, um die Qualität des LLM-Outputs aktiv zu steuern.
Ziel ist es, das LLM zu einem „sauberen“ Programmierer zu erziehen, der nicht nur Code schreibt, sondern diesen auch nach Best Practices formatiert und dokumentiert.
1. Gut dokumentierter Code
Gute Dokumentation ist unerlässlich. Sie erklärt, was der Code tut, wie er es tut und warum bestimmte Entscheidungen getroffen wurden. Python nutzt hierfür primär Docstrings (für Module, Klassen, Funktionen) und Kommentare.
Aufgabe: Docstrings & Kommentare mit LLM erstellen
Formuliert einen Prompt, mit dem ihr das LLM anweist, für eine bestehende Python-Klasse und alle ihre Methoden aussagekräftige Docstrings zu generieren. Die Docstrings sollen folgende Aspekte enthalten:
- Zweck der Klasse bzw. Methode
- Beschreibung der Parameter
- Rückgabewerte
- ggf. aufgeworfene Fehler
Zusätzlich soll das LLM prägnante Inline-Kommentare für komplexere Codezeilen ergänzen, um die Logik verständlicher zu machen.
Beispiel für eine Prompt-Erweiterung:
„Ergänze die Klasse und alle Methoden mit umfassenden Docstrings, die deren Zweck, Parameter und Rückgabewerte klar beschreiben. Füge auch sinnvolle Inline-Kommentare hinzu, um komplexere Logik zu erklären.“
Erwarteter Code-Output:
class Perceptron:
"""
Implementiert ein einfaches Perzeptron-Modell zur binären Klassifikation.
Attribute:
num_features (int): Anzahl der Eingabefeatures.
learning_rate (float): Die Lernrate des Perzeptrons.
epochs (int): Die Anzahl der Trainingsdurchläufe über den gesamten Datensatz.
weights (np.ndarray): Die Gewichte des Perzeptrons.
bias (float): Der Bias des Perzeptrons.
"""
def __init__(self, num_features, learning_rate=0.01, epochs=100):
# Initialisiere Gewichte zufällig oder mit Nullen
self.weights = np.zeros(num_features)
self.bias = 0.0
self.learning_rate = learning_rate
self.epochs = epochs
def predict(self, features):
"""
Macht eine Vorhersage basierend auf den aktuellen Gewichten.
Args:
features (np.ndarray): Ein Array von Eingabefeatures.
Returns:
int: Die Klassenvorhersage (0 oder 1).
"""
# ... Implementierung der Vorhersage-Logik ...
2. PEP 8-Konformität
PEP 8 ist der offizielle Style Guide für Python-Code. Er legt fest, wie Python-Programme geschrieben und formatiert werden sollen, damit sie lesbar, konsistent und wartbar bleiben. Ein einheitlicher Stil erleichtert die Zusammenarbeit im Team und macht es einfacher, fremden Code schnell zu verstehen
Was genau ist PEP 8?
- PEP = Python Enhancement Proposal: Das sind Dokumente, in denen neue Standards oder Empfehlungen für die Sprache festgehalten werden.
- PEP 8 wurde 2001 von Guido van Rossum (dem Python-Erfinder) und anderen erstellt und ist seitdem der maßgebliche Leitfaden für Code-Stil.
- Es gilt für die Standardbibliothek von Python, wird aber auch in der Praxis von fast allen Projekten übernommen.
Zentrale Regeln von PEP 8
- Einrückung: 4 Leerzeichen pro Ebene (keine Tabs).
- Zeilenlänge: Maximal 79 Zeichen pro Zeile.
- Leerzeilen: Funktionen und Klassen durch zwei Leerzeilen trennen.
- Namenskonventionen:
- Variablen und Funktionen:
lower_case_with_underscores - Klassen:
CamelCase - Konstanten:
ALL_CAPS
- Variablen und Funktionen:
- Importe: Am Anfang der Datei, jeweils eine Zeile pro Modul.
- Kommentare & Docstrings: Klar, präzise und nach PEP 257 (Docstring-Konventionen).
Aufgabe: PEP 8-konformen Code mit LLM generieren
Formuliert einen Prompt, mit dem ihr das LLM anweist, Python-Code zu erzeugen, der den offiziellen PEP 8 Style Guides entspricht. Achtet dabei insbesondere auf folgende Punkte:
- Benennung:
- Klassen:
CamelCase - Funktionen und Variablen:
snake_case
- Klassen:
- Einrückung: Immer 4 Leerzeichen verwenden
- Leerzeichen: Korrekte Nutzung um Operatoren und nach Kommas
- Zeilenlänge: Maximal 79–99 Zeichen pro Zeile
- Importe: Saubere Organisation am Anfang des Codes
Beispiel-Erweiterung für euren Prompt:
„Stelle sicher, dass der generierte Python-Code strikt den PEP 8 Style Guides folgt. Achte insbesondere auf korrekte Benennungen (z. B. snake_case für Methoden und Attribute, CamelCase für die Klasse), 4-Leerzeichen-Einrückungen und die Organisation von Importen.“
Erwarteter Code-Output:
import numpy as np # Importe am Anfang
class Perceptron: # Klasse: CamelCase
def __init__(self, num_features, learning_rate=0.01, epochs=100):
self.weights = np.zeros(num_features) # Attribute: snake_case
self.bias = 0.0
self.learning_rate = learning_rate # Variablen: snake_case
self.epochs = epochs
def _activation_function(self, x): # Private Helper-Funktion: _snake_case
return 1 if x >= 0 else 0
def predict(self, features):
linear_output = np.dot(features, self.weights) + self.bias
return self._activation_function(linear_output)
3. Optimale Struktur
Neben grundlegender Dokumentation und Stil geht es auch um die Architektur des Codes, um ihn wartbarer zu machen.
Aufgabe: Strukturierter Python-Code mit LLM-Unterstützung
Formuliert einen Prompt, mit dem ihr das LLM anweist, Python-Code übersichtlich und strukturiert zu generieren. Achtet dabei insbesondere auf folgende Punkte:
- Imports: Alle benötigten Importe (z. B.
numpy) stehen am Anfang der Datei. - Klassen & Methoden: Logische Strukturierung, klare Trennung der Verantwortlichkeiten.
- Helper-Funktionen: Wenn sinnvoll, extrahiert wiederkehrende oder spezialisierte Logik in private Hilfsmethoden (mit Unterstrich beginnend), um Hauptmethoden wie
predictodertrainübersichtlicher zu halten.
Beispiel-Erweiterung für euren Prompt:
„Strukturiere den Code übersichtlich, platziere alle benötigten Importe (z. B. ’numpy‘) am Anfang der Datei. Wenn es sinnvoll ist, um die ‚predict‘- oder ‚train‘-Methode klarer zu gestalten, extrahiere wiederkehrende oder spezialisierte Logik in private Hilfsmethoden (beginnend mit einem Unterstrich).“
Arbeitsauftrag
- Diskutiert in der Klasse, ob und wo Helper-Funktionen sinnvoll eingesetzt wurden.
- Erstellt einen Prompt nach obigem Muster.
- Lasst das LLM einen Beispielcode generieren (z. B. eine kleine Klasse mit
train– undpredict-Methoden). - Überprüft, ob die Imports korrekt am Anfang stehen und die Methoden logisch strukturiert sind.
Erwarteter Code-Output (Auszug, mit Helper-Funktion):
import numpy as np
class Perceptron:
# ... __init__ Methode ...
def _step_function(self, x): # Eine private Helper-Methode für die Aktivierung
return np.where(x >= 0, 1, 0) # Vektorisierte Step-Funktion
def predict(self, features):
linear_output = np.dot(features, self.weights) + self.bias
return self._step_function(linear_output) # Nutzung der Helper-Funktion
# ... train Methode ...
Durch das bewusste Einbinden solcher Anweisungen in eure Prompts transformiert ihr das LLM von einem reinen Codegenerator zu einem „Junior-Entwickler“, der von Anfang an auf eine hohe Codequalität achtet. Dies ist eine Schlüsselkompetenz, um die erzeugten Ergebnisse effektiv in größere Projekte zu integrieren.
Geschwindigkeitsvergleich: Verständnis vor Effizienz
Nachdem ihr gesehen habt, wie schnell ein LLM den Code für das Perzeptron generieren kann, stellt sich die Frage: Warum haben wir dann die Blöcke 3 bis 7 darauf verwendet, es manuell zu implementieren?
- Die Antwort liegt im Verständnis: Die manuelle Implementierung jedes Schrittes (von der Funktion bis zur Klasse) hat euch das tiefe konzeptionelle Verständnis vermittelt. Ohne dieses Verständnis:
- Könntet ihr den komplexen „One-Shot“-Prompt nicht korrekt formulieren. Ihr wüsstet nicht, welche Parameter, welche Methoden und welche Optimierungen (z.B. Vektorisierung) notwendig sind.
- Könntet ihr den vom LLM generierten Code nicht validieren. Ihr wärt nicht in der Lage zu erkennen, ob der Code korrekt ist, Fehler enthält oder optimiert werden kann.
- Wäret ihr nicht in der Lage, den Code bei Fehlern selbst zu debuggen oder anzupassen, da das grundlegende Wissen fehlen würde.
Dieser „Speedrun“ ist daher nicht nur eine Demonstration von KI-Effizienz, sondern eine Bestätigung, dass das in den Vorblöcken erworbene fundamentale Verständnis die absolute Grundlage dafür ist, generative KIs als mächtiges Werkzeug effektiv und verantwortungsbewusst einzusetzen.
Robuste Programmierung und Fehlerbehandlung
Nachdem wir gelernt haben, wie man mit LLMs schnell und qualitativ hochwertigen Code generiert, wenden wir uns nun einem entscheidenden Aspekt professioneller Softwareentwicklung zu: der Robustheit und Fehlerbehandlung. Selbst der beste Code kann bei unerwarteten Eingaben oder unvorhergesehenen Umständen fehlschlagen. Eine robuste Anwendung fängt solche Probleme ab, informiert den Nutzer und stürzt nicht einfach ab.
In diesem Abschnitt werden wir lernen, wie man LLMs anleitet, Code zu generieren, der nicht nur funktioniert, sondern auch try-except-Blöcke und Validierungslogik enthält, um eure ML-Anwendungen zuverlässiger zu machen.
Prompting für Robustheit: Antizipieren von Problemen
Der erste Schritt zu robusterem Code ist die Fähigkeit, potenzielle Fehlerquellen zu identifizieren. Ein LLM kann uns dabei helfen, diese systematisch in den Code zu integrieren.
- Prompt-Anweisung: Wie weist man das LLM an, den generierten Code mit sinnvollen Fehlerprüfungen zu versehen? Man muss es explizit nach den Typen von Problemen fragen, die auftreten könnten.
- Generelle Strategie: Fügt Anweisungen hinzu, die das LLM dazu auffordern, Code für die Validierung von Eingaben und das Abfangen von Ausnahmen zu generieren.
Spezifische Anwendungsfälle für die Perceptron-Klasse:
Wir werden die Perceptron-Klasse um folgende wichtige Robustheits-Checks erweitern:
1. Daten-Validierung (Input-Checks):
Damit ein Perzeptron korrekt arbeiten kann, müssen die Eingabedaten bestimmte Anforderungen erfüllen. Dazu gehören die richtige Anzahl an Features, numerische Werte und das Fehlen von fehlerhaften Einträgen wie NaN. Werden diese Kriterien nicht eingehalten, kann das Modell keine zuverlässigen Ergebnisse liefern oder bricht mit Fehlermeldungen ab. Die Datenvalidierung ist daher ein zentraler Schritt, um die Qualität und Robustheit eurer Implementierung sicherzustellen. In diesem Abschnitt lernt ihr, wie ihr das LLM gezielt anweist, entsprechende Prüfungen in den Code einzubauen und bei fehlerhaften Eingaben aussagekräftige Fehlermeldungen auszugeben.
Aufgabe: Datenvalidierung im Perzeptron mit LLM-Unterstützung
Problemstellung: Ein Perzeptron erwartet Eingabedaten (Features und Labels), die bestimmte Kriterien erfüllen – z. B. die korrekte Anzahl an Features, numerische Werte und keine NaN-Werte. Was passiert, wenn die Eingabe falsche Dimensionen hat, nicht numerisch ist oder NaN-Werte enthält?
Lernziel: Ihr lernt, wie ihr das LLM anweist, Code zu generieren, der die Form (Shape) und den Datentyp der Eingabedaten prüft und bei fehlerhaften Eingaben geeignete Ausnahmen auslöst.
Auftrag:
- Erstellt einen Prompt, mit dem ihr das LLM auffordert, in die
train– undpredict-Methoden des Perzeptrons eine Datenvalidierung einzubauen. - Die Validierung soll sicherstellen, dass:
featuresundlabels(beitrain) vom Typnumpy.ndarraysind- die Dimensionen (Shape) korrekt zum Perzeptron passen
- keine NaN-Werte enthalten sind
- Bei fehlerhaften Eingaben soll eine aussagekräftige
ValueErrorException ausgelöst werden.
Beispiel-Erweiterung für euren Prompt:
„Füge in die predict– und train-Methoden Code zur Datenvalidierung ein. Überprüfe, ob die features und labels (im Fall von train) vom Typ numpy.ndarray sind und die korrekte Dimension (Shape) für das Perzeptron haben. Bei Fehlern soll eine aussagekräftige ValueError Exception ausgelöst werden.“
Erwarteter Code-Output:
def train(self, training_features, labels):
if not isinstance(training_features, np.ndarray) or training_features.ndim != 2:
raise ValueError("training_features muss ein 2D NumPy-Array sein.")
if training_features.shape[1] != self.num_features:
raise ValueError(f"training_features muss {self.num_features} Spalten haben, aber hat {training_features.shape[1]}.")
if not isinstance(labels, np.ndarray) or labels.ndim != 1:
raise ValueError("labels muss ein 1D NumPy-Array sein.")
if len(labels) != training_features.shape[0]:
raise ValueError("Anzahl der Labels muss der Anzahl der Trainingsbeispiele entsprechen.")
if np.isnan(training_features).any() or np.isnan(labels).any():
raise ValueError("Eingabedaten oder Labels dürfen keine NaN-Werte enthalten.")
# ... Rest der Trainingslogik ...
2. Exception Handling (try...except-Blöcke):
Auch wenn die Eingaben korrekt sind, können interne mathematische Operationen (z. B. Division durch Null, Überlauf, Speicherprobleme) unerwartete Fehler verursachen. Solche Laufzeitfehler sollten nicht dazu führen, dass das gesamte Programm abstürzt.
Aufgabe: Fehlerbehandlung mit try...except im Perzeptron
Lernziel: Ihr lernt, wie ihr das LLM anweist, kritische Abschnitte des Codes mit try...except-Blöcken zu versehen, um Laufzeitfehler abzufangen und sinnvoll zu behandeln.
Auftrag:
- Erstellt einen Prompt, mit dem ihr das LLM auffordert, die Kernlogik der
train– undpredict-Methoden des Perzeptrons intry...except-Blöcke zu kapseln. - Definiert, welche Fehlerarten sinnvoll abgefangen werden sollen (z. B.
ValueError,ZeroDivisionError,MemoryError). - Stellt sicher, dass bei einem Fehler eine aussagekräftige Fehlermeldung ausgegeben wird, die den Grund des Problems klar benennt.
- Testet den generierten Code mit absichtlich fehlerhaften Eingaben (z. B. falsche Dimensionen oder Division durch Null), um die Fehlerbehandlung zu überprüfen.
Beispiel-Erweiterung für euren Prompt:
„Kapsle die Kernlogik der predict– und train-Methoden in try...except-Blöcke. Fange dabei typische Fehler wie ValueError oder ZeroDivisionError ab und gib eine klare Fehlermeldung aus, die den Grund des Problems beschreibt.“
Erwarteter Code-Output:
def predict(self, features):
try:
# Sicherstellen, dass features korrekt sind, bevor wir weiterrechnen
if not isinstance(features, np.ndarray) or features.ndim not in [1, 2]:
raise ValueError("Features müssen ein 1D oder 2D NumPy-Array sein.")
if features.ndim == 1 and self.num_features != features.shape[0]:
raise ValueError(f"Features müssen {self.num_features} Elemente haben für 1D Eingabe.")
elif features.ndim == 2 and self.num_features != features.shape[1]:
raise ValueError(f"Features müssen {self.num_features} Spalten haben für 2D Eingabe.")
linear_output = np.dot(features, self.weights) + self.bias
return self._step_function(linear_output)
except ValueError as ve:
print(f"Fehler bei der Vorhersage (Input-Validierung): {ve}")
raise # Wirf den spezifischen Fehler weiter
except Exception as e:
print(f"Ein unerwarteter Fehler ist bei der Vorhersage aufgetreten: {e}")
raise # Wirf den generischen Fehler weiter
Durch die Integration dieser Techniken in den Prompt lernt ihr nicht nur, funktionierenden Code zu generieren, sondern auch proaktiv über die Zuverlässigkeit eurer KI-Anwendungen nachzudenken. Dies ist ein entscheidender Schritt hin zu professionelleren und einsatzbereiteren Machine-Learning-Lösungen.
Code-Validierung und Debugging mit LLMs
Nachdem wir Code generiert und mit Fehlerbehandlung versehen haben, kommt der kritische Schritt: Wie stellen wir sicher, dass der Code auch wirklich das tut, was er soll, und wie finden wir Fehler, wenn er es nicht tut? In diesem Abschnitt tauchen wir in die Welt der Code-Validierung und des Debuggings ein, wobei wir generative KI-Tools als mächtige Assistenten einsetzen.
Unit-Testing-Grundlagen: Automatisierte Qualitätskontrolle
Automatisierte Tests sind das Rückgrat der Softwarequalität. Sie erlauben es uns, schnell zu überprüfen, ob einzelne Komponenten (wie unsere Perceptron-Klasse) korrekt funktionieren, auch wenn sich der Code im Laufe der Entwicklung ändert.
Aufgabe: Einführung in Unit Tests am Beispiel des Perzeptrons
Lernziel: Ihr erhaltet eine Einführung in die Bedeutung und die grundlegenden Konzepte von Unit Tests. Ziel ist nicht die detaillierte Arbeit mit komplexen Test-Frameworks wie unittest oder pytest, sondern das Verständnis dafür, was getestet werden sollte und wie ein Test grundlegend aufgebaut ist.
Prompt-Ziel: Ihr werdet das LLM anweisen, einfache Unit Tests für die von euch generierte Perceptron-Klasse zu erstellen. Dadurch überprüft ihr die Funktionalität und lernt, wie man Code testbar gestaltet.
Typische Tests für das Perzeptron:
- Test der
__init__-Methode: Prüfen, ob Gewichte und Bias korrekt initialisiert wurden. - Test der
predict-Methode: Mit bekannten Gewichten und Eingaben testen (z. B. 2×2 mit Gewichten(1,0)und Bias-1sollte bei Eingabe(0.5, 0.5)eine0ergeben). - Test der
train-Methode: Mit einem kleinen, linear trennbaren Datensatz prüfen, ob Gewichte und Bias korrekt konvergieren.
Beispiel-Erweiterung für euren Prompt:
„Erstelle Python-Code für einfache Unit Tests der Perceptron-Klasse. Schreibe Tests für die Initialisierung, die predict-Methode mit bekannten Beispiel-Features und Gewichten sowie einen Test für die train-Methode mit einem kleinen, linear trennbaren Datensatz, um die korrekte Gewichtsaktualisierung zu überprüfen.“
Arbeitsauftrag
- Diskutiert in der Klasse, welche Aspekte des Perzeptrons sinnvoll getestet werden können und warum.
- Formuliert einen Prompt nach obigem Muster.
- Lasst das LLM die Unit Tests generieren.
- Führt die Tests aus und überprüft, ob sie erfolgreich durchlaufen.
Erwarteter Code-Output
import unittest
import numpy as np
# from your_perceptron_module import Perceptron # Annahme, dass Perceptron importierbar ist
class TestPerceptron(unittest.TestCase):
def test_initialization(self):
# Testet, ob Gewichte und Bias korrekt initialisiert werden
perceptron = Perceptron(num_features=2, learning_rate=0.1, epochs=10)
self.assertEqual(perceptron.weights.shape[0], 2)
self.assertEqual(perceptron.bias, 0.0)
def test_predict_method(self):
# Testet die Vorhersage bei bekannten Gewichten
perceptron = Perceptron(num_features=2)
perceptron.weights = np.array([1, 1])
perceptron.bias = -0.5 # Schwelle
# Testfall 1: Input, der 1 ergeben sollte
self.assertEqual(perceptron.predict(np.array([0.5, 0.5])), 1)
# Testfall 2: Input, der 0 ergeben sollte
self.assertEqual(perceptron.predict(np.array([0.1, 0.1])), 0)
def test_train_method_simple_dataset(self):
# Testet die Trainingsfähigkeit auf einem einfachen linear trennbaren Datensatz
perceptron = Perceptron(num_features=2, learning_rate=0.1, epochs=100)
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([0, 0, 0, 1]) # XOR ist nicht linear trennbar, nehmen wir AND oder OR
# Für AND:
# y = np.array([0, 0, 0, 1])
# Für OR:
y_or = np.array([0, 1, 1, 1])
perceptron.train(X, y_or)
# Nach dem Training sollten die Vorhersagen für den Trainingsdatensatz korrekt sein
for i in range(len(X)):
self.assertEqual(perceptron.predict(X[i]), y_or[i],
f"Fehler bei Beispiel {i}: {X[i]}")
if __name__ == '__main__':
unittest.main()
Debugging-Strategien mit LLMs: Der KI-Assistent bei der Fehlersuche
Selbst mit Tests können Fehler auftreten. Hier wird das LLM zu einem wertvollen Partner beim Debugging. Es kann helfen, komplexe Fehlermeldungen zu interpretieren und potenzielle Lösungen vorzuschlagen.
Aufgabe: Debugging mit LLM-Unterstützung
Lernziel: Ihr lernt, wie ihr das LLM effektiv als Debugging-Assistenten einsetzt. Diese Fähigkeit ist zentral und wird euch in eurer gesamten Entwicklerkarriere begleiten.
Prozess:
- Fehlerhafter Code + Stack Trace: Gebt dem LLM einen fehlerhaften Code-Ausschnitt (z. B. einen initialen Lernschritt mit Fehlern oder einen absichtlich eingebauten Bug) zusammen mit dem vollständigen Stack Trace bzw. der Fehlermeldung von Python.
- Kontext bereitstellen: Erklärt dem LLM, was ihr mit dem Code erreichen wolltet und wie ihr die Fehlermeldung bisher versteht.
- LLM-Analyse und Vorschläge: Das LLM soll den Fehler analysieren, die Ursache identifizieren und konkrete Korrekturvorschläge machen. Es kann auch helfen, logische Fehler oder Laufzeitprobleme zu erkennen.
Beispiel-Prompt für Debugging:
„Ich erhalte diesen Fehler in meiner
Perceptron-Klasse, wenn ich dietrain-Methode aufrufe. Hier ist mein Code und der Stack Trace. Kannst du den Fehler finden und mir erklären, warum er auftritt und wie ich ihn beheben kann? [HIER DEINEN FEHLERHAFTEN CODE EINFÜGEN] [HIER DEN VOLLSTÄNDIGEN STACK TRACE EINFÜGEN] Der Fehler scheint bei der Aktualisierung der Gewichte aufzutreten. Ich habe erwartet, dass die Gewichte gemäß der Perzeptron-Lernregel angepasst werden.“
Arbeitsauftrag
- Diskutiert in der Klasse, ob die Analyse und die vorgeschlagenen Korrekturen des LLM sinnvoll und nachvollziehbar sind.
- Baut absichtlich einen kleinen Fehler in eure
Perceptron-Klasse ein (z. B. falsche Dimension, Tippfehler oder fehlerhafte Berechnung). - Führt den Code aus und kopiert den vollständigen Stack Trace oder die Fehlermeldung.
- Erstellt einen Prompt nach obigem Muster und gebt Code + Fehlermeldung an das LLM.
Vorteile: Dieses Vorgehen beschleunigt nicht nur die Fehlersuche, sondern schult euch auch darin, Fehlermeldungen besser zu verstehen und analytischer an Debugging-Probleme heranzugehen, da ihr die Erklärungen des LLMs nachvollziehen und bewerten müsst.
