Ich habe ein Programm zur Lösung der klassischen Steuerung von OpenAI Gym mit Deep Q Network, allgemein bekannt als DQN, erstellt, das Deep Learning und intensives Lernen kombiniert. Dieses Mal möchte ich die Implementierung vorstellen.
Über DQN selbst
Der Artikel ist sehr einfach zu verstehen und ich habe ihn unter Bezugnahme auf die hier vorgestellten Artikel und den GitHub-Code implementiert. Wenn Sie die Theorie des verstärkenden Lernens und des DQN kennenlernen möchten, lesen Sie bitte hier.
Wie Sie unter dem Namen Deep Q Network sehen können, approximiert DQN das Q-Lernen, das eines der Verstärkungslernen ist, funktional mit einem mehrschichtigen neuronalen Netz. Darüber hinaus scheint es, dass es nur nach Einbeziehung der folgenden drei Methoden als DQN bezeichnet werden kann.
Die Methode, die ich dieses Mal implementiert habe, war nur 1 und 2, und ich habe die Belohnung von 3 nicht abgeschnitten. Um genau zu sein, ist es nicht DQN. Also habe ich mich für DQN "Modoki" entschieden.
Eine Open Source-Plattform, die es einfach macht, eine Umgebung für verbessertes Lernen zu erstellen. Es ist eine Python-Bibliothek
$ pip install gym
Einfach zu installieren mit. Weitere Informationen finden Sie auf der offiziellen Website.
Jetzt möchte ich den implementierten Code vorstellen. Ein Teil des hier gezeigten Codes ist weggelassen. Überprüfen Sie daher das Ganze von hier.
Ich habe es mit Chainer implementiert. 100 Einheiten haben 3 Schichten und die Aktivierungsfunktion ist Leaky ReLU.
class Neuralnet(Chain):
def __init__(self, n_in, n_out):
super(Neuralnet, self).__init__(
L1 = L.Linear(n_in, 100),
L2 = L.Linear(100, 100),
L3 = L.Linear(100, 100),
Q_value = L.Linear(100, n_out, initialW=np.zeros((n_out, 100), dtype=np.float32))
)
def Q_func(self, x):
h = F.leaky_relu(self.L1(x))
h = F.leaky_relu(self.L2(h))
h = F.leaky_relu(self.L3(h))
h = self.Q_value(h)
return h
Agent
Implementierung des Agententeiles des erweiterten Lernens.
Es definiert die Parameter für die Durchführung des Verstärkungslernens. Das früher eingeführte neuronale Netzwerk wird auch gemäß der Anzahl der Lesezustände und der Anzahl der Aktionen definiert. Erstellen Sie für das Q-Netzwerk mit festem Ziel eine ausführliche Kopie der erstellten Q-Funktion. Mit anderen Worten gibt es zwei Q-Funktionen. Anfangs fiel es mir schwer, diesen Teil zu verstehen ...
class Agent():
def __init__(self, n_st, n_act, seed):
self.n_act = n_act
self.model = Neuralnet(n_st, n_act)
self.target_model = copy.deepcopy(self.model)
self.optimizer = optimizers.Adam()
self.optimizer.setup(self.model)
self.memory = deque()
self.loss = 0
self.step = 0
self.gamma = 0,99 # Diskontsatz
self.mem_size = 1000 #Experience Anzahl der Erfahrungen, an die Sie sich bei der Wiedergabe erinnern sollten
self.batch_size = 100 # Mini-Batch-Größe für Experience Replay
self.train_freq = 10 # Trainingsintervall für neuronale Netze
self.target_update_freq = 20 # Zielnetzwerk-Synchronisationsintervall
# ε-greedy
self.epsilon = 1 # Anfangswert von ε
self.epsilon_decay = 0,005 # Abklingwert von ε
self.epsilon_min = 0 #Mindestwert von ε
self.exploration = 1000 # Anzahl der Schritte, bis ε zu verfallen beginnt (diesmal bis sich Speicher angesammelt hat)
Für Experience Replay
Die fünf Elemente von werden als Erfahrung abgehört und im Speicher gespeichert. Wenn es die zu Beginn definierte Speichergröße überschreitet, wird es in einem Tokoroten-Stil von dem zuerst eingegebenen verworfen. Anfangs war Memory nur eine Liste, aber ich habe gehört, dass es eine Deque gibt, die Anhänge und Pops an beiden Enden binden kann, also habe ich diese verwendet.
def stock_experience(self, st, act, r, st_dash, ep_end):
self.memory.append((st, act, r, st_dash, ep_end))
if len(self.memory) > self.mem_size:
self.memory.popleft()
Experience Replay
Dies ist der Implementierungsteil von Experience Replay, einer der wichtigsten Techniken in DQN. Mische den gespeicherten Speicher, schneide ihn in der definierten Mini-Batch-Größe aus und lerne.
def suffle_memory(self):
mem = np.array(self.memory)
return np.random.permutation(mem)
def parse_batch(self, batch):
st, act, r, st_dash, ep_end = [], [], [], [], []
for i in xrange(self.batch_size):
st.append(batch[i][0])
act.append(batch[i][1])
r.append(batch[i][2])
st_dash.append(batch[i][3])
ep_end.append(batch[i][4])
st = np.array(st, dtype=np.float32)
act = np.array(act, dtype=np.int8)
r = np.array(r, dtype=np.float32)
st_dash = np.array(st_dash, dtype=np.float32)
ep_end = np.array(ep_end, dtype=np.bool)
return st, act, r, st_dash, ep_end
def experience_replay(self):
mem = self.suffle_memory()
perm = np.array(xrange(len(mem)))
for start in perm[::self.batch_size]:
index = perm[start:start+self.batch_size]
batch = mem[index]
st, act, r, st_d, ep_end = self.parse_batch(batch)
self.model.zerograds()
loss = self.forward(st, act, r, st_d, ep_end)
loss.backward()
self.optimizer.update()
Dies ist der Aktualisierungsteil der Q-Funktion unter Verwendung des neuronalen Netzwerks. Es ist wichtig, die kopierte Q-Funktion (self.target_model.Q_func) in dem Teil zu verwenden, der den maximalen Q-Wert im nächsten Zustand berechnet (st_dash).
def forward(self, st, act, r, st_dash, ep_end):
s = Variable(st)
s_dash = Variable(st_dash)
Q = self.model.Q_func(s)
tmp = self.target_model.Q_func(s_dash)
tmp = list(map(np.max, tmp.data))
max_Q_dash = np.asanyarray(tmp, dtype=np.float32)
target = np.asanyarray(copy.deepcopy(Q.data), dtype=np.float32)
for i in xrange(self.batch_size):
target[i, act[i]] = r[i] + (self.gamma * max_Q_dash[i]) * (not ep_end[i])
loss = F.mean_squared_error(Q, Variable(target))
return loss
Bei der Berechnung des Verlusts scheint es, dass das Lernen schneller sein wird, wenn die Differenz zwischen dem Q-Wert und dem Ziel auf -1 zu 1 gekürzt wird, aber ich konnte es nicht implementieren, weil ich die Theorie aufgrund mangelnder Studien nicht verstehen konnte (es tut mir leid ...
Dies ist der Teil, der die auszuführende Aktion zurückgibt, wenn sie gemäß der erlernten Q-Funktion eingegeben wird. Die Methode der Aktionsauswahl verwendet ε-gierig.
def get_action(self, st):
if np.random.rand() < self.epsilon:
return np.random.randint(0, self.n_act)
else:
s = Variable(st)
Q = self.model.Q_func(s)
Q = Q.data[0]
a = np.argmax(Q)
return np.asarray(a, dtype=np.int8)
Dies ist der Teil, um mit dem Lernen fortzufahren, wenn genügend Speicher angesammelt ist. Der Schritt wird jedes Mal angekreuzt und die Q-Funktion für das Ziel wird in regelmäßigen Abständen synchronisiert. Nach Abschluss der Suche nimmt ε mit jedem Schritt ab.
def reduce_epsilon(self):
if self.epsilon > self.epsilon_min and self.exploration < self.step:
self.epsilon -= self.epsilon_decay
def train(self):
if len(self.memory) >= self.mem_size:
if self.step % self.train_freq == 0:
self.experience_replay()
self.reduce_epsilon()
if self.step % self.target_update_freq == 0:
self.target_model = copy.deepcopy(self.model)
self.step += 1
Ich habe versucht, es so zu gestalten, dass bei Eingabe des Umgebungsnamens des Classic-Steuerelements die Anzahl der Status und die Anzahl der Aktionen ohne Erlaubnis beurteilt werden Es ist vielleicht etwas chaotisch und schwer zu verstehen geworden ^^;
def main(env_name):
env = gym.make(env_name)
view_path = "./video/" + env_name
n_st = env.observation_space.shape[0]
if type(env.action_space) == gym.spaces.discrete.Discrete:
# CartPole-v0, Acrobot-v0, MountainCar-v0
n_act = env.action_space.n
action_list = range(0, n_act)
elif type(env.action_space) == gym.spaces.box.Box:
# Pendulum-v0
action_list = [np.array([a]) for a in [-2.0, 2.0]]
n_act = len(action_list)
agent = Agent(n_st, n_act, seed)
env.monitor.start(view_path, video_callable=None, force=True, seed=seed)
for i_episode in xrange(1000):
observation = env.reset()
for t in xrange(200):
env.render()
state = observation.astype(np.float32).reshape((1,n_st))
act_i = agent.get_action(state)
action = action_list[act_i]
observation, reward, ep_end, _ = env.step(action)
state_dash = observation.astype(np.float32).reshape((1,n_st))
agent.stock_experience(state, act_i, reward, state_dash, ep_end)
agent.train()
if ep_end:
break
env.monitor.close()
Ich habe die Ergebnisse in OpenAI Gym hochgeladen
Ich denke, Acrobot und Pendel sind ziemlich gute Ergebnisse, aber Cart Pole ist subtil. Es scheint, dass die Ergebnisse in Abhängigkeit von der Aktualisierungsfrequenz der Q-Funktion für das Ziel, der Größe der ε-Dämpfung und der Optimierungsmethode variieren werden. interessant!
Ich möchte es in Zukunft mit Atari-Spielen versuchen. Zu diesem Zeitpunkt scheint es notwendig zu sein, die Belohnung abzuschneiden. Sollte ich Normalisierung und DropOut in Betracht ziehen?
Recommended Posts