Flashbax

repository·main·Indexed 43 days ago

https://github.com/instadeepai/flashbax

A high-speed experience replay library for JAX, designed for seamless integration with Just-In-Time (JIT) compilation and training loops. Version 0.1.3 provides various buffer types including Flat, Item, Trajectory, Prioritised, and Trajectory/Flat Queues. It includes Vault for persistent storage in offline reinforcement learning and supports in-place updates via jax.jit's donate_argnums to optimize device memory usage.

Tokens
16.5K
Snippets
44
Records
56
Agent score
53%

What's inside Flashbax

  1. What is Vault and when to use it?

    main

    Vault is an efficient mechanism for saving Flashbax buffers to persistent storage, specifically designed for offline reinforcement learning.

    It handles large datasets by extending the temporal/sequential dimension ($T$) of a buffer to a virtually unconstrained degree. It works by reading and writing slices of buffers along the temporal axis, allowing gigantic buffer stores to reside on disk while sub-buffers are loaded into RAM/VRAM for efficient training. Vault supports the item, flat, and trajectory buffers.

  2. Overview of Flashbax buffer types

    main

    Flashbax is a JAX-compatible library for experience replay buffers in reinforcement learning. It provides several buffer variants designed to work within fully compiled functions (jax.jit) and training loops (jax.pmap).

    Core buffer types include:

    • Flat Buffer: Similar to transition buffers used in DQN; stores sequences of $(s_t, s_{t+1})$ with a period of 1.
    • Item Buffer: Stores individual items (e.g., observation, action, reward tuples) independent of each other.
    • Trajectory Buffer: Facilitates sampling multi-step trajectories, useful for recurrent networks (e.g., R2D2).
    • Prioritised Buffers: Prioritised versions of both Flat and Trajectory buffers, allowing sampling based on user-defined priorities (aligned with PER principles).
    • Trajectory/Flat Queue: A FIFO queue structure for on-policy algorithms.
  3. Manage effective buffer size and the max_size argument

    main

    Flashbax buffers use a block-like structure where the effective size depends on the add_batch_size. When using the max_size argument (which sets the total desired number of timesteps), the internal time axis length is calculated as max_size // add_batch_size.

    Because this uses integer division (rounding down), your effective buffer size might be smaller than expected. To ensure accuracy, you should either:

    1. Use the max_length (time axis) argument explicitly.
    2. Ensure the max_size argument is a multiple of the add_batch_size.
  4. Handle sequential data and episode truncation

    main

    Flashbax is built on a trajectory buffer foundation, meaning data must be added sequentially.

    Sequential Constraints

    For the flat buffer, each added timestep must be followed by its consecutive timestep. When transitioning from the end of episode n to the start of episode n+1 within the same batch, the user is responsible for handling the sequence relationship (e.g., using auto-reset wrappers or using discount values like 1 for non-terminal states and 0 for terminal states to mask value functions).

    Episode Truncation

    When truncating episodes, you must set a done flag or a discount value appropriately.

    Using the Item Buffer for Independence

    If your data lacks sequential information, you can use the item buffer. This is a wrapped trajectory buffer configured with a sequence_dimension of 1 and a period of 1.

    • Pros: Easier to use for independent transitions; the algorithm doesn't need to handle the 'final-to-first' timestep transition between episodes.
    • Cons: If storing transition items like (observation, action, reward, discount, next_observation), it results in duplicate observations, increasing memory consumption.
    • Alternative: If speed is preferred over memory efficiency for independent items, use a trajectory buffer with sequence=1 and period=1 storing full transition data.
  5. Compare Flashbax with other buffer libraries

    main

    If you are evaluating different replay buffer implementations, the following libraries are frequently compared or used in the community:

    • Dejax: JAX-compatible replay buffers.
    • Reverb: Efficient replay buffers for local and large-scale distributed RL.
    • Dopamine: Research framework for fast prototyping with core replay buffers.
    • StableBaselines3: RL baselines suite with easy-to-use replay buffers.
  6. Avoid data loss when using Vault with ring buffers

    main

    Vault extends the temporal axis of a Flashbax buffer state to store experience data to disk. While Vault handles bookkeeping (only saving new, unseen data), Flashbax states are implemented as ring buffers.

    Critical Requirement: You must update the Vault frequently enough to prevent unseen data from being overwritten in the ring buffer. If your buffer state has a time-axis length of $\tau$, you must save to the Vault at least every $\tau - 1$ steps.

  7. How to use Flashbax buffers

    main

    Flashbax buffers follow a functional pattern. You first create a buffer object using a make_* function, then use its pure functions to manage state. The buffer object itself is a container for these functions.

    Workflow:

    1. Create: Use fbx.make_flat_buffer(...), fbx.make_trajectory_buffer(...), etc.
    2. Initialise: Call buffer.init(example_data) to create the initial state.
    3. Add Data: Call state = buffer.add(state, new_data) to update the state.
    4. Sample: Call batch = buffer.sample(state, rng_key) to retrieve data.
    import flashbax as fbx
    
    # 1. Create
    buffer = fbx.make_flat_buffer(max_length=32, min_length=2, sample_batch_size=1)
    
    # 2. Initialise
    state = buffer.init(example_timestep)
    
    # 3. Add Data
    state = buffer.add(state, example_data)
    
    # 4. Sample
    batch = buffer.sample(state, rng_key)
  8. Get started with Flashbax

    main

    Flashbax provides high-speed buffers implemented in JAX, designed for efficient data storage and sampling in reinforcement learning or large-scale simulations.

    To use Flashbax, you typically follow a three-step workflow:

    1. Initialize a buffer with a specific capacity and data structure.
    2. Add Data to the buffer using the buffer's API.
    3. Sample Data from the buffer for training or evaluation.

    Flashbax is built to be highly performant by leveraging JAX's capabilities.

    # Typical workflow pattern:
    # 1. Initialize
    # 2. Add Data
    # 3. Sample Data
  9. Perform in-place updates of buffer state with JAX

    main

    Because Flashbax buffers are often large and occupy significant device memory, you should perform in-place updates to avoid the overhead of creating copies. To enable this, you must specify the buffer state argument for donation when using jax.jit via the donate_argnums parameter.

    If you omit donate_argnums, JAX will force a copy of the state for any modifications, which can negate performance benefits.

    def train(train_state, buffer_state):
        ...
        return train_state, buffer_state
    
    # Initialise the buffer state
    buffer_fn = fbx.make_trajectory_buffer(...)
    buffer_state = buffer_fn.init(example_timestep)
    
    # Initialise some training state
    train_state = train_state.init(...)
    
    # Compile the training function and specify the donation of the buffer state argument
    train_state, buffer_state = jax.jit(train, donate_argnums=(1,))(
        train_state, buffer_state
    )
  10. Install Flashbax with examples

    main

    To use the example notebooks and dependencies provided in the repository, install the package with the [examples] extra. You may also need to clone the repository first if you are working locally with the source code.

    !git clone https://github.com/instadeepai/flashbax.git
    !pip install ./flashbax[examples]
  11. How the Trajectory Buffer works

    main

    The Trajectory Buffer is designed for reinforcement learning tasks that require temporal ordering, such as those using TD-lambda errors, n-step returns, or recurrent networks.

    Unlike a flat buffer, it stores and samples batches of trajectories (sequences of experiences). It operates as a FIFO (First-In-First-Out) circular buffer. Sampling is performed uniformly according to a specified period, which allows you to control the overlap of sampled sequences.

    Key characteristics:

    • Temporal Ordering: Preserves the sequence of experiences.
    • Sampling: Returns batches of sequences rather than individual transitions.
    • JAX Compatibility: The buffer is composed of pure functions and is compatible with jax.pmap for multi-device training.
    # Example of the structure of a sampled batch
    # If fake_timestep is {'obs': jnp.array([5, 4]), 'reward': jnp.array(1.0)}
    # and sample_batch_size=4, sample_sequence_length=8
    # The sampled batch.experience['reward'].shape will be (4, 8)