-
Notifications
You must be signed in to change notification settings - Fork 4.4k
[Dev] restore DSv4 tflops calc in training and fix the packed seq case #5358
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 all commits
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 |
|---|---|---|
|
|
@@ -652,50 +652,81 @@ def transformer_flops(): | |
| https://arxiv.org/abs/2305.10403 | ||
| https://arxiv.org/abs/2205.05198 | ||
| ''' | ||
| ## MLA | ||
| if args.q_lora_rank is None: | ||
| q_term = ( | ||
| args.hidden_size | ||
| * args.num_attention_heads | ||
| * (args.qk_head_dim + args.qk_pos_emb_head_dim) | ||
| ) | ||
| else: | ||
| if args.experimental_attention_variant == "dsv4_hybrid": | ||
| ## DSv4 hybrid MLA projections (per layer, per token). | ||
| ## In dsv4_hybrid mode, qk_head_dim + qk_pos_emb_head_dim == v_head_dim | ||
| ## (qk_head_dim is derived as v_head_dim - qk_pos_emb_head_dim), and the | ||
| ## joint KV is produced by a single hidden -> v_head_dim projection. | ||
| ## Full core attention is replaced by sparse attention and is accounted | ||
| ## for in the dsv4_hybrid branch below. | ||
| q_term = args.q_lora_rank * ( | ||
| args.hidden_size | ||
| + args.num_attention_heads * (args.qk_head_dim + args.qk_pos_emb_head_dim) | ||
| + 1 | ||
| + args.num_attention_heads * args.v_head_dim | ||
| + 1 # q norm | ||
| ) | ||
| # Token-linear part of MLA self-attention (lora projs, kv proj, RoPE, output proj). | ||
| standard_self_attn_term = ( | ||
| forward_backward_expansion_factor | ||
| * fma_expansion_factor | ||
| * ( | ||
| ## q lora + rope + q norm | ||
| q_term | ||
| ## kv lora + rope + kv norm | ||
| + args.kv_lora_rank | ||
| * ( | ||
| kv_term = ( | ||
| args.hidden_size * args.v_head_dim + args.v_head_dim | ||
| ) # kv proj + kv norm | ||
| ## Grouped low-rank output projection: | ||
| ## wo_a: (n_head * v_head_dim) -> (o_groups * o_lora_rank) | ||
| ## linear_proj: (o_groups * o_lora_rank) -> hidden | ||
| o_term = ( | ||
| args.num_attention_heads * args.v_head_dim * args.o_lora_rank | ||
| + args.o_groups * args.o_lora_rank * args.hidden_size | ||
| ) | ||
| standard_self_attn_term = ( | ||
| forward_backward_expansion_factor | ||
| * fma_expansion_factor | ||
| * (q_term + kv_term + o_term) | ||
| ) | ||
| # Sparse attention replaces full core attention; its cost is captured | ||
| # in dsv4_hybrid_extra_term below. | ||
| standard_self_attn_core_term = 0 | ||
| else: | ||
| ## MLA | ||
| if args.q_lora_rank is None: | ||
| q_term = ( | ||
| args.hidden_size | ||
| * args.num_attention_heads | ||
| * (args.qk_head_dim + args.qk_pos_emb_head_dim) | ||
| ) | ||
| else: | ||
| q_term = args.q_lora_rank * ( | ||
| args.hidden_size | ||
| + args.num_attention_heads * (args.qk_head_dim + args.v_head_dim) | ||
| + args.num_attention_heads * (args.qk_head_dim + args.qk_pos_emb_head_dim) | ||
| + 1 | ||
| ) | ||
| + args.hidden_size * args.qk_pos_emb_head_dim | ||
| ## o proj | ||
| + (args.num_attention_heads * args.v_head_dim) * args.hidden_size | ||
| # Token-linear part of MLA self-attention (lora projs, kv proj, RoPE, output proj). | ||
| standard_self_attn_term = ( | ||
| forward_backward_expansion_factor | ||
| * fma_expansion_factor | ||
| * ( | ||
| ## q lora + rope + q norm | ||
| q_term | ||
| ## kv lora + rope + kv norm | ||
| + args.kv_lora_rank | ||
| * ( | ||
| args.hidden_size | ||
| + args.num_attention_heads * (args.qk_head_dim + args.v_head_dim) | ||
| + 1 | ||
| ) | ||
| + args.hidden_size * args.qk_pos_emb_head_dim | ||
| ## o proj | ||
| + (args.num_attention_heads * args.v_head_dim) * args.hidden_size | ||
| ) | ||
| ) | ||
| ) | ||
| # Core-attention (L^2) part: ``QK^T`` and ``(softmax(QK^T)) V``. The | ||
| # ``/2`` accounts for the causal mask and the ``*2`` cancels it via FMA. | ||
| standard_self_attn_core_term = ( | ||
| forward_backward_expansion_factor | ||
| * fma_expansion_factor | ||
| * ( | ||
| args.num_attention_heads | ||
| * (args.qk_head_dim + args.qk_pos_emb_head_dim) | ||
| / 2 | ||
| + args.num_attention_heads * args.v_head_dim / 2 | ||
| # Core-attention (L^2) part: ``QK^T`` and ``(softmax(QK^T)) V``. The | ||
| # ``/2`` accounts for the causal mask and the ``*2`` cancels it via FMA. | ||
| standard_self_attn_core_term = ( | ||
| forward_backward_expansion_factor | ||
| * fma_expansion_factor | ||
| * ( | ||
| args.num_attention_heads | ||
| * (args.qk_head_dim + args.qk_pos_emb_head_dim) | ||
| / 2 | ||
| + args.num_attention_heads * args.v_head_dim / 2 | ||
| ) | ||
| ) | ||
| ) | ||
|
|
||
| else: | ||
| ## MHA or GQA | ||
|
|
@@ -730,6 +761,8 @@ def transformer_flops(): | |
| * 2 # QK^T and (QK^T)V | ||
| ) | ||
|
|
||
| dsv4_hybrid_extra_term = 0 | ||
| dsv4_hybrid_extra_core_term = 0 | ||
| if is_linear_attention_variant(args.experimental_attention_variant): | ||
| # Calculate number of dense and MoE Transformer MLPs. | ||
| if isinstance(args.linear_attention_freq, int): | ||
|
|
@@ -792,6 +825,108 @@ def transformer_flops(): | |
| "Invalid experimental_attention_variant: " | ||
| f"{args.experimental_attention_variant}" | ||
| ) | ||
| elif args.experimental_attention_variant == "dsv4_hybrid": | ||
| # DSv4 hybrid: full core attention is replaced by sparse attention (CSA), | ||
| # and selected layers additionally run a learned indexer (DSA). | ||
| # The MLA-style projection cost per layer is captured in | ||
| # ``standard_self_attn_term`` above; here we add the extra per-layer FLOPs | ||
| # for sparse attention, the main compressor, and the indexer. | ||
| num_linear_attention_layers = 0 | ||
| linear_self_attn_term = 0 | ||
| num_standard_attention_layers = num_layers | ||
|
|
||
| compress_ratios = args.csa_compress_ratios | ||
| assert compress_ratios is not None, ( | ||
| "csa_compress_ratios must be set for dsv4_hybrid" | ||
| ) | ||
| assert len(compress_ratios) == num_layers, ( | ||
| f"Invalid length of csa_compress_ratios: {len(compress_ratios)}, " | ||
| f"expected num_layers + mtp_num_layers ({num_layers})." | ||
| ) | ||
| # ratio == 0: window-only (no compressor, no indexer) | ||
| # ratio == 4: window + learned-topk over compressed KV (compressor + indexer) | ||
| # ratio == 128: window + all compressed KV (compressor only) | ||
| n_layers_r0 = sum(1 for r in compress_ratios if r == 0) | ||
| n_layers_r4 = sum(1 for r in compress_ratios if r == 4) | ||
| n_layers_r128 = sum(1 for r in compress_ratios if r == 128) | ||
|
|
||
| n_head = args.num_attention_heads | ||
| v_head_dim = args.v_head_dim | ||
| window = args.csa_window_size | ||
| seq_len = args.seq_length | ||
|
|
||
| # ---- Sparse attention (replaces full core attention) ---- | ||
| # Split into token-linear parts (window attention, constant per token) | ||
| # and L^2 parts (compressed-KV attention, scales with sequence length) | ||
| # so THD packed sequences get correct seqlen_squared_sum_in_batch scaling. | ||
|
|
||
| # r=0: window-only, fixed per-token cost. | ||
| sparse_attn_r0 = n_layers_r0 * n_head * window * v_head_dim * 2 | ||
|
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. [SUGGESTION Naming] The |
||
|
|
||
| # r=128: window (token-linear) + all compressed KV (L^2). | ||
| # Compressed positions per token ≈ L/(128*2) (causal /2), x2 for | ||
| # QK^T + softmax@V → L^2 coefficient: n_head * v_head_dim / 128. | ||
| sparse_attn_r128_window = n_layers_r128 * n_head * window * v_head_dim * 2 | ||
| sparse_attn_r128_core = n_layers_r128 * n_head * v_head_dim / 128 | ||
|
|
||
| # ---- Main compressor (ratio > 0 layers) ---- | ||
| # Two projections per layer (wkv + wgate): hidden -> coff * v_head_dim. | ||
| # ratio == 4: coff = 2 (overlapping windows) | ||
| # ratio == 128: coff = 1 (non-overlapping) | ||
| main_compressor_term = ( | ||
| n_layers_r4 * args.hidden_size * (2 * v_head_dim) * 2 | ||
| + n_layers_r128 * args.hidden_size * (1 * v_head_dim) * 2 | ||
| ) | ||
|
|
||
| # ---- r=4 layers: sparse attention + indexer ---- | ||
| if n_layers_r4 > 0: | ||
| assert args.dsa_indexer_n_heads is not None, ( | ||
| "dsa_indexer_n_heads must be set for dsv4_hybrid with ratio==4 layers." | ||
| ) | ||
| assert args.dsa_indexer_head_dim is not None, ( | ||
| "dsa_indexer_head_dim must be set for dsv4_hybrid with ratio==4 layers." | ||
| ) | ||
| assert args.dsa_indexer_topk is not None, ( | ||
| "dsa_indexer_topk must be set for dsv4_hybrid with ratio==4 layers." | ||
| ) | ||
| idx_n_heads = args.dsa_indexer_n_heads | ||
| idx_head_dim = args.dsa_indexer_head_dim | ||
| idx_topk = args.dsa_indexer_topk | ||
|
|
||
| effective_topk_4 = min(idx_topk, seq_len // 4) | ||
| avg_comp_4 = effective_topk_4 * (1 - effective_topk_4 * 4 / (2 * seq_len)) | ||
| sparse_attn_r4 = ( | ||
| n_layers_r4 * n_head * (window + avg_comp_4) * v_head_dim * 2 | ||
| ) | ||
|
|
||
| # Indexer token-linear: compressor (coff=2, wkv + wgate), Q proj, | ||
| # weights proj. | ||
| indexer_token_term = ( | ||
| n_layers_r4 * args.hidden_size * (2 * idx_head_dim) * 2 | ||
| + n_layers_r4 * args.q_lora_rank * idx_n_heads * idx_head_dim | ||
| + n_layers_r4 * args.hidden_size * idx_n_heads | ||
| ) | ||
| # Indexer L^2: scoring each query against ~L/4 compressed positions. | ||
| indexer_scoring_core = n_layers_r4 * idx_n_heads * idx_head_dim / 4 | ||
| else: | ||
| sparse_attn_r4 = 0 | ||
| indexer_token_term = 0 | ||
| indexer_scoring_core = 0 | ||
|
|
||
| sparse_attn_token_term = ( | ||
| sparse_attn_r0 + sparse_attn_r4 + sparse_attn_r128_window | ||
| ) | ||
|
|
||
| dsv4_hybrid_extra_term = ( | ||
| forward_backward_expansion_factor | ||
| * fma_expansion_factor | ||
| * (sparse_attn_token_term + main_compressor_term + indexer_token_term) | ||
| ) | ||
| dsv4_hybrid_extra_core_term = ( | ||
| forward_backward_expansion_factor | ||
| * fma_expansion_factor | ||
| * (sparse_attn_r128_core + indexer_scoring_core) | ||
| ) | ||
| else: | ||
| num_linear_attention_layers = 0 | ||
| linear_self_attn_term = 0 | ||
|
|
@@ -802,9 +937,14 @@ def transformer_flops(): | |
| self_attn_term = ( | ||
| linear_self_attn_term * num_linear_attention_layers | ||
| + standard_self_attn_term * num_standard_attention_layers | ||
| + dsv4_hybrid_extra_term | ||
| ) | ||
| # Core attention (L^2) FLOPs. Standard attention has a uniform per-layer | ||
| # coefficient; DSv4 sparse attention varies by layer type and is pre-summed. | ||
| self_attn_core_term = ( | ||
| standard_self_attn_core_term * num_standard_attention_layers | ||
| + dsv4_hybrid_extra_core_term | ||
| ) | ||
| # Core attention (L^2) FLOPs per standard-attention layer. | ||
| self_attn_core_term = standard_self_attn_core_term * num_standard_attention_layers | ||
|
|
||
| # Token-linear FLOPs scale with the real (unpadded) token count. | ||
| # For BSHD this falls back to ``batch_size * seq_length`` (no padding). | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[SUGGESTION Simplification] The error message says "expected num_layers + mtp_num_layers" but the local variable
num_layersalready includes MTP layers (set at line 607 asargs.num_layers + mtp_num_layers). The description reads as if it'snum_layers_var + mtp_num_layers(double-counting MTP). Consider clarifying:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is for
args. num_layersandargs. mtp_num_layers. I think there is no need to change.