<jupyter_start><jupyter_text>Tutorial - Stable Baselines

 Índice

- [O que é Stable Baselines?](O-que-é-Stable-Baselines?)
- [Como usar Stable Baselines?](Como-usar-Stable-Baselines?)
  - [Importando o Gym](Importando-o-Gym)
  - [Como Funciona um Ambiente do Gym?](Como-Funciona-um-Ambiente-do-Gym?)
  - [Criando um Ambiente](Criando-um-Ambiente)
  - [Criando um Agente](Criando-um-Agente)
  - [Rodando um Episódio](Rodando-um-Episódio)
  - [Avaliando o Agente](Avaliando-o-Agente)
  - [Treinamento](Treinamento)
  - [Monitorando o Treinamento](Monitorando-o-Treinamento)
  - [Customizando a Rede Neural](Customizando-a-Rede-Neural)  O que é Stable Baselines?

A **[Stable Baselines](https://github.com/DLR-RM/stable-baselines3)** é uma biblioteca de Aprendizado por Reforço que implementa diversos algoritmos de agentes de RL, além de várias funcionalidades úteis para o treinamento de um agente. Suas implementações são bem simples e intuitivas, mas sem deixarem de ser otimizadas e poderosas, buscando facilitar o desenvolvimento de projetos de Aprendizado por Reforço de alta qualidade.

![Logo](https://github.com/hill-a/stable-baselines/raw/master/docs//_static/img/logo.png "Logo da Stable Baselines")  Como usar Stable Baselines?

Com Stable Baselines, o processo de criar e treinar um agente é bem simples. Entretanto, caso você não saiba muito de Aprendizado por Reforço, é primeiro preciso passar por alguns conhecimentos básicos.  Importando o Gym

O **[Gym](https://gym.openai.com/)** é uma biblioteca desenvolvida pela OpenAI que contém várias implementações prontas de ambientes de Aprendizado por Reforço. Ela é muito utilizada quando se quer testar um algoritmo de agente sem ter o trabalho de programar seu próprio ambiente.


Exemplo de Ambientes do Gym


Para se ter acesso a esses ambientes, basta importar o Gym da seguinte forma:<jupyter_code>import gym<jupyter_output><empty_output><jupyter_text>Como Funciona um Ambiente do Gym?

Agora que você já sabe o que é um ambiente, é preciso entender como nosso agente interage efetivamente com ele. Todos os ambientes do Gym possuem alguns métodos simples para facilitar a comunicação com eles:



| Método               | Funcionalidade                                          |
| :------------------- |:------------------------------------------------------- |
| `reset()`              | Inicializa o ambiente e recebe a observação inicial     |
| `step(action)`         | Executa uma ação e recebe a observação e a recompensa   |
| `render()`             | Renderiza o ambiente                                    |
| `close()`              | Fecha o ambiente                                        |



Assim, o código para interagir com o ambiente costuma seguir o seguinte modelo:

---

```python
ambiente = gym.make("Nome do Ambiente")                          Cria o ambiente
observação = ambiente.reset()                                    Inicializa o ambiente
acabou = False

while not acabou:
    ambiente.render()                                            Renderiza o ambiente
    observação, recompensa, acabou, info = ambiente.step(ação)   Executa uma ação
    
ambiente.close()                                                 Fecha o ambiente
```

---  Criando um Ambiente

Para utilizar um dos ambientes do Gym, nós utilizamos a função ```gym.make()```, passando o nome do ambiente desejado como parâmetro e guardando seu valor retornado em uma variável que chamaramos de ```env```. A lista com todos os ambiente pode ser encontrada [aqui](https://gym.openai.com/envs/classic_control).<jupyter_code>env = gym.make("CartPole-v1")
env.seed(0)<jupyter_output><empty_output><jupyter_text>Nesse caso, nós vamos utilizar o ambiente ```CartPole-v1```, um ambiente bem simples que modela um pêndulo invertido em cima de um carrinho buscando seu estado de equilíbrio.



 CartPole

Antes de treinar qualquer agente, primeiro é preciso entender melhor quais as características do nosso ambiente.

O **Espaço de Observação** do CartPole é definido por 4 informações:



|     | Informação                         | Min     | Max    |
| :-- | :--------------------------------- | :-----: | :----: |
| 0   | Posição do Carrinho                | -4.8    | 4.8    |
| 1   | Velocidade do Carrinho             | -Inf    | Inf    |
| 2   | Ângulo da Barra                    | -24 deg | 24 deg |
| 3   | Velocidade na Extremidade da Barra | -Inf    | Inf    |



Dessa forma, a cada instante recebemos uma lista da observação com o seguinte formato:<jupyter_code>print(env.observation_space.sample())<jupyter_output>[-1.2699846e+00  7.7429995e+37  2.1132760e-02 -2.2716169e+38]<jupyter_text>Já o **Espaço de Ação** é composto por duas ações únicas: mover o carrinho para a **esquerda** ou para a **direita**.

Quando queremos mover o carrinho para a esquerda, fazemos um `env.step(0)`; quando queremos movê-lo para a direita, enviamos um `env.step(1)`<jupyter_code>print(env.action_space.sample())<jupyter_output>0<jupyter_text>Criando um Agente

Depois de escolhermos nosso ambiente, já podemos pensar em qual algoritmo de agente queremos usar.

A biblioteca disponibiliza algoritmos de diversos tipos, como *Deep Q-Networks* e *Actor-Critics*. Nem todos eles suportam todos os tipos de ambientes, então é recomendável dar uma olhada na [página oficial dos algoritmos](https://stable-baselines3.readthedocs.io/en/master/guide/algos.html).

 Inicialização

Todos os algoritmos são inicializados de uma forma parecida, nós instanciamos eles com alguns parâmetros em comum: ```policy```, que define a arquitetura da rede neural e ```env```, que define o ambiente no qual o agente vai treinar. Assim, a inicialização segue o seguinte formato:

```python
agente = ALGORITMO(policy, env)
```

Como exemplo, vamos criar um **PPO**, um tipo de Actor-Critic, para resolver o ambiente do **CartPole**:<jupyter_code>from stable_baselines3 import PPO

model = PPO('MlpPolicy', env, seed=1, verbose=1)<jupyter_output>Using cuda device
Wrapping the env in a DummyVecEnv.<jupyter_text>Inicializamos o PPO com a `policy` 'MlpPolicy', que cria uma rede neural artificial com 2 camadas ocultas de 64 neurônios cada, no `env` do CartPole. Todos os agentes também possuem alguns métodos em comuns bem importantes de se conhecer:



| Método        | Funcionalidade                          |
| :------------ |:--------------------------------------- |
| `learn()`       | Treina o agente                         |
| `predict(obs)`  | Escolhe uma ação com base na observação |
| `save(caminho)` | Salve o agente                          |
| `load(caminho)` | Carrega o agente                        |



Dessa forma, se quisermos escolher a próxima ação do nosso agente, nós rodamos:

```python
agente.predict(observação)
```  Rodando um Episódio

Bom, agora que já temos nosso agente e nosso ambiente, já podemos rodar nosso primeiro episódio!

Para isso, vamos criar uma função `run_episode` para simplificar o processo:<jupyter_code>import itertools

# A função recebe o ambiente e o agente como parâmetros
def run_episode(env, model, render=False):
    # Primeiro, inicializamos o ambiente e guardamos a observação inicial em 'obs'
    obs = env.reset()
    
    # Loop do episódio
    for t in itertools.count():
        # Nosso modelo prediz a ação 'action' a ser tomada com base na nossa observação 'obs'
        action, _states = model.predict(obs)
        
        # Tomamos a ação 'action', e recebemos uma nova observação 'obs', uma recompensa 'reward'
        # e se o episódio terminou 'done'
        obs, reward, done, info = env.step(action)
        
        # Renderiza o ambiente, caso desejado
        if render:
            env.render()
            
        # Finaliza o episódio, caso tenha terminado
        if done:
            break
    
    # Quando terminado, fechamos o ambiente
    env.close()
    
    # Imprimindo a duração do ambiente
    print("Duração do Episódio: " + str(t+1))<jupyter_output><empty_output><jupyter_text>Pronto, já podemos rodar o nosso agente!<jupyter_code>run_episode(env, model, render=True)<jupyter_output>Duração do Episódio: 13<jupyter_text>Provavelmente o resultado não foi tão bom assim. Isso é porque precisamos treinar nosso agente para que ele saiba as melhores ações a se tomar.  Avaliando o Agente

Para melhor avaliar o desempenho do nosso agente, podemos utilizar a função `evaluate_policy` da biblioteca, que roda uma quantidade determinável de episódios e retorna a recompensa média obtida.<jupyter_code>from stable_baselines3.common.evaluation import evaluate_policy

# Ambiente separado para avaliação
eval_env = gym.make('CartPole-v1')

# Avaliando o agente
mean_reward, std_reward = evaluate_policy(model, eval_env, n_eval_episodes=25, deterministic=True)

print(f"Recompensa Média: {mean_reward:.2f} +/- {std_reward}")<jupyter_output>Recompensa Média: 28.16 +/- 4.788987366865776<jupyter_text>Treinamento

O treinamento do agente acontece de maneira bem simples, basta rodar o método `.learn()` com a quantidade de instantes de tempo `total_timesteps` que desejamos treinar.

Ao longo do treinamento, nosso agente vai mostrando algumas informações importantes no ouput, como a função de custo `loss`.<jupyter_code>model.learn(total_timesteps=25000)<jupyter_output>-----------------------------
| time/              |      |
|    fps             | 440  |
|    iterations      | 1    |
|    time_elapsed    | 4    |
|    total_timesteps | 2048 |
-----------------------------
-----------------------------------------
| time/                   |             |
|    fps                  | 279         |
|    iterations           | 2           |
|    time_elapsed         | 14          |
|    total_timesteps      | 4096        |
| train/                  |             |
|    approx_kl            | 0.008619683 |
|    clip_fraction        | 0.172       |
|    clip_range           | 0.2         |
|    entropy_loss         | -0.686      |
|    explained_variance   | -4.02e+03   |
|    learning_rate        | 0.0003      |
|    loss                 | 7.61        |
|    n_updates            | 10          |
|    policy_gradient_loss | -0.0165     |
|    value_loss           | 55          |
-----------------------------------------
----------------------------------[...]<jupyter_text>Agora que já treinamos o nosso agente, podemos rodar mais um episódio para ver como ele melhorou:<jupyter_code>run_episode(env, model, render=True)<jupyter_output>Duração do Episódio: 500<jupyter_text>Para melhor avaliar nosso desempenho em múltiplos episódios, vamos rodar novamente a função `evaluate_policy`:<jupyter_code>from stable_baselines3.common.evaluation import evaluate_policy

# Avaliando o agente
mean_reward, std_reward = evaluate_policy(model, eval_env, n_eval_episodes=25, deterministic=True)

print(f"Recompensa Média: {mean_reward:.2f} +/- {std_reward}")<jupyter_output>Recompensa Média: 500.00 +/- 0.0<jupyter_text>Parabéns, você acabou de criar o seu primeiro agente com a Stable Baselines!

Esse conhecimento já é suficiente para aplicar em vários ambientes simples, mas ainda existem várias outras funcionalidades muito interessantes da biblioteca que valem a pena aprender.  Monitorando o Treinamento

Para obter mais informações do treinamento, podemos utilizar o *wrapper* `Monitor` da biblioteca para monitorar o desempenho do nosso agente mesmo durante o treino.

Para isto, primeiro devemos criar uma pasta de logs:<jupyter_code>import os

# Cria um diretório de logs
log_dir = "./logs/"
os.makedirs(log_dir, exist_ok=True)<jupyter_output><empty_output><jupyter_text>Em seguida, criamos o nosso ambiente e passamos ele para o nosso *wrapper*:<jupyter_code>from stable_baselines3.common.monitor import Monitor

# Cria o ambiente
env = gym.make('CartPole-v1')

# Encapsula ele no wrapper Monitor
env = Monitor(env, log_dir)<jupyter_output><empty_output><jupyter_text>A partir daí, basta treinar o nosso agente como normal, utilizando o nosso novo ambiente encapsulado pelo *wrapper*:<jupyter_code>model = PPO('MlpPolicy', env, seed=1, verbose=1).learn(total_timesteps=40000)<jupyter_output>Using cuda device
Wrapping the env in a DummyVecEnv.
------------------------------------------
| rollout/                |              |
|    ep_len_mean          | 23.5         |
|    ep_rew_mean          | 23.5         |
| time/                   |              |
|    fps                  | 445          |
|    iterations           | 1            |
|    time_elapsed         | 4            |
|    total_timesteps      | 2048         |
| train/                  |              |
|    approx_kl            | 0.0058214897 |
|    clip_fraction        | 0.0469       |
|    clip_range           | 0.2          |
|    entropy_loss         | -0.533       |
|    explained_variance   | -1.51        |
|    learning_rate        | 0.0003       |
|    loss                 | 51.8         |
|    n_updates            | 130          |
|    policy_gradient_loss | -0.0034      |
|    value_loss           | 53           |
------------------------------------------
----------------------------------------
| r[...]<jupyter_text>Terminado o treinamento, podemos plotar os resultados utilizando o `result_plotter`:<jupyter_code>from stable_baselines3.common import results_plotter

# Plota os resultados
results_plotter.plot_results([log_dir], 1e5, results_plotter.X_TIMESTEPS, "PPO CartPole")<jupyter_output><empty_output><jupyter_text>Pronto! Agora temos uma visualização da performance do nosso agente durante o seu treinamento. Entretanto, podemos criar a nossa própria função de visualização para melhor analisar o modelo:<jupyter_code>import matplotlib.pyplot as plt

def plot_results(log_folder, window=10, title='Curva de Aprendizado'):
    """
    Plota os resultados.

    :param log_folder: (str) diretório dos resultados a serem plotados
    :param window: (int) tamanho da janela da média móvel
    :param title: (str) título do plot
    """
    # Obtém os resultados
    results = results_plotter.load_results(log_folder)
    x, y = results_plotter.ts2xy(results, 'timesteps')
    
    # Calcula a média móvel do retorno
    y_smoothed = results_plotter.rolling_window(y, window=window).mean(axis=1)
    
    # Plota os resultados
    fig = plt.figure(title, figsize=(10, 5))
    plt.scatter(x, y, s=2)
    plt.plot(x[window-1:], y_smoothed, color='darkblue', label="Média Móvel")
    plt.xlabel('Timesteps')
    plt.ylabel('Retorno Médio')
    plt.title(title)
    plt.legend()
    plt.show()<jupyter_output><empty_output><jupyter_text>Agora conseguimos visualizar melhor o treinamento:<jupyter_code>plot_results(log_dir)<jupyter_output><empty_output><jupyter_text>Customizando a Rede NeuralSe quisermos mais controle a respeito da arquitetura do nosso modelo, podemos modificar o parâmetro `policy_kwargs` para customizar a nossa rede neural. Por exemplo, podemos alterar: - `activation_fn`: a função de ativação da rede, como `torch.nn.Tanh` ou `torch.nn.ReLU`. - `net_arch`: a quantidade de camadas e neurônios da rede. Um Actor-Critic com duas redes de duas camadas de 32 neurônios teria uma arquitetura `[dict(pi=[32, 32], vf=[32, 32])]`, por exemplo.<jupyter_code>import torch

# Cria o ambiente com o wrapper Monitor
env = Monitor(gym.make('CartPole-v1'), log_dir)

# Parâmetros das redes neurais
policy_kwargs = dict(activation_fn=torch.nn.ReLU,                # Troca a função de ativação para ReLU
                     net_arch=[dict(pi=[32, 32], vf=[32, 32])])  # Define a arquitetura das redes do Actor-Critic

# Cria o nosso modelo com os novos parâmetros
model = PPO('MlpPolicy', env, seed=1, verbose=1, policy_kwargs=policy_kwargs).learn(total_timesteps=40000)
plot_results(log_dir)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11184085885696242, 'fraction_numerical': 0.01768234922639722, 'mean_word_length': 2.6631968540365487, 'pattern_counts': {'":': 0, '<': 64, '<?xml version=': 0, '>': 64, 'https://': 5, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '18', 'dir': 'jupyter-structured-clean-dedup', 'id': '519993', 'license': 'MIT', 'n_tokens_mistral': 5467, 'n_tokens_neox': 5200, 'n_words': 1508, 'path': 'Bibliotecas/Stable Baselines/Stable Baselines.ipynb', 'repo_name': 'turing-usp/Aprendizado-por-Reforco', 'size': '15835'}