Zrozumienie pochodnej polityki gradientowej

19

Próbuję odtworzyć bardzo prosty przykład zasady Gradient, z jego bloga źródłowego blogu Andreja Karpathy'ego . W tym artykule znajdziesz przykład z CartPole i Policy Gradient z listą masy i aktywacją Softmax. Oto mój odtworzony i bardzo prosty przykład gradientu zasad CartPole, który działa idealnie .

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)

wprowadź opis zdjęcia tutaj

.

.

Pytanie

Próbuję zrobić, prawie ten sam przykład, ale z aktywacją Sigmoid (tylko dla uproszczenia). To wszystko, co muszę zrobić. Przełącz aktywację w modelu z softmaxna sigmoid. Które powinny na pewno zadziałać (na podstawie poniższych wyjaśnień). Ale mój model Gradientu zasad niczego się nie uczy i zachowuje losowość. Jakieś sugestie?

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)

Spisywanie całej nauki jest losowe. Nic nie pomaga w dostrajaniu hiper parametrów. Poniżej przykładowego obrazu.

wprowadź opis zdjęcia tutaj

Referencje :

1) Nauka głębokiego wzmacniania: Pong z pikseli

2) Wprowadzenie do Gradientów polityki z Cartpole i Doom

3) Wyprowadzanie gradientów polityki i wdrażanie REINFORCE

4) Trik dnia w uczeniu maszynowym (5): Log Trick Derick 12


AKTUALIZACJA

Wygląda na to, że odpowiedź poniżej może trochę popracować z grafiką. Ale to nie jest log prawdopodobieństwa, ani nawet gradientu polityki. I zmienia cały cel RL Gradient Policy. Sprawdź referencje powyżej. Po obrazie następna wypowiedź.

wprowadź opis zdjęcia tutaj

Muszę wziąć funkcję Gradient dziennika w mojej Polityce (która jest po prostu wagą i sigmoidaktywacją).

GensaGames
źródło
4
Sugeruję, abyś zamieścił to pytanie na Data Science Stack Exchange, ponieważ jest to głównie pytanie teoretyczne (przepełnienie stosu dotyczy głównie kodowania pytań). Dotrzesz także do większej liczby osób posiadających wiedzę w tej dziedzinie.
Gilles-Philippe Paillé
@ Gilles-PhilippePaillé Dodałem kod, który reprezentuje problem. To, co muszę zrobić, to naprawić niektóre części za pomocą aktywacji. Sprawdź zaktualizowaną odpowiedź.
GensaGames,
1
Aby wyprowadzić gradienty zasad, oto artykuł referencyjny z działającym przykładem tego samego rodzaju uzgodnień. Mam nadzieję, że dowiesz się szczegółowo: medium.com/@thechrisyoon/… .
Muhammad Usman,
@MuhammadUsman. Dziękuję za informację. Redytuję to źródło. W tej chwili jest to jasne i powyższy przykład. Próbuję zmienić aktywację z softmaxna signmoid. To tylko jedna rzecz, którą muszę zrobić w powyższym przykładzie.
GensaGames,
2
@JasonChia sigmoid generuje liczbę rzeczywistą w zakresie, [0, 1]który można interpretować jako prawdopodobieństwo pozytywnego działania (na przykład skręć w prawo w CartPole). Wtedy prawdopodobieństwo negatywnego działania (skręć w lewo) jest 1 - sigmoid. Suma tych prawdopodobieństw wynosi 1. Tak, jest to standardowe środowisko karty biegunowej.
Pavel Tyshevskyi

Odpowiedzi:

8

Problem dotyczy gradmetody.

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

W oryginalnym kodzie użyto Softmax wraz z funkcją utraty CrossEntropy. Po zmianie aktywacji na Sigmoid właściwą funkcją utraty staje się Binary CrossEntropy. Teraz celem tej gradmetody jest obliczenie gradientu funkcji straty wrt. ciężary Oszczędzając szczegóły, właściwy (probs - action) * statetermin podano w terminologii twojego programu. Ostatnią rzeczą jest dodanie znaku minus - chcemy zmaksymalizować minus funkcji straty.

Właściwa gradmetoda:

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

Kolejną zmianą, którą możesz chcieć dodać, jest zwiększenie szybkości uczenia się. LEARNING_RATE = 0.0001i NUM_EPISODES = 5000wyprodukuje następujący wykres:

Prawidłowa średnia nagroda vs. liczba odcinków

Konwergencja będzie znacznie szybsza, jeśli wagi zostaną zainicjowane przy użyciu rozkładu Gaussa z zerową średnią i małą wariancją:

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

wprowadź opis zdjęcia tutaj

AKTUALIZACJA

Dodano pełny kod do odtworzenia wyników:

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
źródło
Dziękuję Ci bardzo. Wypróbuję to rozwiązanie później.
GensaGames,
Nie jestem pewien, skąd bierzesz takie pochodne dla mojej funkcji. Jak możesz sprawdzić obrazek powyżej. Musiałbym wziąć gradient dziennika polisy . Gdzie Polityka w moim przypadku jest po prostu ważna sigmoid. Ale twój gradient w odpowiedzi nie powinien mieć nic wspólnego z moim gradientem. Dobrze?
GensaGames,
Zauważ, że nie dołączasz żadnych informacji o tym, jakie działanie zostało wykonane. Zgodnie z wykładem na temat Gradientów Polityki (slajd 13) aktualizacja powinna wyglądać (action - probs) * sigmoid_grad(probs), ale pominąłem ją z sigmoid_gradpowodu znikającego problemu z gradientem sigmoidalnym.
Pavel Tyshevskyi
Najważniejsze jest tutaj wskazanie kierunku, w którym chcemy zmieniać wagi. Jeśli action = 1chcemy probsbyć bliżej 1, zwiększając wagi (gradient dodatni). Jeśli action=0chcemy probsbyć bliżej 0, to zmniejszamy wagi (gradient ujemny).
Pavel Tyshevskyi
1
W każdym razie powyższe zmiany w ogóle nie działają, czy możesz udostępnić cały plik? W tym samym czasie chcę zrobić wyraźną próbkę i nie dbam o zniknięcie problemu w tym przypadku. I (action - probs)to tylko kolejny sposób zmiany tych samych poglądów.
GensaGames,