Künstliche Intelligenz II - Exam
Aufgabe 1)
Du bist beauftragt, ein neuronales Netzwerk zur Klassifikation von Handgeschriebenen Ziffern (MNIST Datensatz) zu entwerfen und zu trainieren. Das Netzwerk besteht aus mehreren Schichten und verwendet verschiedene Optimierungstechniken. Es ist wichtig, das Netzwerk zu bewerten und zu optimieren, um eine hohe Genauigkeit und geringe Überanpassung zu erzielen. Dein Netzwerk soll Feedback- und Feedforward-Mechanismen implementieren.
a)
Beschreibe die Architektur Deines neuronalen Netzwerks zur Klassifikation des MNIST Datensatzes. Wie viele Schichten würdest Du verwenden? Welche Funktionen haben die einzelnen Schichten? Achte darauf, die Eingabe-, versteckten und Ausgabeschichten richtig zu definieren.
Lösung:
Architektur des Neuronalen Netzwerks zur Klassifikation des MNIST Datensatzes
- Eingabeschicht:
- Diese Schicht nimmt die Eingabedaten entgegen. Da der MNIST Datensatz handgeschriebene Ziffern in Form von 28x28 Pixel Bildern enthält, hat die Eingabeschicht 784 Neuronen (28*28).
- Erste versteckte Schicht:
- Eine dichte (fully connected) Schicht mit 128 Neuronen. Aktivierungsfunktion: ReLU (Rectified Linear Unit).
- Zweite versteckte Schicht:
- Eine dichte Schicht mit 64 Neuronen. Aktivierungsfunktion: ReLU.
- Dritte versteckte Schicht:
- Eine dichte Schicht mit 32 Neuronen. Aktivierungsfunktion: ReLU.
- Ausgabeschicht:
- Die Ausgabeschicht besteht aus 10 Neuronen, da es 10 mögliche Klassen (Ziffern 0 bis 9) gibt. Aktivierungsfunktion: Softmax, um Wahrscheinlichkeiten für jede Klasse zu erhalten.
Zusätzliche Optimierungstechniken:
- Dropout:
- Um Überanpassung zu vermeiden, wird nach jeder versteckten Schicht Dropout angewendet (zum Beispiel 20% Neuronen deaktivieren).
- Batch-Normalisierung:
- Batch-Normalisierung könnte in den versteckten Schichten verwendet werden, um den Training-Prozess zu beschleunigen und Stabilität zu gewährleisten.
Implementierung in Python (unter Verwendung von Keras):
from keras.models import Sequentialfrom keras.layers import Dense, Dropout, Flattenfrom keras.optimizers import Adam# Erstelle das Modellmodel = Sequential()# Eingabeschicht und erste versteckte Schichtmodel.add(Dense(128, input_shape=(784,), activation='relu'))model.add(Dropout(0.2))# Zweite versteckte Schichtmodel.add(Dense(64, activation='relu'))model.add(Dropout(0.2))# Dritte versteckte Schichtmodel.add(Dense(32, activation='relu'))model.add(Dropout(0.2))# Ausgabeschichtmodel.add(Dense(10, activation='softmax'))# Kompiliere das Modellmodel.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy'])# Modellzusammenfassungmodel.summary()
b)
Implementiere den Feedforward- und Backpropagation-Prozess für Dein neuronales Netzwerk in Python. Nutze die folgende Struktur:
'import numpy as npdef initialize_parameters(layer_dims): # Initialisiere Parameter passdef forward_propagation(X, parameters): # Implementiere den Feedforward-Schritt hier passdef compute_cost(AL, Y): # Berechne die Kosten passdef backward_propagation(AL, Y, caches): # Implementiere den Backpropagation-Schritt hier passdef update_parameters(parameters, grads, learning_rate): # Aktualisiere die Parameter passlayer_dims = [784, 128, 64, 10] # Beispiel Layerstrukturparameters = initialize_parameters(layer_dims)X = np.random.randn(784, 100) # BeispieldatenY = np.random.randn(10, 100) # Beispiel-Labels'
Fülle die Lücken in den Funktionen aus und erkläre die mathematischen Formeln, die Du verwendet hast.
Lösung:
Implementierung des Feedforward- und Backpropagation-Prozesses in Python
import numpy as np
1. Initialisierung der Parameter
Um die Parameter (Gewichte und Biases) für jede Schicht zu initialisieren:
def initialize_parameters(layer_dims): np.random.seed(1) parameters = {} for l in range(1, len(layer_dims)): parameters['W' + str(l)] = np.random.randn(layer_dims[l], layer_dims[l-1]) * 0.01 parameters['b' + str(l)] = np.zeros((layer_dims[l], 1)) return parameters
- Erklärungen:Jede Gewichtsmatrix Wl wird mit kleinen Zufallswerten initialisiert, während die Bias-Vektoren bl auf Null gesetzt werden.
2. Forward Propagation
Implementiere den Feedforward-Prozess:
def forward_propagation(X, parameters): caches = {} A = X L = len(parameters) // 2 # Anzahl der Schichten im Netzwerk (ohne die Eingabeschicht) for l in range(1, L): A_prev = A Z = np.dot(parameters['W' + str(l)], A_prev) + parameters['b' + str(l)] A = np.maximum(0, Z) # ReLU Aktivierungsfunktion caches['A' + str(l-1)] = A_prev caches['Z' + str(l)] = Z caches['A' + str(l)] = A # Ausgabeschicht mit Softmax-Aktivierung ZL = np.dot(parameters['W' + str(L)], A) + parameters['b' + str(L)] AL = np.exp(ZL) / np.sum(np.exp(ZL), axis=0, keepdims=True) caches['Z' + str(L)] = ZL caches['A' + str(L)] = AL return AL, caches
- Erklärungen:Die ReLU-Aktivierungsfunktion wird für die versteckten Schichten verwendet, während die Softmax-Aktivierung in der Ausgabeschicht verwendet wird, um Wahrscheinlichkeiten zu erhalten.
3. Berechnung der Kosten
Die Cross-Entropy Kostenfunktion:
def compute_cost(AL, Y): m = Y.shape[1] cost = -1/m * np.sum(Y * np.log(AL)) cost = np.squeeze(cost) # Um sicherzustellen, dass cost eine skalare Ausgabe ist return cost
- Erklärungen:Die Cross-Entropy Kostenfunktion berechnet den Verlust zwischen der vorhergesagten Verteilung, AL, und den echten Labels, Y.
4. Backpropagation
Implementiere den Backpropagation-Prozess:
def backward_propagation(AL, Y, caches): grads = {} L = len(caches) // 3 # Anzahl der Schichten (ohne die Eingabeschicht) m = AL.shape[1] Y = Y.reshape(AL.shape) dZL = AL - Y grads['dW' + str(L)] = 1/m * np.dot(dZL, caches['A' + str(L-1)].T) grads['db' + str(L)] = 1/m * np.sum(dZL, axis=1, keepdims=True) dA_prev = np.dot(parameters['W' + str(L)].T, dZL) for l in reversed(range(1, L)): dZ = np.array(dA_prev, copy=True) dZ[caches['Z' + str(l)] <= 0] = 0 # ReLU Backward grads['dW' + str(l)] = 1/m * np.dot(dZ, caches['A' + str(l-1)].T) grads['db' + str(l)] = 1/m * np.sum(dZ, axis=1, keepdims=True) dA_prev = np.dot(parameters['W' + str(l)].T, dZ) return grads
- Erklärungen:Die Backpropagation durch die Schichten erfolgt durch Berechnen der Gradienten für die Gewichtsmatrizen und Biases, beginnend von der Ausgabeschicht bis hin zur ersten versteckten Schicht. Bei der ReLU-Aktivierungsfunktion wird die Ableitung berücksichtigt.
5. Aktualisierung der Parameter
Die Parameter werden mithilfe des Gradientenabstiegs aktualisiert:
def update_parameters(parameters, grads, learning_rate): L = len(parameters) // 2 # Anzahl der Schichten im Netzwerk (ohne die Eingabeschicht) for l in range(1, L+1): parameters['W' + str(l)] -= learning_rate * grads['dW' + str(l)] parameters['b' + str(l)] -= learning_rate * grads['db' + str(l)] return parameters
- Erklärungen:Jede Gewichtsmatrix und jeder Bias-Vektor werden mit ihrem entsprechenden Gradienten und dem Lernrate aktualisiert.
Kompletter Code inklusive Funktionsaufrufe:
import numpy as npdef initialize_parameters(layer_dims): np.random.seed(1) parameters = {} for l in range(1, len(layer_dims)): parameters['W' + str(l)] = np.random.randn(layer_dims[l], layer_dims[l-1]) * 0.01 parameters['b' + str(l)] = np.zeros((layer_dims[l], 1)) return parametersdef forward_propagation(X, parameters): caches = {} A = X L = len(parameters) // 2 for l in range(1, L): A_prev = A Z = np.dot(parameters['W' + str(l)], A_prev) + parameters['b' + str(l)] A = np.maximum(0, Z) caches['A' + str(l-1)] = A_prev caches['Z' + str(l)] = Z caches['A' + str(l)] = A ZL = np.dot(parameters['W' + str(L)], A) + parameters['b' + str(L)] AL = np.exp(ZL) / np.sum(np.exp(ZL), axis=0, keepdims=True) caches['Z' + str(L)] = ZL caches['A' + str(L)] = AL return AL, cachesdef compute_cost(AL, Y): m = Y.shape[1] cost = -1/m * np.sum(Y * np.log(AL)) cost = np.squeeze(cost) return costdef backward_propagation(AL, Y, caches): grads = {} L = len(caches) // 3 m = AL.shape[1] Y = Y.reshape(AL.shape) dZL = AL - Y grads['dW' + str(L)] = 1/m * np.dot(dZL, caches['A' + str(L-1)].T) grads['db' + str(L)] = 1/m * np.sum(dZL, axis=1, keepdims=True) dA_prev = np.dot(parameters['W' + str(L)].T, dZL) for l in reversed(range(1, L)): dZ = np.array(dA_prev, copy=True) dZ[caches['Z' + str(l)] <= 0] = 0 grads['dW' + str(l)] = 1/m * np.dot(dZ, caches['A' + str(l-1)].T) grads['db' + str(l)] = 1/m * np.sum(dZ, axis=1, keepdims=True) dA_prev = np.dot(parameters['W' + str(l)].T, dZ) return gradsdef update_parameters(parameters, grads, learning_rate): L = len(parameters) // 2 for l in range(1, L+1): parameters['W' + str(l)] -= learning_rate * grads['dW' + str(l)] parameters['b' + str(l)] -= learning_rate * grads['db' + str(l)] return parameterslayer_dims = [784, 128, 64, 10]parameters = initialize_parameters(layer_dims)X = np.random.randn(784, 100)Y = np.random.randn(10, 100)AL, caches = forward_propagation(X, parameters)cost = compute_cost(AL, Y)grads = backward_propagation(AL, Y, caches)parameters = update_parameters(parameters, grads, learning_rate=0.01)
c)
Diskutiere die Vor- und Nachteile der verschiedenen Optimierungsverfahren (SGD, Adam), die Du zum Training Deines Netzwerks verwenden kannst. Welche Methode würdest Du wählen und warum? Berücksichtige dabei auch den Einfluss von Hyperparametern wie der Lernrate und der Batchgröße.
Lösung:
Optimierungsverfahren für das Training neuronaler Netzwerke: Eine Diskussion
- Stochastic Gradient Descent (SGD):
- Vorteile:
- Einfache Implementierung und guter theoretischer Hintergrund.
- Kann in Kombination mit anderen Optimierungsverfahren verwendet werden.
- Nachteile:
- Langsame Konvergenz, besonders bei hoher Dimension der Daten.
- Konvergiert möglicherweise nicht zum globalen Minimum.
- Sensitiv gegenüber dem Rauschen in Daten.
- Adam (Adaptive Moment Estimation):
- Vorteile:
- Schnellere Konvergenz dank adaptiver Lernraten, die sich für jede Parameter individuell anpassen.
- Funktioniert gut bei großen und komplexen Datensätzen.
- Robust gegenüber Rauschen und daher stabiler als SGD.
- Nachteile:
- Einstellungen der Hyperparameter können komplex sein (z.B. Betas für den Exponential Moving Average).
- Kann manchmal zum Überanpassen neigen, da es schneller konvergiert und in lokalen Minima übermäßig verharren könnte.
Auswahl des Optimierungsverfahrens
Für das Training eines neuronalen Netzwerks zur Klassifikation des MNIST Datensatzes würde ich Adam als Optimierungsverfahren wählen. Der Hauptgrund hierfür ist die schnellere und stabilere Konvergenz im Vergleich zu SGD, insbesondere in der hochdimensionalen Datenlandschaft des MNIST Datensatzes. Dies beschleunigt den Trainingsprozess und führt in der Regel zu besseren Ergebnissen.
Einfluss von Hyperparametern
- Lernrate:
- Eine zu hohe Lernrate kann das Modell dazu bringen, zu stark von den Gradienten zu springen und das Minimum zu verpassen.
- Eine zu niedrige Lernrate führt zu sehr langsamer Konvergenz und kann den Trainingsprozess verlängern.
- Batchgröße:
- Eine kleinere Batchgröße führt zu einem weniger stabilen Training (mehr Rauschen im Gradienten), kann aber schneller lernen und besser verallgemeinern.
- Eine größere Batchgröße stabilisiert das Training, erfordert aber mehr Speicher und kann langsamer in der Iteration sein.
Durch die Wahl von Adam als Optimierungsverfahren können diese Herausforderungen leichter gemeistert werden, da Adam adaptiv auf sich ändernde Bedingungen im Lernprozess reagiert und dabei typische Probleme von SGD abmildert.
Aufgabe 2)
In dieser Aufgabe betrachten wir das Problem des Overfittings und Regularisierungstechniken, um dieses zu vermeiden. Stellen Sie sich vor, Sie sind ein Data Scientist und haben ein komplexes Modell trainiert, das bei den Trainingsdaten eine sehr hohe Genauigkeit erreicht, aber bei neuen, ungesehenen Testdaten eine niedrige Genauigkeit zeigt. Verwenden Sie Ihr Wissen über Overfitting und verschiedene Regularisierungstechniken, um die folgenden Fragen zu beantworten.
a)
a. Erklären Sie den Begriff Overfitting und warum dies ein Problem darstellt. Verwenden Sie dabei Begriffe wie Trainingsgenauigkeit und Testgenauigkeit sowie das Konzept des Datenrauschens.
Lösung:
Erklärung des Begriffs Overfitting
Overfitting tritt auf, wenn ein Modell so stark an die Trainingsdaten angepasst wird, dass es die zugrunde liegenden Muster schlecht verallgemeinern kann und stattdessen das Datenrauschen erlernt. Das bedeutet, dass das Modell bei den Trainingsdaten eine sehr hohe Genauigkeit (Trainingsgenauigkeit) erreicht, aber bei neuen, ungesehenen Testdaten eine deutlich niedrigere Genauigkeit (Testgenauigkeit) aufweist.
Hier sind die wichtigsten Punkte, die Overfitting und dessen Probleme verdeutlichen:
- Trainingsgenauigkeit vs. Testgenauigkeit: Ein Modell, das overfittet, zeigt eine hohe Leistungsfähigkeit auf den Trainingsdaten, aber nicht auf den Testdaten. Ein gut generalisierendes Modell hingegen sollte auf beiden Datensätzen ähnlich gut abschneiden.
- Datenrauschen: Datenrauschen bezieht sich auf zufällige Fehler oder Variabilität in den Trainingsdaten, die nicht zum zugrunde liegenden Muster der Daten gehören. Ein overfit Modell lernt dieses Rauschen mit, was dazu führt, dass es bei neuen Daten nicht gut arbeitet.
- Modellkomplexität: Overfitting tritt häufig bei sehr komplexen Modellen auf, die mehr Parameter haben, als nötig, um die zugrunde liegenden Muster in den Daten zu erfassen.
Zusammenfassend stellt Overfitting ein Problem dar, weil es die Fähigkeit des Modells, neue, ungesehene Daten korrekt vorherzusagen, vermindert und somit die Generalisierbarkeit einschränkt.
b)
b. Gegeben sei die Verlustfunktion eines linearen Modells:
\( J(w) = \frac{1}{2m} \sum_{i=1}^m (h_w(x^{(i)}) - y^{(i)})^2 \)
Wenden Sie L2-Regularisierung (Ridge) auf diese Verlustfunktion an und geben Sie die neue Verlustfunktion unter Berücksichtigung des Regularisierungsterms an.
Lösung:
Verlustfunktion mit L2-Regularisierung (Ridge)
Die ursprüngliche Verlustfunktion eines linearen Modells lautet:
\( J(w) = \frac{1}{2m} \sum_{i=1}^m (h_w(x^{(i)}) - y^{(i)})^2 \)
Um L2-Regularisierung (auch als Ridge-Regression bekannt) auf diese Verlustfunktion anzuwenden, fügen wir einen Regularisierungsterm hinzu, der proportional zur Summe der Quadrate der Gewichte ist. Dies führt zu folgender neuer Verlustfunktion:
\( J_{reg}(w) = \frac{1}{2m} \sum_{i=1}^m (h_w(x^{(i)}) - y^{(i)})^2 + \frac{\lambda}{2} \sum_{j=1}^n w_j^2 \)
Hierbei ist \( \lambda \) ein Hyperparameter, der die Stärke der Regularisierung kontrolliert. Der zweite Term ist der Regularisierungsterm, der die Summe der Quadrate der Gewichte \( w_j \) ist und somit zur Strafe für große Gewichte führt.
Zusammengefasst bedeutet die L2-Regularisierung, dass wir die ursprüngliche Verlustfunktion um den Regularisierungsterm \( \frac{\lambda}{2} \sum_{j=1}^n w_j^2 \) erweitern, um Overfitting zu vermeiden, indem sehr große Gewichtswerte bestraft werden.
c)
c. Nehmen wir an, Sie trainieren ein neuronales Netzwerk mit der Dropout-Methode. Beschreiben Sie in detaillierten Schritten, wie Dropout während des Trainingsprozesses angewendet wird und erläutern Sie, warum es hilft, Overfitting zu reduzieren.
Lösung:
Dropout-Methode beim Training eines neuronalen Netzwerks
Die Dropout-Methode ist eine Technik, die verwendet wird, um Overfitting in neuronalen Netzwerken zu reduzieren. Dropout funktioniert, indem zufällig einige Neuronen während des Trainingsprozesses „ausgelassen“ (gedroppt) werden. Dies bedeutet, dass diese Neuronen für diesen Trainingsdurchlauf vorübergehend nicht berücksichtigt werden. Im Folgenden werden die detaillierten Schritte beschrieben, wie Dropout angewendet wird und warum es hilft, Overfitting zu reduzieren:
- Schritt 1: Einfügen der Dropout-SchichtenVor Beginn des Trainings fügt der Data Scientist Dropout-Schichten in das neuronale Netzwerk ein. Diese Schichten werden in der Regel nach den Aktivierungsschichten platziert.
- Schritt 2: Aktivierung während des TrainingsWährend jedes Trainingsdurchlaufs (Forward Pass) wird für jede Dropout-Schicht ein Teil der Neuronen zufällig ausgewählt und auf null gesetzt. Diese zufällige Auswahl erfolgt unabhängig für jeden Trainingsdurchlauf, basierend auf einer Wahrscheinlichkeitsrate, die als Dropout-Rate (p) bekannt ist. Beispielsweise bedeutet eine Dropout-Rate von p = 0.5, dass 50% der Neuronen in dieser Schicht zufällig deaktiviert werden.
- Schritt 3: Skalierung der AktivierungenWährend des Trainings werden die verbleibenden, nicht gedroppten Neuronen in der Dropout-Schicht entsprechend skaliert. Dies geschieht, um sicherzustellen, dass die erwarteten Werte der Neuronen unverändert bleiben. Zum Beispiel, wenn 50% der Neuronen gedroppt werden (p = 0.5), werden die Aktivierungen der verbleibenden Neuronen mit 1/(1-p) = 2 skaliert.
- Schritt 4: Training des NetzwerksMit den zufällig gedroppten Neuronen und den skalierten Aktivierungen wird das Netzwerk weiter trainiert. Der Backpropagation-Algorithmus berechnet die Gradienten und aktualisiert die Netzwerkgewichte wie gewohnt.
- Schritt 5: Aktivierung während der Vorhersage (Inference)Nach dem Abschluss des Trainings und beim Einsatz (Inference) des Modells, werden alle Neuronen verwendet. Es wird keine Dropout angewendet. Allerdings sind die Gewichte der Neuronen jetzt wirksamer, da sie während des Trainings gelernt haben, auch in einer Umgebung mit zufälligem Dropout gut zu arbeiten.
Warum hilft Dropout, Overfitting zu reduzieren?
- Verbesserte Generalisierung: Da während des Trainings zufällige Neuronen deaktiviert werden, kann das Netzwerk keine Abhängigkeiten zwischen bestimmten Neuronen aufbauen. Dies zwingt die Neuronen, robuster und voneinander unabhängiger zu lernen, was die Generalisierung auf ungesehene Daten verbessert.
- Vermeidung von neuronaler Ko-Adaptation: Dropout verhindert, dass Neuronen zu stark voneinander abhängen, was zu einem redundanteren und somit robusteren Netzwerk führt, das weniger anfällig für Overfitting ist.
- Datenrauschen: Dropout wirkt wie eine Art Datenrauschen, das übermäßige Anpassungen an die Trainingsdaten verhindert und die Robustheit des Modells erhöht.
d)
d. Sie verwenden Early Stopping als Regularisierungstechnik. Beschreiben Sie detailliert, wie Early Stopping funktioniert und wie Sie feststellen, wann Sie das Training eines Modells vorzeitig beenden sollten. Gehen Sie dabei auch auf potenzielle Vor- und Nachteile von Early Stopping ein.
Lösung:
Early Stopping als Regularisierungstechnik
Early Stopping ist eine Regularisierungstechnik, die bei der Überwachung des Trainingsprozesses eines Modells verwendet wird, um Overfitting zu vermeiden. Sie funktioniert, indem das Training des Modells vorzeitig beendet wird, sobald die Leistung auf einem Validierungsdatensatz sich nicht mehr verbessert. Hier sind die detaillierten Schritte und Überlegungen, wie Early Stopping funktioniert:
- Schritt 1: Aufteilung der DatenZu Beginn wird der Trainingsdatensatz in drei Teile geteilt: Trainingsdaten, Validierungsdaten und Testdaten. Die Validierungsdaten werden verwendet, um das Modell während des Trainings zu überwachen und zu entscheiden, wann das Training beendet werden soll.
- Schritt 2: Überwachung der ValidierungsleistungWährend des Trainings wird die Leistung des Modells regelmäßig auf dem Validierungsdatensatz überprüft (z.B. nach jeder Epoche). Dies kann durch Messungen wie der Validierungsverlust oder -genauigkeit erfolgen.
- Schritt 3: Bewertung der VerbesserungNach jeder Bewertung wird überprüft, ob die Leistung auf dem Validierungsdatensatz sich verbessert hat. Wenn die Leistung sich verbessert, wird diese Version des Modells als „beste Version“ gespeichert.
- Schritt 4: Geduld und AbbruchkriteriumWenn die Leistung sich über eine bestimmte Anzahl von Epochen (genannt „Patience“) hinweg nicht verbessert, wird das Training abgebrochen. Die Anzahl der Gedulds-Epochen wird normalerweise vor dem Training festgelegt.
- Schritt 5: ModellwiederherstellungNach dem Abbruch des Trainings wird das Modell auf die „beste Version“ zurückgesetzt, die während des Trainings gespeichert wurde, basierend auf der bestmöglichen Leistung auf dem Validierungsdatensatz.
Vorteile von Early Stopping
- Reduzierung von Overfitting: Early Stopping hilft, das Modell davor zu bewahren, zu stark an die Trainingsdaten angepasst zu werden und somit besser auf ungesehene Daten zu generalisieren.
- Effizienz: Es spart Rechenressourcen und Zeit, da das Training beendet wird, sobald keine weiteren Verbesserungen erzielt werden, anstatt bis zur maximal festgelegten Epochenzahl zu trainieren.
- Einfachheit: Es ist eine einfach zu implementierende Methode, die keine zusätzlichen Hyperparameter-Optimierungen erfordert.
Nachteile von Early Stopping
- Suboptimale Modelle: In einigen Fällen kann das Modell möglicherweise untertrainiert bleiben, wenn das Abbruchkriterium zu streng war oder die Gedulds-Epochen zu gering angesetzt wurden.
- Empfindlichkeit gegenüber Validierungsdaten: Die Wahl des Validierungsdatensatzes ist sehr wichtig. Wenn die Validierungsdaten nicht repräsentativ für das Testen sind, kann dies zu einer falschen Einschätzung der Modellleistung und somit zu einem falschen Abbruchzeitpunkt führen.
Insgesamt kann Early Stopping eine effektive Methode zur Vermeidung von Overfitting sein, insbesondere in Kombination mit anderen Regularisierungstechniken wie Dropout oder L2-Regularisierung.
Aufgabe 3)
Ein Agent soll in einer einfachen Gridworld-Umgebung lernen. Die Umgebung besteht aus einem 4x4 Gitter, in dem jeder Zustand ein Feld repräsentiert. Der Agent kann sich mit den Aktionen 'oben', 'unten', 'links' und 'rechts' bewegen. Ziel ist es, aus dem Startzustand oben links (Zustand 0) in den Zielzustand unten rechts (Zustand 15) zu gelangen. Wenn der Agent das Ziel erreicht, erhält er eine Belohnung von +10. Jede Bewegung in ein anderes Feld bringt eine Belohnung von -1. Wände verhindern, dass der Agent das Gitter verlässt. Die Episode endet, wenn das Ziel erreicht ist oder 25 Schritte gemacht wurden.
b)
(b) Erkläre, wie der Agent durch Value-Learning in dieser Umgebung lernen kann, Zustände zu bewerten und skizziere die Bellman-Gleichung, die dabei zur Anwendung kommt.
Lösung:
- Beim Value-Learning lernt der Agent, die Zustände der Umgebung zu bewerten, um bessere Entscheidungen zu treffen. Dies erfolgt durch die Berechnung und Aktualisierung von Wertfunktionen, die die erwartete zukünftige Belohnung in einem Zustand darstellen.
- Die Wertfunktion \(V(s)\) gibt den erwarteten kumulativen Belohnungswert an, den der Agent ab Zustand \(s\) erwarten kann, wenn er der optimalen Policy folgt.
- Die Bellman-Gleichung beschreibt die Beziehung zwischen dem Wert eines Zustandes und dem Wert der möglichen Nachfolgezustände. Sie basiert auf dem Prinzip der optimalen Substruktur, demzufolge der Wert eines Zustandes die direkte Belohnung und den diskontierten Wert der Nachfolgezustände umfasst.
- Die Bellman-Gleichung für die Wertfunktion \(V(s)\) lautet:
\[ V(s) = \max_a \left[ \sum_{s'} P(s'|s,a) \left( R(s, a, s') + \gamma V(s') \right) \right] \]
- wobei:
- \(P(s'|s,a)\) die Wahrscheinlichkeit ist, in Zustand \(s'\) überzugehen, wenn Aktion \(a\) im Zustand \(s\) durchgeführt wird,
- \(R(s, a, s')\) die erhaltene Belohnung für den Übergang von \(s\) nach \(s'\) unter Aktion \(a\) ist,
- \(\gamma\) der Diskontierungsfaktor ist, der zukünftige Belohnungen abwertet.
- Value Iteration Algorithmus
- Der Value Iteration Algorithmus ist ein Prozess zur Berechnung der optimalen Wertfunktion \(V^*(s)\) durch wiederholtes Anwenden der Bellman-Gleichung.
- Algorithmus:
- Initialisiere: \(V(s) = 0\) für alle Zustände \(s\).
- Wiederhole bis Konvergenz:
\[ V(s) \leftarrow \max_a \left[ \sum_{s'} P(s'|s,a) \left( R(s, a, s') + \gamma V(s') \right) \right] \]
- Anwendung in der Gridworld-Umgebung
- In der Gridworld-Umgebung wird der Agent mit jedem Schritt, abhängig von der Aktion und dem resultierenden Zustand, entweder eine negative Belohnung (-1) oder eine positive Belohnung (+10 bei Erreichen des Zieles) erhalten.
- Der Agent wird lernen, Zustände zu bewerten, indem er die erwarteten zukünftigen Belohnungen unter Verwendung der oben genannten Bellman-Gleichung aktualisiert.
Durch Value-Learning lernt der Agent, Zustände auf Basis erwarteter zukünftiger Belohnungen zu bewerten. Dies wird durch Anwendung der Bellman-Gleichung erreicht, die den Wert eines Zustands durch Betrachtung der direkten Belohnung und der Werte der möglichen Nachfolgezustände definiert. Iterative Anwendung dieser Gleichung durch Value Iteration ermöglicht es dem Agenten, die optimalen Werte und letztlich die optimale Policy zu bestimmen.
c)
(c) Gegeben sei eine initiale Q-Tabelle mit allen Werten auf 0 gesetzt. Beschreibe den Prozess des Q-Learnings für den Agenten. Erläutere, wie die Q-Werte aktualisiert werden und formuliere die entsprechende Aktualisierungsregel.
Lösung:
- Q-Learning-Prozess für die Gridworld-Umgebung
- Q-Learning ist eine Form des Reinforcement Learnings, bei der der Agent lernt, die Qualität von Aktionen in verschiedenen Zuständen zu bewerten. Diese Bewertungen werden in einer sogenannten Q-Tabelle gespeichert, die für jede Zustands-Aktions-Kombination einen Q-Wert enthält.
- Die Q-Tabelle wird initialisiert, indem alle Q-Werte auf 0 gesetzt werden:
\[ Q(s, a) = 0 \text{ für alle } s, a \]
- Der Agent wählt in jedem Schritt eine Aktion basierend auf einer Aktionsauswahlstrategie. Eine häufig verwendete Strategie ist die \(\epsilon\)-greedy Strategie, bei der der Agent meist die Aktion mit dem höchsten Q-Wert wählt (Exploit), aber mit einer kleinen Wahrscheinlichkeit \(\epsilon\) eine zufällige Aktion wählt (Explore).
- Der Agent führt die gewählte Aktion aus, bewegt sich in den neuen Zustand und erhält eine Belohnung.
- Q-Wert-Aktualisierungsregel
- Die Q-Werte werden basierend auf der erhaltenen Belohnung und dem maximalen Q-Wert des neuen Zustands aktualisiert. Die Aktualisierungsregel lautet:
\[ Q(s, a) \leftarrow Q(s, a) + \alpha \left[ r + \gamma \max_{a'} Q(s', a') - Q(s, a) \right] \]
- \( s \) der aktuelle Zustand,
- \( a \) die gewählte Aktion,
- \( r \) die erhaltene Belohnung,
- \( s' \) der resultierende neue Zustand,
- \( \alpha \) die Lernrate (0 < \( \alpha \) ≤ 1),
- \( \gamma \) der Diskontierungsfaktor (0 ≤ \( \gamma \) ≤ 1).
- Der Agent wiederholt diesen Prozess über viele Episoden. Mit der Zeit nähern sich die Q-Werte den tatsächlichen Werten an, wenn der Agent alle Zustands-Aktions-Paare ausreichend erkundet hat.
Q-Learning ist ein iterativer Prozess, bei dem der Agent lernt, die durchgeführten Aktionen zu bewerten, um die am besten bewerteten Aktionen in der Zukunft zu bevorzugen. Durch die Anwendung der Q-Wert-Aktualisierungsregel verbessert der Agent seine Fähigkeit, optimale Wege zu finden, um von einem Startzustand zu einem Zielzustand zu gelangen und dabei die maximale Belohnung zu erzielen.
d)
(d) Implementiere eine einfache Q-Learning-Iteration in Pseudocode für einen Agenten, der in dieser Gridworld-Umgebung agiert. Stelle sicher, dass die Regeln für die Belohnungen und die maximale Episodenlänge berücksichtigt werden.
Lösung:
- Q-Learning-Iteration in Pseudocode
- Nachfolgend ist eine einfache Implementierung einer Q-Learning-Iteration in Pseudocode dargestellt, die die Gridworld-Umgebung berücksichtigt:
- Q-Tabelle wird initialisiert mit Q(s, a) = 0 für alle Zustand-Aktions-Paare.
- Setze die Lernparameter: die Lernrate (α), Diskontierungsfaktor (γ) und Explorationsrate (ε).
- Für jede Episode, beginnend im Startzustand s = 0:
- Schrittzähler t wird auf 0 gesetzt.
- In jeder Episode wird eine Schleife solange ausgeführt, bis der Agent den Zielzustand erreicht hat oder 25 Schritte überschritten wurden.
- Aktion a wird gewählt mit Mischung aus zufälliger Wahl (Exploration) und greediest Wahl (Exploitation).
- Die gewählte Aktion a wird ausgeführt, der resultierende neue Zustand s' und die erhaltene Belohnung r werden beobachtet.
- Die Q-Werte werden gemäß der Q-Learning-Aktualisierungsregel aktualisiert.
- Der neue Zustand s' wird zum aktuellen Zustand s, und der Schrittzähler t wird erhöht.
- Dieser Prozess wird für jede Episode wiederholt.
Aufgabe 4)
Ein Supermarkt verwendet Markov-Entscheidungsprozesse (MDPs), um optimale Entscheidungen für logistisches Management zu treffen. Der Supermarkt hat vier Lagerzustände: 'Voll', 'Mittel', 'Leer', 'Notfall'. Er kann drei Aktionen durchführen: 'Bestellen', 'Reduzieren', 'Keine Aktion'. Die Übergangswahrscheinlichkeiten (T) und die Belohnungsfunktionen (R) sind wie folgt definiert:
- Zustände (S): 'Voll', 'Mittel', 'Leer', 'Notfall'
- Aktionen (A): 'Bestellen', 'Reduzieren', 'Keine Aktion'
- Übergangswahrscheinlichkeiten (T):
- T(Voll, Bestellen, Mittel) = 0.1, T(Voll, Bestellen, Leer) = 0.9
- T(Mittel, Reduzieren, Voll) = 0.3, T(Mittel, Reduzieren, Leer) = 0.7
- T(Leer, Bestellen, Voll) = 0.6, T(Leer, Bestellen, Notfall) = 0.4
- T(Notfall, Keine Aktion, Leer) = 1.0
- Belohnungsfunktionen (R):
- R(Voll) = 10
- R(Mittel) = 5
- R(Leer) = -10
- R(Notfall) = -50
- Diskontierungsfaktor: \(\gamma = 0.9\)
Bestimme die optimale Politik \(\pi\) für den Supermarkt, um den erwarteten kumulierten Nutzen zu maximieren.
a)
Vergleiche die Methode der Policy Iteration mit der Value Iteration. Welche Methode wäre effizienter für den gegebenen Supermarkt und warum?
Lösung:
Um die Methoden der Policy Iteration und der Value Iteration zu vergleichen und zu bestimmen, welche Methode effizienter für den gegebenen Supermarkt wäre, betrachten wir die grundlegenden Unterschiede und die Arbeitsweise beider Methoden:
- Policy Iteration:
- Policy Evaluation: In diesem Schritt wird der Wert jeder Zustand-Politik-Kombination berechnet, basierend auf der aktuellen Politik \(\pi\). Dies kann iterativ geschehen, bis der Wert für jede Kombination konvergiert.
- Policy Improvement: In diesem Schritt wird die Politik \(\pi\) verbessert, indem für jeden Zustand die Aktion gewählt wird, die den höchsten Wert liefert, basierend auf den in der Policy Evaluation berechneten Werten. Dies wird iteriert, bis die Politik stabil wird (d.h., sich nicht mehr ändert).
- Vorteile: Policy Iteration konvergiert in einer endlichen Anzahl von Iterationen und liefert oft die exakt optimale Politik.
- Nachteile: Die Policy Evaluation kann bei großen Zustandsräumen zeitaufwendig sein, da jeder Schritt genaue Berechnungen erfordert.
- Value Iteration:
- Iterative Berechnung des Wertes: Anstatt zwischen Politik-Bewertung und Politik-Verbesserung zu wechseln, berechnet die Value Iteration direkt die Werte für Zustände, indem sie wiederholt die Bellman-Gleichung anwendet. Dies geschieht, bis der Unterschied zwischen den aufeinanderfolgenden Wertverteilungen kleiner als ein vorgegebener Schwellenwert wird.
- Vorteile: Value Iteration ist oft einfacher zu implementieren und benötigt in der Regel weniger Iterationen, um zu einem genauen Ergebnis zu gelangen, insbesondere wenn die Zustandsräume groß sind.
- Nachteile: Die Methode kann für jede Iteration etwas länger dauern, da sie die Bellman-Gleichung für alle Zustände berechnen muss.
Welche Methode wäre effizienter für den gegebenen Supermarkt und warum?
- Da der Supermarkt nur vier Lagerzustände ('Voll', 'Mittel', 'Leer', 'Notfall') hat, ist der Zustandsraum relativ klein.
- Bei kleinen Zustandsräumen kann Policy Iteration effizient sein, da die Policy Evaluation und Policy Improvement Schritte schnell durchgeführt werden können. Zusätzlich liefert Policy Iteration in einer endlichen Anzahl von Iterationen die exakt optimale Politik.
- Obwohl Value Iteration ebenfalls effizient sein kann, insbesondere bei größeren Zustandsräumen, könnte sie für diesen konkreten Fall längere Berechnungszeiten pro Iteration benötigen, da sie die Bellman-Gleichung wiederholt für alle Zustände anwenden muss.
Daher wäre in diesem Fall die Policy Iteration tendenziell die effizientere Methode, um die optimale Politik für den Supermarkt zu bestimmen.
b)
Berechne für den Lagerzustand 'Mittel' die Wertefunktion \(V^{\pi}(Mittel)\) unter Verwendung der Bellman-Gleichung und der gegebenen Übergangswahrscheinlichkeiten und Belohnungsfunktionen. Beachte dabei, dass 'Reduzieren' die zurzeit gewählte Aktion ist.
Lösung:
Um die Wertefunktion für den Lagerzustand 'Mittel' zu berechnen, während 'Reduzieren' die gewählte Aktion ist, verwenden wir die Bellman-Gleichung. Die Bellman-Gleichung für einen Zustand \(s\) unter einer bestimmten Politik \(\pi\) ist definiert als:
Bellman-Gleichung:
- \[V^{\pi}(s) = R(s) + \gamma \sum_{s'} P(s'|s, \pi(s)) V^{\pi}(s')\]
Für den Zustand 'Mittel' (\(s = Mittel\)) und die Aktion 'Reduzieren' (\(\pi(Mittel) = Reduzieren\)), ergeben sich die Übergangswahrscheinlichkeiten (\(P(s'|s, Reduzieren)\)) wie folgt:
- T(Mittel, Reduzieren, Voll) = 0.3
- T(Mittel, Reduzieren, Leer) = 0.7
Die Belohnung für den Zustand 'Mittel' (\(R(Mittel)\)) beträgt 5 und der Diskontierungsfaktor (\(\gamma\)) ist 0.9. Um die Wertefunktion \(V^{\pi}(Mittel)\) zu berechnen, müssen wir die Werte der Nachfolgezustände 'Voll' und 'Leer' kennen. Unter der Annahme, dass wir bereits deren Werte (\(V^{\pi}(Voll)\) und \(V^{\pi}(Leer)\)) kennen, können wir die Wertefunktion wie folgt berechnen:
- \[V^{\pi}(Mittel) = R(Mittel) + \gamma \left[ P(Voll|Mittel, Reduzieren) V^{\pi}(Voll) + P(Leer|Mittel, Reduzieren) V^{\pi}(Leer) \right]\]
Einfach ausgedrückt:
- \[V^{\pi}(Mittel) = 5 + 0.9 \left( 0.3 V^{\pi}(Voll) + 0.7 V^{\pi}(Leer) \right)\]
Um diesen Wert explizit zu berechnen, müssen wir die Werte von \(V^{\pi}(Voll)\) und \(V^{\pi}(Leer)\) entweder kennen oder schätzen. Vorausgesetzt, diese Werte wären verfügbar, setzt man sie einfach in diese Gleichung ein, um \(V^{\pi}(Mittel)\) zu berechnen.
Das ergibt die Wertefunktion in Abhängigkeit der Werte der Nachfolgezustände.