Análisis del código fuente del entorno FrozenLake (escena) en OpenAI Gym (5)

Continuación del artículo anterior: análisis del código fuente del entorno FrozenLake (escena) en OpenAI Gym (4)

El artículo anterior depuró el segundo paso clave a través de pdb:

  • env.reset()

 Este artículo analiza el tercer paso clave:

  • env.action_space.sample()

Para facilitar la visualización y la depuración, salga de la depuración anterior y vuelva a ejecutar el siguiente comando para iniciar una nueva depuración:

python -m pdb frozen_lake2.py 

Los comandos y los resultados son los siguientes:

$ python -m pdb frozen_lake2.py 
> /home/penghao/OpenAI-Gym/sample_code/frozen_lake2.py(1)<module>()
-> import numpy as np
(Pdb) 

env.action_space.sample() está en la línea 70 de frozen_lake2.py, así que establezca el punto de interrupción en la línea 70 del archivo. Los comandos y los resultados son los siguientes:

$ python -m pdb frozen_lake2.py 
> /home/penghao/OpenAI-Gym/sample_code/frozen_lake2.py(1)<module>()
-> import numpy as np
(Pdb) b 70
Breakpoint 1 at /home/penghao/OpenAI-Gym/sample_code/frozen_lake2.py:70
(Pdb) 

Luego ingrese c para que el programa continúe ejecutándose (ejecutar hasta este punto de interrupción). Como sigue:

(Pdb) c
The observation space: Discrete(16)
16
The action space: Discrete(4)
4
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
> /home/penghao/OpenAI-Gym/sample_code/frozen_lake2.py(70)<module>()
-> action = env.action_space.sample()
(Pdb) 

Se puede ver que el programa se ha detenido en el punto de interrupción. Ingrese s, afine la ejecución, lo que comúnmente se conoce como Step In, es decir, ingrese la función o el método. Como sigue:

-> action = env.action_space.sample()
(Pdb) s
--Call--
> /home/penghao/.local/lib/python3.11/site-packages/gym/core.py(253)action_space()
-> @property
(Pdb) 

Como puede ver, el programa ha entrado en la función action_space. Lo más importante es que indica dónde se encuentra la función action_space, que está en el archivo gym/core.py. El código del método action_space es el siguiente:

    @property
    def action_space(self) -> spaces.Space[ActType]:
        """Returns the action space of the environment."""
        if self._action_space is None:
            return self.env.action_space
        return self._action_space

Esta función es un método en la clase Wrapper(Env[ObsType, ActType]). Continúe con el seguimiento de la depuración:

-> action = env.action_space.sample()
(Pdb) s
--Call--
> /home/penghao/.local/lib/python3.11/site-packages/gym/core.py(253)action_space()
-> @property
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/core.py(256)action_space()
-> if self._action_space is None:
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/core.py(257)action_space()
-> return self.env.action_space
(Pdb) n
--Return--
> /home/penghao/.local/lib/python3.11/site-packages/gym/core.py(257)action_space()->Discrete(4)
-> return self.env.action_space
(Pdb) s
--Call--
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/discrete.py(47)sample()
-> def sample(self, mask: Optional[np.ndarray] = None) -> int:
(Pdb) 

Este método de muestra se encuentra en el archivo gym/spaces/discrete.py. El código del método de ejemplo es el siguiente:

def sample(self, mask: Optional[np.ndarray] = None) -> int:
        """Generates a single random sample from this space.

        A sample will be chosen uniformly at random with the mask if provided

        Args:
            mask: An optional mask for if an action can be selected.
                Expected `np.ndarray` of shape `(n,)` and dtype `np.int8` where `1` represents valid actions and `0` invalid / infeasible actions.
                If there are no possible actions (i.e. `np.all(mask == 0)`) then `space.start` will be returned.

        Returns:
            A sampled integer from the space
        """
        if mask is not None:
            assert isinstance(
                mask, np.ndarray
            ), f"The expected type of the mask is np.ndarray, actual type: {type(mask)}"
            assert (
                mask.dtype == np.int8
            ), f"The expected dtype of the mask is np.int8, actual dtype: {mask.dtype}"
            assert mask.shape == (
                self.n,
            ), f"The expected shape of the mask is {(self.n,)}, actual shape: {mask.shape}"
            valid_action_mask = mask == 1
            assert np.all(
                np.logical_or(mask == 0, valid_action_mask)
            ), f"All values of a mask should be 0 or 1, actual values: {mask}"
            if np.any(valid_action_mask):
                return int(
                    self.start + self.np_random.choice(np.where(valid_action_mask)[0])
                )
            else:
                return self.start

        return int(self.start + self.np_random.integers(self.n))

Hacer un seguimiento:

--Call--
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/discrete.py(47)sample()
-> def sample(self, mask: Optional[np.ndarray] = None) -> int:
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/discrete.py(60)sample()
-> if mask is not None:
(Pdb) p mask
None
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/discrete.py(81)sample()
-> return int(self.start + self.np_random.integers(self.n))
(Pdb) 

self.start se refiere al inicio de la clase, donde la clase del método de ejemplo es la clase Discrete(Space[int]), y el valor de inicio es 0 en este momento, como se muestra a continuación:

(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/discrete.py(81)sample()
-> return int(self.start + self.np_random.integers(self.n))
(Pdb) p self.start
0

Ingrese s para continuar con Step In e ingrese el método np_random. Como sigue:

(Pdb) s
--Call--
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/space.py(72)np_random()
-> @property
(Pdb) 

El método np_random se encuentra en el archivo gym/spaces/space.py. El código del método np_random es el siguiente:

@property
    def np_random(self) -> np.random.Generator:
        """Lazily seed the PRNG since this is expensive and only needed if sampling from this space."""
        if self._np_random is None:
            self.seed()

        return self._np_random  # type: ignore  ## self.seed() call guarantees right type.

Continúe con el seguimiento de la depuración paso a paso:

--Call--
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/space.py(72)np_random()
-> @property
(Pdb) s
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/space.py(75)np_random()
-> if self._np_random is None:
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/space.py(76)np_random()
-> self.seed()
(Pdb) s
--Call--
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/space.py(103)seed()
-> def seed(self, seed: Optional[int] = None) -> list:
(Pdb) 

El método seed está en el mismo archivo (gym/spaces/space.py), el código es el siguiente:

def seed(self, seed: Optional[int] = None) -> list:
        """Seed the PRNG of this space and possibly the PRNGs of subspaces."""
        self._np_random, seed = seeding.np_random(seed)
        return [seed]

Hacer un seguimiento:

-> self._np_random, seed = seeding.np_random(seed)
(Pdb) s
--Call--
> /home/penghao/.local/lib/python3.11/site-packages/gym/utils/seeding.py(9)np_random()
-> def np_random(seed: Optional[int] = None) -> Tuple[np.random.Generator, Any]:
(Pdb) 

El método np_random se encuentra en gym/utils/seeding.py, y el código es el siguiente (este archivo es muy simple, solo hay esta función, así que publique todo el código del archivo):

"""Set of random number generator functions: seeding, generator, hashing seeds."""
from typing import Any, Optional, Tuple

import numpy as np

from gym import error


def np_random(seed: Optional[int] = None) -> Tuple[np.random.Generator, Any]:
    """Generates a random number generator from the seed and returns the Generator and seed.

    Args:
        seed: The seed used to create the generator

    Returns:
        The generator and resulting seed

    Raises:
        Error: Seed must be a non-negative integer or omitted
    """
    if seed is not None and not (isinstance(seed, int) and 0 <= seed):
        raise error.Error(f"Seed must be a non-negative integer or omitted, not {seed}")

    seed_seq = np.random.SeedSequence(seed)
    np_seed = seed_seq.entropy
    rng = RandomNumberGenerator(np.random.PCG64(seed_seq))
    return rng, np_seed


RNG = RandomNumberGenerator = np.random.Generator

El método np_random devuelve 2 valores: rng y np_seed.

La línea de código rng = RandomNumberGenerator(np.random.PCG64(seed_seq)) en realidad se ve así:

rng = np.random.Generator(np.random.PCG64(seed_seq))

Y np_seed = seed_seq.entropy esta vez el código se ve así:

np_seed = np.random.SeedSequence(seed).entropy

Aquí debe agregar algunos conocimientos sobre números aleatorios numpy.


Consulte los siguientes blogs:

Generación de números aleatorios para el aprendizaje numpy (1) - Blog de qianerwauestc - Blog de CSDN

Explicación de ejemplo de Python numpy.random.SeedSequence - Se busca programador

Módulos involucrados:

numpy.aleatorio

principio:

El programa de números aleatorios de numpy genera números pseudoaleatorios usando una combinación de BitGenerators para crear secuencias y Generadores para muestrear diferentes distribuciones estadísticas:

  • BitGeneradores

Un objeto que genera números aleatorios. Suelen ser palabras enteras sin signo llenas de secuencias de 32 o 64 bits aleatorios.

  • Constructor

Un objeto que convierte una secuencia de bits aleatoria en BitGenerator en una secuencia de números siguiendo una distribución de probabilidad específica (como uniforme, normal o binomial) dentro de un intervalo específico.

concepto:

  • Generador aleatorio

Los generadores brindan acceso a varias distribuciones y sirven como reemplazo de RandomState. La principal diferencia entre los dos es que los generadores se basan en un BitGenerator adicional para administrar el estado y generar bits aleatorios, que luego se convierten en valores aleatorios útilmente distribuidos. El BitGenerator predeterminado utilizado por Generator es PCG64. Este BitGenerator se puede cambiar pasando un BitGenerator instanciado al Generador.

El método de creación de Generador:

1)numpy.random.default_rng(seed=Ninguno)

Descripción general: construya un nuevo generador con el BitGenerator predeterminado (PCG64).

2)numpy.random.Generator(bit_generator)

Descripción general: devuelve un bitGernerator.

SeedSequence mezcla fuentes de entropía de manera repetible para establecer BitGenerators de estado inicial independientes y probablemente no superpuestos.

uso:

clase numpy.random.SeedSequence(entropía=Ninguno, *, spawn_key=(), pool_size=4)


Finalmente, los dos valores que devuelve el método np_random son los siguientes:

--Call--
> /home/penghao/.local/lib/python3.11/site-packages/gym/utils/seeding.py(9)np_random()
-> def np_random(seed: Optional[int] = None) -> Tuple[np.random.Generator, Any]:
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/utils/seeding.py(21)np_random()
-> if seed is not None and not (isinstance(seed, int) and 0 <= seed):
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/utils/seeding.py(24)np_random()
-> seed_seq = np.random.SeedSequence(seed)
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/utils/seeding.py(25)np_random()
-> np_seed = seed_seq.entropy
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/utils/seeding.py(26)np_random()
-> rng = RandomNumberGenerator(np.random.PCG64(seed_seq))
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/utils/seeding.py(27)np_random()
-> return rng, np_seed
(Pdb) p rng
Generator(PCG64) at 0x7F7477B6AEA0
(Pdb) p np_seed
256403554427214301906482223862250111160
(Pdb) 

Esto significa que el valor de retorno del método semilla en gym/spaces/space.py es [256403554427214301906482223862250111160], de la siguiente manera:

> /home/penghao/.local/lib/python3.11/site-packages/gym/utils/seeding.py(27)np_random()
-> return rng, np_seed
(Pdb) p rng
Generator(PCG64) at 0x7F7477B6AEA0
(Pdb) p np_seed
256403554427214301906482223862250111160
(Pdb) n
--Return--
> /home/penghao/.local/lib/python3.11/site-packages/gym/utils/seeding.py(27)np_random()->(Generator(PCG...0x7F7477B6AEA0, 256403554427214301906482223862250111160)
-> return rng, np_seed
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/space.py(106)seed()
-> return [seed]
(Pdb) n
--Return--
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/space.py(106)seed()->[256403554427214301906482223862250111160]
-> return [seed]
(Pdb) 

Subiendo un nivel, el valor de retorno del método np_random en el archivo gym/spaces/space.py es el siguiente:

> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/space.py(106)seed()
-> return [seed]
(Pdb) n
--Return--
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/space.py(106)seed()->[256403554427214301906482223862250111160]
-> return [seed]
(Pdb) n
> /home/penghao/.local/lib/python3.11/site-packages/gym/spaces/space.py(78)np_random()
-> return self._np_random  # type: ignore  ## self.seed() call guarantees right type.
(Pdb) p self._np_random
Generator(PCG64) at 0x7F7477B6AEA0
(Pdb) 

Subiendo un nivel, self.np_random.integers(self.n) es en realidad así:

np.random.Generator(np.random.PCG64(seed_seq)).integers(self.n)

En este momento, self.n es 4, lo que significa que representa las cuatro acciones de arriba, abajo, izquierda y derecha.

La función numpy.random.Generator.integers se describe a continuación:


Consulte los siguientes blogs:

Uso de Python numpy.random.Generator.integers y ejemplos de código - Pure Sky

uso:

random.Generator.integers (bajo, alto = Ninguno, tamaño = Ninguno, dtype = np.int64, punto final = Falso)

Devuelve un número entero aleatorio de bajo (inclusivo) a alto (exclusivo), o si endpoint=True, de bajo (inclusivo) a alto (inclusivo). Reemplace RandomState.randint(endpoint=False) y RandomState.random_integers(endpoint=True)

Devuelve números enteros aleatorios de la distribución "uniforme discreta" del tipo de d especificado. Si alto es Ninguno (el valor predeterminado), el resultado es de 0 a bajo. 


Finalmente, el valor de retorno de self.np_random.integers(self.n) en el código return int(self.start + self.np_random.integers(self.n)) es 3, y el valor de retorno general es 0+3= 3.

Parece que se llama a un código largo, pero la función real es seleccionar aleatoriamente una de las cuatro acciones de arriba, abajo, izquierda y derecha.

Supongo que te gusta

Origin blog.csdn.net/phmatthaus/article/details/131744764
Recomendado
Clasificación