ZoRRo Train API Reference
Overview
Complete API reference for ZoRRo Train's core classes and methods for prompt deduplication in RL training. Covers DeduplicatedActor, ZoRRoTrain, and supporting utilities.
ZoRRo Train API Reference
Overview
ZoRRo Train provides transparent prompt deduplication for reinforcement learning training. The main entry points are:
DeduplicatedActor— High-level reference harness for running deduplicated forward and backward passesZoRRoTrain— Static utility class implementing core deduplication logicQwen3ModelOncePatcher— Production patcher installed onto models (installed automatically byDeduplicatedActor)
DeduplicatedActor
Reference harness around Qwen3ModelOncePatcher for running deduplicated forward and backward passes.
Constructor
DeduplicatedActor(
model_name_or_path: str,
device: str = "cuda",
logits_optimization: str = "none",
use_split_attention: bool = True,
attn_implementation: str = "eager",
world_size: int = 1,
max_token_len: int = 4096,
dtype: torch.dtype = torch.bfloat16,
)
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
model_name_or_path | str | — | Hugging Face model identifier or local path (Qwen3 family checkpoint) |
device | str | "cuda" | Device to load the model on |
logits_optimization | str | "none" | logprob/entropy dispatch: "none" | "memory" | "compute". "memory" requires initialized torch.distributed process group; "none" / "compute" do not |
use_split_attention | bool | True | Use split attention (prompt-to-prompt + response-to-full) vs. single reconstructed attention call |
attn_implementation | str | "eager" | Attention implementation: "eager", "flash_attention_2", "flash_attention_3". Flash variants require GPU + flash-attn |
world_size | int | 1 | Data-parallel world size; used to sync shard counts in "memory" mode |
max_token_len | int | 4096 | Reserved; forwarded to patcher (currently unused) |
dtype | torch.dtype | torch.bfloat16 | Model dtype |
Methods
forward()
Run deduplicated forward pass.
forward(
micro_batch: Dict[str, torch.Tensor],
temperature: float = 1.0,
calculate_entropy: bool = False,
) -> ModelOutput
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
micro_batch | Dict[str, torch.Tensor] | — | Dict with input_ids, position_ids, attention_mask (all [batch_size, seq_len]) and responses ([batch_size, response_len]) |
temperature | float | 1.0 | Temperature for scaling logits |
calculate_entropy | bool | False | Whether to compute per-token entropy |
Returns:
ModelOutput with:
logprobs: 1D tensor of shape[num_valid_response_tokens], packed in original sample order with padding removedentropy: 1D tensor (ifcalculate_entropy=True), same shape aslogprobs
Example:
actor = DeduplicatedActor(
model_name_or_path="Qwen/Qwen3-0.6B",
device="cuda",
use_split_attention=True,
)
batch = create_dummy_batch(
batch_size=8,
num_unique_prompts=2,
prompt_len=4096,
response_len=512,
device="cuda",
include_training_fields=True,
)
output = actor.forward(batch, temperature=1.0, calculate_entropy=True)
log_probs = output.logprobs # [num_valid_response_tokens]
entropy = output.entropy # [num_valid_response_tokens]
compute_policy_loss_and_backward()
Run PPO training step (forward + backward).
compute_policy_loss_and_backward(
micro_batch: Dict[str, torch.Tensor],
temperature: float = 1.0,
gradient_accumulation: int = 1,
) -> Dict[str, float]
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
micro_batch | Dict[str, torch.Tensor] | — | Same as forward() |
temperature | float | 1.0 | Temperature for scaling logits |
gradient_accumulation | int | 1 | Gradient accumulation steps |
Returns:
Dict with metrics:
actor/policy_loss: PPO clipped policy lossactor/pg_loss: Policy gradient lossactor/pg_clipfrac: Clipping fractionactor/ppo_kl: Approximate KL divergence
Example:
actor.model.train()
metrics = actor.compute_policy_loss_and_backward(
batch,
temperature=1.0,
gradient_accumulation=1
)
print(f"Policy loss: {metrics['actor/policy_loss']:.4f}")
patch()
(Re)install Qwen3ModelOncePatcher onto the model.
patch(
response_len: int,
rollout_n: int,
temperature: float,
) -> Qwen3ModelOncePatcher
Parameters:
| Parameter | Type | Description |
|---|---|---|
response_len | int | Length of response section |
rollout_n | int | Number of responses sharing a prompt |
temperature | float | Temperature for scaling logits |
Returns:
Qwen3ModelOncePatcher instance (patcher is permanently applied to the model).
Note: The patcher is applied "once" in the sense that it mutates the model's forward methods. Re-patching swaps the forward closures without re-installing the patcher, allowing efficient model reuse across different configurations.
train() / eval()
Set model to training/eval mode.
train() -> None
eval() -> None
ZoRRoTrain
Static utility class implementing core (model-free) deduplication tensor logic.
Methods
find_prompt_groups()
Identify which samples share the same prompt.
@staticmethod
find_prompt_groups(
input_ids: torch.Tensor,
response_length: int,
) -> Tuple[List[List[int]], torch.Tensor]
Parameters:
| Parameter | Type | Description |
|---|---|---|
input_ids | torch.Tensor | [batch_size, seq_len] concatenated prompt + response |
response_length | int | Length of response portion |
Returns:
prompt_groups: List of lists; each inner list contains sample indices sharing a promptunique_prompts:[num_unique, prompt_len]tensor of unique prompt sequences
Example:
from arctic_platform.rl.zorro_train import ZoRRoTrain
input_ids = torch.tensor([
[1, 2, 3, 4, 5, 6], # Prompt A + Response 1
[1, 2, 3, 7, 8, 9], # Prompt A + Response 2
[10, 11, 12, 13, 14, 15], # Prompt B + Response 3
])
prompt_groups, unique_prompts = ZoRRoTrain.find_prompt_groups(
input_ids, response_length=3
)
# prompt_groups: [[0, 1], [2]]
# unique_prompts: [[1, 2, 3], [10, 11, 12]]
create_deduplicated_batch()
Create deduplicated batch by concatenating tokens into a single sequence.
@staticmethod
create_deduplicated_batch(
input_ids: torch.Tensor,
position_ids: torch.Tensor,
response_length: int,
prompt_groups: List[List[int]],
unique_prompts: torch.Tensor,
attention_mask: Optional[torch.Tensor] = None,
use_unpad: bool = False,
) -> Tuple[torch.Tensor, torch.Tensor, Dict]
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
input_ids | torch.Tensor | — | [batch_size, seq_len] input token IDs (may contain padding) |
position_ids | torch.Tensor | — | [batch_size, seq_len] position IDs |
response_length | int | — | Length of response section (including padding) |
prompt_groups | List[List[int]] | — | Indices of samples sharing same prompt |
unique_prompts | torch.Tensor | — | Tensor of unique prompts |
attention_mask | Optional[torch.Tensor] | None | [batch_size, seq_len] (1 for valid, 0 for padding) |
use_unpad | bool | False | If True, remove padding to produce packed format |
Returns:
dedup_input_ids:[1, total_tokens]deduplicated (packed ifuse_unpad=True)position_ids: Original structure or unpadded/packed ifuse_unpad=Truereconstruction_info: Dict with metadata to reconstruct original batch
Example:
prompt_groups, unique_prompts = ZoRRoTrain.find_prompt_groups(input_ids, 512)
dedup_ids, dedup_pos, recon_info = ZoRRoTrain.create_deduplicated_batch(
input_ids=input_ids,
position_ids=position_ids,
response_length=512,
prompt_groups=prompt_groups,
unique_prompts=unique_prompts,
attention_mask=attention_mask,
use_unpad=True,
)
reconstruct_sequences()
Reconstruct the full batch from deduplicated per-token output.
@staticmethod
reconstruct_sequences(
dedup_hidden: torch.Tensor,
reconstruction_info: Dict,
) -> torch.Tensor
Parameters:
| Parameter | Type | Description |
|---|---|---|
dedup_hidden | torch.Tensor | [1, total_tokens, hidden_dim] deduplicated hidden states |
reconstruction_info | Dict | Metadata returned by create_deduplicated_batch() |
Returns:
torch.Tensor of shape [batch_size, seq_len, hidden_dim] reconstructed to original batch layout.
deduplicate_sequences()
Inverse of reconstruct_sequences(). Extract deduplicated format from full batch.
@staticmethod
deduplicate_sequences(
full_hidden: torch.Tensor,
reconstruction_info: Dict,
) -> torch.Tensor
Parameters:
| Parameter | Type | Description |
|---|---|---|
full_hidden | torch.Tensor | [batch_size, seq_len, hidden_dim] full batch hidden states |
reconstruction_info | Dict | Metadata returned by create_deduplicated_batch() |
Returns:
torch.Tensor of shape [1, total_tokens, hidden_dim] in deduplicated format.
extract_unpadded_responses_from_deduped_packed_ids()
Pull each rollout's own response tokens from the packed sequence.
@staticmethod
extract_unpadded_responses_from_deduped_packed_ids(
packed_ids: torch.Tensor,
reconstruction_info: Dict,
offset: int = 0,
) -> torch.Tensor
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
packed_ids | torch.Tensor | — | Deduplicated packed token IDs |
reconstruction_info | Dict | — | Metadata from create_deduplicated_batch() |
offset | int | 0 | Token offset |
Returns:
torch.Tensor of response tokens in deduplicated packed format.
responses_in_orig_sample_order()
Undo the prompt-group permutation, returning tokens in original sample order.
@staticmethod
responses_in_orig_sample_order(
packed_responses: torch.Tensor,
reconstruction_info: Dict,
) -> torch.Tensor
Parameters:
| Parameter | Type | Description |
|---|---|---|
packed_responses | torch.Tensor | Response tokens in deduplicated packed format |
reconstruction_info | Dict | Metadata from create_deduplicated_batch() |
Returns:
torch.Tensor of response tokens reordered to original sample order.
attention_mask_to_flash_attn_params()
Convert packed attention mask to Flash Attention varlen parameters.
@staticmethod
attention_mask_to_flash_attn_params(
attention_mask: torch.Tensor,
device: torch.device = None,
) -> Dict[str, torch.Tensor]
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
attention_mask | torch.Tensor | — | [total_q_tokens, total_kv_tokens] sparse block-diagonal mask (1 for valid, 0 for masked) |
device | torch.device | None | Device for output tensors (defaults to attention_mask.device) |
Returns:
Dict containing:
cu_seqlens_q:[num_sequences + 1]cumulative sequence lengths for queriescu_seqlens_k:[num_sequences + 1]cumulative sequence lengths for keys/valuesmax_seqlen_q: Maximum query sequence lengthmax_seqlen_k: Maximum key/value sequence lengthtotal_q_tokens: Total number of query tokenstotal_kv_tokens: Total number of key/value tokensnum_sequences: Number of packed sequencesseq_lengths_q: List of query sequence lengthsseq_lengths_k: List of key sequence lengths
Example:
# Self-attention with 3 sequences: [5, 3, 4] tokens each
flash_params = ZoRRoTrain.attention_mask_to_flash_attn_params(attention_mask)
# cu_seqlens_q: [0, 5, 8, 12]
# cu_seqlens_k: [0, 5, 8, 12]
# max_seqlen_q: 5
# num_sequences: 3
ReconstructionInfo
Dict subclass used to persist and share reconstruction metadata through closures.
class ReconstructionInfo(dict):
"""Ensures object identity persists; forces use of update() to overwrite contents."""
pass
Usage:
from arctic_platform.rl.zorro_train import ReconstructionInfo
recon_info = ReconstructionInfo(a=1, b=2)
old_id = id(recon_info)
recon_info.update(a=2, b=3) # Overwrites contents, same object
new_id = id(recon_info)
assert old_id == new_id # Object identity preserved
Utilities
packed_ppo_policy_loss()
PPO clipped policy loss over packed (1D) response tokens.
def packed_ppo_policy_loss(
log_prob: torch.Tensor,
old_log_prob: torch.Tensor,
advantages: torch.Tensor,
ref_log_prob: Optional[torch.Tensor] = None,
clip_ratio: float = 0.2,
kl_loss_coef: float = 0.001,
) -> Tuple[torch.Tensor, Dict[str, float]]
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
log_prob | torch.Tensor | — | 1D log probabilities of current policy |
old_log_prob | torch.Tensor | — | 1D log probabilities of old policy |
advantages | torch.Tensor | — | 1D advantage estimates |
ref_log_prob | Optional[torch.Tensor] | None | Reference log probabilities (for KL penalty) |
clip_ratio | float | 0.2 | PPO clip coefficient |
kl_loss_coef | float | 0.001 | KL loss coefficient |
Returns:
policy_loss: Scalar loss tensor (before gradient accumulation scaling)metrics: Dict with keys"actor/pg_loss","actor/policy_loss","actor/pg_clipfrac","actor/ppo_kl", and optionally"actor/kl_loss"
Supported Models
ZoRRo Train currently supports these model families:
qwen3qwen3-moeqwen3-next-moeqwen3.6qwen3.6-moe
Support spans dense, MoE, and hybrid (linear + full attention) architectures. Additional models will be added in the future.
Complete Example
from arctic_platform.rl.zorro_train import ZoRRoTrain
from arctic_platform.rl.zorro_train.actor import DeduplicatedActor
from arctic_platform.rl.zorro_train.tests import create_dummy_batch
# 1. Initialize actor (installs Qwen3ModelOncePatcher on first forward)
actor = DeduplicatedActor(
model_name_or_path="Qwen/Qwen3-0.6B",
device="cuda",
logits_optimization="none",
use_split_attention=True,
attn_implementation="eager",
)
# 2. Create batch with shared prompts
batch = create_dummy_batch(
batch_size=8,
num_unique_prompts=2, # Only 2 unique prompts for 8 samples
prompt_len=4096,
response_len=512,
device="cuda",
include_training_fields=True,
add_padding=True,
)
# 3. Forward pass (automatically deduplicates)
output = actor.forward(batch, temperature=1.0, calculate_entropy=True)
log_probs = output.logprobs # [num_valid_response_tokens]
entropy = output.entropy # [num_valid_response_tokens]
# 4. Training step
actor.model.train()
metrics = actor.compute_policy_loss_and_backward(
batch,
temperature=1.0,
gradient_accumulation=1
)
print(f"Policy loss: {metrics['actor/policy_loss']:.4f}")
Performance Expectations
Expected speedups depend on deduplication ratio:
| Scenario | Batch Size | Unique Prompts | Prompt Len | Response Len | Expected Speedup |
|---|---|---|---|---|---|
| High dedup | 16 | 1 | 8K | 1K | ~2-4x |
| Medium dedup | 16 | 4 | 8K | 1K | ~1.5-2x |
| Low dedup | 16 | 16 | 8K | 1K | ~1x (no benefit) |
Note: Actual speedups depend on hardware (GPU compute vs memory bottleneck), attention implementation (Flash Attention gives best results), and sequence lengths (longer prompts = more benefit).
Testing
Run the test suite to verify correctness:
# All ZoRRo Train tests
pytest tests/zorro_train/
# Specific test suites
pytest tests/zorro_train/test_dedup.py # CPU: dedup algorithm round-trips
pytest tests/zorro_train/test_once_patcher.py # GPU: forward/backward correctness
pytest tests/zorro_train/test_seqlen_balancing.py # CPU: sequence-length balancing
# Performance benchmark
python arctic_platform/rl/zorro_train/test_perf.py
References
- README: ZoRRo Train motivation and architecture overview
- GitHub: Arctic Platform Repository
- License: Apache License 2.0