Von nun an reisen Sie von Sapporo in Hokkaido nach Naha in Okinawa. Mit verstärkendem Lernen.
Diese Durchquerung Japans hat die folgenden zwei Ziele.
Und ich möchte die folgenden Regeln anwenden.
Ja, das ist richtig. Es kann mithilfe der Diagrammsuchmethode leicht gefunden werden. Für mehr Informationen
Bitte sehen Sie. Aber hier geht es darum, ab heute etwas über die Stärkung des Lernens zu lernen.
Wird in Grundlagen der Graphentheorie und Grundlagen der Graphentheorie in matplotlib Animation verwendet. Die folgenden Daten werden verwendet.
Laden Sie die Breiten- / Längengraddaten des Präfekturbüros herunter.
import urllib.request
url = 'https://raw.githubusercontent.com/maskot1977/ipython_notebook/master/toydata/location.txt'
urllib.request.urlretrieve(url, 'location.txt') #Daten herunterladen
('location.txt', <http.client.HTTPMessage at 0x110e78ba8>)
Laden Sie die heruntergeladenen Daten.
import pandas as pd
location = pd.read_csv('location.txt').values
pd.DataFrame(location)
0 | 1 | 2 | |
---|---|---|---|
0 | Sapporo | 43.0642 | 141.347 |
1 | Aomori | 40.8244 | 140.74 |
2 | Morioka | 39.7036 | 141.153 |
3 | Sendai | 38.2689 | 140.872 |
... | ... | ... | ... |
44 | Miyazaki | 31.9111 | 131.424 |
45 | Kagoshima | 31.5603 | 130.558 |
46 | Naha | 26.2125 | 127.681 |
47 rows × 3 columns
Lassen Sie uns die gelesenen Daten visualisieren.
%matplotlib inline
import matplotlib.pyplot as plt
plt.figure(figsize=(11, 9))
plt.scatter(location[:, 2], location[:, 1])
for city, y, x in location:
plt.text(x, y, city, alpha=0.5, size=12)
plt.grid()
Auf Google Maps finden Sie Daten, um herauszufinden, wie viele Stunden es dauert, um sich zu Fuß zwischen den Hauptstädten der Präfektur zu bewegen (wenn Sie sich nicht zu Fuß bewegen können, verwenden Sie eine Fähre). Laden Sie dies herunter.
url = 'https://raw.githubusercontent.com/maskot1977/ipython_notebook/master/toydata/walk.txt'
urllib.request.urlretrieve(url, 'walk.txt')
('walk.txt', <http.client.HTTPMessage at 0x1220b8a58>)
Laden Sie die heruntergeladenen Daten.
walk = pd.read_csv('walk.txt', sep='\s+')
walk
Town1 | Town2 | Hour | Ferry | |
---|---|---|---|---|
0 | Sapporo | Aomori | 55 | True |
1 | Akita | Aomori | 36 | NaN |
2 | Akita | Sendai | 45 | NaN |
3 | Akita | Niigata | 52 | NaN |
... | ... | ... | ... | ... |
96 | Nagasaki | Kumamoto | 18 | NaN |
97 | Nagasaki | Saga | 20 | NaN |
98 | Kagoshima | Naha | 26 | NaN |
99 rows × 4 columns
Das? Mir ist gerade aufgefallen, dass der Abschnitt Kagoshima-Naha keine Fähre ist ... nun, er hat keinen Einfluss auf diese Datenanalyse. Lassen Sie uns die Beziehung zwischen den hier erhaltenen Städten visualisieren.
plt.figure(figsize=(11, 9))
for city, y, x in location:
plt.text(x, y, city, alpha=0.4, size=8)
for w in walk.values:
t1 = np.where(location[:, 0] == w[0])[0][0]
t2 = np.where(location[:, 0] == w[1])[0][0]
n1, y1, x1 = location[t1]
n2, y2, x2 = location[t2]
plt.plot([x1, x2], [y1, y2])
plt.scatter(location[:, 2], location[:, 1])
plt.grid()
Die Stadt, in der sich dieses Präfekturbüro befindet, heißt "top" </ b>, die Linie zwischen den Städten heißt "side" </ b> und die Tops sind direkt durch Seiten verbunden. Nennen wir es neben </ b>. Ich habe Daten zur Gehzeit zwischen Städten, aber der Einfachheit halber werde ich die Informationen auf "Seiten" in der nachfolgenden Analyse verwenden, aber nicht die Daten zur Gehzeit.
Finden Sie die Abstandsmatrix zwischen den Präfekturbüros anhand des Breiten- und Längengrads der Präfekturbüros.
import numpy as np
from scipy.spatial import distance
dist_mat = distance.cdist(location[:, 1:], location[:, 1:], metric='euclidean') #Euklidische Entfernung
Erstellen Sie eine Funktion zur Visualisierung der Matrix mit einer Farbkarte.
import pandas as pd
def visualize_matrix(matrix):
plt.figure(figsize=(12, 10))
plt.imshow(matrix, interpolation='nearest', cmap=plt.cm.coolwarm)
plt.colorbar()
tick_marks = np.arange(len(matrix))
plt.xticks(tick_marks, matrix.columns, rotation=90)
plt.yticks(tick_marks, matrix.columns)
plt.tight_layout()
Wenn die erhaltene Distanzmatrix visualisiert wird
visualize_matrix(pd.DataFrame(dist_mat, columns=location[:, 0]))
Diese Distanzmatrix wird in dieser Analyse für zwei Zwecke verwendet.
Beim intensiven Lernen wird das zu lernende "Gehirn" als "Agent" </ b> bezeichnet. Der Agent erfasst die Aktionsoptionen, die ergriffen werden können, und passt das Gewicht der Beurteilung dieser Aktion durch Lernen an.
Die Variable Theta (θ, Theta) soll die Aktionsoptionen darstellen, die zu diesem Zeitpunkt ergriffen werden können. In der Graphentheorie repräsentiert es eine "Seite" (die Verbindung zwischen Eckpunkten). Berechnen wir.
import numpy as np
theta_zero = np.full((len(location), len(location)), np.nan)
for w in walk.values:
i = np.where(location[:, 0] == w[0])[0][0]
j = np.where(location[:, 0] == w[1])[0][0]
theta_zero[i, j] = 1
theta_zero[j, i] = 1
Mit dem Obigen kann die Nummer der Stadt neben der Stadt $ i $ durch "theta_zero [i]" erhalten werden.
Die Variable pi (π, pi) soll die Wahrscheinlichkeit der zu diesem Zeitpunkt ergriffenen Maßnahme darstellen. In der Graphentheorie repräsentiert es "Seitengewichte". Beim erweiterten Lernen wird dieses "pi" als "Richtlinie" </ b> bezeichnet und optimiert.
Betrachten Sie zunächst als Anfangswert den Fall, in dem mehrere Optionen vorhanden sind und eine davon mit derselben Wahrscheinlichkeit zufällig ausgewählt wird.
def normalize_pi(theta):
[m, n] = theta.shape
pi = np.zeros((m, n))
for i in range(0, m):
pi[i, :] = theta[i, :] / np.nansum(theta[i, :])
return np.nan_to_num(pi)
Auf diese Weise wird der Anfangswert von "pi" pi_zero erhalten.
pi_zero = normalize_pi(theta_zero)
Lassen Sie uns pi_zero
visualisieren.
visualize_matrix(pd.DataFrame(pi_zero, columns=location[:, 0]))
Beachten Sie, dass pi_zero
keine symmetrische Matrix ist. Zum Beispiel gibt es eine Seite von Sapporo nach Aomori und eine Seite von Aomori nach Sapporo, aber die Gewichte sind nicht gleich. Dies liegt daran, dass es in dieser Grafik nur eine Option von Sapporo zur nächsten Stadt gibt, aber es gibt mehrere Optionen von Aomori zur nächsten Stadt.
Beim verstärkten Lernen werden "Verhalten" und "Zustand" getrennt behandelt. Durch Auswahl einer bestimmten Aktion in einem bestimmten Zustand wechselt sie in einen anderen Zustand.
Die in diesem Artikel behandelten Beispiele sind Sonderfälle, in denen nicht zwischen "Verhalten" und "Zustand" unterschieden werden muss. Mit anderen Worten, "Staat" bezieht sich auf die "Stadt zu dieser Zeit", "Aktion" bezieht sich auf die "nächste Stadt" und das ist der nächste "Staat". Der Code ist einfacher, da Sie nicht zwischen Aktionen und Status unterscheiden müssen.
Versuchen wir zunächst eine Reise durch Japan mit dieser Richtlinie "pi" immer noch "pi_zero". Sobald Sie eine Stadt erreicht haben, ist es ein zufälliger Spaziergang, der zufällig entscheidet, in welche Stadt Sie als nächstes gehen möchten.
Erstellen Sie eine Funktion, die die nächste Stadt gemäß der in der Richtlinie pi
angegebenen Wahrscheinlichkeit auswählt.
def get_next(town, pi):
return np.random.choice(range(len(pi)), p=pi[town, :])
Zum Beispiel wird gemäß "pi_zero" die nächste Stadt in Tokio (Nr. 12) wie folgt berechnet.
get_next(12, pi_zero)
13
Natürlich ändert sich das Ergebnis bei jeder Ausführung, da es zufällig entschieden wird.
Lassen Sie uns nun nach dem Zufallsprinzip suchen.
def explore(pi, start=0, goal=46): #Start ist 0 (Sapporo), Ziel ist 46 (Naha)
route = [start] #Liste zum Einfügen des Reiseverlaufs
town = start
while True:
next_town = get_next(town, pi) #Bestimmen Sie die nächste Stadt
if next_town in route: #Wenn Sie dieselbe Stadt zweimal besuchen
break #Spiel ist aus
route.append(next_town) #Fügen Sie der Geschichte die nächste Stadt hinzu
if next_town == goal: #Am Ziel ankommen
break #Herzliche Glückwünsche
else: #Noch kein Ziel
town = next_town #Stellen Sie "nächste Stadt" als "aktuelle Position" ein
return route
Starten Sie einen zufälligen Spaziergang gemäß pi_zero
route = explore(pi_zero)
route
[0, 1, 2, 3, 4, 5]
Es ist zufällig, daher ändert sich das Ergebnis natürlich mit jedem Lauf. Wenn es schwierig ist, sich nur die Städtenummer vorzustellen, ist es einfacher, sich vorzustellen, wenn Sie sie wie folgt in den Städtenamen konvertieren.
[location[i, 0] for i in route]
['Sapporo', 'Aomori', 'Morioka', 'Sendai', 'Akita', 'Yamagata']
In diesem Beispiel ist das Spiel beendet, weil ich von Sapporo nach Yamagata gelaufen bin und fälschlicherweise eine besuchte Stadt am nächsten Ziel ausgewählt habe.
Können wir mit einem so zufälligen Spaziergang wirklich von Sapporo nach Naha gelangen?
Lassen Sie uns einen Weg finden, um die Qualität der Suchergebnisse zu bewerten. Berechnen Sie die direkte Entfernung von Ihrem aktuellen Standort zu Ihrem Ziel (Naha) und die Gesamtentfernung von Ihrem Abflugort (Sapporo) zu Ihrem aktuellen Standort.
def evaluate(route, goal=46):
dist_goal = dist_mat[route[-1], goal]
len_route = sum([dist_mat[route[i], route[i + 1]] for i in range(len(route) - 1)])
return [dist_goal, len_route]
"dist_goal" ist die "direkte Entfernung von Ihrem aktuellen Standort zu Ihrem Ziel (Naha)" und "len_route" ist die "Gesamtreiseentfernung von Ihrem Abflugort (Sapporo) zu Ihrem aktuellen Standort".
Dann ist es ein Ausführungsbeispiel.
route = explore(pi_zero)
[location[i, 0] for i in route], evaluate(route)
(['Sapporo', 'Aomori'], [19.597025248636594, 2.3205099949149073])
In diesem Beispiel ist das Spiel vorbei, weil ich den Weg von Sapporo nach Aomori und zurück von Aomori nach Sapporo gewählt habe. Die Entfernung vom aktuellen Standort (Aomori) zum Ziel (Naha) beträgt "19.597025248636594", und die Entfernung vom Abfahrtsort (Sapporo) zum aktuellen Standort (Aomori) beträgt "2.3205099949149073".
Das Ziel einer Reise durch Japan ist es, eine Route zu finden, bei der dieses "dist_goal" Null ist, und dann die mit der kleinsten "len_route" zu finden. Kürzere len_route
ist keine gute Sache.
Von nun an werde ich das Spiel des Wechsels von Sapporo nach Naha viele Male versuchen, aber wenn ich ein bestimmtes Ergebnis erhalte, erstellen wir eine Funktion, um zu beurteilen, ob es das bisher beste ist.
best_dist_goal = 1000000 #Initialisieren Sie einen unglaublich großen Wert
best_len_route = 1000000 #Initialisieren Sie einen unglaublich großen Wert
def is_best_ever():
if best_dist_goal >= dist_goal: #Beste dist_Wenn gleich oder kleiner als der Zielwert
if best_dist_goal > dist_goal: #Best dist der Vergangenheit_Wenn weniger als der Zielwert
return True #Das Beste überhaupt
elif best_len_route > len_route: #Beste Länge der Vergangenheit_Wenn es kleiner als der Routenwert ist
return True #Das Beste überhaupt
else:
return False #Nicht das beste
else:
return False #Nicht das beste
Jetzt, da wir bereit sind, wiederholen wir die Suche nach zufälligen Spaziergängen 50.000 Mal.
%%time #Ausführungszeit messen
pi = pi_zero.copy() #Initialisierung von pi
theta = theta_zero.copy() #Initialisierung von Theta
best_dist_goal = 1000000 #Initialisieren Sie einen unglaublich großen Wert
best_len_route = 1000000 #Initialisieren Sie einen unglaublich großen Wert
dist_goal_history0 = [] # dist_Geschichte des Ziels
len_route_history0 = [] # len_Routenverlauf
best_route0 = [] #Beste Route
for itera in range(50000): #50.000 Mal wiederholen
route = explore(pi) #Suche
dist_goal, len_route = evaluate(route) #Auswertung
dist_goal_history0.append(dist_goal) #Aufzeichnung
len_route_history0.append(len_route) #Aufzeichnung
if is_best_ever(): #Wenn das Beste aus der Vergangenheit
best_dist_goal = dist_goal #Beste dist_goal
best_len_route = len_route #Beste len_route
best_route0 = route #Beste Route
CPU times: user 10 s, sys: 118 ms, total: 10.2 s
Wall time: 11.8 s
Erstellen Sie eine Funktion zur Visualisierung des Ergebnisses. Erstens eine Funktion, die die Verteilung des erhaltenen "dist_goal" "len_route" visualisiert
def draw_histgrams(dist_goal_history, len_route_history):
plt.figure(figsize=(max(len_route_history) / 4, max(dist_goal_history) / 4))
x_max = max(dist_goal_history + len_route_history)
ax = plt.subplot(2,1,1)
ax.hist(dist_goal_history, label='dist_goal', bins=20)
ax.set_xlim([0, x_max])
ax.grid()
ax.legend()
ax = plt.subplot(2,1,2)
ax.hist(len_route_history, label='len_route', bins=20)
ax.set_xlim([0, x_max])
ax.grid()
ax.legend()
draw_histgrams(dist_goal_history0, len_route_history0)
Von Sapporo aus ist es schwierig, Naha zu erreichen. Wenn Sie Glück haben, können Sie dorthin gelangen. Sie können sehen, dass es viele Fälle gibt, in denen das Spiel zu Beginn der Suche beendet ist.
Als nächstes schauen wir uns die Beziehung von dist_goal`` len_route
an.
def draw_scatter(dist_goal_history, len_route_history):
plt.figure(figsize=(max(len_route_history) / 4, max(dist_goal_history) / 4))
plt.scatter(len_route_history, dist_goal_history, alpha=0.5)
plt.ylabel('dist_goal')
plt.xlabel('len_route')
plt.ylim([0, max(dist_goal_history) + 1])
plt.xlim([0, max(len_route_history) + 1])
plt.grid()
plt.show()
draw_scatter(dist_goal_history0, len_route_history0)
Sie können sehen, dass wir Naha nicht erreicht haben, und selbst wenn das Spiel am selben Ort beendet ist, gibt es eine Vielzahl von Entfernungen (verschiedene Routen werden ausgewählt).
Als nächstes wollen wir sehen, wie sich dist_goal`` len_route
geändert hat.
def visualize_history(history, interval=50, window=50):
plt.plot(range(0, len(history), interval), #Gesamtmaximum
[np.array(history)[:i].max() for i in range(len(history)) if (i%interval) == 1], label='max')
plt.plot(range(window, len(history)+window, interval),
[np.array(history)[i:i + window].mean() for i in range(len(history)) if (i%interval) == 1],
label='mean(recent)') #Durchschnitt der letzten Intervallzeiten
plt.plot(range(0, len(history), interval),
[np.array(history)[:i].mean() for i in range(len(history)) if (i%interval) == 1], label='mean') #Gesamtdurchschnittswert
plt.plot(range(0, len(history), interval),
[np.array(history)[:i].min() for i in range(len(history)) if (i%interval) == 1], label='min') #Gesamtminimum
plt.legend()
plt.grid()
plt.show()
visualize_history(dist_goal_history0)
dist_goal
aktualisiert manchmal den Mindestwert, erreicht aber Naha nicht. Unabhängig davon, wie oft Sie die Suche wiederholen, wird sich der Durchschnittswert nicht verbessern. Es ist natürlich, weil ich nichts gelernt habe.
visualize_history(len_route_history0)
In ähnlicher Weise aktualisiert "len_route" manchmal den Maximalwert, erreicht jedoch nicht Naha und der Durchschnittswert verbessert sich nicht, unabhängig davon, wie oft die Suche wiederholt wird. Es ist natürlich, weil ich nichts gelernt habe.
Lassen Sie uns abschließend die beste Route anzeigen, die in diesem zufälligen Spaziergang gefunden wurde.
def draw_route(route):
plt.figure(figsize=(11, 9))
for i in route:
plt.text(location[i, 2], location[i, 1], location[i, 0], alpha=0.4, size=12)
plt.grid()
plt.plot([location[i][2] for i in route], [location[i][1] for i in route])
plt.ylabel('latitude')
plt.xlabel('longitude')
plt.show()
draw_route(best_route0)
Ich habe mich sehr bemüht, aber ich konnte nur Saga erreichen. Wenn Sie Glück haben, können Sie Naha erreichen. Aber du lernst es nicht.
Jetzt ist es Zeit, das Lernen zu stärken </ b>. Die Methode des verstärkenden Lernens scheint grob in die Richtliniengradientenmethode </ b> und die Wertiterationsmethode </ b> unterteilt zu sein. Lassen Sie uns zunächst die Policy-Gradient-Methode ausführen.
Die erhaltene Route "Route" kann unter dem Gesichtspunkt "wie nahe der Endpunkt der Route am Ziel liegt" als gut oder schlecht bewertet werden. Aktualisieren Sie die Richtlinie "pi" so, dass je kürzer der Abstand zum Ziel ist, desto wahrscheinlicher ist es, dass Sie eine Kante auf diesem Pfad für zukünftige Erkundungen auswählen.
def update_pi(pi, route, goal=46, eta=0.1): #Eta (η),eta) ist die Lernrate
new_pi = pi.copy() #Numpy-Array kopieren
for i in range(len(route) - 1): #Für jede Seite der Route
town1 = route[i] #Herkunftsort der Seite i
town2 = route[i + 1] #Stadt am Ende der Seite i
new_pi[town1, town2] += eta / (dist_mat[route[-1], goal] + 1)
#Je näher der Endpunkt der Route am Ziel liegt, desto höher ist die Punktzahl
return normalize_pi(new_pi) #Aktualisiere pi
Der Grund für die Verwendung von "normalize_pi" am Ende besteht darin, die Summe der Zeilenwerte auf 1,0 anzupassen.
Beginnen wir jetzt mit der Suche.
%%time
pi = pi_zero.copy()
best_dist_goal = 1000000
best_len_route = 1000000
dist_goal_history1 = []
len_route_history1 = []
best_route1 = []
for itera in range(50000):
route = explore(pi)
dist_goal, len_route = evaluate(route)
dist_goal_history1.append(dist_goal)
len_route_history1.append(len_route)
pi = update_pi(pi, route)
if is_best_ever():
best_dist_goal = dist_goal
best_len_route = len_route
best_route1 = route
CPU times: user 59.9 s, sys: 340 ms, total: 1min
Wall time: 1min 1s
Unten finden Sie ein Beispiel für die Ergebnisse. Die Ergebnisse variieren von Lauf zu Lauf, und in einigen Fällen erreichen Sie Ihr Ziel, Naha, möglicherweise auch nach 50.000 Lernsitzungen nicht.
Die Verteilung des resultierenden "dist_goal" "len_route" unterscheidet sich stark von der Verteilung durch zufälliges Gehen. Die Zahl der Ankünfte am Zielort Naha hat überwältigend zugenommen.
draw_histgrams(dist_goal_history1, len_route_history1)
Die Beziehung von dist_goal`` len_route
ist wie folgt.
draw_scatter(dist_goal_history1, len_route_history1)
Die Geschichte von dist_goal
. Es erreichte das Ziel Naha viel früher als der zufällige Spaziergang, und danach wurde es einfacher, das Ziel zu erreichen.
visualize_history(dist_goal_history1)
Dies ist das erste 5000 Mal in der Geschichte von "dist_goal".
visualize_history(dist_goal_history1[:5000])
Ebenso die Geschichte von len_route
.
visualize_history(len_route_history1)
Dies ist das erste 5000 Mal in der Geschichte von "len_route".
visualize_history(len_route_history1[:5000])
Hier ist die Ausgabe als beste Route. Sie können sehen, dass diejenige ausgewählt ist, die der kürzesten Route am nächsten liegt. Nur einer ist bedauerlich. Ich fahre von Fukuoka über Saga nach Kumamoto. Ich hätte direkt von Fukuoka nach Kumamoto fahren sollen, ohne durch Saga zu fahren.
draw_route(best_route1)
Speichern Sie die resultierende Richtlinie pi
als Alias pi_pg
.
pi_pg = pi.copy()
Visualisieren Sie diese Richtlinie pi_pg
visualize_matrix(pd.DataFrame(pi_pg, columns=location[:, 0]))
Die absolut wichtige Route ist rot. Wenn Sie beispielsweise in Kagoshima ankommen, kann es nur Naha als nächstes geben. Es ist absolut nutzlos, etwas anderes auszuwählen (Kumamoto, Miyazaki). Es ist eine gute Darstellung davon.
Auf diese Weise können Sie überprüfen, wie es sich gegenüber dem Anfangswert geändert hat.
visualize_matrix(pd.DataFrame(pi_pg - pi_zero, columns=location[:, 0]))
Die Orte, die nicht wichtig sind, haben sich nicht viel geändert. Zum Beispiel scheinen die Regionen Shikoku und Kanto nicht sehr wichtig zu sein.
Im Gegensatz zu der obigen "Policy Gradient Method" werden die hier eingeführte Epsilon-Greedy-Methode und das als nächstes eingeführte Q-Learning als "Value Iteration Method" bezeichnet. ..
Bei der Wertiterationsmethode scheint die Richtlinie aus irgendeinem Grund "Q" anstelle von "π" zu heißen. Während "pi" mit einer vollkommen gleichen Wahrscheinlichkeit beginnt, beginnt "Q" mit einer ungleichen zufälligen Wahrscheinlichkeit.
def generate_Q(theta): #Generieren Sie ungleiche Zufallswahrscheinlichkeiten aus den Auswahlmöglichkeiten, die Sie treffen können
[m, n] = theta.shape
Q = np.zeros((m, n))
rand = np.random.rand(m, n)
for i in range(m):
for j in range(n):
if theta[i, j] == 1:
Q[i, j] = rand[i, j]
return normalize_pi(Q)
Erzeugt einen Anfangswert "Q_zero" für eine ungleichmäßige Richtlinie "Q"
Q_zero = generate_Q(theta_zero)
Visualisieren Sie das generierte Q_zero
.
visualize_matrix(pd.DataFrame(Q_zero, columns=location[:, 0]))
Dies ist der Unterschied zwischen "Q_zero" und "pi".
visualize_matrix(pd.DataFrame(Q_zero - pi_zero, columns=location[:, 0]))
Bei der Richtliniengradientenmethode wurde die nächste Aktion "get_next" zufällig gemäß der durch die Richtlinie "pi" angegebenen Wahrscheinlichkeit ausgewählt. In der Wertiterationsmethode wird die nächste Aktion "get_next" wie folgt umgeschrieben.
def get_next(town, Q, epsilon=0.1):
if np.random.rand() < epsilon:
return np.random.choice(range(len(Q)), p=Q[town, :])
else:
return np.nanargmax(Q[town, :])
Mit anderen Worten, ein neuer Parameter "epsilon" wird eingeführt, und es gibt Fälle, in denen Aktionen zufällig gemäß der durch Richtlinie "Q" angegebenen Wahrscheinlichkeit ausgewählt werden, und Fälle, in denen Richtlinie "Q" die Aktion auswählt, die den Maximalwert annimmt. Anfangs setzt epsilon
einen großen Wert. Mit anderen Worten, es gibt viele Möglichkeiten, zufällige Entscheidungen zu treffen. Reduzieren Sie dann schrittweise das "Epsilon", um die Wahrscheinlichkeit zu verringern, zufällige Entscheidungen zu treffen.
Wir haben auch das Konzept der Belohnung </ b> eingeführt. Wenn Sie Ihr Ziel erreichen (wenn Sie das Ziel erreichen), erhalten Sie 1 Punkt, und wenn Sie versagen (wenn Sie dieselbe Stadt zweimal besuchen und das Spiel vorbei ist) -1 Punkte und Fortschritt sind 0 Punkte.
Bei der folgenden Methode namens "sarsa" wird beim Aktualisieren des "Q" -Werts für eine bestimmte Aktion (Seite auf der Route) auch auf den "Q" -Wert für die nächste Aktion (nächste Seite auf der Route) verwiesen. Ich werde aktualisieren. Zu diesem Zeitpunkt multiplizieren Sie mit dem Zeitabzinsungssatz "Gamma".
def sarsa(town, Q, prev_t, next_t, reward, eta=0.1, gamma=0.9, goal=46):
if reward == 1: #dist_mat[town, goal] == 0:
Q[prev_t, town] = Q[prev_t, town] + eta * (reward - Q[prev_t, town])
elif reward >= 0:
Q[prev_t, town] = Q[prev_t, town] + eta * (reward - Q[prev_t, town] + gamma * Q[town, next_t])
else:
Q[prev_t, town] = Q[prev_t, town] - eta * Q[prev_t, town]
return normalize_pi(Q)
Auf diese Weise beeinflussen die am Ziel verdienten Belohnungen die Auswahl der Aktionen in Richtung des Ziels. Die "negative Belohnung", wenn das Spiel vorbei ist, wirkt sich auch negativ auf die Auswahl vergangener Aktionen aus, um diesen Punkt zu erreichen.
def explore_epsilon_greedy(Q, epsilon=0.1, eta=0.1, gamma=0.9, start=0, goal=46, min_epsilon=0.01):
epsilon = max(epsilon, min_epsilon)
route = [start]
town = get_next(start, Q, epsilon)
prev_t = start
while True:
if town in route:
reward = -1
Q = sarsa(town, Q, prev_t, next_t, reward, eta, gamma)
break
elif town == goal:
reward = 1
route.append(town)
Q = sarsa(town, Q, prev_t, next_t, reward, eta, gamma)
break
else:
reward = 0
route.append(town)
next_t = get_next(town, Q, epsilon)
Q = sarsa(town, Q, prev_t, next_t, reward, eta, gamma)
prev_t = town
town = next_t
return [route, Q]
Beginnen wir jetzt mit der Suche.
%%time
eta = 0.1 #Lernrate
gamma = 0.9 #Zeitabzinsungssatz
epsilon = 0.5
Q = Q_zero.copy()
best_dist_goal = 1000000
best_len_route = 1000000
best_route2 = []
dist_goal_history2 = []
len_route_history2 = []
for itera in range(50000):
epsilon = epsilon * 0.99
route, Q = explore_epsilon_greedy(Q, epsilon, eta, gamma)
dist_goal, len_route = evaluate(route)
dist_goal_history2.append(dist_goal)
len_route_history2.append(len_route)
if is_best_ever():
best_dist_goal = dist_goal
best_len_route = len_route
best_route2 = route
CPU times: user 7min 50s, sys: 948 ms, total: 7min 50s
Wall time: 7min 53s
Dies ist ein Beispiel für die Suchergebnisse nach der Epsilon-Greedy-Methode. Tatsächlich sind die Ergebnisse nicht sehr stabil und können sich bei jeder Ausführung erheblich ändern (probieren Sie es aus).
Erstens die Verteilung von "dist_goal" und "len_route"
draw_histgrams(dist_goal_history2, len_route_history2)
Beziehung zwischen dist_goal
und len_route
draw_scatter(dist_goal_history2, len_route_history2)
Geschichte von dist_goal
Eine Erhöhung der Lernrate "eta" führt zu einer schnelleren Konvergenz, erhöht jedoch die Wahrscheinlichkeit, in eine lokale Lösung zu geraten. Je kleiner der Wert ist, desto langsamer ist die Konvergenz, aber desto weniger wahrscheinlich ist es, dass sie in eine lokale Lösung fällt.
visualize_history(dist_goal_history2)
Die ersten 5000 Male in der Geschichte von dist_goal
visualize_history(dist_goal_history2[:5000])
Geschichte von len_route
visualize_history(len_route_history2)
Die ersten 5000 Male in der Geschichte von len_route
visualize_history(len_route_history2[:5000])
Der kürzeste Weg nach der Epsilon-Greedy-Methode
Auch hier war das Ergebnis etwas anders als auf der kürzesten Strecke. Vergleichen Sie mit den Ergebnissen, die mit der Policy-Gradient-Methode erzielt wurden.
draw_route(best_route2)
Bei der Richtliniengradientenmethode wurde die Entfernung zum Ziel Naha verwendet, um den "pi" -Wert zu aktualisieren, sodass eine Route gelernt wurde, die gleichzeitig "Ziel 1: Erreichen des Ziels" und "Ziel 2: Finden der kürzesten Route" erfüllt. Hat gemacht. Unabhängig davon, wie oft Sie es ausführen, erhalten Sie daher ein Ergebnis nahe der kürzesten Route.
Bei dieser Epsilon-Greedy-Methode gibt die angegebene "Belohnung" nur an, ob Sie das Ziel erreicht haben und ob das Spiel beendet ist (haben Sie dieselbe Stadt zweimal besucht). Daher werden wir lernen, wie man "Ziel 1: Ziel erreichen", aber nicht "Ziel 2: Den kürzesten Weg finden". Aus diesem Grund kann es vorkommen, dass ein Ergebnis nahe der kürzesten Route erzielt wird. In der Realität konvergiert es jedoch häufig zu einem Ergebnis weit entfernt von der kürzesten Route (probieren Sie es aus).
Lassen Sie uns darüber nachdenken, wie Sie mit der Epsilon-Greedy-Methode (hier nicht behandelt) für "Ziel 2: Finden Sie den kürzesten Weg" lernen können.
Speichern Sie den Q-Wert nach dem Training mit der Epsilon-Greedy-Methode als Q_eg.
Q_eg = Q.copy()
Sein Wert ist wie folgt:
visualize_matrix(pd.DataFrame(Q_eg, columns=location[:, 0]))
Vergleichen wir es mit dem "pi_pg", das mit der Policy-Gradient-Methode erhalten wurde.
"Q-Learning" </ b> ist als eine andere Methode zur Wertwiederholung bekannt. Es ähnelt im Wesentlichen der Epsilon-Greedy-Methode, enthält jedoch nicht die Zufälligkeit, die bei der Auswahl der "nächsten Aktion" auftritt. Es wird gesagt, dass die Konvergenz um diesen Betrag schneller sein wird.
Solange ich den folgenden Code mehrmals ausführe, ist die Konvergenz möglicherweise schneller, aber dies ist nicht immer der Fall, und ich falle häufig in eine lokale Lösung (ich kann das Ziel Naha nicht erreichen). Ich habe den Eindruck, dass es enden wird.
def Q_learning(town, Q, prev_t, reward, eta=0.1, gamma=0.9, goal=46):
if reward == 1: #dist_mat[town, goal] == 0:
Q[prev_t, town] = Q[prev_t, town] + eta * (reward - Q[prev_t, town])
elif reward >= 0:
Q[prev_t, town] = Q[prev_t, town] + eta * (reward - Q[prev_t, town] + gamma * np.nanmax(Q[town, :]))
else:
Q[prev_t, town] = Q[prev_t, town] - eta * Q[prev_t, town]
return normalize_pi(Q)
def explore_Q_learning(Q, epsilon=0.1, eta=0.1, gamma=0.9, start=0, goal=46):
prev_t = start
route = [start]
town = get_next(start, Q, epsilon)
while True:
if town in route:
reward = -1
Q = Q_learning(town, Q, prev_t, reward, eta, gamma)
break
elif town == goal:
reward = 1
route.append(town)
Q = Q_learning(town, Q, prev_t, reward, eta, gamma)
break
else:
reward = 0
dist_goal, len_route = evaluate(route)
if best_dist_goal > dist_goal:
reward = 1
route.append(town)
next_t = get_next(town, Q, epsilon)
Q = Q_learning(town, Q, prev_t, reward, eta, gamma)
prev_t = town
town = next_t
return [route, Q]
%%time
eta = 0.1 #Lernrate
gamma = 0.9 #Zeitabzinsungssatz
epsilon = 0.5
Q = Q_zero.copy()
best_dist_goal = 1000000
best_len_route = 1000000
best_route3 = []
dist_goal_history3 = []
len_route_history3 = []
for itera in range(50000):
epsilon = epsilon * 0.99
route, Q = explore_Q_learning(Q, epsilon, eta, gamma)
dist_goal, len_route = evaluate(route)
dist_goal_history3.append(dist_goal)
len_route_history3.append(len_route)
if is_best_ever():
best_dist_goal = dist_goal
best_len_route = len_route
best_route3 = route
CPU times: user 9min 50s, sys: 1.41 s, total: 9min 52s
Wall time: 9min 54s
Dies ist ein Beispiel für Suchergebnisse durch Q-Learning. Wie bei der Epsilon-Greedy-Methode sind die Ergebnisse nicht sehr stabil und können sich bei jeder Ausführung erheblich ändern (probieren Sie es aus).
Verteilung von dist_goal
und len_route
draw_histgrams(dist_goal_history3, len_route_history3)
Beziehung zwischen dist_goal
und len_route
draw_scatter(dist_goal_history3, len_route_history3)
Geschichte von dist_goal
visualize_history(dist_goal_history3)
Die ersten 5000 Male in der Geschichte von dist_goal
visualize_history(dist_goal_history3[:5000])
Geschichte von len_route
visualize_history(len_route_history3)
Die ersten 5000 Male in der Geschichte von len_route
visualize_history(len_route_history3[:5000])
Der kürzeste Weg, der durch Q-Lernen erreicht wird
draw_route(best_route3)
Wie Sie in diesem Beispiel deutlich sehen können, ist dies nicht der wirklich kürzeste Weg. Der Grund ist, dass wir, wie im Fall der Epsilon-Greedy-Methode erwähnt, aufgrund des Problems des "Belohnungs" -Designs lernen, "Ziel 1: Ziel erreichen", aber "Ziel 2: Den kürzesten Weg finden". Das liegt daran, dass wir nicht dafür gelernt haben.
Wie oben erwähnt, konvergiert das Q-Lernen innerhalb meines Beobachtungsbereichs möglicherweise schneller, erreicht jedoch in vielen Fällen das Ziel Naha auch nach 50.000-maliger Berechnung nicht. Ich denke, dass der "kürzeste Weg, der durch Q-Lernen erreicht wird" tendenziell länger ist als der "kürzeste Weg, der durch die Epsilon-Greedy-Methode erreicht wird". Der Grund dafür ist, dass die Zufälligkeit bei der Auswahl der "nächsten Aktion" unterdrückt wird. Wenn Sie also zufällig das Ziel auf der ausgewählten Route erreichen, besteht die Möglichkeit, die Epsilon zu ändern, auch wenn die Route lang ist. Ich denke, das liegt daran, dass es weniger als die Greedy-Methode ist.
Q_qlearn = Q.copy()
visualize_matrix(pd.DataFrame(Q_qlearn, columns=location[:, 0]))
damit. Das ist alles für das verstärkte Lernen. Nun, Okinawa ist weit weg.
Es gibt Deep Strengthing Learning </ b>, das Deep Learning damit mischt, aber das ist eine andere Gelegenheit. Chao.
Recommended Posts