Comprensione della derivazione della politica delle sfumature


19

Sto cercando di ricreare un esempio molto semplice di Policy Gradient, dal suo blog sulle risorse di origine Andrej Karpathy . In questo articolo troverai esempi con CartPole e Policy Gradient con un elenco di peso e attivazione di Softmax. Ecco il mio esempio ricreato e molto semplice del gradiente delle politiche di CartPole, che funziona perfettamente .

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)

inserisci qui la descrizione dell'immagine

.

.

Domanda

Sto provando a fare, quasi lo stesso esempio ma con l'attivazione di Sigmoid (solo per semplicità). Questo è tutto ciò che devo fare. Passa l'attivazione nel modello da softmaxa sigmoid. Che dovrebbe funzionare di sicuro (in base alla spiegazione di seguito). Ma il mio modello di sfumatura politica non impara nulla e diventa casuale. Qualche suggerimento?

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)

Tracciare tutto l'apprendimento rimane casuale. Nulla aiuta con l'ottimizzazione dei parametri iper. Sotto l'immagine di esempio.

inserisci qui la descrizione dell'immagine

Riferimenti :

1) Apprendimento di rinforzo profondo: Pong da pixel

2) Un'introduzione ai gradienti politici con Cartpole e Doom

3) Derivazione dei gradienti delle politiche e attuazione di REINFORCE

4) Trucco automatico del giorno (5): Trucco derivativo del registro 12


AGGIORNARE

Sembra che la risposta qui sotto potrebbe fare qualche lavoro dal grafico. Ma non è la probabilità di registro e nemmeno il gradiente della politica. E cambia l'intero scopo della politica di gradiente RL. Si prega di controllare i riferimenti sopra. Seguendo l'immagine della prossima affermazione.

inserisci qui la descrizione dell'immagine

Devo prendere una sfumatura della funzione Log della mia politica (che è semplicemente pesi e sigmoidattivazione).


4
Ti suggerisco di pubblicare questa domanda su Data Science Stack Exchange poiché si tratta principalmente di una domanda teorica (Stack Overflow è principalmente per le domande di codifica). Raggiungerai anche più persone competenti in questo dominio.
Gilles-Philippe Paillé,

@ Gilles-PhilippePaillé Ho aggiunto il codice, che rappresenta il problema. Quello che devo fare è solo riparare alcune parti con l'attivazione. Si prega di controllare la risposta aggiornata.
GensaGames

1
Per ricavare i gradienti dalla politica, ecco un articolo di riferimento con un esempio funzionante dello stesso tipo di accordi, si spera che imparerai in dettaglio: medium.com/@thechrisyoon/… .
Muhammad Usman,

@MuhammadUsman. Grazie per le informazioni Ho rosso quella fonte. In questo momento è chiaro e sotto forma di esempio sopra, sto cercando di cambiare l'attivazione da softmaxa signmoid. Questa è solo una cosa che devo fare nell'esempio sopra.
GensaGames

2
@JasonChia sigmoid genera un numero reale nell'intervallo [0, 1]che può essere interpretato come probabilità di azione positiva (ad esempio, girare a destra in CartPole). Quindi la probabilità di azione negativa (girare a sinistra) è 1 - sigmoid. La somma di queste probabilità è 1. Sì, si tratta di un ambiente di carte pole standard.
Pavel Tyshevskyi,

Risposte:


8

Il problema è con il gradmetodo.

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

Nel codice originale è stato utilizzato Softmax insieme alla funzione di perdita CrossEntropy. Quando si passa l'attivazione a Sigmoid, la funzione di perdita corretta diventa Binary CrossEntropy. Ora, lo scopo del gradmetodo è calcolare il gradiente della funzione di perdita wrt. pesi. A parte i dettagli, (probs - action) * statenella terminologia del programma viene fornito il gradiente corretto . L'ultima cosa è aggiungere il segno meno: vogliamo massimizzare il negativo della funzione di perdita.

gradMetodo corretto quindi:

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

Un altro cambiamento che potresti voler aggiungere è quello di aumentare il tasso di apprendimento. LEARNING_RATE = 0.0001e NUM_EPISODES = 5000produrrà la seguente trama:

Ricompensa media corretta vs numero di episodi

La convergenza sarà molto più veloce se i pesi vengono inizializzati utilizzando la distribuzione gaussiana con media zero e varianza ridotta:

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

inserisci qui la descrizione dell'immagine

AGGIORNARE

Aggiunto codice completo per riprodurre i risultati:

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)

Grazie mille. Proverò questa soluzione più tardi.
GensaGames

Non sono sicuro di dove tu prenda tale derivazione per la mia funzione. Come puoi controllare l'immagine qui sopra. Avrei bisogno di prendere il gradiente del registro della politica . Dove la politica nel mio caso pesa semplicemente sigmoid. Ma il tuo gradiente in risposta non dovrebbe avere nulla a che fare con il mio gradiente. Giusto?
GensaGames

Si noti che non si incorporano informazioni su quale azione è stata eseguita. Secondo questa lezione su Gradienti politici (diapositiva 13) , l'aggiornamento dovrebbe apparire come (action - probs) * sigmoid_grad(probs), ma ho omesso a sigmoid_gradcausa del problema evanescente con gradiente sigmoideo.
Pavel Tyshevskyi,

Il punto cruciale qui è indicare la direzione in cui vogliamo cambiare i pesi. Se action = 1, vogliamo probsessere più vicini 1, aumentando i pesi (gradiente positivo). Se action=0, vogliamo probsessere più vicini 0, quindi diminuendo i pesi (gradiente negativo).
Pavel Tyshevskyi,

1
In ogni caso, le modifiche sopra non funzionano affatto, puoi condividere l'intero file? Allo stesso tempo, voglio fare un campione chiaro e in questo caso non mi preoccupo del problema di sparizione. Ed (action - probs)è solo un altro modo di cambiare gli stessi wights.
GensaGames
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.