Freigeben über



November 2018

Band 33, Nummer 11

Künstlich intelligent: ein genauerer Blick auf Verstärkungslernen

Von Frank La La

Frank La VigneIn der Kolumne des letzten Monats habe ich einige grundlegende Konzepte des Verstärkungslernens (Reinforcement Learning, RL) untersucht, indem ich sowohl einen streng zufälligen Ansatz zur Navigation in einer einfachen Umgebung ausprobiert als auch eine Q-Tabelle implementiert habe, um mich an frühere Aktionen zu erinnern und zu erfassen, welche Aktionen zu welchen Belohnungen geführt haben. In der Demo konnte ein zufällig arbeitender Agent in etwa einem Prozent der Fälle den Zielzustand erreichen. Wenn er eine Q-Tabelle verwendete, um sich frühere Aktionen zu merken, war dies in etwa der Hälfte der Fälle möglich. Dieses Experiment kratzte jedoch nur an der Oberfläche des vielversprechenden und sich entwickelnden RL-Fachgebiets.

Erinnern Sie sich daran, dass in der vorherigen Kolumne (msdn.com/magazine/mt830356) ein RL-Problembereich aus einer Umgebung, einem Agenten, Aktionen, Zuständen und Belohnungen besteht. Ein Agent untersucht den Zustand einer Umgebung und führt eine Aktion aus. Die Aktion ändert dann den Zustand des Agents und/oder der Umgebung. Der Agent erhält eine Belohnung und untersucht den aktualisierten Zustand seiner Umgebung. Der Zyklus startet dann erneut und durchläuft mehrere Iterationen, bis der Agent erfolgreich ist oder an einem vordefinierten Ziel scheitert. Wenn ein Agent erfolgreich ist oder scheitert, endet die Simulation. Mit einer Q-Tabelle merkt sich ein Agent, welche Aktionen positive Belohnungen erbracht haben und bezieht sich darauf, wenn er in nachfolgenden Simulationen Entscheidungen trifft.

Das Problem des mehrarmigen Banditen

Eines der klassischen Probleme in RL ist die Spannung zwischen Erforschung und Nutzung. Spielautomaten, die oft auch als „einarmige Banditen“ bezeichnet werden, sind die Inspiration für dieses Problem. Eine Gruppe von Spielautomaten bildet dann einen „mehrarmigen Banditen“. Jeder dieser Spielautomaten weist eine Wahrscheinlichkeit auf, einen Jackpot auszuzahlen oder nicht. Die Wahrscheinlichkeit jedes Durchgangs, der zu einem Jackpot führt, kann als P dargestellt werden, und die Wahrscheinlichkeit, nicht auszuzahlen, ist 1 - P. Wenn ein Automat eine Jackpotwahrscheinlichkeit (JP) von 0,5 hat, dann besteht bei jedem Ziehen am Hebel die gleiche Chance zu gewinnen oder zu verlieren. Umgekehrt würde ein Automat mit einer JP von 0,1 in 90 Prozent der Fälle zu einem verlorenen Ergebnis führen.

Stellen Sie sich jetzt eine Gruppe von fünf Spielautomaten vor. Der Spieler (oder Agent) hat das Ziel, Gewinne zu maximieren und Verluste zu minimieren. Ohne Vorkenntnisse über die Jackpotwahrscheinlichkeit (JP) der Automaten muss der Agent zunächst einige Risiken eingehen. Mit dem ersten Ziehen des Hebels gewinnt der Agent und erhält eine Auszahlung. Nachfolgende Versuche zeigen jedoch, dass dieser Automat etwa in der Hälfte der Fälle auszahlt: eine JP von 0,54. Für Spielautomaten ist dies ziemlich großzügig. Der Agent muss entscheiden, ob er die aktuell bekannte Ressource nutzen oder einen neuen Automaten erforschen soll. Wenn die Wahrscheinlichkeit, dass der erste Spielautomat eine Auszahlung vornimmt, so groß ist, lohnt es sich dann überhaupt, einen der anderen Automaten in der Gruppe zu testen, um zu sehen, ob seine Auszahlungschancen noch besser sind?

Die beste Möglichkeit, diesen Problembereich weiter zu untersuchen, besteht in etwas Python-Code in einem Jupyter Notebook. Erstellen Sie ein Python 3-Notebook auf Ihrer bevorzugten Plattform. Ich habe Jupyter Notebooks in einem früheren Artikel (msdn.com/magazine/mt829269) behandelt. Erstellen Sie eine leere Zelle, und geben Sie den folgenden Code ein. Führen Sie die Zelle dann aus.

import numpy as np
import matplotlib.pyplot as plt
number_of_slot_machines = 5
np.random.seed(100)
JPs =  np.random.uniform(0,1, number_of_slot_machines)
print(JPs)
plt.bar(np.arange(len(JPs)),JPs)
plt.show()

Die Ausgabe sollte Werte und einen Plot der Werte anzeigen, wie in Abbildung 1 dargestellt.

[0.54340494 0.27836939 0.42451759 0.84477613 0.00471886]

Jackpotwahrscheinlichkeiten der fünf Spielautomaten
Abbildung 1: Jackpotwahrscheinlichkeiten der fünf Spielautomaten

Der Code erstellt ein Array von JP-Werten für eine Gruppe von fünf Spielautomaten im Bereich von 0,004 bis 0,844. Der erste Automat, den der Agent ausprobiert hat, ist zwar großzügig, aber nicht der beste. Offensichtlich ist der vierte Spielautomat mit einer Auszahlungsrate von 84,4 Prozent der am besten auszahlende Automat in der Umgebung. Es ist auch erwähnenswert, dass der letzte Spielautomat die schlechtesten Chancen hat, einen Jackpot auszuzahlen. Denken Sie daran, dass der Agent keine Vorkenntnisse über die Auszahlungsraten hat und diese selbst herausfinden muss. Wäre der Agent beim ersten Automaten geblieben und hätte die Nutzung anstelle der Erforschung gewählt, hätte der Agent nie den am besten auszahlenden Spielautomaten ermittelt.

Um darzustellen, was der Agent zu Beginn einer Simulation weiß, fügen Sie einer neuen Zelle den folgenden Code hinzu:

known_JPs = np.zeros(number_of_slot_machines)

Dies erstellt ein Array aus Nullen, was bedeutet, dass der Agent annimmt, dass der JP jedes Spielautomaten Null ist. Dies ist zwar nicht in allen Fällen der beste Ausgangswert, aber für unsere Zwecke hier ausreichend. Um eine Simulation eines Spielautomaten zu erstellen, fügen Sie einer neuen Zelle den folgenden Code hinzu und führen ihn aus:

def play_machine(slot_machine):
  x = np.random.uniform(0, 1)
  if (x <= JPs[slot_machine]):
    return(10)
  else:
    return(-1)

Dieser Codeausschnitt simuliert einen Spielautomaten, der eine Belohnung von 10 ausgibt, wenn der Automat auszahlt, und eine negative Belohnung von -1, wenn dies nicht der Fall ist. Die Wahrscheinlichkeit einer Auszahlung basiert auf der Wahrscheinlichkeit, die im Numpy-Array der JP definiert ist. Um den Code zu testen, geben Sie den folgenden Python-Code in eine neue Zelle ein und führen ihn aus:

# Test Slot Machine 4
for machines in range(10):
  print(play_machine(3))
print ("------")      
# Test Slot Machine 5
for machines in range(10):
  print(play_machine(4))

Dieser Code unterscheidet zwischen dem Automaten mit der besten Gewinnchance und dem Automaten mit der schlechtesten Gewinnchance. Da dies alles auf Zufall basiert, gibt es keine Garantie für die Ausgabeergebnisse. Die Ergebnisse sollten dies widerspiegeln, mit einer Mehrheit von 10er Werten für Automat 4 und fast ausschließlich Werten von -1 für Automat 5. Da sich der Code für simulierte Spielautomaten wie erwartet verhält, ist es nun an der Zeit, einen gängigen Algorithmus in RL zu untersuchen: Epsilon-Greedy.

Der Epsilon-Greedy-Algorithmus

Das Kerndilemma, mit dem der Agent hier konfrontiert ist, besteht darin, ob er Gier (den Wunsch, eine bekannte Ressource auszubeuten) oder Neugierde (den Wunsch, andere Spielautomaten in der Hoffnung auf eine bessere Chance auf Belohnungen zu erforschen) priorisieren soll. Einer der einfachsten Algorithmen zur Lösung dieses Dilemmas ist der Epsilon-Greedy-Algorithmus, bei dem der Agent nach dem Zufallsprinzip zwischen der Verwendung des Spielautomaten mit den besten bisher beobachteten Auszahlungschancen oder dem Ausprobieren eines anderen Automaten in der Hoffnung wählt, dass er eine bessere Auszahlung bieten kann. Mit einem niedrigen Wert von Epsilon folgt dieser Algorithmus dem gierigen Algorithmus, probiert aber gelegentlich einen anderen Spielautomaten aus. Wenn der Epsilon-Wert beispielsweise 0,1 beträgt, entscheidet sich der Algorithmus dafür, 90 Prozent der Zeit auszubeuten und nur 10 Prozent der Zeit zu erforschen. Standardwerte für Epsilon liegen tendenziell in der Regel zwischen 0,05 und 0,1. Kurz gesagt, wird der Agent in erster Linie den besten Spielautomaten spielen, der ermittelt wurde und ihm bekannt ist, und manchmal einen neuen Automaten ausprobieren. Denken Sie daran, dass jedes Ziehen des Hebels mit Kosten verbunden ist und der Agent nicht weiß, was wir wissen: dass Automat 4 am besten auszahlt.

Dies unterstreicht das Konzept von RL. Der Agent weiß zunächst nichts über die Umgebung, also muss er sie zuerst erforschen und dann später ausnutzen. Das Lernen wird während des gesamten Vorgangs fortgesetzt. Im Wesentlichen ist dies das Konzept der verzögerten Belohnung, und es ist im besten Interesse des Agents, nicht völlig gierig zu sein, damit etwas Raum für die Erforschung verbleibt.

Testen der Epsilon-Greedy-Hypothese

Um diese Hypothese zu testen, fügen Sie den Code in Abbildung 2 einer neuen Zelle hinzu und führen ihn aus. Dieser Code erstellt die multi_armed_bandit-Funktion, die eine Reihe von Ausführungen für eine Gruppe von Spielautomaten simuliert. Die Funktion speichert die beobachtete Wahrscheinlichkeit einer Jackpotauszahlung. Bei jeder Iteration spielt der Agent zufällig den Spielautomaten mit der besten Auszahlung, die er bisher beobachtet hat, oder probiert willkürlich einen anderen Automaten aus. Die argmax-Funktion gibt den höchsten Wert im Numpy-Array zurück. Hier bedeutet dies den Spielautomaten mit den besten Chancen, einen Jackpot zu knacken. Die Parameter der Funktion ermöglichen die Steuerung der Anzahl der Spielautomaten, der Anzahl der auszuführenden Iterationen und des Werts von Epsilon. 

Abbildung 2: Code für Verstärkungslernen

def multi_armed_bandit(arms, iterations, epsilon):
  total_reward, optimal_action = [], []
  estimated_payout_odds = np.zeros(arms)
  count = np.zeros(arms)
  for i in range(0, iterations):
    epsilon_random = np.random.uniform(0, 1)
    if epsilon_random > epsilon :
      # exploit
      action = np.argmax(estimated_payout_odds)
    else :
      # explore
      action = np.random.choice(np.arange(arms))
    reward = play_machine(action)
    estimated_payout_odds[action] = estimated_payout_odds[action] +
      (1/(count[action]+1)) *
      (reward - estimated_payout_odds[action])
    total_reward.append(reward)
    optimal_action.append(action == np.argmax(estimated_payout_odds))
    count[action] += 1
  return(estimated_payout_odds, total_reward)

Nachdem der RL-Code vorhanden ist, ist es nun an der Zeit, den Epsilon-Greedy-Algorithmus zu testen. Geben Sie den Code aus Abbildung 3 in eine leere Zelle ein, und führen Sie ihn aus. Die Ergebnisse zeigen das Diagramm aus Abbildung 1 zur einfachen Referenz, gefolgt von den Wahrscheinlichkeiten, die der RL-Code beobachtet hat.

Abbildung 3: Code zum Vergleichen der tatsächlichen Gewinnchancen des Spielautomaten mit den Beobachtungen des Agents

print ("Actual Odds")
plt.bar(np.arange(len(JPs)),JPs)
plt.show()
print (JPs)
print("----------------------------------")
iterations = 1000
print("\n----------------------------------")
print ("Learned Odds with epsilon of .1")
print("----------------------------------")
learned_payout_odds, reward = multi_armed_bandit(number_of_slot_machines, iterations, .1)
plt.bar(np.arange(len(learned_payout_odds)),learned_payout_odds)
plt.show()
print (learned_payout_odds)
print ("Reward: ", sum(reward))

Wie Sie in Abbildung 4 erkennen können, hat der Algorithmus ausgezeichnete Arbeit geleistet, nicht nur bei der Bestimmung der Spielautomaten mit den günstigsten Gewinnchancen, sondern auch bei der Generierung ziemlich genauer Auszahlungswahrscheinlichkeiten für die anderen vier Spielautomaten. Die Diagramme stimmen ziemlich gut überein. Die Ausnahme ist der fünfte Spielautomat, der so geringe Gewinnchancen hat, dass er in den Beobachtungen des Agents negativ abschneidet.

Ergebnisse mit einem Epsilon-Wert von 0,1
Abbildung 4: Ergebnisse mit einem Epsilon-Wert von 0,1

Jetzt, da die Basislinie festgelegt ist, ist es an der Zeit, weiter zu experimentieren. Was würde passieren, wenn Epsilon auf Null festgelegt würde, was bedeutet, dass der Algorithmus nie andere Automaten erforschen wird? Geben Sie den folgenden Code in eine neue Zelle ein, und führen Sie ihn aus, um dieses Experiment umzusetzen:

print("\n----------------------------------")
print ("Learned Odds with epsilon of 0")
print("----------------------------------")
learned_payout_odds, reward =
  multi_armed_bandit(number_of_slot_machines, iterations, 0)
plt.bar(np.arange(len(learned_payout_odds)),learned_payout_odds)
plt.show()
print (learned_payout_odds)
print ("Reward: ", sum(reward))

Das sich ergebende Diagramm wird mit einem Wert größer als Null angezeigt. Ein Automat dominiert die anderen, was deutlich macht, dass der Agent einen Automaten gefunden hat und bei ihm geblieben ist. Führen Sie den Code jedoch mehrmals aus, und Sie werden ggf. bemerken, dass sich gelegentlich ein interessantes Muster entwickelt. Es wird einen oder mehrere Automaten mit negativen Werten geben, mit einem Automaten mit einem Wert größer als Null. In diesen Fällen hat der Agent auf einem bestimmten Automaten verloren und dann auf einem anderen Automaten gewonnen. Sobald der Agent einen Automaten entdeckt, bei dem er gewinnt, bleibt er bei diesem Automaten, da es der Automat ist, den die argmax-Funktion auswählt. Wenn Epsilon auf Null festgelegt ist, erforscht der Agent möglicherweise immer noch andere Automaten, aber es wird nicht mit Absicht geschehen. Somit liegen die beobachteten Gewinnchancen der Spielautomaten bei weitem nicht in der Nähe der tatsächlichen Gewinnchancen. Es ist auch erwähnenswert, dass die „gierige“ Methode einen niedrigeren Belohnungswert ergibt als bei der Festlegung von Epsilon auf 0,1. Gier (zumindest absolute Gier) scheint kontraproduktiv zu sein.

Was wäre, wenn Epsilon auf 1 festgelegt würde, sodass der Agent jedes Mal einen Automaten erforscht und Gewinne überhaupt nicht ausnutzen würde? Geben Sie den folgenden Code in eine neue Zelle ein, und führen Sie ihn aus:

print("\n----------------------------------")
print ("Learned Odds with epsilon of 1")
print("----------------------------------")
learned_payout_odds, reward  = multi_armed_bandit(number_of_slot_machines, iterations, 1)
plt.bar(np.arange(len(learned_payout_odds)),learned_payout_odds)
plt.show()
print (learned_payout_odds)
print ("Reward: ", sum(reward))

Die Ergebnisse zeigen, dass der Agent ausgezeichnete Arbeit bei der Beobachtung von Gewinnchancen geleistet hat, die denen der wahren Gewinnchancen ähnlich sind, und das Diagramm stimmt sehr genau mit Abbildung 1 überein. Tatsächlich sehen die Ergebnisse der Festlegung von Epsilon auf 1 sehr ähnlich wie die Ergebnisse mit dem Wert 0,1 aus. Bei Betrachtung des Belohnungswerts gibt es jedoch einen großen Unterschied. Wenn Epsilon auf 0,1 festgelegt wurde, wird der Belohnungswert fast immer höher sein als wenn der Wert auf 1 festgelegt ist. Wenn der Agent so festgelegt ist, dass er nur erforscht, probiert er bei jeder Wiederholung einen Automaten nach dem Zufallsprinzip aus. Auch wenn er aus der Beobachtung lernen mag, handelt er nicht nach diesen Beobachtungen.

Zusammenfassung

Verstärkungslernen (RL) stellt einen der interessantesten Bereiche der künstlichen Intelligenz dar. In diesem Artikel habe ich den Epsilon-Greedy-Algorithmus mit dem klassischen Problem des „Vielarmigen Banditen“ untersucht und insbesondere das Erforschen-oder-Nutzen-Dilemma beleuchtet, dem sich Agents gegenüber sehen. Ich möchte Sie ermutigen, die Wechselbeziehungen weiter zu untersuchen, indem Sie mit verschiedenen Werten von Epsilon und einer größeren Anzahl von Spielautomaten experimentieren.


Frank La Vigne arbeitet bei Microsoft als AI Technology Solutions-Experte und unterstützt Unternehmen dabei, mehr zu erreichen, indem mit Analysen und KI Daten optimal genutzt werden. Er ist auch Co-Host des DataDriven-Podcasts. Er bloggt regelmäßig auf FranksWorld.com, und Sie können ihm auf seinem YouTube-Kanal „Frank's World TV“ (FranksWorld.TV) folgen.

Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: Andy Leonard


Diesen Artikel im MSDN Magazine-Forum diskutieren