Repository navigation
refactor: MFU as torchtitan computes it; relative packing cost #3840
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
39c2825
9f28e75
bc7f98e
3fc3d36
b30c5dd
4596d3d
4fa181e
543e3df
7dbdf0a
a306149
bb7ca5f
b1f05d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,5 @@ | ||
| """Autograd and compile-friendly distributed collectives.""" | ||
|
|
||
| import prime_kernels | ||
| import torch | ||
| import torch.distributed as dist | ||
| from torch.distributed import ProcessGroup | ||
|
|
@@ -100,6 +99,9 @@ def _mxfp8_all_to_all( | |
| group_name: str, | ||
| quantized: bool, | ||
| ) -> torch.Tensor: | ||
| # Imported here because prime-kernels is an optional linux-only extra. | ||
| import prime_kernels | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was this change just for local testing? |
||
| kernel = prime_kernels.load("mxfp8_moe") | ||
| operation = kernel.all_to_all_dispatch if quantized else kernel.all_to_all_combine | ||
| return operation( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |
|
|
||
| from collections import OrderedDict | ||
|
|
||
| from transformers import AutoConfig | ||
| from transformers import AutoConfig, GenerationConfig | ||
| from transformers.configuration_utils import PretrainedConfig | ||
| from transformers.models.auto.auto_factory import _BaseAutoModelClass, _LazyAutoMapping, auto_class_update | ||
| from transformers.models.auto.configuration_auto import CONFIG_MAPPING_NAMES | ||
|
|
@@ -97,6 +97,35 @@ def get_custom_causal_lm_cls(model_config: PretrainedConfig) -> type[PreTrainedM | |
| return _CUSTOM_CAUSAL_LM_BY_MODEL_TYPE[model_config.model_type] | ||
|
|
||
|
|
||
| def set_pad_token_id(model_config: PretrainedConfig) -> None: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need this for the flops counter? |
||
| """Ensure pad_token_id is set (some models like Qwen3MoE don't have it). | ||
|
|
||
| In transformers v5, token IDs moved from PretrainedConfig to GenerationConfig. | ||
| """ | ||
| if not hasattr(model_config, "pad_token_id") or model_config.pad_token_id is None: | ||
| gen_config = GenerationConfig.from_model_config(model_config) | ||
| # Use `is not None` instead of truthiness: token ID 0 is valid. | ||
| pad_token_id = next( | ||
| ( | ||
| v | ||
| for v in [gen_config.pad_token_id, gen_config.eos_token_id, getattr(model_config, "eos_token_id", None)] | ||
| if v is not None | ||
| ), | ||
| None, | ||
| ) | ||
| # Some HF configs (e.g. Llama 3.2) set pad_token_id to a list, which | ||
| # crashes both huggingface_hub's strict setter and transformers' | ||
| # GenerationConfig.validate(). Unwrap before assigning. | ||
| if isinstance(pad_token_id, list): | ||
| pad_token_id = pad_token_id[0] | ||
| model_config.pad_token_id = pad_token_id | ||
|
|
||
| # Handle list pad_token_id that was already set on the config (not from our | ||
| # fallback above, but directly in the model's config.json). | ||
| if isinstance(getattr(model_config, "pad_token_id", None), list): | ||
| model_config.pad_token_id = model_config.pad_token_id[0] | ||
|
|
||
|
|
||
| def supports_custom_impl(model_config: PretrainedConfig) -> bool: | ||
| """Check if the model configuration supports the custom PrimeRL implementation. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,49 @@ | ||
| from collections.abc import Iterable | ||
| from dataclasses import dataclass | ||
| from typing import Literal | ||
| from typing import Literal, Protocol, runtime_checkable | ||
|
|
||
| from torch import Tensor | ||
| from torch import Tensor, nn | ||
| from transformers.modeling_utils import PreTrainedModel | ||
|
|
||
| from prime_rl.trainer.models.layers.moe import MoE | ||
| from prime_rl.utils.cp import CPContext | ||
|
|
||
| CPStyle = Literal["ring", "ulysses"] | ||
| ALL_CP_STYLES: frozenset[CPStyle] = frozenset({"ring", "ulysses"}) | ||
|
|
||
|
|
||
| @runtime_checkable | ||
| class AttentionFlops(Protocol): | ||
| """A sequence-mixing layer (softmax, sliding-window, sparse or linear attention, Mamba).""" | ||
|
|
||
| def attention_flops(self, seq_len: int) -> int: | ||
| """Training FLOPs of this layer's sequence mixing for one document of `seq_len` tokens.""" | ||
| ... | ||
|
|
||
|
|
||
| def count_active_params(model: nn.Module, params: Iterable[nn.Parameter]) -> int: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. model signature is redundant? Can get
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, looks like this is for lora support and special casing? Would suggest making |
||
| """Elements of `params` that a token multiplies through, as torchtitan's `get_nparams_and_active_nparams`. | ||
|
|
||
| Routed experts count by `top_k / num_experts`; embedding tables count only when tied to the lm_head. | ||
| """ | ||
| params = {id(param): param for param in params} | ||
| lm_head_params = {id(param) for param in model.lm_head.parameters()} | ||
| inactive = 0 | ||
| for module in model.modules(): | ||
| if isinstance(module, nn.Embedding): | ||
| inactive += sum( | ||
| param.numel() | ||
| for param in module.parameters(recurse=False) | ||
| if id(param) in params and id(param) not in lm_head_params | ||
| ) | ||
| elif isinstance(module, MoE): | ||
| # Expert weights lead with `num_experts`, so the division is exact. | ||
| num_experts, top_k = module.experts.num_experts, module.router.top_k | ||
| expert_params = sum(param.numel() for param in module.experts.parameters() if id(param) in params) | ||
| inactive += expert_params * (num_experts - top_k) // num_experts | ||
|
Comment on lines
+30
to
+41
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we missing the attention params? Thought we'd need another if case for
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could also fold those dense attn matmul costs into what |
||
| return sum(param.numel() for param in params.values()) - inactive | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class CPSupport: | ||
| """Which context-parallel styles an architecture can train under, and why not the rest.""" | ||
|
|
@@ -77,6 +111,10 @@ def get_correct_experts_implementation(self, requested_experts: str | None) -> s | |
| """PrimeRL models always use eager experts implementation.""" | ||
| return "eager" | ||
|
|
||
| def num_active_params(self) -> int: | ||
| """Parameters a text token multiplies through, LoRA adapters included.""" | ||
| return count_active_params(self, self.parameters()) | ||
|
|
||
| @classmethod | ||
| def is_hf_state_dict(cls, state_dict: dict[str, Tensor]) -> bool: | ||
| """ | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.