Interleaved RoPE + NoPE
Arcee Trinity
Easy · 🔒 Plus required
3:1 RoPE/NoPE layer pattern
Independent study note. Written from the public paper and official code. This is not TensorTonic Plus and does not reproduce their exercises, starter code, or tests. For the official version, subscribe on TensorTonic.
Overview
Every Trinity model repeats a four-layer attention motif: three local sliding-window layers that use RoPE, then one global causal layer that uses no positional encoding (NoPE). Section 2.2 calls this a 3:1 local:global ratio and attributes the layout to Yang et al. (2025). Trinity keeps QK-norm on both kinds of layer.
The split is an efficiency and long-context choice. Local layers cheapen attention at length and carry relative position; global NoPE layers let information travel the full prefix and, in their context-extension ablations, recovered loss faster when only the global layers were stretched.
How it works
Index layers from \ell=0. A layer is local if (\ell+1)\bmod 4\neq 0, global otherwise. On Large that is layer_types: three "sliding_attention" then one "full_attention", sixty times ($$ global layers). The same rule is the default in AfmoeConfig via global_attn_every_n_layers=4.
Queries and keys after QK-norm are
\widehat{\mathbf{q}}_{t,i}=\begin{cases}\mathrm{RoPE}(\mathbf{q}_{t,i}) & \text{local}\\ \mathbf{q}_{t,i} & \text{global (NoPE)}\end{cases} \qquad \widehat{\mathbf{k}}_{t,j}=\begin{cases}\mathrm{RoPE}(\mathbf{k}_{t,j}) & \text{local}\\ \mathbf{k}_{t,j} & \text{global}\end{cases}
with allowed sources (equation (8))
\mathcal{S}_{t}=\begin{cases}\{s\mid \max(1,t-w+1)\le s\le t\} & \text{local}\\ \{s\mid 1\le s\le t\} & \text{global.}\end{cases}
On Large, w=4096 and pretraining T=8192, so a local layer sees half the pretrain context; after extension the global layers grow (report: they extend global context and leave the local window fixed). RoPE uses \theta=10^{4} and rotates the full d_h=128 dims.
AfmoeModel still builds one (\cos,\sin) pair for the batch. AfmoeAttention applies apply_rotary_pos_emb only when is_local_attention. Global layers receive the same tuple and ignore it. Masks are chosen from a dict: sliding_attention vs full_attention.
The pattern is independent of the dense/MoE split. Layers 0,1,2 are local-dense; 3 is global-dense; 4,5 are local-dense; MoE starts at 6 (local). A 3:1 checker that also assumes “global iff MoE” is wrong.
Official code
Schedule construction is in configuration_afmoe.py (layer_types / global_attn_every_n_layers). Application is AfmoeAttention.forward (if self.is_local_attention) and the mask map in AfmoeModel.forward in modeling_afmoe.py. The paper statement is arcee-ai/trinity-large-tech-report §2.2.
Watch-outs
- Off-by-one on the 4-cycle: layer 0 is local. Treating \ell\bmod 4=0 as global flips the entire stack.
- RoPE and the sliding window travel together. A global layer with RoPE, or a local layer with a full mask, is not the paper’s 3:1.
- Values are never rotated. Applying RoPE to V (or skipping K) breaks compatibility with the stored cache.
- Context extension in the report leaves local w at the pretrain value. Inflating the window on every layer is a different recipe than the one they adopted.
Sources
- Paper: Arcee Trinity Large Technical Report, §2.2 equations (6)–(8), Table 2
- Code: arcee-ai/trinity-large-tech-report; transformers
models/afmoe