Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions miles/backends/fsdp_utils/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
)
from miles.backends.training_utils.loss import compute_advantages_and_returns, get_log_probs_and_entropy, loss_function
from miles.backends.training_utils.parallel import get_parallel_state, set_parallel_state
from miles.backends.training_utils.sampling_mask import get_rollout_sampling_masks
from miles.ray.train_actor import TrainRayActor
from miles.utils import async_utils, train_dump_utils, train_metric_utils
from miles.utils.context_utils import with_defer
Expand All @@ -32,6 +33,7 @@
from miles.utils.processing_utils import load_processor, load_tokenizer
from miles.utils.profile_utils import TrainProfiler
from miles.utils.ray_utils import Box
from miles.utils.sampling_mask import sampling_support_replay_enabled
from miles.utils.timer import Timer, inverse_timer, timer
from miles.utils.tracking_utils.tracking import init_tracking

Expand Down Expand Up @@ -369,6 +371,7 @@ def _compute_log_prob(
active_model.eval()
else:
active_model = self.model
use_rollout_sampling_mask = model_tag == "actor" and sampling_support_replay_enabled(self.args)

try:
forward_data_store = []
Expand All @@ -392,13 +395,18 @@ def _compute_log_prob(
"response_lengths",
"max_seq_lens",
]
if use_rollout_sampling_mask:
forward_only_keys.extend(["rollout_sampling_mask_ids", "rollout_sampling_mask_offsets"])
batch = get_batch(
data_iterator,
forward_only_keys,
self.args.data_pad_size_multiplier,
self.args.qkv_format,
get_position_ids=True,
)
rollout_sampling_mask = None
if use_rollout_sampling_mask:
rollout_sampling_mask = get_rollout_sampling_masks(batch)

model_args = self._get_model_inputs_args(batch)
# keep logits in native bf16 (chunks upcast to fp32 downstream); avoids a full-vocab fp32 tensor (~5GB)
Expand All @@ -413,6 +421,7 @@ def _compute_log_prob(
response_lengths=batch["response_lengths"],
with_entropy=(store_prefix == ""),
max_seq_lens=batch.get("max_seq_lens", None),
rollout_sampling_mask=rollout_sampling_mask,
)

batch_result = {
Expand Down Expand Up @@ -501,6 +510,11 @@ def _train_core(self, rollout_id: int, rollout_data) -> None:
with routing_replay.stage(routing_replay.REPLAY_BACKWARD), timer("actor_train"):
data_iterator.reset()
num_steps_per_rollout = len(num_microbatches)
sampling_mask_keys = (
("rollout_sampling_mask_ids", "rollout_sampling_mask_offsets")
if sampling_support_replay_enabled(self.args)
else ()
)

for step_id in range(num_steps_per_rollout):
self.optimizer.zero_grad(set_to_none=True)
Expand All @@ -523,6 +537,7 @@ def _train_core(self, rollout_id: int, rollout_data) -> None:
"returns",
"ref_log_probs",
"rollout_log_probs",
*sampling_mask_keys,
],
self.args.data_pad_size_multiplier,
self.args.qkv_format,
Expand Down
2 changes: 2 additions & 0 deletions miles/backends/megatron_utils/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from miles.utils.ray_utils import Box
from miles.utils.reloadable_process_group import destroy_process_groups, monkey_patch_torch_dist, reload_process_groups
from miles.utils.replay_base import all_replay_managers, routing_replay_manager
from miles.utils.sampling_mask import sampling_support_replay_enabled
from miles.utils.test_utils.ft_test_actions import FTTestActionActorExecutor
from miles.utils.timer import Timer, inverse_timer, timer
from miles.utils.tracking_utils.structured_log import with_logs
Expand Down Expand Up @@ -430,6 +431,7 @@ def compute_log_prob(
rollout_id=rollout_id,
store_prefix=store_prefix,
fp32_output=False,
use_rollout_sampling_mask=store_prefix == "" and sampling_support_replay_enabled(self.args),
)

@with_logs
Expand Down
41 changes: 30 additions & 11 deletions miles/backends/megatron_utils/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@
from miles.backends.megatron_utils.ft.types import TrainStepOutcome
from miles.backends.megatron_utils.local_weight_checksum import dump_local_weight_checksums
from miles.backends.megatron_utils.optimizer_state_reset import reset_optimizer_states
from miles.backends.training_utils.sampling_mask import get_rollout_sampling_masks
from miles.utils.audit_utils.witness.allocator import WitnessInfo
from miles.utils.audit_utils.witness.module import witness_dump_and_clear_stale
from miles.utils.dumper_utils import DumperMegatronUtil, DumperPhase
from miles.utils.memory_utils import clear_memory
from miles.utils.multi_lora import is_multi_lora_enabled
from miles.utils.sampling_mask import sampling_support_replay_enabled
from miles.utils.test_utils.ft_test_actions import FTTestActionActorExecutor
from miles.utils.tracking_utils.structured_log import log_structured

Expand Down Expand Up @@ -268,6 +270,7 @@ def forward_only(
rollout_id: int,
store_prefix: str = "",
fp32_output: bool = True,
use_rollout_sampling_mask: bool = False,
) -> dict[str, list[torch.Tensor]]:
"""Run forward passes only and collect non-loss outputs (e.g., logprobs).

Expand All @@ -283,6 +286,8 @@ def forward_only(
rollout_id: Rollout identifier (selects the per-rollout dump subdirectory).
store_prefix: Prefix to prepend to stored output keys.
fp32_output: Whether Megatron should upcast the complete model output to FP32.
use_rollout_sampling_mask: Whether to score over each rollout token's
captured sampling support.

Returns:
Aggregated outputs keyed by ``store_prefix + key``.
Expand All @@ -304,17 +309,20 @@ def forward_step(
) -> tuple[torch.Tensor, Callable[[torch.Tensor], dict[str, list[torch.Tensor]]]]:
"""Return the model output and its batch-bound loss callback."""

forward_only_keys = [
"tokens",
"loss_masks",
"multimodal_train_inputs",
"total_lengths",
"response_lengths",
"max_seq_lens",
"witness_ids",
]
if use_rollout_sampling_mask:
forward_only_keys.extend(["rollout_sampling_mask_ids", "rollout_sampling_mask_offsets"])
batch = get_batch(
data_iterator,
[
"tokens",
"loss_masks",
"multimodal_train_inputs",
"total_lengths",
"response_lengths",
"max_seq_lens",
"witness_ids",
],
forward_only_keys,
args.data_pad_size_multiplier,
args.qkv_format,
allgather_cp=args.allgather_cp,
Expand All @@ -324,6 +332,7 @@ def forward_step(
packed_seq_params = get_packed_seq_params(batch, args)
total_lengths = batch["total_lengths"]
response_lengths = batch["response_lengths"]
rollout_sampling_mask = get_rollout_sampling_masks(batch) if use_rollout_sampling_mask else None

if "adapter_token_counts" in batch:
from megatron.bridge.peft.multi_lora_layers import set_tokens_per_adapter_slot
Expand All @@ -342,15 +351,18 @@ def forward_step(
fp32_output=fp32_output,
)

return output_tensor, partial(
f,
callback_kwargs = dict(
args=args,
unconcat_tokens=unconcat_tokens,
total_lengths=total_lengths,
response_lengths=response_lengths,
with_entropy=args.use_rollout_entropy,
max_seq_lens=batch.get("max_seq_lens", None),
)
if use_rollout_sampling_mask:
callback_kwargs["rollout_sampling_mask"] = rollout_sampling_mask

return output_tensor, partial(f, **callback_kwargs)

# Turn on evaluation mode which disables dropout.
for model_module in model:
Expand Down Expand Up @@ -407,6 +419,12 @@ def run_forward_backward_pass(
):
"""One pipeline forward/backward pass over the microbatches; no optimizer interaction."""

sampling_mask_keys = (
("rollout_sampling_mask_ids", "rollout_sampling_mask_offsets")
if sampling_support_replay_enabled(args) and args.loss_type == "policy_loss"
else ()
)

@dumper_phase_util.wrap_forward_step
def forward_step(data_iterator: DataIterator, model: GPTModel, return_schedule_plan: bool = False) -> tuple[
torch.Tensor,
Expand Down Expand Up @@ -447,6 +465,7 @@ def forward_step(data_iterator: DataIterator, model: GPTModel, return_schedule_p
"loss_weights",
"target_tokens",
"sample_indices",
*sampling_mask_keys,
],
args.data_pad_size_multiplier,
args.qkv_format,
Expand Down
4 changes: 4 additions & 0 deletions miles/backends/training_utils/loss_hub/losses.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
compute_policy_loss,
)
from miles.backends.training_utils.parallel import get_parallel_state
from miles.backends.training_utils.sampling_mask import get_rollout_sampling_masks
from miles.utils.function_registry import load_function
from miles.utils.sampling_mask import sampling_support_replay_enabled
from miles.utils.types import RolloutBatch


Expand Down Expand Up @@ -111,6 +113,7 @@ def policy_loss_function(
total_lengths = batch["total_lengths"]
max_seq_lens = batch.get("max_seq_lens", None)
calculate_entropy = args.entropy_coef != 0 or args.observe_training_entropy
rollout_sampling_mask = get_rollout_sampling_masks(batch) if sampling_support_replay_enabled(args) else None

log_probs_and_entropy = get_log_probs_and_entropy(
logits,
Expand All @@ -121,6 +124,7 @@ def policy_loss_function(
with_entropy=calculate_entropy,
entropy_requires_grad=args.entropy_coef != 0,
max_seq_lens=max_seq_lens,
rollout_sampling_mask=rollout_sampling_mask,
)

log_probs = log_probs_and_entropy["log_probs"]
Expand Down
18 changes: 17 additions & 1 deletion miles/backends/training_utils/sampling_mask.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
from collections.abc import Sequence
from collections.abc import Mapping, Sequence

import torch

from miles.utils.sampling_mask import RolloutSamplingMask


def get_rollout_sampling_masks(batch: Mapping[str, object]) -> list[RolloutSamplingMask]:
"""Reconstruct the per-sample masks carried by the training-data wire."""
ids_batch = batch.get("rollout_sampling_mask_ids")
offsets_batch = batch.get("rollout_sampling_mask_offsets")
if ids_batch is None or offsets_batch is None:
raise ValueError("sampling-support actor scoring requires both sampling-mask wire fields")
if not isinstance(ids_batch, Sequence) or not isinstance(offsets_batch, Sequence):
raise TypeError("rollout sampling-mask ids and offsets must be sequences with one entry per sample")
if len(ids_batch) != len(offsets_batch):
raise ValueError(f"sampling-mask ids batch size {len(ids_batch)} != offsets batch size {len(offsets_batch)}")
return [
RolloutSamplingMask(ids=torch.as_tensor(ids), offsets=torch.as_tensor(offsets))
for ids, offsets in zip(ids_batch, offsets_batch, strict=True)
]


def build_local_sampling_mask(
logits: torch.Tensor,
sampling_mask: RolloutSamplingMask,
Expand Down
7 changes: 6 additions & 1 deletion miles/rollout/generate_hub/multi_turn.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ async def generate(input: GenerateFnInput) -> GenerateFnOutput:
for _turn in range(args.generate_max_turns):
# ----------------------- Call inference endpoint -------------------------

payload, halt_status = compute_request_payload(args, sample.tokens, input.sampling_params)
payload, halt_status = compute_request_payload(
args,
sample.tokens,
input.sampling_params,
evaluation=input.evaluation,
)
if payload is None:
sample.status = halt_status
break
Expand Down
6 changes: 5 additions & 1 deletion miles/rollout/generate_hub/single_turn.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ async def generate(input: GenerateFnInput) -> GenerateFnOutput:
input_ids = prompt_ids

payload, halt_status = compute_request_payload(
args, input_ids=input_ids, sampling_params=sampling_params, multimodal_inputs=sample.multimodal_inputs
args,
input_ids=input_ids,
sampling_params=sampling_params,
multimodal_inputs=sample.multimodal_inputs,
evaluation=input.evaluation,
)
if payload is None:
sample.status = halt_status
Expand Down
10 changes: 10 additions & 0 deletions miles/rollout/generate_utils/generate_endpoint_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import numpy as np
import pybase64

from miles.rollout.generate_utils.sampling_mask import append_sampling_metadata, should_return_sampling_mask
from miles.utils.lora import LORA_ADAPTER_NAME, lora_rollout_enabled
from miles.utils.processing_utils import encode_image_for_rollout_engine, extract_multimodal_train_inputs
from miles.utils.types import Sample
Expand Down Expand Up @@ -54,6 +55,8 @@ def compute_request_payload(
input_ids: list[int],
sampling_params: dict,
multimodal_inputs: dict | None = None,
*,
evaluation: bool = False,
) -> tuple[dict[str, Any] | None, Sample.Status | None]:
sampling_params = deepcopy(sampling_params)
max_new_tokens = sampling_params.pop("max_new_tokens", args.rollout_max_response_len)
Expand All @@ -62,13 +65,17 @@ def compute_request_payload(
if max_new_tokens <= 0:
return None, Sample.Status.TRUNCATED

return_sampling_mask = should_return_sampling_mask(args, sampling_params, evaluation=evaluation)

payload = {
"input_ids": input_ids,
"sampling_params": {**sampling_params, "max_new_tokens": max_new_tokens},
"return_logprob": True,
"return_routed_experts": args.use_rollout_routing_replay,
"return_indexer_topk": args.use_rollout_indexer_replay,
}
if return_sampling_mask:
payload["return_sampling_mask"] = True
if lora_rollout_enabled(args):
payload["lora_path"] = LORA_ADAPTER_NAME
if image_data := (multimodal_inputs or {}).get("images"):
Expand All @@ -90,6 +97,9 @@ async def update_sample_from_response(
else:
new_response_tokens, new_response_log_probs = [], []

if payload.get("return_sampling_mask", False):
new_response_log_probs = append_sampling_metadata(sample, new_response_tokens, output["meta_info"])

# Update sample with tokens directly - avoiding re-tokenization
sample.tokens = sample.tokens + new_response_tokens
sample.response_length += len(new_response_tokens)
Expand Down
Loading
Loading