메뉴
HN
Hacker News 16일 전

Flash-MSA: 희소 어텐션으로 백만 토큰 학습 가속화

IMP
8/10
핵심 요약

최첨단 AI 모델들이 추론 속도를 높이기 위해 사용하는 미니맥스 희소 어텐션(MSA)을 효율적으로 학습할 수 있는 세계 최초의 오픈소스 커널이 공개되었습니다. 이 기술은 Hopper 및 Blackwell GPU 환경에서 블록 단위의 희소 패턴을 적용하여 메모리 부담을 줄이고 백만 개 이상의 긴 맥락을 처리하는 학습 속도를 획기적으로 단축해 줍니다. 최신 AI 모델들의 핵심 기술을 최적화하여 대규모 언어 모델(LLM) 학습의 병목 현상을 해결한다는 점에서 실무 개발자들에게 매우 중요합니다.

번역된 본문

[깃허브] [MiniMax 논문] [트레이너] Flash-MSA vs Flash-Attention 독립 학습 단계 비교.

최근 여러 최첨단 모델 [1, 2, 3, 4, 5]들이 추론 속도를 크게 높이기 위해 희소 어텐션(Sparse Attention)을 사용하고 있지만, 이를 효율적으로 학습할 수 있는 코드를 공개한 곳은 없었습니다. 오늘 저는 Hopper 및 Blackwell GPU를 위한 CuTeDSL 기반의 'MiniMax Sparse Attention(MSA)' 세계 최초의 고성능 오픈소스 학습 커널을 소개합니다. 저는 Spheron에서 H100 및 B200을 임대하여 모든 개발 작업을 수행했으며, FA4, MSA 추론 코드, 그리고 Codex를 참고했습니다.

참고: 이것은 공식 구현이 아니며, 저는 MiniMax와 어떠한 제휴 관계도 아닙니다.

MSA 정보 MSA는 Deepseek Sparse Attention과 유사하지만, 몇 가지 핵심적인 변화가 있습니다 (MSS 논문의 그림 1 참조).

  1. 블록 단위 희소성(Blockwise sparsity) 메인 어텐션을 위해 개별 KV(Key-Value)를 선택하는 대신, 프록시 점수에 대해 최대 풀링(max-pooling)을 사용하여 128개의 블록 단위로 KV를 선택합니다. 이를 통해 커널에 훌륭한 캐싱 속성을 도입할 수 있습니다.

  2. 메인 어텐션에 MLA 대신 GQA 사용 이 부분은 특히 중요합니다. 제가 아는 한 서구의 어떤 연구소도 학습에 MLA(Multi-Head Latent Attention)를 채택하지 않았기 때문에, GLM-5.2, DSv4와 같은 최첨단 모델들에서 MLA에 맞춰진 기존 희소 어텐션 방식을 다른 모델에서 사용할 수 없었습니다.

  3. 프록시 헤드의 그룹별 특화(Group-wise specialization of proxy heads) MLA를 GQA(Grouped Query Attention)로 대체하면 각 레이어 내에 독립적인 쿼리 그룹이 생깁니다. 이를 통해 DSA(Deepseek Sparse Attention)처럼 전체 어텐션 레이어에 대해 합산하고 점수를 매기는 대신, 각 프록시 헤드마다 다른 KV 하위 집합을 선택할 수 있습니다. 어텐션 헤드가 본질적으로 서로 다른 토큰에 주의를 기울인다는 증거가 있으므로, 이러한 변화는 메인 어텐션의 표현력을 높여줄 것입니다.

커널 디자인 커널 시퀀스에 대한 높은 수준의 개요입니다. MSA를 효율적으로 실행하려면 작업을 최대한 반복하지 않아야 하며, 레지스터나 공유 메모리를 과도하게 로드하지 않아야 합니다. 일반적인 플래시 레지스터(Q 타일, KV 타일, O 누산기, LSE 누산기) 외에도 순방향(forward)에서 스트리밍 top-k 누산기를 고려해야 합니다. 역방향(backward)에서는 메인 어텐션 기울기(gradient)와 프록시 어텐션 기울기를 모두 계산할 수 있도록 이중 어텐션 결합 패스를 위한 공간을 만들어야 합니다. 프록시 기울기는 프록시 및 메인 어텐션 확률 모두에 액세스해야 하기 때문입니다. 블록 희소성(block-sparsity)의 좋은 이점 중 하나는 DSA처럼 개별 토큰 대신 블록의 인덱스만 캐시하면 된다는 것입니다. 즉, 역방향 패스까지 블록 인덱스를 저장하는 것이 가능하며, 전체 학습 단계에서 프록시 순방향만 컨텍스트 길이에 대해 제곱에 비례(quadratic)하고, 나머지는 모두 프록시 순방향에서 캐시된 희소 블록을 사용합니다.

순방향 (Forward) 작업 순서는 프록시 어텐션 -> 희소 메인 어텐션 -> 메인 어텐션 출력을 다음 레이어로 보내고 역방향을 위해 메인 LSE를 저장하는 것입니다.

  • 프록시 어텐션 (Proxy attention) 더 이상 출력을 누적할 필요가 없기 때문에 프록시 닷 프로덕트(dot-product)는 일반적인 Flash Attention과 약간 다릅니다. 대신 키를 스트리밍하면서 상위 k개의 어텐션 점수와 각각의 인덱스를 추적해야 합니다. 또한 저는 Flash와 달리 역방향을 위해 LSE를 누적하지 않고, 역방향 동안 LSE를 얻기 위해 희소 활성화에 대해 프록시 닷 프로덕트의 매우 가벼운 재계산(recompute)을 실행합니다. 실제로 이 방법이 순방향에서 LSE와 top-k를 융합(fusing)하는 것보다 더 빨랐습니다. 각 QK^T 타일이 계산됨에 따라 각 청크에 대한 인과적 로컬 최대 점수(causal local max score)를 가져와 레지스터에 보관된 각 쿼리 행의 현재 상위 k 값에 삽입 정렬(insertion sort)을 수행합니다. 이러한 top-k 레지스터를 위한 공간을 확보하기 위해 키 블록을 반으로 잘라야 했습니다. 또한 MSA는 각 토큰에 대한 로컬 블록이 마스킹되지 않은 슬라이딩 윈도우 방식이어야 하므로, 각 쿼리에 대한 로컬 KV 블록의 어텐션 점수를 무한(inf)으로 설정합니다.

  • 메인 어텐션 (Main attention) 메인 어텐션은 단순히 블록 희소 플래시 어텐션(block-sparse flash attention) 순방향 패스입니다. 이는 MoBA에서 이미 구현된 바 있으므로, 블록 희소 어텐션을 varlen 플래시로 재매개변수화하는 그들의 영리한 트릭을 그대로 가져왔습니다.

역방향 (Backward) 프록시 헤드에 대한 기울기를 계산하기 위해 프록시와 메인 어텐션 역방향 패스를 융합해야 합니...

원문 보기
원문 보기 (영어)
[Github] [MiniMax Paper] [Trainer] Flash-MSA vs Flash-Attention isolated train step. 1 Several frontier models [ 1 , 2 , 3 , 4 , 5 ] use sparse attention to greatly speedup their inference, though no one has posted code to train it efficiently. Today I introduce the world's first performant open-source training kernels for Minimax Sparse Attention in CuTeDSL for Hopper and Blackwell GPUs. I did all of the dev work on Spheron H100 and B200 rentals and with the help of referencing FA4 , MSA inference , and Codex. Disclaimer: This is not an official implementation and I am not affiliated with MiniMax About MSA MSA is similar to Deepseek Sparse Attention , with some core changes Fig. 1 from the MSA Paper 1. Blockwise sparsity Instead of the proxy attention selecting individual KVs for the main attention, it selects them in blocks of 128 using max-pooling over the proxy scores. This introduces some nice caching properties for the kernels. 2. GQA instead of MLA for the main attention This one is particularly important because no western labs, to the best of my knowledge, have adopted MLA into their training, making the prevailing sparse attention formulation in frontier models like GLM-5.2, DSv4, which are fit to MLA, inaccessible to models here. 3. Group-wise specialization of proxy heads Replacing MLA with GQA introduces independent groups of queries within each layer, giving us the option to select a different subset of KVs with each proxy head instead of summing and scoring for the entire attention layer like DSA does. There is some evidence suggesting attention heads organically attend to different tokens, so this change should increase main attention expressivity. Kernel Design High level overview of kernel sequence To run MSA efficiently we need to repeat as little work as possible and not overload the registers / shared memory too much. In addition to regular flash registers (Q tile, KV tile, O accumulator, LSE accumulator), we have to take into account streaming top-k accumulators in the forward. In the backward, we have to make space for a double-attention combined pass so we can calculate main attention grads and proxy attention grads, since proxy grads require access to both proxy and main attn probs. One nice benefit of block-sparsity is that since we only have to cache the indices of blocks instead of individual tokens like in DSA, it is feasible to store block indices all the way through until the backward - meaning in the entire train step, only the proxy forward is quadratic w.r.t. context length, everything else uses the cached sparse blocks from the proxy forward. Forward Order of operations is proxy attention -> sparse main attention -> send main attention output to next layer, save main LSE for backward. Proxy attention The proxy dot-product is a little different from regular Flash Attn as we don't have to accumulate output anymore, but we do have to keep track of the top k attention scores and their respective indices as we stream across keys. Unlike Flash I also do not accumulate LSE for the backward and instead run a very cheap recompute of proxy dot-product over sparse activations to get the LSE during the backward. This was faster than fusing LSE+topk in the forward for me in practice. As each tile of QK^T is calculated, I grab the causal local max score for each chunk and do an insertion sort into the current top-k values for each query row, held in registers. I had to slice key blocks in half to make space for these top-k registers. Also, MSA dictates that the local block for each token must be unmasked sliding-window style, so I set the attention scores for the local KV block for each query to inf. Main attention The main attention is just a block-sparse flash attention forward. This has been done before in MoBA , so I copied their clever trick to re-parameterize block sparse attention into varlen flash. Backward To calculate gradients for the proxy heads, we need to fuse the proxy and main attention backwards because the proxy training signal requires access to both proxy attn probs and main attn probs at once. Since we saved block indices from the forward and only train both attentions on the sparse KV activations, the backward can be run in linear time. First we pull our cached block_indices and invert the mapping of $B$[batch,proxy head,query,top_k_slot]->[key block] into $B^{\ast}$[batch,proxy head,key block]->[queries that use this block]. We use $B^{\ast}$ to schedule query chunks to optimize for reuse of shared sparse KV blocks. Then we run a quick sparse proxy attention pass over the selected blocks (again using the MoBA varlen trick) to get the proxy LSE, then we stream the fused proxy-main backward tasks, loading chunks of QKV, Q_proxy, K_proxy, and main_lse. To account for loading so many heads into the registers we have to reduce the size of the Q chunks and KV chunks we use at a time. In each stream we calculate main and proxy attention probs, compute dQ,dK,dV, then compute proxy dQ,dK from the KL training term: KL Divergence Loss Recall the original KL loss term from DSA is $L^\iota = \sum_t{D_{KL}(p_t,s_t \Vert Softmax(I_t, s_t))}$ Materializing both indexer and main attn prob. distributions to accumulate KL divergence would require many read/writes to shared mem and usage of additional registers that would significantly slow down training. Fortunately, there is a trick we can use to backprop atomically and still be mathematically equivalent to a full KL loss. With the proxy attn prob as $p_{px}$ and main attn prob as $p$, expand the KL term: \[L^\iota = \sum_t{D_{KL}(p_t \Vert p_{px,t})} = \sum_t{p_t * log(\frac{p_t}{p_{px,t}})}\] Use log rules to expand again: \[L^\iota = \sum_t{p (log(p_t) - log(p_{px,t}))} = \sum_t{(p_t*log(p_t) - p_t*log(p_{px,t}))}\] We want to calculate grads into the next latent which is the pre-softmax attention score at position i (not t), $z_{px,i}$ \[\frac{\partial L^\iota}{\partial z_{px,i}} = \frac{\partial}{\partial z_{px,i}}\sum_t{(p_t*log(p_t) - p_t*log(p_{px,t}))}\] Main probs $p_t$ are detached from the proxy/KL-loss graph, so they are constant in this partial. \[\frac{\partial L^\iota}{\partial z_{px,i}} = -\frac{\partial}{\partial z_{px,t}} \sum_t{p_t*log(p_{px,t})}\] We know the softmax logprob partial of a softmax output at position t ($p_{px,t}$) w.r.t. pre-softmax logit ($z_{px,i}$) is $\frac{\partial log(p_{px,t})}{\partial{z_{px,i}}} = \delta_{it}-p_{px,i}$ \[\frac{\partial L^\iota}{\partial z_{px,i}} = -\sum_t{ p_t*(\delta_{it}-p_{px,i})} = -\sum_t{ p_t\delta_{it}} + \sum_t{p_tp_{px,i}}\] Kronecker delta function $\delta_{it}$ is only nonzero at i==t, so $\sum_t{p_t\delta_{it}}=p_i$. \[\frac{\partial L^\iota}{\partial z_{px,i}} = -p_i + \sum_t{p_tp_{px,i}} = -p_i + p_{px,i}\sum_t{p_t}\] Because $p_t$ is a probability distribution, $\sum_t{p_t}=1$. \[\frac{\partial L^\iota}{\partial z_{px,i}} = -p_i+p_{px,i}\] In other words, gradient to proxy score from KL loss = proxy prob - main prob. This is the term I use in the kernel to calculate proxy gradients without needing to ever fully materialize KL. Warmup kernels In warmup mode, the main attention forward is dense and does not use block_indices, so the proxy forward can be skipped entirely, we can train it fully in the backward. For the main attention warmup forward kernel I just call flash and save its returned output and lse, and return a placeholder KL. In the backward I call dense flash on the indexer just to get the LSE, then reuse the fused proxy+main attention backward from the sparse MSA kernels. Correctness To verify correctness of the the kernel forward and backward, I implemented MSA in eager Pytorch and swept for cosine similarity between both implementations' forward outputs and backward grads over several configs. The sweep is run in bf16 precision and the backwards include both target output loss and the internal KL loss. Typically the tolerance for precision at bf16 is 0.01. Eager vs. kernel cosine sim