nemo_automodel.components.models.deepseek_v4.layers
nemo_automodel.components.models.deepseek_v4.layers
DeepSeek V4 Attention Layer.
Architecture (from official inference/model.py):
Q path: x -> wq_a [hidden -> q_lora_rank] -> q_norm (RMSNorm) -> wq_b [q_lora_rank -> n_heads * head_dim] -> reshape [n_heads, head_dim] -> per-head RMSNorm (q_norm applied per-head in official code) -> apply_rotary_emb on last rope_head_dim dims
KV path (K = V, single latent): x -> wkv [hidden -> head_dim] # single KV head, K = V = kv -> kv_norm (RMSNorm on head_dim) -> apply_rotary_emb on last rope_head_dim dims K = V = kv (one latent vector serves both key and value)
Output path (grouped): o [bsz, seq, n_heads, head_dim] -> reshape [bsz, seq, n_groups, n_heads_per_group * head_dim] -> wo_a einsum per group: [n_heads_per_group * head_dim] -> [o_lora_rank] -> reshape [bsz, seq, n_groups * o_lora_rank] -> wo_b [n_groups * o_lora_rank -> hidden]
attn_sink: learnable per-head scalar bias added to attention-sink position score.
HC (Hyper-Connections):
Each Block maintains hc_mult=4 copies of the hidden state.
hc_pre reduces [bsz, seq, hc_mult, dim] -> [bsz, seq, dim] via Sinkhorn mixing.
hc_post expands [bsz, seq, dim] -> [bsz, seq, hc_mult, dim].
See DeepseekV4HyperConnection.compute_weights and
optimized_kernels.dsv4_sinkhorn_normalize for the torch reference and
optional TileKernels Sinkhorn path.
Compress-ratio attention (Compressor + Indexer) is wired into DeepseekV4Attention.forward for layers with compress_ratio > 0. All layers share the same sliding-window causal mask on the local KV path.
Module Contents
Classes
Functions
Data
API
Bases: Module
Sliding-window attention + Compressor + Indexer + attention sink.
Single-head KV (num_key_value_heads=1), grouped low-rank output via
DeepseekV4GroupedLinear. compress_ratio == 0 layers skip
the compressor / indexer and run pure SWA.
Expand, normalize, and partially rotate the low-rank query.
Parameters:
Tensor of shape [batch, sequence, q_lora_rank].
Tensor of shape [batch, sequence, rotary_channels], where
rotary_channels is qk_rope_head_dim and the first half
contains the unique adjacent-pair frequencies.
Tensor of shape [batch, sequence, rotary_channels] with cos’s frequency order.
Returns: torch.Tensor
Tensor of shape [batch, heads, sequence, channels], where channels
Run sliding-window, compressed, and sparse attention for one layer.
Parameters:
Tensor of shape [batch, local_sequence, hidden], where local_sequence is this rank’s contiguous query shard under CP.
Main-rope cosine and sine, each of shape [batch, local_sequence, rotary_channels].
Optional additive mask of shape [batch, 1, local_sequence, keys] over local queries and CP-global keys. None on the canonical unmasked sparse path.
Optional compress-rope cosine and sine with the main tables’ shapes; compressed layers use them for queries and keys.
Rotary module for compressed keys.
First query position.
Optional tensor of shape [batch, local_sequence] with each token’s position inside its document.
Optional packed_seq_ids and vision_token_types
tensors of shape [batch, local_sequence], _dsv4_cp_group,
and the model-validated flags _dsv4_all_queries_nonempty
(every query sees its diagonal key, so sparse kernels may skip
empty-row filtering) and _dsv4_complete_compressor_windows.
Returns: torch.Tensor
Output of shape [batch, local_sequence, hidden], and eager attention
Model-owned context-parallel hook, called by moe.parallelizer.apply_cp.
DSV4 runs Miles-style CP (contiguous query shard + all-gathered K/V), so there
is no TE DotProductAttention to configure — we just record the CP process
group that forward uses to all-gather K/V across CP ranks.
Bases: Module
HF PR 45616 port. Long-range KV branch. Pools compress_ratio tokens
into one compressed KV; when ratio == 4 the Indexer narrows the pool.
Bases: Module
Callable holder for fp32 tensors that need their own FSDP unit.
Bases: Linear
Block-diagonal grouped linear (HF PR 45616 port).
weight parameter has the standard nn.Linear shape
[out_features, in_features_per_group] so quantizers keyed on
nn.Linear.weight still find it; forward does per-group bmm.
Bases: Module
Per-site HyperConnection mixer (attention or FFN). Ported from
transformers/src/transformers/models/deepseek_v4/modular_deepseek_v4.py
class DeepseekV4HyperConnection.
Owns fn (packed linear), base (bias), and scale (scalar
per-head gains). compute_weights produces three mixer tensors:
pre[B, S, H] : sigmoid-gated collapse weightspost[B, S, H] : sigmoid-gated expand weightscomb[B, S, H, H] : doubly-stochastic combination matrix from Sinkhorn-normalising sigmoid gates
All math runs in fp32 regardless of the outer cast policy; parameters
cast themselves via .float() on each forward. HF lists these params
in _keep_in_fp32_modules_strict — the KAutomodel adapter does the
same via submodule-name matching.
Fuse the four-stream reduction without launching tiny batched GEMMs.
Parameters:
Tensor of shape [batch, sequence, hidden].
Tensor of shape [batch, sequence, streams, hidden].
FP32 coefficients of shape [batch, sequence, streams].
FP32 coefficients of shape [batch, sequence, input_streams, output_streams].
Returns: torch.Tensor
Tensor of shape [batch, sequence, streams, hidden] in the residual
Predict mixing coefficients before Sinkhorn normalization.
Parameters:
Tensor of shape [batch, sequence, streams, hidden].
FP32 projection of shape [mix_components, streams * hidden], where mix_components is streams * (streams + 2).
FP32 offsets of shape [mix_components], ordered pre, post, combination.
FP32 gains of shape [3], ordered pre, post, combination.
Number of residual streams.
RMS normalization epsilon.
Additive epsilon for the pre coefficients.
Returns: torch.Tensor
FP32 pre/post coefficients of shape [batch, sequence, streams] and
Collapse residual streams in FP32 before restoring the activation dtype.
Parameters:
Tensor of shape [batch, sequence, streams, hidden].
FP32 coefficients of shape [batch, sequence, streams].
Returns: torch.Tensor
Tensor of shape [batch, sequence, hidden] in the activation dtype.
Predict and normalize the residual mixing coefficients.
Parameters:
Tensor of shape [batch, sequence, streams, hidden].
Returns: torch.Tensor
FP32 pre/post tensors of shape [batch, sequence, streams] and
Mix the residual and sublayer output with the existing BF16 rounding boundaries.
Parameters:
Tensor of shape [batch, sequence, hidden].
Tensor of shape [batch, sequence, streams, hidden].
FP32 coefficients of shape [batch, sequence, streams].
FP32 coefficients of shape [batch, sequence, input_streams, output_streams].
Returns: torch.Tensor
Tensor of shape [batch, sequence, streams, hidden] in the residual dtype.
Initialize HyperConnection parameters using the DeepSeek-V4 reference scheme.
Parameters:
Standard deviation for the fn weight initialization.
Bases: Module
Final HC-stream collapse before the shared RMSNorm + lm_head.
Ported from modular_deepseek_v4.py class DeepseekV4HyperHead.
Sigmoid-weighted sum over the hc_mult streams (no Sinkhorn). Used
once at the end of DeepseekV4Model.forward to go from
[B, S, H, D] back to [B, S, D].
Collapse residual streams using FP32 mixing coefficients.
Parameters:
Tensor of shape [batch, sequence, hc_mult, hidden].
Returns: torch.Tensor
Tensor of shape [batch, sequence, hidden] in the input dtype.
Initialize HyperHead parameters using the DeepSeek-V4 reference scheme.
Parameters:
Standard deviation for the hc_fn weight initialization.
Bases: Module
HF PR 45616 port. Picks the top-k compressed positions per query when
compress_ratio == 4. Owns its own pool at index_head_dim plus a
query projection + weights_proj head-mixer.
Score the compressed pool and select keys; see forward for the contract.
Parameters:
Tensor of shape [batch, local_sequence, hidden].
Tensor of shape [batch, local_sequence, q_lora_rank].
Rotary module for compressed keys.
Query cosine and sine, each of shape [batch, local_sequence, rotary_channels].
Per-attention-call scratch cache.
Index of the current transformer layer.
First query position.
Optional tensor of shape [batch, local_sequence].
Optional tensor of shape [batch, local_sequence].
Optional context-parallel process group.
Returns: torch.LongTensor
Int64 tensor of shape [batch, local_sequence, selected_keys]
Select compressed keys, reusing frozen decisions during checkpoint replay.
The replay owns only integer indices. The main compressor and attention retain their normal differentiable paths. Trainable indexers keep the existing path; each checkpoint invocation owns an independent record.
Parameters:
Tensor of shape [batch, local_sequence, hidden], where local_sequence is this rank’s query shard under CP.
Tensor of shape [batch, local_sequence, q_lora_rank].
Rotary module for compressed keys.
Query cosine and sine, each of shape
[batch, local_sequence, rotary_channels] with rotary_channels
equal to qk_rope_head_dim.
Per-attention-call scratch cache.
Index of the current transformer layer.
First query position.
Optional tensor of shape [batch, local_sequence] with each token’s position inside its document.
Optional tensor of shape [batch, local_sequence] with positive document identities; zero marks padding.
Optional context-parallel process group.
Returns: torch.LongTensor
Int64 tensor of shape [batch, local_sequence, selected_keys], with
Bases: Module
V4 rotary embedding. Produces (cos, sin) sized to qk_rope_head_dim
(via partial_rotary_factor = qk_rope_head_dim / head_dim), matching HF.
YaRN: when rope_scaling is a YaRN-typed dict
({"type": "yarn", "factor": F, "original_max_position_embeddings": L0, "beta_fast": ..., "beta_slow": ...}), modify inv_freq per
dsv4flash/inference/model.py:precompute_freqs_cis — frequency
interpolation with a smooth linear ramp between beta_fast/beta_slow
correction dims. Used by the compress-rope (theta=160000) on layers
with compress_ratio > 0. The main rope (theta=10000, used only on
sliding-window layers) gets rope_scaling=None because the reference
builds it with original_seq_len=0 for those layers.
Build rotary tables using FP32 position-frequency products.
Parameters:
Tensor of shape [batch, sequence, hidden], supplying output dtype and device.
Tensor of shape [batch, sequence] containing absolute token positions.
Returns: torch.Tensor
Cosine and sine tensors of shape [batch, sequence, rotary_dim], where
Training-only cache shim mirroring the three methods DeepseekV4Compressor
/ DeepseekV4Indexer call on DeepseekV4Cache.
KAutomodel training forward is stateless — we never persist KV or compressor
windows across calls. Each DeepseekV4Attention.forward creates a fresh
cache instance, which holds per-layer scratch dicts for the duration of the
call. When a full window hasn’t accumulated yet we return an empty tensor
and let the downstream code handle it.
Bases: Function
Per-group matmul that reads and writes token-major activations in place.
torch.bmm wants the group axis first. Permuting a token-major [tokens, groups, d_in]
activation is a free view on the input side, but the [groups, tokens, d_out] result had to be
permuted back and copied (flatten / reshape by the caller) and, in backward, the
permuted input gradient was copied again by the reshape that produced the grouped view
(ViewBackward): two 0.5 GB copies per attention layer at the DeepSeek-V4.1 benchmark shape.
cuBLAS handles a strided output, so the forward and the input gradient are written straight
into token-major buffers through out= and the copies disappear.
Input gradient written token-major; weight gradient as a grouped bmm over the token axis. Both are
computed in the forward’s compute dtype and returned in the dtype of the tensor they belong to (what
autograd does for a plain autocast bmm whose operands were cast).
Args: x [tokens, groups, d_in] (contiguous), w [groups, d_out, d_in]. Returns [tokens, groups, d_out].
torch.bmm(..., out=) is not an autocast op, so the cast autocast would apply to a plain bmm is done
here: under autocast both operands and the output take the autocast dtype (an FP32 weight meets BF16
activations in AMP training, and an FP32 input returns BF16 as before); without autocast the dtypes are
used as given, so the same-dtype path costs no copy.
Split x along its last dim into nope (first) and rope (last
rope_head_dim) slices, rotate only the rope slice with INTERLEAVED
pair-RoPE (pairs (2k, 2k+1)), concat back.
The DSV4-Flash released checkpoint uses interleaved RoPE end-to-end
(see dsv4flash/inference/model.py:apply_rotary_emb — complex
multiplication on view_as_complex of pairs). HF transformers PR
45616 / PR 45643 ship a Llama-style rotate_half here instead, which
pairs (d, d+rd/2). Same algebra but a different dim-to-frequency
mapping — the released weights expect the interleaved layout, so the
Llama-style helper produces wrong activations on the released checkpoint
(verified empirically: kv_post_rope cosine drops from 0.9999 to 0.866
after one block under Llama-style; matches at >0.999 under interleaved).
Parameters:
Tensor of shape […, sequence, channels] rotated on its last
rope_head_dim channels. The eager path accepts any leading
dimensions broadcastable with the tables, for example
[batch, heads, sequence, channels] or [batch, 1, sequence, channels];
the Triton path requires exactly [batch, heads, sequence, channels].
Tensor of shape [batch, sequence, rope_head_dim]; the first half holds the unique adjacent-pair frequencies.
Tensor with cos’s shape and frequency order; negate it for the inverse rotation.
Even number of trailing channels to rotate.
"triton" uses the fused CUDA kernel when x is on CUDA and
the tables do not require gradients; otherwise the eager path runs.
Returns: torch.Tensor
Tensor with x’s shape and dtype. The leading channels - rope_head_dim
Interleaved RoPE on the last rope_head_dim dims of x (pairs are
(2k, 2k+1)). Matches the DeepSeek inference reference’s complex-mul
formulation in dsv4flash/inference/model.py:apply_rotary_emb: the
released DSV4-Flash weights were trained with this layout, NOT the
Llama-style rotate_half layout HF transformers PR 45616/45643 still
uses (pairs (d, d+rd/2)).
Inverse rotation: pass -sin instead of sin (caller’s
responsibility — same as our existing inverse-rope call site).
Parameters:
[..., rope_head_dim] (or larger trailing dim with rope on the
last rope_head_dim slice). Typical attention-layout shapes:
[B, H, S, D] for q/k or [B, 1, S, D] for shared-KV.
shape [B, S, rope_head_dim] produced by the Llama-style
cat([freqs, freqs], -1) rotary; we take the first half which
contains the unique per-pair frequencies (the second half is a
duplicate that the Llama-style helper needs and we don’t).
Must be even.
Build the additive compressed-position mask for Indexer-selected pool IDs.
Use TileLang DSV4 sparse kernels only when the attention backend requests them.
Use optional TileKernels sinkhorn when available, otherwise torch fallback.
Reshape [B, S, ratio, 2*head_dim] -> [B, S, 2*ratio, head_dim] with the
cross-window overlap from the DeepSeek inference reference (Compressor.overlap_transform
in dsv4flash/inference/model.py:307-314).
Window N consumes:
- positions
[ratio:]of the new tensor: the second half of the feature dim of window N (current block). - positions
[:ratio]of the new tensor: the first half of the feature dim of window N-1 (previous block, i.e. the overlap into the past).
Window 0 has no previous block, so its [:ratio] slice is left at fill_value
(0 for the kv tensor, -inf for the score tensor so softmax masks it out).
Softmax-gated sum-pool over ratio consecutive tokens.
Non-overlap mode (HF PR 45616 layout, ratio==128 in V4-Flash):
Input kv/gate of shape [B, length, head_dim].
Reshape to [B, length/ratio, ratio, head_dim] and pool over the ratio axis.
Overlap mode (DeepSeek inference reference layout, ratio==4 in V4-Flash):
Input kv/gate of shape [B, length, 2*head_dim] (wkv/wgate
project to 2*head_dim so each window can carry both its own kv and a
half-overlap into the next window).
Reshape to [B, length/ratio, ratio, 2*head_dim], apply _overlap_transform
to remap to [B, length/ratio, 2*ratio, head_dim], then pool over the 2*ratio
axis. Each compressed token thus aggregates 2*ratio = 8 raw tokens — the
ratio tokens of the current window plus the ratio tokens of the previous
window — giving smoother compression boundaries that the released checkpoint
was trained under.
HF PR 45616 omits the overlap path entirely; the released DSV4-Flash safetensors
have ape/wkv/wgate shapes that only match the overlap layout ([ratio, 2*head_dim] and [2*head_dim, hidden]), so we must support it here to load
the released weights.
RMS-normalize the last dim without materializing an x.square() tensor.
Rotate half the hidden dims of the input (Llama / GPT-NeoX style).
Port of transformers.models.llama.modeling_llama.apply_rotary_pos_emb.
Build a 4D additive causal+padding (+optional sliding-window) mask
compatible with eager_attention_with_sink.
Mirrors HF’s create_sliding_window_causal_mask (used in
DeepseekV4Model.forward): each query at position i attends only to
keys at positions [max(0, i - sliding_window + 1), i]. The DSV4-Flash
weights were trained with this banding on every layer, so dropping it makes
the softmax see a different distribution than training and degrades loss.
Inputs
attention_mask: 2D [B, S] tensor with 1=valid, 0=padding (HF convention),
or already-4D additive mask, or None.
sliding_window: if not None, mask out keys further back than this many
positions from the query (in addition to causal masking).
Returns:
[B, 1, S, S] additive mask of dtype (0 where keep, large negative
where mask).
Build a 4D additive block-causal mask from packed-sequence lengths.
Eager attention with per-head sink: appends an extra softmax column
whose logit is module.sinks[h] and whose value-slot is zero. Ported
verbatim from HF PR 45616.
Port of transformers.models.llama.modeling_llama.repeat_kv.