Import the QRLHF class
mainThe main entry point for the library is the QRLHF class, which is exported from the llama_qrlhf package. You can import it directly from the top-level package.
from llama_qrlhf import QRLHFrepository·main·Indexed 41 days ago
https://github.com/lucidrains/llama-qrlhfA library implementing Q-learning for Large Language Models (LLMs) using an autoregressive formulation. It enables training models to maximize rewards by treating token generation as an action-selection process, featuring a Dueling Q-head architecture, conservative regularization to prevent Q-value overestimation, and support for Rotary Positional Embeddings (RoPE) and GEGLU activations.
The main entry point for the library is the QRLHF class, which is exported from the llama_qrlhf package. You can import it directly from the top-level package.
from llama_qrlhf import QRLHFThe conservative_regularization_loss function implements a regularization term to prevent overestimation of Q-values. It penalizes Q-values for actions not present in the dataset by pushing them towards a minimum reward value.
Arguments:
q_values: Float['b n a'] - Predicted Q-values for all actions at each time step.states_and_actions: Int['b n'] - The actual actions taken in the dataset.action_mask: Bool['b n'] - A mask indicating which time steps should be included in the loss.reward_min: float - The baseline reward value to regularize towards (defaults to 0.0).reg_loss = conservative_regularization_loss(
q_values=q_values,
states_and_actions=states_and_actions,
action_mask=action_mask,
reward_min=0.0
)The autoregressive_q_learn function implements the core Q-learning logic adapted for language modeling. It calculates the loss based on the difference between predicted Q-values and target Q-values, using a hybrid approach: classic Bellman updates for time steps with rewards, and a max-Q target for time steps without rewards (as per the paper's contribution).
Key features:
discount_gamma to encourage brevity.eos_id is provided, the sequence is considered terminal after the first occurrence of that token.Arguments:
model: The current model being trained.ema_model: The target model (typically an EMA version of the model).states: Int['b n'] - The entire sequence (prompt + generated tokens).prompt_len: Int['b n'] - The length of the prompt part of the sequence.next_states: Int['b'] - The selected action (token) that becomes the next state.rewards: Float['b n'] - Rewards provided at specific time steps.eos_id: int | None - The token ID used to identify the end of a sequence.discount_gamma: float - The reward discount factor.loss = autoregressive_q_learn(
model=model,
ema_model=ema_model,
states=states,
prompt_len=prompt_len,
next_states=next_states,
rewards=rewards,
eos_id=tokenizer.eos_token_id,
discount_gamma=0.998
)The Llama class is the main entry point for the architecture. It implements a transformer-based model with optional support for a Dueling Q-head, which is used for Reinforcement Learning (QRLHF).
Key parameters:
num_tokens: Vocabulary size.dim: Embedding dimension.depth: Number of transformer layers.dim_head: Dimension of each attention head.heads: Number of attention heads.ff_mult: Multiplier for the FeedForward layer dimension.dueling_q_head: Boolean. If True, uses a DuelingHead to output Q-values instead of a standard linear layer.dueling_q_head_expansion_factor: Expansion factor for the Dueling Head's hidden dimension.When calling forward(x, return_q_values=True), the model returns both the language modeling logits and the q_values (if dueling_q_head is enabled).
from llama_qrlhf.llama import Llama
import torch
model = Llama(
num_tokens = 50257,
dim = 768,
depth = 12,
dueling_q_head = True
)
# x shape: (batch, seq_len)
x = torch.randint(0, 50257, (1, 128))
# Returns (logits, q_values)
logits, q_values = model(x, return_q_values=True)The RotaryEmbedding class is used to generate frequency components for rotary positional embeddings.
Parameters:
dim: The dimension of the head (dim_head).theta: The base frequency (default 10000).from llama_qrlhf.llama import RotaryEmbedding
# dim should match the dim_head used in Attention
rotary_emb = RotaryEmbedding(dim = 64, theta = 10000)The Attention module implements multi-head attention with support for Rotary Positional Embeddings (RoPE).
Parameters:
dim: Input dimension.dim_head: Dimension per head (default 64).heads: Number of heads (default 8).It applies a causal mask during the forward pass to ensure autoregressive properties. If rotary_emb is provided, it is applied to the Query (q) and Key (k) tensors using apply_rotary_pos_emb.
from llama_qrlhf.llama import Attention
import torch
attn = Attention(dim = 768, dim_head = 64, heads = 8)
x = torch.randn(1, 128, 768)
# rotary_emb should be generated by RotaryEmbedding(dim_head)
# and passed to the forward call
out = attn(x, rotary_emb = some_rotary_embedding)The FeedForward module implements a gated feed-forward network using the GEGLU activation function.
Parameters:
dim: Input dimension.mult: Multiplier for the hidden dimension (default 4).The architecture follows: RMSNorm -> Linear (to dim_hidden * 2) -> GEGLU -> Linear (to dim).
from llama_qrlhf.llama import FeedForward
import torch
ff = FeedForward(dim = 768, mult = 4)
x = torch.randn(1, 128, 768)
out = ff(x)The QRLHF class is the main wrapper for the Q-learning training process. It manages both the active model and a target model using Exponential Moving Average (EMA) to stabilize training.
Arguments:
model: The base torch.nn.Module (e.g., a Llama model).dataset: The torch.utils.data.Dataset used for training.accelerate_kwargs: Dictionary of arguments passed to Hugging Face accelerate.ema_kwargs: Dictionary of arguments passed to the EMA wrapper (e.g., beta=0.99).from llama_qrlhf.llama_qrlhf import QRLHF
# Assuming 'model' and 'dataset' are already defined
trainer = QRLHF(
model=model,
dataset=dataset,
ema_kwargs=dict(beta=0.99)
)The DuelingHead is a specialized module designed to decompose Q-values into state values and action advantages. This is a core component for the QRLHF approach.
It uses a stem (Linear + SiLU) to project the input dimension to a hidden dimension (controlled by expansion_factor), then splits into:
to_values: Predicts a scalar value for the state.to_advantages: Predicts advantages for each token in the vocabulary.The final Q-values are calculated as q_values = values + (advantages - advantages.mean()).
from llama_qrlhf.llama import DuelingHead
import torch
dueling_head = DuelingHead(
dim = 768,
num_tokens = 50257,
expansion_factor = 2
)
# x shape: (batch, seq_len, dim)
x = torch.randn(1, 128, 768)
q_values = dueling_head(x)
# q_values shape: (1, 128, 50257)