Grundlegendes zur Ableitung von Gradientenrichtlinien

19

Ich versuche, ein sehr einfaches Beispiel für Policy Gradient aus dem Blog Andrej Karpathy zu erstellen . In diesem Artikel finden Sie ein Beispiel für CartPole und Policy Gradient mit Gewichtsliste und Softmax-Aktivierung. Hier ist mein neu erstelltes und sehr einfaches Beispiel für einen CartPole-Richtlinienverlauf, der perfekt funktioniert .

import gym
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
import copy

NUM_EPISODES = 4000
LEARNING_RATE = 0.000025
GAMMA = 0.99


# noinspection PyMethodMayBeStatic
class Agent:
    def __init__(self):
        self.poly = PolynomialFeatures(1)
        self.w = np.random.rand(5, 2)

    def policy(self, state):
        z = state.dot(self.w)
        exp = np.exp(z)
        return exp/np.sum(exp)

    def __softmax_grad(self, softmax):
        s = softmax.reshape(-1,1)
        return np.diagflat(s) - np.dot(s, s.T)

    def grad(self, probs, action, state):
        dsoftmax = self.__softmax_grad(probs)[action,:]
        dlog = dsoftmax / probs[0,action]
        grad = state.T.dot(dlog[None,:])
        return grad

    def update_with(self, grads, rewards):

        for i in range(len(grads)):
            # Loop through everything that happend in the episode
            # and update towards the log policy gradient times **FUTURE** reward

            total_grad_effect = 0
            for t, r in enumerate(rewards[i:]):
                total_grad_effect += r * (GAMMA ** r)
            self.w += LEARNING_RATE * grads[i] * total_grad_effect
            print("Grads update: " + str(np.sum(grads[i])))



def main(argv):
    env = gym.make('CartPole-v0')
    np.random.seed(1)

    agent = Agent()
    complete_scores = []

    for e in range(NUM_EPISODES):
        state = env.reset()[None, :]
        state = agent.poly.fit_transform(state)

        rewards = []
        grads = []
        score = 0

        while True:

            probs = agent.policy(state)
            action_space = env.action_space.n
            action = np.random.choice(action_space, p=probs[0])

            next_state, reward, done,_ = env.step(action)
            next_state = next_state[None,:]
            next_state = agent.poly.fit_transform(next_state.reshape(1, 4))
            grad = agent.grad(probs, action, state)

            grads.append(grad)
            rewards.append(reward)

            score += reward
            state = next_state

            if done:
                break

        agent.update_with(grads, rewards)
        complete_scores.append(score)

    env.close()
    plt.plot(np.arange(NUM_EPISODES),
             complete_scores)
    plt.savefig('image1.png')


if __name__ == '__main__':
    main(None)

Geben Sie hier die Bildbeschreibung ein

.

.

Frage

Ich versuche es, fast das gleiche Beispiel, aber mit Sigmoid-Aktivierung (nur der Einfachheit halber). Das ist alles was ich tun muss. Schalten Sie die Aktivierung im Modell von softmaxauf sigmoid. Welches sollte sicher funktionieren (basierend auf der Erklärung unten). Aber mein Policy Gradient-Modell lernt nichts und bleibt zufällig. Irgendein Vorschlag?

import gym
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures

NUM_EPISODES = 4000
LEARNING_RATE = 0.000025
GAMMA = 0.99


# noinspection PyMethodMayBeStatic
class Agent:
    def __init__(self):
        self.poly = PolynomialFeatures(1)
        self.w = np.random.rand(5, 1) - 0.5

    # Our policy that maps state to action parameterized by w
    # noinspection PyShadowingNames
    def policy(self, state):
        z = np.sum(state.dot(self.w))
        return self.sigmoid(z)

    def sigmoid(self, x):
        s = 1 / (1 + np.exp(-x))
        return s

    def sigmoid_grad(self, sig_x):
        return sig_x * (1 - sig_x)

    def grad(self, probs, action, state):
        dsoftmax = self.sigmoid_grad(probs)
        dlog = dsoftmax / probs
        grad = state.T.dot(dlog)
        grad = grad.reshape(5, 1)
        return grad

    def update_with(self, grads, rewards):
        if len(grads) < 50:
            return
        for i in range(len(grads)):
            # Loop through everything that happened in the episode
            # and update towards the log policy gradient times **FUTURE** reward

            total_grad_effect = 0
            for t, r in enumerate(rewards[i:]):
                total_grad_effect += r * (GAMMA ** r)
            self.w += LEARNING_RATE * grads[i] * total_grad_effect


def main(argv):
    env = gym.make('CartPole-v0')
    np.random.seed(1)

    agent = Agent()
    complete_scores = []

    for e in range(NUM_EPISODES):
        state = env.reset()[None, :]
        state = agent.poly.fit_transform(state)

        rewards = []
        grads = []
        score = 0

        while True:

            probs = agent.policy(state)
            action_space = env.action_space.n
            action = np.random.choice(action_space, p=[1 - probs, probs])

            next_state, reward, done, _ = env.step(action)
            next_state = next_state[None, :]
            next_state = agent.poly.fit_transform(next_state.reshape(1, 4))

            grad = agent.grad(probs, action, state)
            grads.append(grad)
            rewards.append(reward)

            score += reward
            state = next_state

            if done:
                break

        agent.update_with(grads, rewards)
        complete_scores.append(score)

    env.close()
    plt.plot(np.arange(NUM_EPISODES),
             complete_scores)
    plt.savefig('image1.png')


if __name__ == '__main__':
    main(None)

Das Zeichnen des gesamten Lernens bleibt zufällig. Nichts hilft beim Einstellen von Hyperparametern. Unter dem Beispielbild.

Geben Sie hier die Bildbeschreibung ein

Referenzen :

1) Deep Reinforcement Learning: Pong aus Pixeln

2) Eine Einführung in Policy Gradients mit Cartpole und Doom

3) Ableiten von Richtlinienverläufen und Implementieren von REINFORCE

4) Trick des Tages beim maschinellen Lernen (5): Log Derivative Trick 12


AKTUALISIEREN

Scheint, als ob die Antwort unten etwas Arbeit von der Grafik machen könnte. Aber es ist keine Protokollwahrscheinlichkeit und nicht einmal ein Gradient der Richtlinie. Und ändert den gesamten Zweck der RL-Verlaufsrichtlinie. Bitte überprüfen Sie die Referenzen oben. Nach dem Bild folgen wir der nächsten Aussage.

Geben Sie hier die Bildbeschreibung ein

Ich muss einen Verlauf der Protokollfunktion meiner Richtlinie erstellen (dies sind lediglich Gewichte und sigmoidAktivierung).

GensaGames
quelle
4
Ich empfehle Ihnen, diese Frage in Data Science Stack Exchange zu veröffentlichen, da es sich hauptsächlich um eine theoretische Frage handelt (Stack Overflow dient hauptsächlich zum Codieren von Fragen). Sie werden auch mehr Personen erreichen, die sich in diesem Bereich auskennen.
Gilles-Philippe Paillé
@ Gilles-PhilippePaillé Ich habe Code hinzugefügt, der das Problem darstellt. Was ich tun muss, ist nur, einige der Teile mit Aktivierung zu reparieren. Bitte überprüfen Sie die aktualisierte Antwort.
GensaGames
1
Um Richtlinienverläufe abzuleiten, finden Sie hier einen Referenzartikel mit einem Arbeitsbeispiel für die gleiche Art von Vereinbarungen. Hoffentlich erfahren Sie dies im Detail: medium.com/@thechrisyoon/… .
Muhammad Usman
@ MuhammadUsman. Danke für die Info. Ich habe diese Quelle rot. Im Moment ist es klar und Form Beispiel oben, ich versuche die Aktivierung von softmaxauf zu ändern signmoid. Das ist nur eine Sache, die ich im obigen Beispiel tun muss.
GensaGames
2
@JasonChia Sigmoid gibt eine reelle Zahl im Bereich aus [0, 1] , die als Wahrscheinlichkeit einer positiven Aktion interpretiert werden kann (biegen Sie beispielsweise in CartPole rechts ab). Dann ist die Wahrscheinlichkeit einer negativen Aktion (links abbiegen) 1 - sigmoid. Die Summe dieser Wahrscheinlichkeiten ist 1. Ja, dies ist eine Standard-Polkartenumgebung.
Pavel Tyshevskyi

Antworten:

8

Das Problem ist mit der gradMethode.

def grad(self, probs, action, state):
    dsoftmax = self.sigmoid_grad(probs)
    dlog = dsoftmax / probs
    grad = state.T.dot(dlog)
    grad = grad.reshape(5, 1)
    return grad

Im ursprünglichen Code wurde Softmax zusammen mit der CrossEntropy-Verlustfunktion verwendet. Wenn Sie die Aktivierung auf Sigmoid umschalten, wird die richtige Verlustfunktion zu Binary CrossEntropy. Der Zweck der gradMethode besteht nun darin, den Gradienten der Verlustfunktion wrt zu berechnen. Gewichte. Wenn Sie die Details schonen , wird der richtige Gradient (probs - action) * statein der Terminologie Ihres Programms angegeben. Das Letzte ist, ein Minuszeichen hinzuzufügen - wir wollen das Negativ der Verlustfunktion maximieren.

Richtige gradMethode also:

def grad(self, probs, action, state):
    grad = state.T.dot(probs - action)
    return -grad

Eine weitere Änderung, die Sie möglicherweise hinzufügen möchten, ist die Erhöhung der Lernrate. LEARNING_RATE = 0.0001und NUM_EPISODES = 5000wird das folgende Diagramm erzeugen:

Richtige mittlere Belohnung gegen Anzahl der Folgen

Die Konvergenz ist viel schneller, wenn die Gewichte unter Verwendung der Gaußschen Verteilung mit dem Mittelwert Null und der kleinen Varianz initialisiert werden:

def __init__(self):
    self.poly = PolynomialFeatures(1)
    self.w = np.random.randn(5, 1) * 0.01

Geben Sie hier die Bildbeschreibung ein

AKTUALISIEREN

Vollständiger Code hinzugefügt, um die Ergebnisse zu reproduzieren:

import gym
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures

NUM_EPISODES = 5000
LEARNING_RATE = 0.0001
GAMMA = 0.99


# noinspection PyMethodMayBeStatic
class Agent:
    def __init__(self):
        self.poly = PolynomialFeatures(1)
        self.w = np.random.randn(5, 1) * 0.01

    # Our policy that maps state to action parameterized by w
    # noinspection PyShadowingNames
    def policy(self, state):
        z = np.sum(state.dot(self.w))
        return self.sigmoid(z)

    def sigmoid(self, x):
        s = 1 / (1 + np.exp(-x))
        return s

    def sigmoid_grad(self, sig_x):
        return sig_x * (1 - sig_x)

    def grad(self, probs, action, state):
        grad = state.T.dot(probs - action)
        return -grad

    def update_with(self, grads, rewards):
        if len(grads) < 50:
            return
        for i in range(len(grads)):
            # Loop through everything that happened in the episode
            # and update towards the log policy gradient times **FUTURE** reward

            total_grad_effect = 0
            for t, r in enumerate(rewards[i:]):
                total_grad_effect += r * (GAMMA ** r)
            self.w += LEARNING_RATE * grads[i] * total_grad_effect


def main(argv):
    env = gym.make('CartPole-v0')
    np.random.seed(1)

    agent = Agent()
    complete_scores = []

    for e in range(NUM_EPISODES):
        state = env.reset()[None, :]
        state = agent.poly.fit_transform(state)

        rewards = []
        grads = []
        score = 0

        while True:

            probs = agent.policy(state)
            action_space = env.action_space.n
            action = np.random.choice(action_space, p=[1 - probs, probs])

            next_state, reward, done, _ = env.step(action)
            next_state = next_state[None, :]
            next_state = agent.poly.fit_transform(next_state.reshape(1, 4))

            grad = agent.grad(probs, action, state)
            grads.append(grad)
            rewards.append(reward)

            score += reward
            state = next_state

            if done:
                break

        agent.update_with(grads, rewards)
        complete_scores.append(score)

    env.close()
    plt.plot(np.arange(NUM_EPISODES),
             complete_scores)
    plt.savefig('image1.png')


if __name__ == '__main__':
    main(None)
Pavel Tyshevskyi
quelle
Vielen Dank. Ich werde diese Lösung später versuchen.
GensaGames
Ich bin mir nicht sicher, wo Sie eine solche Ableitung für meine Funktion nehmen. Wie Sie das Bild oben überprüfen können. Ich müsste einen Gradienten des Protokolls der Richtlinie nehmen . Wo Politik in meinem Fall einfach mit gewichtet sigmoid. Aber Ihr Farbverlauf als Antwort sollte nichts mit meinem Farbverlauf zu tun haben. Recht?
GensaGames
Beachten Sie, dass Sie keine Informationen darüber enthalten, welche Aktion ausgeführt wurde. Gemäß dieser Vorlesung über Richtlinienverläufe (Folie 13) sollte das Update so aussehen (action - probs) * sigmoid_grad(probs), aber ich habe es sigmoid_gradwegen des verschwindenden Problems mit dem Sigmoid-Verlauf weggelassen .
Pavel Tyshevskyi
Der springende Punkt hier ist, die Richtung anzugeben, in die wir die Gewichte ändern möchten. Wenn action = 1wir probsnäher dran sein wollen 1, erhöhen wir die Gewichte (positiver Gradient). Wenn action=0wir probsnäher sein wollen 0, verringern wir die Gewichte (negativer Gradient).
Pavel Tyshevskyi
1
In jedem Fall funktionieren die oben genannten Änderungen überhaupt nicht. Können Sie die gesamte Datei freigeben? Gleichzeitig möchte ich eine klare Probe machen und kümmere mich in diesem Fall nicht um das Verschwinden des Problems. Und (action - probs)es ist nur eine andere Möglichkeit, die gleichen Gewichte zu ändern.
GensaGames