<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Chizoba Obasi blog</title>
    <description>Exploring Deep Learning.</description>
    <link>https://chizkidd.github.io//</link>
    <atom:link href="https://chizkidd.github.io//feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Tue, 20 Oct 2026 06:54:08 +0000</pubDate>
    <lastBuildDate>Tue, 20 Oct 2026 06:54:08 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>FlashAttention: Part 2</title>
        <description>&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;This is Part 2 of a two-part FlashAttention series. If you haven’t read &lt;a href=&quot;https://chizkidd.github.io/2026/09/13/flashattention/&quot;&gt;Part 1&lt;/a&gt; yet, start there. Part 1 covers the fundamental memory problem, the mathematical trick, and the FA1 implementation.&lt;/em&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;From FA1 to FA4: The Evolution of FlashAttention&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The reference &lt;a href=&quot;https://drive.google.com/file/d/1CLyK-9Cflcvi3fRl3qAHyzYvwJFjCyVg/view&quot;&gt;handbook&lt;/a&gt; for this blog post was inspired by this &lt;a href=&quot;https://x.com/techNmak/status/2098057360908685358&quot;&gt;tweet&lt;/a&gt; and is titled: &lt;em&gt;Understanding FlashAttention: How IO-Aware Attention Makes &lt;a href=&quot;https://chizkidd.github.io/2026/04/17/transformers/&quot;&gt;Transformers&lt;/a&gt; Faster Without Approximating Attention.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In Part 1, we covered the fundamental problem FlashAttention-1 (FA1) addresses, the mathematical tricks it uses, the GPU implementation of those tricks, and the architectural compatibility (&lt;a href=&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/#multi-head-attention&quot;&gt;MHA&lt;/a&gt;/&lt;a href=&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/#multi-query-attention-mqa&quot;&gt;MQA&lt;/a&gt;/&lt;a href=&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/#grouped-query-attention-gqa&quot;&gt;GQA&lt;/a&gt;) of FlashAttention.&lt;/p&gt;

&lt;p&gt;In this blog post, we continue the story through the evolution of the FlashAttention family (FA2, FA3, FA4), its relationship to other efficient attention techniques (PagedAttention, &lt;a href=&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/#deepseek-sparse-attention-dsa&quot;&gt;sparse&lt;/a&gt;, linear), the training vs. inference distinction, current PyTorch integration, and the practical mental model to walk away with.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;table-of-contents&quot;&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;#1-flashattention-evolution&quot;&gt;1. FlashAttention Evolution&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#11-flashattention-2-what-changed&quot;&gt;1.1 FlashAttention-2: what changed&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#12-fa2-parallelism-across-sequence-tiles&quot;&gt;1.2 FA2 parallelism across sequence tiles&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#13-fa2-work-partitioning-and-non-matmul-flops&quot;&gt;1.3 FA2 work partitioning and non-matmul FLOPs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#14-flashattention-3-the-hopper-generation&quot;&gt;1.4 FlashAttention-3: the Hopper generation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#15-fa3-asynchrony-overlap-data-movement-gemm-and-softmax&quot;&gt;1.5 FA3 asynchrony: overlap data movement, GEMM, and softmax&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#16-fa3-fp8-performance-without-pretending-precision-is-free&quot;&gt;1.6 FA3 FP8: performance without pretending precision is free&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#17-flashattention-4-the-blackwell-generation&quot;&gt;1.7 FlashAttention-4: the Blackwell generation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#18-fa4-and-asymmetric-hardware-scaling&quot;&gt;1.8 FA4 and asymmetric hardware scaling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#19-fa4-implementation-and-current-status&quot;&gt;1.9 FA4 implementation and current status&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#110-flashattention-1-through-fa-4-compared&quot;&gt;1.10 FlashAttention-1 through FA-4 compared&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;#2-using-flashattention-in-frameworks&quot;&gt;2. Using FlashAttention in Frameworks&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#21-pytorch-scaled-dot-product-attention-today&quot;&gt;2.1 PyTorch scaled-dot-product attention today&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#22-exactness-is-not-bitwise-identity&quot;&gt;2.2 Exactness is not bitwise identity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;#3-flashattention-vs-other-techniques&quot;&gt;3. FlashAttention vs. Other Techniques&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#31-flashattention-vs-pagedattention&quot;&gt;3.1 FlashAttention vs. PagedAttention&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#32-flashattention-vs-sparse-and-linear-attention&quot;&gt;3.2 FlashAttention vs. sparse and linear attention&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;#4-training-vs-inference&quot;&gt;4. Training vs. Inference&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#41-training-prefill-and-decode-are-different-regimes&quot;&gt;4.1 Training, prefill, and decode are different regimes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;#5-practical-engineering-for-flashattention&quot;&gt;5. Practical Engineering for FlashAttention&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#51-common-implementation-mistakes&quot;&gt;5.1 Common implementation mistakes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#52-common-misconceptions&quot;&gt;5.2 Common Misconceptions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;#6-conclusion&quot;&gt;6. Conclusion&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#61-the-mental-model-to-have&quot;&gt;6.1 The mental model to have&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#62-flashattention-summary&quot;&gt;6.2 FlashAttention Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;appendix&quot;&gt;&lt;strong&gt;Appendix&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#reference-map&quot;&gt;Reference map&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#citation&quot;&gt;Citation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1-flashattention-evolution&quot;&gt;&lt;strong&gt;1. FlashAttention Evolution&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;11-flashattention-2-what-changed&quot;&gt;1.1 FlashAttention-2: What Changed&lt;/h3&gt;

&lt;p&gt;FA2 didn’t change the goal. It still computes exact attention without materializing quadratic intermediates. However, it made much better use of the GPU.&lt;sup id=&quot;fnref:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; The FA2 paper identifies three main changes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Reduce the number of non-matmul FLOPs.&lt;/li&gt;
  &lt;li&gt;Parallelize attention across sequence tiles, even for a single head, to improve occupancy.&lt;/li&gt;
  &lt;li&gt;Repartition work among warps within a thread block to reduce shared-memory communication.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The roughly 2x speedup of FA2 over FA1 on A100 in the original paper’s experiments is real,&lt;sup id=&quot;fnref:5:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; but it’s a benchmark- and GPU-specific number that depends on dtype, shapes, causal mode, and software versions. It is not a universal speed guarantee.&lt;/p&gt;

&lt;!-- My breakdown of what each generation was actually doing:

- **FA1:** make exact attention IO-aware.
- **FA2:** reduce non-matmul FLOPs, parallelize better across sequence tiles, improve warp-level work partitioning. --&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;The question FA2 is really asking: &lt;em&gt;“How can we utilize the GPU more effectively?”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;The FlashAttention pattern across generations&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;ul&gt;
  &lt;li&gt;FA1’s breakthrough was IO-aware tiled exact attention.&lt;br /&gt;&lt;/li&gt;
  &lt;li&gt;FA2’s breakthrough was better parallelism and work partitioning around the same high-level algorithmic idea.&lt;br /&gt;
Every subsequent FlashAttention evolution since has followed the same pattern: keep the semantics, and adapt the kernel pipeline to the resources and bottlenecks of newer GPU architectures.&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;12-fa2-parallelism-across-sequence-tiles&quot;&gt;1.2 FA2 Parallelism Across Sequence Tiles&lt;/h3&gt;

&lt;p&gt;The practical scenario where this matters:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;batch size is small,&lt;/li&gt;
  &lt;li&gt;number of heads is small,&lt;/li&gt;
  &lt;li&gt;sequence length is huge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In that case, FA1 might not expose enough independent work. A GPU has many processing clusters known as streaming multiprocessors (SMs) that need enough independent work to be kept busy. If we only parallelize over batch times heads, some of those units sit idle. FA2 adds parallelism along the sequence dimension, so different query-row tiles can be processed independently. That gives the GPU more work to schedule, since more resident blocks means more available parallel work.&lt;/p&gt;

&lt;p&gt;Let’s consider a simplified mental picture:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Q rows [ block 0 | block 1 | block 2 | block 3 | block 4 | block 5 ]
           ↓                   ↓                   ↓
          CTA                 CTA                 CTA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Query rows get split into blocks (block 0, block 1, …, block 5), and each block gets assigned to a CTA.&lt;/li&gt;
  &lt;li&gt;A CTA, &lt;strong&gt;Cooperative Thread Array&lt;/strong&gt; also known as a thread block, is a logical grouping of threads that execute together on a single SM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More parallel blocks don’t automatically mean better performance. We still have constraints: &lt;strong&gt;occupancy, registers, shared memory, tile size, data reuse.&lt;/strong&gt; Kernel design is an optimization problem over hardware resources, not just a request for maximum thread count.&lt;sup id=&quot;fnref:15&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Key insight&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;strong&gt;Long sequence length itself can become a source of parallelism.&lt;/strong&gt;&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;13-fa2-work-partitioning-and-non-matmul-flops&quot;&gt;1.3 FA2 Work Partitioning and Non-Matmul FLOPs&lt;/h3&gt;

&lt;p&gt;FA2 also changes how work is divided among warps in a thread block. In FA1, warp partitioning could require partial results to be written to shared memory and then combined.&lt;sup id=&quot;fnref:5:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; That creates communication overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Picture it:&lt;/strong&gt; a warp writes a partial result to shared memory, another warp reads it, combines it, and writes again. Every round trip through shared memory costs cycles.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[warp 0] --&amp;gt; shared memory --&amp;gt; [warp 1] --&amp;gt; shared memory --&amp;gt; ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;FA2 repartitions the work so warps cooperate in a way that cuts down those shared-memory reads and writes, thereby reducing communication. It also reduces non-matmul operations via algebraic manipulation around softmax rescaling and backward computations.&lt;sup id=&quot;fnref:5:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; This matters because:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A FLOP isn’t a universal unit of performance across all operation types.&lt;/li&gt;
  &lt;li&gt;Matrix multiply-accumulate (MMA) has significantly higher throughput than exponentials, scalar reductions, conversions, and shared-memory synchronization.&lt;/li&gt;
  &lt;li&gt;A tensor-core matmul and an exponential aren’t equivalent from a hardware-throughput perspective.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can track the bottlenecks addressed by each FlashAttention generation so far:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;FA1 attacked HBM traffic.&lt;/li&gt;
  &lt;li&gt;FA2 exposed the following as the next important bottlenecks: &lt;strong&gt;occupancy, warp communication, non-matmul operations.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FA2 also simplifies what gets saved for backward. Instead of keeping probabilities around, it stores a single row-wise log-sum-exp value per query row and recomputes the tiles, keeping the same linear-memory footprint.&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Bottlenecks shift&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;A useful optimization principle from the FlashAttention evolution so far: &lt;strong&gt;after fixing one bottleneck, another bottleneck emerges.&lt;/strong&gt;&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;14-flashattention-3-the-hopper-generation&quot;&gt;1.4 FlashAttention-3: The Hopper Generation&lt;/h3&gt;

&lt;p&gt;&lt;u&gt;Why does FA3 exist at all?&lt;/u&gt; GPU hardware changed. Hopper introduced capabilities that changed the best way to schedule attention, and FA2 didn’t fully exploit what the H100 could do. The FA3 paper measured FA2 at only around 35% utilization on H100, which leaves a lot of headroom.&lt;/p&gt;

&lt;p&gt;FA3 was designed around NVIDIA Hopper GPUs and attacks that utilization gap with four broad ideas:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Asynchronous computation and data movement.&lt;/li&gt;
  &lt;li&gt;Warp specialization.&lt;/li&gt;
  &lt;li&gt;Interleaving GEMM (General Matrix Multiplication) and softmax work.&lt;/li&gt;
  &lt;li&gt;FP8 support.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;u&gt;The important point:&lt;/u&gt; &lt;strong&gt;The mathematical algorithm didn’t suddenly change.&lt;/strong&gt; The execution workflow did, to exploit Hopper’s hardware architecture.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On H100, the NeurIPS 2024 publication reports a 1.5 to 2.0x speedup over FA2 in its benchmark suite. BF16 throughput reaches up to 840 TFLOPs/s (85% utilization), and FP8 reaches 1.3 PFLOPs/s.&lt;sup id=&quot;fnref:6&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; (The earlier arXiv preprint reported 740 TFLOPs/s and close to 1.2 PFLOPs/s; the figures here are from the final NeurIPS version.) As before, these are hardware- and benchmark-specific empirical results, not universal across all models.&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Exactness vs. precision&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;strong&gt;FA3 is not “a more approximate FlashAttention.”&lt;/strong&gt; The FP16/BF16 path preserves the exact-attention algorithmic goal. The FP8 path intentionally introduces lower-precision arithmetic and therefore needs its own numerical-accuracy discussion.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;15-fa3-asynchrony-overlap-data-movement-gemm-and-softmax&quot;&gt;1.5 FA3 Asynchrony: Overlap Data Movement, GEMM, and Softmax&lt;/h3&gt;

&lt;p&gt;Picture a naive pipeline:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;load tile → GEMM → softmax → PV GEMM → load next tile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each stage waits for the previous one. That leaves periods where some hardware resources sit idle while others work. Tensor cores go quiet during softmax. Memory buses go quiet during GEMM. FA3 builds a more overlapping pipeline:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Load K/V tile 1
       Compute tile 1
              Load K/V tile 2
                     Compute tile 2
                            Load K/V tile 3
                                   Compute tile 3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;More explicitly:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Load K/V tile 1
         QK^T GEMM
            softmax/update
                     PV GEMM
                          Load K/V tile 2
                                  QK^T GEMM
                                     softmax/update
                                              PV GEMM
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The exact implementation is more sophisticated than the toy depictions above, but the architectural intuition is what matters. Two Hopper capabilities make this overlapping possible:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Tensor Memory Accelerator (TMA)&lt;/strong&gt;, which moves data asynchronously.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Asynchronous warp-group matrix-multiply-accumulate (WGMMA)&lt;/strong&gt;, which lets tensor-core work proceed without blocking the issuing warp.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FA3 also uses &lt;strong&gt;warp specialization&lt;/strong&gt; to allow data movement and computation to overlap.&lt;sup id=&quot;fnref:6:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; Different warps take responsibility for different stages of the pipeline: some handle data movement, some issue GEMMs, some handle softmax and update. That way a warp that’s waiting on a memory transfer doesn’t stall the whole block.&lt;/p&gt;

&lt;p&gt;FA3 also uses a &lt;strong&gt;ping-pong style schedule&lt;/strong&gt; to interleave block matrix multiplication and softmax. The goal is to avoid leaving tensor cores idle while non-matmul operations (scalar and special-function work) are performed, and vice versa.&lt;sup id=&quot;fnref:6:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The online-softmax recurrence itself hasn’t changed. The normalization and value-accumulation stages still do what &lt;a href=&quot;https://chizkidd.github.io/2026/09/13/flashattention/&quot;&gt;Part 1&lt;/a&gt; describes in FlashAttention-1. What changed is the hardware pipeline used to execute them.&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Progression: What bottleneck is being addressed?&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;ul&gt;
  &lt;li&gt;FA1 asks “How do we tackle HBM traffic?”&lt;br /&gt;&lt;/li&gt;
  &lt;li&gt;FA2 asks “How do we partition &amp;amp; parallelize the work better?”&lt;br /&gt;&lt;/li&gt;
  &lt;li&gt;FA3 asks “How do we maximize the use of distinct execution resources on Hopper?”&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;16-fa3-fp8-performance-without-pretending-precision-is-free&quot;&gt;1.6 FA3 FP8: Performance Without Pretending Precision Is Free&lt;/h3&gt;

&lt;p&gt;&lt;u&gt;Now let&apos;s focus on precision.&lt;/u&gt; Hopper gives extremely high tensor-core throughput for FP8. That’s tempting, but it isn’t free. Simply converting every attention operand to FP8 produces unacceptable numerical error.&lt;sup id=&quot;fnref:15:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; Attention contains a chain of operations:&lt;/p&gt;

\[QK^T \rightarrow e^{x} \rightarrow \frac{e^{x}}{\sum e^{x}} \rightarrow PV.\]

&lt;p&gt;These operations have different numerical sensitivities. Dot products tolerate some loss. The exponential is much more sensitive. Normalization involves division. The value accumulation sums over long sequences. Each stage has its own error profile.&lt;/p&gt;

&lt;p&gt;FA3 introduces an FP8 path with two techniques to control that error:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Block quantization&lt;/strong&gt;, which scales values relative to their local tile rather than globally.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Incoherent processing&lt;/strong&gt;, which multiplies Q and K by a random orthogonal matrix to spread outlier values across dimensions before quantization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the final NeurIPS 2024 publication, FA3 reaches 1.3 PFLOPs/s on H100 with this path, and reports &lt;strong&gt;2.6x lower numerical error&lt;/strong&gt; than the baseline FP8 attention method used for comparison.&lt;sup id=&quot;fnref:6:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;So low precision doesn’t automatically buy free performance. It shifts error into specific parts of the pipeline, therefore we have to design around that shift. This is exactly where “exact” starts to mean two different things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Algorithmic exactness.&lt;/strong&gt; The algorithm still represents $\mathrm{softmax}(QK^T)V$. No pairs are skipped, no low-rank approximation is made.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Numerical precision.&lt;/strong&gt; The actual calculation may use FP8, and therefore experiences quantization and rounding error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An algorithm can be exact in its mathematical formulation while a particular low-precision implementation of that algorithm is numerically less accurate. Both things are true at once, and neither cancels the other. A model’s acceptable precision depends on the full training and inference setup. FP8 support is a hardware-and-numerics feature layered onto the FlashAttention execution strategy, not proof that FP8 is universally interchangeable with BF16 or FP16.&lt;sup id=&quot;fnref:15:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;u&gt;One more scope note&lt;/u&gt;: &lt;strong&gt;&lt;em&gt;FA3’s FP8 path is Hopper-specific.&lt;/em&gt;&lt;/strong&gt; The official repository still separates FA3’s implementation from other backends, so these aren’t universal features of “FlashAttention.”&lt;sup id=&quot;fnref:8&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h3 id=&quot;17-flashattention-4-the-blackwell-generation&quot;&gt;1.7 FlashAttention-4: The Blackwell Generation&lt;/h3&gt;

&lt;p&gt;In FA4, the core pattern of hardware-aware redesign continues, this time for NVIDIA Blackwell.&lt;/p&gt;

&lt;p&gt;Blackwell exhibits &lt;strong&gt;asymmetric hardware scaling&lt;/strong&gt;. Tensor-core throughput roughly doubled relative to Hopper, but several other resources didn’t scale at the same rate. Shared-memory bandwidth and exponential throughput, in particular, lag behind.&lt;sup id=&quot;fnref:7&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;This changes which stage is the bottleneck. See the illustration below:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Tensor cores GEMM   [  |  |  |  |  |  |  |  |  |  ]
Softmax/exponential [  |  ]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The matrix multiplication has become extremely fast. Now the secondary softmax/exponential work becomes relatively expensive, and as such becomes the bottleneck. When one subsystem becomes much faster than the others, operations that used to be secondary can become the bottleneck.&lt;sup id=&quot;fnref:15:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;On Hopper, tensor cores were fast, but the gap to softmax and shared-memory bandwidth was narrower.&lt;/li&gt;
  &lt;li&gt;On Blackwell, the matrix-multiplication bar gets much longer, and the softmax/exponential bar stays roughly the same length, as shown in the illustration above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FA4 redesigns both the forward and backward pipelines rather than just reusing the Hopper schedule. The major improvement techniques include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Fully asynchronous MMA pipelines&lt;/strong&gt; and larger tiles.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Software-emulated exponential plus conditional online-softmax rescaling&lt;/strong&gt; in the forward pass.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tensor Memory (TMEM) and 2-CTA MMA techniques&lt;/strong&gt; to reduce shared-memory traffic and atomic additions in the backward pass.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On B200 with BF16, the paper reports up to &lt;strong&gt;1.3x speedup&lt;/strong&gt; over cuDNN 9.13 and &lt;strong&gt;2.7x&lt;/strong&gt; over its Triton comparison, reaching 1613 TFLOPs/s (71% utilization) under the evaluated configurations.&lt;sup id=&quot;fnref:7:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Same algorithm, different balance&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;FA4 does not change the big-O arithmetic of dense attention. It attacks the new bottlenecks created by a GPU generation whose compute, memory, and function-unit balance differs from Hopper’s.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;18-fa4-and-asymmetric-hardware-scaling&quot;&gt;1.8 FA4 and Asymmetric Hardware Scaling&lt;/h3&gt;

&lt;p&gt;The FlashAttention lineage is a case study in why kernels can’t be optimized once and assumed optimal forever.&lt;sup id=&quot;fnref:15:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; This section contains a broader systems lesson. Let’s look at two hypothetical GPU generations (throughput in FLOP/s):&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;FLOP/s&lt;/th&gt;
      &lt;th&gt;GPU A&lt;/th&gt;
      &lt;th&gt;GPU B&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;GEMM&lt;/td&gt;
      &lt;td&gt;100&lt;/td&gt;
      &lt;td&gt;200&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Softmax&lt;/td&gt;
      &lt;td&gt;50&lt;/td&gt;
      &lt;td&gt;50&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Memory&lt;/td&gt;
      &lt;td&gt;50&lt;/td&gt;
      &lt;td&gt;50&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;GPU B makes GEMM 2x faster. But the overall system &lt;strong&gt;doesn’t&lt;/strong&gt; become 2x faster automatically. Now that GEMM is less of a bottleneck, the relative cost of everything else goes up. Softmax and memory traffic, which were already on the critical path, are now even more clearly on it. This is &lt;strong&gt;asymmetric hardware scaling.&lt;/strong&gt; The ratio between subsystems changes, and the optimal algorithm changes with it.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Asymmetric hardware scaling is one of the major systems principles behind the FA1 → FA4 evolution.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Blackwell is a case of this. Tensor-core throughput outran softmax and exponential throughput. So the forward pass becomes constrained by softmax and exponential work rather than GEMM. FA4 responds with a software-emulated exponential and by skipping online-softmax rescaling when the running maximum doesn’t need it. Both moves take pressure off the relatively slower non-matmul units.&lt;/p&gt;

&lt;p&gt;The backward pass has a different bottleneck. FA4 uses Blackwell’s Tensor Memory (TMEM) and 2-CTA MMA mode to cut shared-memory traffic and atomic accumulation overhead.&lt;/p&gt;

&lt;p&gt;This is why “FA4 is just a faster FA3” misses the point. The algorithm and kernel pipeline are co-designed around the new asymmetries, which is reflected in the paper title: &lt;em&gt;Algorithm and Kernel Pipelining Co-Design for Asymmetric Hardware Scaling.&lt;/em&gt;&lt;sup id=&quot;fnref:7:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;That is also why performance numbers should always name the GPU generation. A speedup on B200 doesn’t reveal anything about H100, A100, or a non-NVIDIA accelerator. The ratios are the whole story.&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;The systems principle&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;strong&gt;A kernel is balanced against a particular hardware ratio.&lt;/strong&gt; If tensor-core throughput improves faster than memory bandwidth or special-function throughput, the optimal algorithmic pipeline can change even when the mathematical function is identical.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;19-fa4-implementation-and-current-status&quot;&gt;1.9 FA4 Implementation and Current Status&lt;/h3&gt;

&lt;!-- FlashAttention-4 is a ***CuTeDSL*** implementation, optimized for Hopper and Blackwell GPUs such as H100 and B200 via the `flash-attn-4` package installation.[^8] CuTeDSL stands for **CUDA Tensor Domain-Specific Language**. It&apos;s a Python-based programming framework developed by NVIDIA for writing highly optimized, low-level, state-of-the-art GPU kernels. --&gt;

&lt;p&gt;FlashAttention-4 is a &lt;strong&gt;CuTe DSL&lt;/strong&gt; implementation, optimized for Hopper and Blackwell GPUs such as the H100 and B200, and installed via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flash-attn-4&lt;/code&gt; package.&lt;sup id=&quot;fnref:8:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; CuTe DSL is a Python-based domain-specific language from NVIDIA’s CUTLASS for writing and just-in-time compiling high-performance GPU kernels.&lt;/p&gt;

&lt;p&gt;One engineering motivation behind the FA4 choice is &lt;strong&gt;compile-time productivity&lt;/strong&gt;. The FA4 paper reports substantially faster compilation, roughly 20 to 30 times, compared with the traditional C++ template-based approach used in its comparison, while still retaining the required low-level expressivity.&lt;sup id=&quot;fnref:7:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; As of September 2026, PyPI classifies &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flash-attn-4&lt;/code&gt; as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Development Status :: 3 - Alpha&lt;/code&gt;, and its latest release (4.0.0b32, uploaded September 23, 2026) is a pre-release.&lt;sup id=&quot;fnref:9&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:9&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;That’s the caveat. &lt;strong&gt;Current implementation status is not the same thing as an evergreen property of FlashAttention.&lt;/strong&gt; Packages, GPU support, CUDA compatibility, PyTorch, hardware, and framework integration all evolve quickly.&lt;/p&gt;

&lt;p&gt;The durable lesson is the algorithmic progression. &lt;strong&gt;The current library support layer changes much faster than the underlying algorithmic ideas.&lt;/strong&gt; Algorithmic knowledge evolves slowly. Library support can change overnight. When deploying FlashAttention, it’s worth separating the two. The algorithm tells us what should be possible. The current library tells us what’s actually available on your specific GPU, CUDA version, and framework.&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Implementation snapshot&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Treat this as a current implementation snapshot, not an evergreen property. &lt;em&gt;“FA4 exists in the official repository”&lt;/em&gt; and &lt;em&gt;“every production environment should replace FA2 with FA4”&lt;/em&gt; are very different claims.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;110-flashattention-1-through-fa-4-compared&quot;&gt;1.10 FlashAttention-1 Through FA-4 Compared&lt;/h3&gt;

&lt;p&gt;Here’s the whole evolution on one page:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Version&lt;/th&gt;
      &lt;th&gt;Main Problem&lt;/th&gt;
      &lt;th&gt;Main Solution&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;FA1&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Excessive memory (IO/HBM) traffic&lt;/td&gt;
      &lt;td&gt;Tiling + online softmax + fused exact attention + IO awareness&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;FA2&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;GPU underutilization&lt;/td&gt;
      &lt;td&gt;More sequence-level parallelism + better warp-level work partitioning + fewer non-matmul FLOPs&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;FA3&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Hopper execution imbalance&lt;/td&gt;
      &lt;td&gt;Asynchronous TMA/WGMMA pipeline + GEMM-softmax overlap + warp specialization + FP8 path&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;FA4&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Blackwell hardware asymmetry&lt;/td&gt;
      &lt;td&gt;Fully async MMA pipeline + larger tiles + optimized non-matmul work (software exp / conditional online-softmax rescale, TMEM, 2-CTA backward techniques)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;A few things to hold onto when reading those numbers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;These are four successive generations of efficient implementations of attention, not four different attention mechanisms. The mathematical target remains fundamentally the same for dense attention.&lt;/li&gt;
  &lt;li&gt;The exact feature matrix differs across implementations. FA3 is strongly associated with Hopper-specific optimization, while the current FA4 repository targets both Hopper and Blackwell through its CuTe DSL path.&lt;sup id=&quot;fnref:8:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2-using-flashattention-in-frameworks&quot;&gt;&lt;strong&gt;2. Using FlashAttention in Frameworks&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;21-pytorch-scaled-dot-product-attention-today&quot;&gt;2.1 PyTorch Scaled-Dot-Product Attention Today&lt;/h3&gt;

&lt;p&gt;Modern PyTorch provides a high-level API:&lt;sup id=&quot;fnref:10&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:10&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;torch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;functional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaled_dot_product_attention&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attn_mask&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dropout_p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;is_causal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;enable_gqa&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Conceptually, this function computes $\mathrm{softmax}(QK^T / \sqrt{d})V$. Internally, PyTorch picks an optimized implementation based on the inputs. The available backends include FlashAttention-2, a memory-efficient attention implementation, cuDNN attention, and the C++ math implementation. To restrict which backend runs, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;torch.nn.attention.sdpa_kernel&lt;/code&gt;:&lt;sup id=&quot;fnref:11&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:11&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;torch.nn.attention&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SDPBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sdpa_kernel&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;torch.nn.functional&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sdpa_kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SDPBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FLASH_ATTENTION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scaled_dot_product_attention&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_causal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If a requested fused kernel can’t run for the given inputs, PyTorch can warn with reasons when fallbacks are disabled. Backend selection and eligibility depend on &lt;strong&gt;device, dtype, shape, mask, and other arguments.&lt;/strong&gt;&lt;sup id=&quot;fnref:11:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:11&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;torch.nn.attention&lt;/code&gt; docs also expose registration and activation hooks for newer implementations, including FA3 and FA4. This surface moves fast, so treat the installed PyTorch version’s docs as the source of truth.&lt;sup id=&quot;fnref:12&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:12&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Which kernel actually ran&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;ul&gt;
  &lt;li&gt;Calling the high-level API does not necessarily mean &lt;em&gt;“I know exactly which kernel executed.”&lt;/em&gt;&lt;br /&gt;&lt;/li&gt;
  &lt;li&gt;If we need to know which backend is running, restrict it explicitly with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sdpa_kernel&lt;/code&gt; or check the PyTorch version’s registration APIs. In production and benchmarking, verify rather than assume.&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;22-exactness-is-not-bitwise-identity&quot;&gt;2.2 Exactness Is Not Bitwise Identity&lt;/h3&gt;

&lt;p&gt;Suppose two implementations, a math kernel and a FlashAttention kernel, compute the same dense SDPA definition. There will still be small numerical differences. The reason is straightforward. &lt;strong&gt;Floating-point addition is not associative:&lt;/strong&gt;&lt;/p&gt;

\[(a + b) + c \neq a + (b + c)\]

&lt;p&gt;in finite precision, for some values. Tiling changes the reduction order. Fusion can change when values are rounded. Accumulation precision can differ.&lt;/p&gt;

&lt;p&gt;Consider a simple example:&lt;/p&gt;

\[f(Q, K, V) = g(Q, K, V) = a + b + c + d\]

&lt;p&gt;But:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Implementation A sums in order $1, 2, 3, 4$: $(((a + b) + c) + d)$&lt;/li&gt;
  &lt;li&gt;Implementation B sums in order $3, 1, 4, 2$: $(((c + a) + d) + b)$&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same mathematical function, slightly different floating-point bits.&lt;/p&gt;

&lt;p&gt;PyTorch’s reproducibility docs note this directly: SDPA backends can produce different results because they accumulate floating-point values in different orders.&lt;sup id=&quot;fnref:13&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:13&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; So the same mathematical function is not necessarily the same floating-point bits. Validation should use numerical tolerances, and those tolerances depend on:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;FP32 vs. BF16/FP16/FP8&lt;/li&gt;
  &lt;li&gt;accumulation precision&lt;/li&gt;
  &lt;li&gt;sequence length and score distribution&lt;/li&gt;
  &lt;li&gt;hardware and backend&lt;/li&gt;
  &lt;li&gt;dropout and randomness&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Bitwise equality shouldn’t be used as the definition of exactness.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Determinism is a separate question from exactness. Libraries expose backend- and version-specific controls for it, and those controls can carry performance or memory costs.&lt;sup id=&quot;fnref:8:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:13:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:13&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Exactness vs Bitwise identity&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;strong&gt;Exact algorithm&lt;/strong&gt; means no deliberate mathematical approximation of dense attention. &lt;strong&gt;Bitwise identical implementation&lt;/strong&gt; means every finite-precision operation lands on exactly the same bits. FlashAttention promises the first idea, not the second across arbitrary kernels.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;3-flashattention-vs-other-techniques&quot;&gt;&lt;strong&gt;3. FlashAttention vs. Other Techniques&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;31-flashattention-vs-pagedattention&quot;&gt;3.1 FlashAttention vs. PagedAttention&lt;/h3&gt;

&lt;p&gt;The names sound related. They’re not. FlashAttention and PagedAttention answer two completely different questions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;FlashAttention:&lt;/strong&gt; how do I compute $QK^T$, softmax, and $PV$ efficiently?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PagedAttention:&lt;/strong&gt; how do I store and retrieve the growing KV cache for many serving requests?&lt;/li&gt;
&lt;/ul&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;FlashAttention&lt;/th&gt;
      &lt;th&gt;PagedAttention&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Main problem&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Efficient attention computation&lt;/td&gt;
      &lt;td&gt;KV-cache memory management&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Main setting&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Attention execution&lt;/td&gt;
      &lt;td&gt;LLM serving&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Mechanism&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Tiling, online softmax, fused/hardware-aware kernels&lt;/td&gt;
      &lt;td&gt;Paging KV cache&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Main state&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Attention tiles and row-wise normalization/output state&lt;/td&gt;
      &lt;td&gt;Persistent per-request KV cache&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Changes dense attention formula?&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;No for dense FlashAttention&lt;/td&gt;
      &lt;td&gt;No, primarily changes cache memory management&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Specific task to tackle&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;$N^2$ score/probability intermediates&lt;/td&gt;
      &lt;td&gt;fragmented per-request KV-cache allocation&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;PagedAttention, introduced with vLLM, reduces KV-cache waste from fragmentation and duplication in high-throughput serving.&lt;sup id=&quot;fnref:14&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:14&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;11&lt;/a&gt;&lt;/sup&gt; FlashAttention doesn’t manage KV cache allocation. It handles how the attention operation consumes $Q/K/V$. How systems access the persistent KV state that lives across requests is a separate concern handled by PagedAttention. &lt;strong&gt;That separation is why they can coexist in the same serving stack without stepping on each other.&lt;/strong&gt; Dao-AILab even ships a KV-cache-oriented FlashAttention interface with optional block tables. Kernel execution and cache paging are two modular concerns that play nicely together.&lt;sup id=&quot;fnref:8:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;In practice, the stack looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Paged KV cache → FlashAttention-style kernel → Attention output
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;When to use&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;ul&gt;
  &lt;li&gt;If the problem statement contains “$N^2$ score/probability intermediates,” think &lt;strong&gt;FlashAttention.&lt;/strong&gt;&lt;br /&gt;&lt;/li&gt;
  &lt;li&gt;If it contains “fragmented per-request KV-cache allocation,” think &lt;strong&gt;PagedAttention.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;32-flashattention-vs-sparse-and-linear-attention&quot;&gt;3.2 FlashAttention vs. Sparse and Linear Attention&lt;/h3&gt;

&lt;p&gt;Three very different strategies, grouped together because they all make attention cheaper in some regime. They are not the same kind of change.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Dense FlashAttention&lt;/th&gt;
      &lt;th&gt;Sparse Attention&lt;/th&gt;
      &lt;th&gt;Linear Attention&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;What it does&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Same dense attention, different execution&lt;/td&gt;
      &lt;td&gt;Fewer interactions&lt;/td&gt;
      &lt;td&gt;Different mathematical formulation&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Attention pattern&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Dense (all pairs)&lt;/td&gt;
      &lt;td&gt;Sparse or local&lt;/td&gt;
      &lt;td&gt;Rearranged via feature maps or associativity&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Scaling&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Still $O(N^2 d)$&lt;/td&gt;
      &lt;td&gt;Can fall below full $N^2$ work&lt;/td&gt;
      &lt;td&gt;Can fall below full $N^2$ work&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Dense FlashAttention&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;computes $\mathrm{softmax}(QK^T)V$ for all required Q-K pairs.&lt;/li&gt;
  &lt;li&gt;changes the execution of dense attention, not the mathematical function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sparse / local attention&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;only computes a selected subset of $Q$-$K$ interactions.&lt;/li&gt;
  &lt;li&gt;$i \rightarrow j$ only if $\lvert i-j \rvert \leq w$ where $w$ is the window size.&lt;/li&gt;
  &lt;li&gt;fewer $Q$-$K$ interactions, and the model’s attention pattern changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Linear attention&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;changes the mathematical formula itself.&lt;/li&gt;
  &lt;li&gt;usually rearranges the attention computation with feature maps or associativity, avoiding an explicit dense softmax score matrix.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The phrase “memory-efficient attention” is too broad to identify a method by itself. Ask two questions instead:&lt;sup id=&quot;fnref:15:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Does it compute the same dense softmax attention?&lt;/li&gt;
  &lt;li&gt;Does it reduce arithmetic pairs, or mostly memory traffic/intermediate storage?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first question splits dense FlashAttention from sparse and linear approaches. The second separates FlashAttention from methods that change the number of interactions that get computed.&lt;/p&gt;

&lt;p&gt;The original FlashAttention paper makes this line explicit. It contrasts its dense exact algorithm with approximate methods on one side, and explores block-sparse FlashAttention as an approximate/sparse extension on the other.&lt;sup id=&quot;fnref:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;FlashAttention reduces memory requirements and IO, but dense attention remains quadratic.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;In summary:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;FlashAttention&lt;/strong&gt;: same dense attention, different execution.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sparse attention&lt;/strong&gt;: fewer interactions, different attention pattern.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Linear attention&lt;/strong&gt;: different mathematical formulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;FlashAttention doesn&apos;t make attention linear&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;strong&gt;“FlashAttention makes attention linear” is wrong.&lt;/strong&gt; If a system shows near-linear scaling because it uses a local window or another sparse pattern, the sparsity is what changed the number of interactions. FlashAttention may still be the kernel underneath.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;4-training-vs-inference&quot;&gt;&lt;strong&gt;4. Training vs. Inference&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;41-training-prefill-and-decode-are-different-regimes&quot;&gt;4.1 Training, Prefill, and Decode Are Different Regimes&lt;/h3&gt;

&lt;p&gt;FlashAttention shines when both query and key sequence dimensions are large. Plenty of query rows means avoiding the materialized attention matrix actually pays off, and tiled GEMMs have real work to do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Training.&lt;/strong&gt; Suppose $N_q = N_k = N$. There are many query and key rows. FlashAttention can exploit large GEMMs, tiling, and massive parallelism, and it avoids $N^2$ intermediates. This is a very favorable regime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefill.&lt;/strong&gt; Suppose the user sends a prompt of $8000$ tokens. The model processes those tokens together. Again $N_q \approx N_k$, so dense attention is substantial. FlashAttention can be very useful here. Prefill is typically compute-bound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decode.&lt;/strong&gt; Now suppose the model has already generated $8000$ tokens and wants to generate token $8001$. The new query might have $N_q = 1$ while $N_k = 8000$. So the computation is now:&lt;/p&gt;

\[1 \times 8000 \text{, not } 8000 \times 8000\]

&lt;p&gt;The bottleneck changes. Decode is heavily interacting with the existing KV cache. The system’s behavior is now driven by how fast it can read that cache, not by how much arithmetic it can do. Decode is typically memory-bound. Serving and batching behavior matter here too.&lt;sup id=&quot;fnref:15:6&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; The official repository exposes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flash_attn_with_kvcache&lt;/code&gt;, which updates and attends to the cache in one kernel and supports MQA/GQA and optional RoPE handling.&lt;sup id=&quot;fnref:8:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The key distinction in one line:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Prefill:&lt;/strong&gt; many query rows → large attention computation.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Decode:&lt;/strong&gt; one query row → large KV-cache memory access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FlashAttention also cannot eliminate other costs. MLP layers, communication in tensor parallelism, KV-cache capacity, model-weight bandwidth, sampling, and scheduler overhead remain separate concerns.&lt;sup id=&quot;fnref:15:7&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;So when someone says “FlashAttention makes LLM inference faster,” ask:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Prefill or decode?&lt;/li&gt;
  &lt;li&gt;Compute-bound or memory-bound?&lt;/li&gt;
  &lt;li&gt;Sequence length?&lt;/li&gt;
  &lt;li&gt;Batch size?&lt;/li&gt;
  &lt;li&gt;KV cache?&lt;/li&gt;
  &lt;li&gt;GPU?&lt;/li&gt;
  &lt;li&gt;Which kernel?&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Phase and bottleneck matter&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;This does not mean FlashAttention is irrelevant to inference. Prefill is an attention-heavy dense regime, and decode can still use specialized kernels from the same implementation family. It means &lt;em&gt;“FlashAttention speeds up LLM inference”&lt;/em&gt; is incomplete unless you say which &lt;strong&gt;phase&lt;/strong&gt; and &lt;strong&gt;bottleneck&lt;/strong&gt;.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;5-practical-engineering-for-flashattention&quot;&gt;&lt;strong&gt;5. Practical Engineering for FlashAttention&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;51-common-implementation-mistakes&quot;&gt;5.1 Common Implementation Mistakes&lt;/h3&gt;

&lt;p&gt;A running list of things that bite people in practice:&lt;sup id=&quot;fnref:15:8&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Assuming the flash backend always ran.&lt;/strong&gt; High-level frameworks may fall back when dtype, device, shape, or mask is unsupported. Use backend controls and profiling when it matters.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Leaving dropout on during evaluation.&lt;/strong&gt; PyTorch SDPA applies &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dropout_p&lt;/code&gt; exactly as supplied, so pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.0&lt;/code&gt; when evaluation requires no dropout.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Using the wrong mask convention.&lt;/strong&gt; Boolean mask semantics differ across APIs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Expecting bitwise agreement.&lt;/strong&gt; Fused kernels can reorder floating-point operations.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ignoring GQA head constraints.&lt;/strong&gt; Current interfaces require compatible/divisible query and KV head counts.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Treating current head-dimension/dtype support as timeless.&lt;/strong&gt; These constraints change across releases and backends.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Benchmarking without synchronization or warmup.&lt;/strong&gt; Asynchronous GPU execution can make naive wall-clock measurements meaningless.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Calling a sparse or local pattern “dense FlashAttention.”&lt;/strong&gt; Kernel and attention pattern are separate choices.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Confusing prefill with decode.&lt;/strong&gt; The shapes and bottlenecks are different.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Assuming the newest generation is always deployable.&lt;/strong&gt; Current FA4 package metadata is alpha, and hardware/software compatibility must be checked.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;52-common-misconceptions&quot;&gt;5.2 Common Misconceptions&lt;/h3&gt;

&lt;p&gt;A list of things people say that aren’t quite right about FlashAttention, and what’s actually true.&lt;sup id=&quot;fnref:15:9&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;“FlashAttention approximates softmax.”&lt;/strong&gt; &lt;br /&gt;No. Dense FlashAttention evaluates the dense softmax-attention function using tiled online normalization. Nothing is thrown away, nothing is approximated.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;“FlashAttention makes attention $O(N)$.”&lt;/strong&gt; &lt;br /&gt;No. Dense arithmetic remains $O(N^2 d)$. What becomes linear is the large auxiliary attention-memory footprint with respect to sequence length. HBM traffic is reduced, but the underlying number of query-key interactions is unchanged.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;“It is just kernel fusion.”&lt;/strong&gt; &lt;br /&gt;Fusion is part of the implementation story. But online-softmax tiling and IO-aware scheduling are algorithmic, not merely concatenating existing kernels. Fusion alone wouldn’t produce the same memory profile or the same IO complexity bound.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;“It is the same as PagedAttention.”&lt;/strong&gt; &lt;br /&gt;No. PagedAttention manages KV-cache allocation for serving. FlashAttention manages how the attention operation itself is executed. Different problems, different solutions.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;“Exact means bitwise identical.”&lt;/strong&gt; &lt;br /&gt;No. Floating-point operation order can change rounding. Two exact implementations of the same function can produce slightly different bits, and that’s expected.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;“Long context becomes free.”&lt;/strong&gt; &lt;br /&gt;No. Dense pairwise arithmetic is still quadratic, and KV-cache and other model costs remain. What FlashAttention gives us is a smaller memory footprint and less HBM traffic, not a free lunch.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;“FA1, FA2, FA3, and FA4 are different attention architectures.”&lt;/strong&gt; &lt;br /&gt;No. They are successive generations of efficient kernels and algorithms, shaped by different hardware bottlenecks. The mathematical target is fundamentally the same for dense attention.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;6-conclusion&quot;&gt;&lt;strong&gt;6. Conclusion&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;61-the-mental-model-to-have&quot;&gt;6.1 The Mental Model to Have&lt;/h3&gt;

&lt;p&gt;Think of FlashAttention as a &lt;strong&gt;streaming matrix computation with exact streaming softmax&lt;/strong&gt;. Picture the full attention matrix, $Q$ on the $y$-axis and $K$ on the $x$-axis. It’s a big grid.&lt;/p&gt;

&lt;!-- $$
\begin{array}{|c|c|c|c|}
\hline
 &amp; &amp; &amp; \\ \hline
 &amp; &amp; &amp; \\ \hline
 &amp; &amp; &amp; \\ \hline
 &amp; &amp; &amp; \\ \hline
\end{array}
$$ --&gt;

\[Q \quad
\begin{array}{|c|c|c|c|}
\hline
 &amp;amp; &amp;amp; &amp;amp; \\ \hline
 &amp;amp; &amp;amp; &amp;amp; \\ \hline
 &amp;amp; &amp;amp; &amp;amp; \\ \hline
 &amp;amp; &amp;amp; &amp;amp; \\ \hline
\end{array} \\\]

\[\qquad K\]

&lt;!-- &lt;table style=&quot;margin: 1rem auto; border-collapse: collapse; font-family: inherit;&quot;&gt;
  &lt;tr&gt;
    &lt;td style=&quot;width: 34px; border: none;&quot;&gt;&lt;/td&gt;
    &lt;td colspan=&quot;4&quot; style=&quot;text-align: center; border: none;&quot;&gt;K&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td rowspan=&quot;4&quot; style=&quot;width: 34px; border: none; vertical-align: middle; text-align: center;&quot;&gt;Q&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;width: 22px; height: 22px; border: 1px solid #888;&quot;&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt; --&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;u&gt;Naive attention:&lt;/u&gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Calculate the entire matrix grid and store it.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;u&gt;Flash attention:&lt;/u&gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Bring one small region of the matrix grid close to the compute units. Calculate that region, normalize it using running statistics, immediately use the result to accumulate the output, then discard the region. Then move on to the next region.&lt;/li&gt;
      &lt;li&gt;The mathematical interactions remain. The physical representation of the intermediate computation does not. That’s the central insight.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The evolution from FA1 to FA4, in one line each:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;FA1&lt;/strong&gt; → “Reduce expensive HBM movement.”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FA2&lt;/strong&gt; → “Now use the GPU more effectively via better parallelism.”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FA3&lt;/strong&gt; → “Now overlap communication and memory movement on Hopper.”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FA4&lt;/strong&gt; → “Now redesign the pipeline for Blackwell’s new hardware balance.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This evolution is why FlashAttention is much more interesting than just &lt;em&gt;“a faster attention kernel.”&lt;/em&gt; It’s a case study in &lt;strong&gt;algorithm-hardware co-design&lt;/strong&gt;. The mathematical function can stay identical while the optimal execution strategy changes dramatically as the memory hierarchy, compute throughput, and specialized hardware change.&lt;/p&gt;

&lt;p&gt;The final practical mental model:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;keep score tiles near compute,&lt;/li&gt;
  &lt;li&gt;carry only the row statistics needed to merge softmax blocks,&lt;/li&gt;
  &lt;li&gt;immediately consume probabilities for each tile into the $V$ accumulation, and&lt;/li&gt;
  &lt;li&gt;avoid sending the full $N^2$ attention matrix through HBM.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;62-flashattention-summary&quot;&gt;6.2 FlashAttention Summary&lt;/h3&gt;

&lt;p&gt;Let’s walk through the whole FlashAttention story in one derivation, and hit the key points to remember.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ordinary attention:&lt;/strong&gt; $QK^T \in \mathbb{R}^{N \times N}$. Materializing it creates an enormous intermediate matrix.&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

\[O = \mathrm{softmax}\left(\frac{QK^T}{\sqrt{d}}\right) V.\]

&lt;p&gt;&lt;strong&gt;Naive execution:&lt;/strong&gt; This creates huge memory traffic.&lt;/p&gt;

&lt;!-- ```
QK^T → store N x N scores → softmax → store N x N probabilities → multiply by V
``` --&gt;

\[QK^T \to \text{store } N \times N \text{ scores} \to \text{softmax} \to \text{store } N \times N \text{ probabilities} \to \text{multiply by } V\]

&lt;p&gt;&lt;strong&gt;FlashAttention:&lt;/strong&gt; Tiling is used. Split $Q \to Q_i$ and $K, V \to K_j, V_j$, then calculate the score tile:&lt;/p&gt;

\[S_{ij} = \frac{Q_i K_j^T}{\sqrt{d}}.\]

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;u&gt;The problem with tiling&lt;/u&gt;: Softmax needs the maximum and the denominator over the &lt;strong&gt;&lt;em&gt;whole row.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;u&gt;The solution&lt;/u&gt;: online softmax&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;Maintain $(m, \ell, a)$ where:
        &lt;ul&gt;
          &lt;li&gt;$m$ = running maximum&lt;/li&gt;
          &lt;li&gt;$\ell$ = running sum of exponentials (the softmax denominator)&lt;/li&gt;
          &lt;li&gt;$a$ = running unnormalized value-weighted sum (accumulator)&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;When a new block has a larger maximum $m’$, calculate a rescaling factor $\alpha$ and rescale the old state ($\ell$ and $a$) by it:&lt;/li&gt;
    &lt;/ol&gt;

\[\alpha = e^{m - m&apos;}\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Therefore, we can process the full FlashAttention pipeline without ever materializing the $N \times N$ matrix:&lt;/p&gt;

    &lt;!-- ```
tile → score → online softmax → V accumulation → discard tile
``` --&gt;

\[\text{tile} \to \text{score} \to \text{online softmax} \to V \text{ accumulation} \to \text{discard tile}.\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;u&gt;Result&lt;/u&gt;: The same exact dense attention mathematics, but much better memory behavior: &lt;strong&gt;&lt;em&gt;less HBM traffic, much smaller intermediates.&lt;/em&gt;&lt;/strong&gt; Arithmetic remains $O(N^2 d)$.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;FlashAttention in one table:&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Property&lt;/th&gt;
      &lt;th&gt;Complexity / Formulation&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Memory complexity&lt;/td&gt;
      &lt;td&gt;$O(Nd)$ auxiliary attention storage&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Compute complexity&lt;/td&gt;
      &lt;td&gt;$O(N^2 d)$ dense attention compute&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Attention formulation&lt;/td&gt;
      &lt;td&gt;Exact dense softmax attention&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Key takeaway&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;strong&gt;Same exact dense attention mathematics, but much better memory behavior.&lt;/strong&gt; Less HBM traffic, much smaller intermediates, arithmetic unchanged at $O(N^2 d)$.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;appendix-1&quot;&gt;&lt;strong&gt;Appendix&lt;/strong&gt;&lt;/h2&gt;

&lt;!-- | Source § | Hierarchical |
|---|---|
| 21-30 | 1.1-1.10 |
| 31-32 | 2.1-2.2 |
| 33-34 | 3.1-3.2 |
| 35 | 4.1 |
| 36-37 | 5.1-5.2 | --&gt;

&lt;h3 id=&quot;reference-map&quot;&gt;Reference Map&lt;/h3&gt;

&lt;p&gt;The table below shows what paper to refer to for each question.&lt;sup id=&quot;fnref:15:10&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Question&lt;/th&gt;
      &lt;th&gt;Best starting source&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;What is the original IO-aware algorithm?&lt;/td&gt;
      &lt;td&gt;FlashAttention-1 paper &lt;sup id=&quot;fnref:4:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Why does online normalization work?&lt;/td&gt;
      &lt;td&gt;Milakov &amp;amp; Gimelshein &lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;14&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Was exact subquadratic-memory attention known earlier?&lt;/td&gt;
      &lt;td&gt;Rabe &amp;amp; Staats &lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;15&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;What specifically changed in FA2?&lt;/td&gt;
      &lt;td&gt;FlashAttention-2 paper &lt;sup id=&quot;fnref:5:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;What is Hopper-specific in FA3?&lt;/td&gt;
      &lt;td&gt;FlashAttention-3 paper &lt;sup id=&quot;fnref:6:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;What changes on Blackwell in FA4?&lt;/td&gt;
      &lt;td&gt;FlashAttention-4 paper &lt;sup id=&quot;fnref:7:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;What does the current official package support?&lt;/td&gt;
      &lt;td&gt;Dao-AILab repository &lt;sup id=&quot;fnref:8:6&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;How does current PyTorch select SDPA kernels?&lt;/td&gt;
      &lt;td&gt;PyTorch SDPA and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sdpa_kernel&lt;/code&gt; docs &lt;sup id=&quot;fnref:10:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:10&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:11:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:11&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Why can exact backends differ numerically?&lt;/td&gt;
      &lt;td&gt;PyTorch reproducibility notes &lt;sup id=&quot;fnref:13:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:13&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;How is PagedAttention different?&lt;/td&gt;
      &lt;td&gt;Kwon et al. &lt;sup id=&quot;fnref:14:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:14&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;A note on how to use this map&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Implementation support changes faster than the mathematics. For deployed systems, treat the installed framework’s documentation and the exact kernel repository/release as authoritative for supported devices, dtypes, head dimensions, masks, dropout, and GQA behavior.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;references&quot;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h3&gt;

&lt;h3 id=&quot;citation&quot;&gt;&lt;strong&gt;Citation&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;If you found this blog post helpful, please consider citing it:&lt;/p&gt;

&lt;div class=&quot;language-bibtex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;@article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;obasi2026FlashAttentionPt2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;FlashAttention: Part 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Obasi, Chizoba&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;chizkidd.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;year&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2026&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;month&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Sep&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://chizkidd.github.io/2026/09/17/flashattention-2/&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:5&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Tri Dao. &lt;a href=&quot;https://arxiv.org/abs/2307.08691&quot;&gt;FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning&lt;/a&gt;. ICLR 2024. &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:5:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;5&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:15&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://drive.google.com/file/d/1CLyK-9Cflcvi3fRl3qAHyzYvwJFjCyVg/view&quot;&gt;Understanding FlashAttention: How IO-Aware Attention Makes Transformers Faster Without Approximating Attention&lt;/a&gt;. Google Drive PDF. Accessed September 2026. &lt;a href=&quot;#fnref:15&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:15:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:15:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:15:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:15:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;5&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:15:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;6&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:15:6&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;7&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:15:7&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;8&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:15:8&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;9&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:15:9&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;10&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:15:10&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;11&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:6&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Jay Shah et al. &lt;a href=&quot;https://arxiv.org/abs/2407.08608&quot;&gt;FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision&lt;/a&gt;. NeurIPS 2024. &lt;a href=&quot;#fnref:6&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:6:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:6:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:6:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:6:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;5&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:8&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Dao-AILab. &lt;a href=&quot;https://github.com/Dao-AILab/flash-attention&quot;&gt;flash-attention official repository and current implementation documentation&lt;/a&gt;. GitHub. Accessed September 2026. &lt;a href=&quot;#fnref:8&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:8:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:8:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:8:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:8:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;5&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:8:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;6&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:8:6&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;7&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:7&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Ted Zadouri et al. &lt;a href=&quot;https://arxiv.org/abs/2603.05451&quot;&gt;FlashAttention-4: Algorithm and Kernel Pipelining Co-Design for Asymmetric Hardware Scaling&lt;/a&gt;. arXiv, 2026. &lt;a href=&quot;#fnref:7&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:7:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:7:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:7:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:7:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;5&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:9&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;PyPI. &lt;a href=&quot;https://pypi.org/project/flash-attn-4/&quot;&gt;flash-attn-4 package metadata and release history&lt;/a&gt;. PyPI. Accessed September 2026. &lt;a href=&quot;#fnref:9&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:10&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;PyTorch. &lt;a href=&quot;https://docs.pytorch.org/docs/stable/generated/torch.nn.functional.scaled_dot_product_attention.html&quot;&gt;torch.nn.functional.scaled_dot_product_attention documentation&lt;/a&gt;. PyTorch Documentation. Accessed September 2026. &lt;a href=&quot;#fnref:10&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:10:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:11&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;PyTorch. &lt;a href=&quot;https://docs.pytorch.org/docs/stable/generated/torch.nn.attention.sdpa_kernel.html&quot;&gt;torch.nn.attention.sdpa_kernel documentation&lt;/a&gt;. PyTorch Documentation. Accessed September 2026. &lt;a href=&quot;#fnref:11&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:11:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:11:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:12&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;PyTorch. &lt;a href=&quot;https://docs.pytorch.org/docs/stable/nn.attention.html&quot;&gt;torch.nn.attention module and FlashAttention implementation registration APIs&lt;/a&gt;. PyTorch Documentation. Accessed September 2026. &lt;a href=&quot;#fnref:12&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:13&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;PyTorch. &lt;a href=&quot;https://docs.pytorch.org/docs/stable/notes/randomness.html&quot;&gt;Reproducibility documentation, including scaled-dot-product attention backend differences&lt;/a&gt;. PyTorch Documentation. Accessed September 2026. &lt;a href=&quot;#fnref:13&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:13:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:13:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:14&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Woosuk Kwon et al. &lt;a href=&quot;https://arxiv.org/abs/2309.06180&quot;&gt;Efficient Memory Management for Large Language Model Serving with PagedAttention&lt;/a&gt;. SOSP 2023. &lt;a href=&quot;#fnref:14&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:14:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Tri Dao et al. &lt;a href=&quot;https://arxiv.org/abs/2205.14135&quot;&gt;FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness&lt;/a&gt;. NeurIPS 2022. &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:4:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Ashish Vaswani et al. &lt;a href=&quot;https://arxiv.org/abs/1706.03762&quot;&gt;Attention Is All You Need&lt;/a&gt;. NeurIPS 2017. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Maxim Milakov and Natalia Gimelshein. &lt;a href=&quot;https://arxiv.org/abs/1805.02867&quot;&gt;Online normalizer calculation for softmax&lt;/a&gt;. arXiv, 2018. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Markus N. Rabe and Charles Staats. &lt;a href=&quot;https://arxiv.org/abs/2112.05682&quot;&gt;Self-attention Does Not Need O(n²) Memory&lt;/a&gt;. arXiv, 2021. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 17 Sep 2026 00:00:00 +0000</pubDate>
        <link>https://chizkidd.github.io//2026/09/17/flashattention-2/</link>
        <guid isPermaLink="true">https://chizkidd.github.io//2026/09/17/flashattention-2/</guid>
        
        
      </item>
    
      <item>
        <title>FlashAttention: Part 1</title>
        <description>&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;This is Part 1 of a two-part FlashAttention series. Check out &lt;a href=&quot;https://chizkidd.github.io/2026/09/17/flashattention-2&quot;&gt;Part 2&lt;/a&gt; next. Part 2 covers the FlashAttention evolution (FA-1/2/3/4).&lt;/em&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;How IO-Aware Attention Makes &lt;a href=&quot;https://chizkidd.github.io/2026/04/17/transformers/&quot;&gt;Transformers&lt;/a&gt; Faster Without Approximating Attention&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The reference &lt;a href=&quot;https://drive.google.com/file/d/1CLyK-9Cflcvi3fRl3qAHyzYvwJFjCyVg/view&quot;&gt;handbook&lt;/a&gt; for this blog post was inspired by this &lt;a href=&quot;https://x.com/techNmak/status/2098057360908685358&quot;&gt;tweet&lt;/a&gt; and is titled: &lt;em&gt;Understanding FlashAttention: How IO-Aware Attention Makes Transformers Faster Without Approximating Attention.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The mechanism, in three ideas: &lt;strong&gt;Tiling + Online Softmax + Recomputation&lt;/strong&gt;. Everything in this handbook&lt;sup id=&quot;fnref:15&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; is elaboration on that summary. The handbook is a technical deep dive on exact tiled attention: &lt;em&gt;GPU memory traffic, online softmax, forward and backward passes, IO complexity, the evolution from FlashAttention-1 through FlashAttention-4, and current framework behavior.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this blog post, we cover the fundamental problem FlashAttention-1 (FA1) addresses, the mathematical tricks it uses, the GPU implementation of those tricks, and the architectural compatibility (&lt;a href=&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/#multi-head-attention&quot;&gt;MHA&lt;/a&gt;/&lt;a href=&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/#multi-query-attention-mqa&quot;&gt;MQA&lt;/a&gt;/&lt;a href=&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/#grouped-query-attention-gqa&quot;&gt;GQA&lt;/a&gt;) of FlashAttention.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;table-of-contents&quot;&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;#0-introduction&quot;&gt;0. Introduction&lt;/a&gt;
&lt;!-- - [0.1 Standard Naive Attention Implementation](#0-introduction) --&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#01-how-to-read-this-handbook&quot;&gt;0.1 How to Read This Handbook&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#02-notation&quot;&gt;0.2 Notation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;#1-the-fundamental-problem&quot;&gt;1. The Fundamental Problem&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#11-what-flashattention-actually-optimizes&quot;&gt;1.1 What FlashAttention actually optimizes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#12-the-attention-equation-is-not-the-implementation&quot;&gt;1.2 The attention equation is not the implementation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#13-gpu-memory-hierarchy-and-why-io-matters&quot;&gt;1.3 GPU memory hierarchy and why IO matters&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#14-why-materializing-the-score-and-probability-matrices-is-expensive&quot;&gt;1.4 Why materializing the score and probability matrices is expensive&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#15-dense-arithmetic-is-still-quadratic&quot;&gt;1.5 Dense arithmetic is still quadratic&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#16-memory-efficient-exact-attention-predates-flashattention&quot;&gt;1.6 Memory-efficient exact attention predates FlashAttention&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;#2-the-mathematical-trick&quot;&gt;2. The Mathematical Trick&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#21-tiling-queries-keys-and-values&quot;&gt;2.1 Tiling queries, keys, and values&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#22-softmax-is-the-difficult-part-of-streaming&quot;&gt;2.2 Softmax is the difficult part of streaming&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#23-online-softmax-from-first-principles&quot;&gt;2.3 Online softmax from first principles&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#24-the-blockwise-merge-recurrence&quot;&gt;2.4 The blockwise merge recurrence&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#25-a-complete-numerical-example&quot;&gt;2.5 A complete numerical example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;#3-putting-the-mathematics-onto-the-gpu&quot;&gt;3. Putting the Mathematics onto the GPU&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#31-flashattention-forward-pass&quot;&gt;3.1 FlashAttention forward pass&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#32-why-dense-flashattention-is-exact&quot;&gt;3.2 Why dense FlashAttention is exact&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#33-io-complexity-what-the-theorem-actually-says&quot;&gt;3.3 IO complexity: what the theorem actually says&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#34-memory-complexity-linear-auxiliary-state-not-linear-compute&quot;&gt;3.4 Memory complexity: linear auxiliary state, not linear compute&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#35-causal-masking-and-tile-skipping&quot;&gt;3.5 Causal masking and tile skipping&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#36-backward-pass-recompute-instead-of-save&quot;&gt;3.6 Backward pass: recompute instead of save&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#37-why-more-flops-can-still-be-faster&quot;&gt;3.7 Why more FLOPs can still be faster&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;#4-architectural-compatibility&quot;&gt;4. Architectural Compatibility&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#41-mha-mqa-and-gqa-compatibility-shared-kv-heads&quot;&gt;4.1 MHA, MQA, and GQA compatibility&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#42-variable-lengths-local-attention-and-dropout&quot;&gt;4.2 Variable lengths, local attention, and dropout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;#5-summary&quot;&gt;5. Summary&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;appendix&quot;&gt;&lt;strong&gt;Appendix&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#reference-map&quot;&gt;Reference Map&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#citation&quot;&gt;Citation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;0-introduction&quot;&gt;&lt;strong&gt;0. Introduction&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;01-how-to-read-this-handbook&quot;&gt;0.1 How to Read This Handbook&lt;/h3&gt;

&lt;p&gt;What stands out to me is the number of round trips to HBM in naive standard attention. Every intermediate value $(S$, $P$, $O)$ has to be written out and read back. That is the problem FlashAttention solves. The handbook frames the subject as easiest to understand when three different questions are kept separate:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;What mathematical function is being computed?&lt;/strong&gt; For dense attention, the target remains ordinary scaled dot-product attention.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;How much arithmetic does that function require?&lt;/strong&gt; Dense all-pairs query-key scoring remains quadratic in sequence length.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;How does the implementation move data through the GPU memory hierarchy?&lt;/strong&gt; This is where FlashAttention changes the execution of the algorithm dramatically.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The lesson I keep coming back to: FLOP count alone doesn’t decide wall-clock speed. An algorithm can do essentially the same math, or even recompute intermediates from scratch, and still finish faster because it shuttles far less data to and from high-bandwidth memory (HBM).&lt;/p&gt;

&lt;!-- &gt; **Core distinction.** Dense FlashAttention is an *exact* attention algorithm: it does not replace softmax attention with a low-rank, sparse, kernelized, or approximate formula. &quot;Exact&quot; refers to the mathematical attention computation. Floating-point kernels can still differ by small rounding effects because operations are reordered.[^4] $^,$ [^10] $^,$ [^13] --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Core distinction&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Dense FlashAttention is an &lt;em&gt;exact&lt;/em&gt; attention algorithm: it does not replace softmax attention with a low-rank, sparse, kernelized, or approximate formula. “Exact” refers to the mathematical attention computation. Floating-point kernels can still differ by small rounding effects because operations are reordered.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The word &lt;em&gt;exact&lt;/em&gt; is doing real work here. Exactness is a statement about the mathematical function, not about bitwise reproducibility. The kernel is free to reorder floating-point operations. It is not free to change the function being computed.&lt;sup id=&quot;fnref:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:10&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:10&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:13&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:13&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h3 id=&quot;02-notation&quot;&gt;0.2 Notation&lt;/h3&gt;

&lt;p&gt;For one attention head, let&lt;/p&gt;

\[Q \in \mathbb{R}^{N_q \times d}, \quad K \in \mathbb{R}^{N_k \times d}, \quad V \in \mathbb{R}^{N_k \times d_v}.\]

&lt;p&gt;For self-attention, typically $N_q = N_k = N$. The scaled score matrix is&lt;/p&gt;

\[S = \frac{QK^T}{\sqrt{d}} + B,\]

&lt;p&gt;where $B$ represents an optional additive mask or bias, and&lt;/p&gt;

\[P = \mathrm{softmax}_{\mathrm{row}}(S), \quad O = PV.\]

&lt;p&gt;Throughout, &lt;strong&gt;HBM&lt;/strong&gt; refers to large off-chip high-bandwidth GPU memory. &lt;strong&gt;On-chip memory&lt;/strong&gt; is a broad teaching term for much smaller, faster storage such as registers and shared memory/&lt;strong&gt;SRAM.&lt;/strong&gt; Exact hardware details vary by GPU generation.&lt;/p&gt;

&lt;p&gt;The contrast in one table:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;HBM&lt;/th&gt;
      &lt;th&gt;SRAM&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;slower&lt;/td&gt;
      &lt;td&gt;faster&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;large&lt;/td&gt;
      &lt;td&gt;smaller&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;off-chip&lt;/td&gt;
      &lt;td&gt;on-chip&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;!-- &gt;**System problem:** Where do all those intermediate values $(S, P, O)$ live while the GPU computes them? --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;System Problem&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Where do all those intermediate values $(S, P, O)$ live while the GPU computes them?&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;That question, not the arithmetic, is what FlashAttention was built to answer.&lt;/p&gt;

&lt;h2 id=&quot;1-the-fundamental-problem&quot;&gt;&lt;strong&gt;1. The Fundamental Problem&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;11-what-flashattention-actually-optimizes&quot;&gt;1.1 What FlashAttention Actually Optimizes&lt;/h3&gt;

&lt;p&gt;Start with the ordinary attention function:&lt;/p&gt;

\[O = \mathrm{softmax}\left(\frac{QK^T}{\sqrt{d}} + B\right) V.\]

&lt;p&gt;A textbook implementation boils this down to three big steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;form the score matrix $S$,&lt;/li&gt;
  &lt;li&gt;apply row-wise softmax to get $P$, then&lt;/li&gt;
  &lt;li&gt;multiply by $V$.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mathematically, that’s perfectly fine. &lt;strong&gt;On a GPU, though, writing a giant intermediate to HBM and reading it back is often far more expensive than the equation lets on.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;FlashAttention’s real contribution is an &lt;strong&gt;IO-aware implementation&lt;/strong&gt;. It splits the computation into tiles that fit in fast on-chip memory, streams blocks of $K$ and $V$ past them, and keeps just enough row-wise softmax state around to produce the exact output. No full $N \times N$ attention matrix ever needs to land in HBM.&lt;sup id=&quot;fnref:4:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;My working definition of &lt;strong&gt;IO-awareness&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;minimize data movement between the different levels of GPU memory (HBM and SRAM), rather than just trying to reduce the number of mathematical operations (FLOPs).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The reasoning is that the speed bottleneck in modern AI hardware is often not how fast the GPU can compute math, but how fast it can &lt;strong&gt;read and write data&lt;/strong&gt;. This is the memory-compute tradeoff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem, in one line:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;compute → HBM write → HBM read → compute
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The arithmetic often isn’t the bottleneck. The round trips are. &lt;strong&gt;Tiling&lt;/strong&gt; is the fix. Instead of one big $QK^T$, process it in blocks:&lt;/p&gt;

\[Q K^T \rightarrow Q_i K_j^T \qquad (\text{for small blocks } Q_i, K_j)\]

&lt;p&gt;Each score block gets:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;computed,&lt;/li&gt;
  &lt;li&gt;softmax-processed,&lt;/li&gt;
  &lt;li&gt;immediately multiplied by its corresponding $V_j$, and&lt;/li&gt;
  &lt;li&gt;discarded.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For $N = 8192$, instead of an $8192 \times 8192$ intermediate matrix, you process blocks like $128 \times 128$. All the $QK$ interactions still happen, just in manageable pieces.&lt;/p&gt;

&lt;!-- &gt; **What changes:** the execution schedule, memory traffic, and stored intermediates.
&gt;
&gt; **What does not change:** the dense scaled-dot-product attention function being evaluated. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;strong&gt;What changes:&lt;/strong&gt; the execution schedule, memory traffic, and stored intermediates.&lt;br /&gt;
&lt;strong&gt;What does not change:&lt;/strong&gt; the dense scaled-dot-product attention function being evaluated.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This framing matters. Calling FlashAttention “a faster kind of attention” misses the point. It’s better understood as an algorithm and kernel family for &lt;strong&gt;evaluating attention efficiently on accelerators.&lt;/strong&gt; A model can use causal masking, RoPE, &lt;a href=&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/#multi-query-attention-mqa&quot;&gt;MQA&lt;/a&gt;/&lt;a href=&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/#grouped-query-attention-gqa&quot;&gt;GQA&lt;/a&gt;, or any other attention feature and still run a FlashAttention kernel underneath it.&lt;/p&gt;

&lt;p&gt;The original paper draws a line between this and approximate attention methods, which cut arithmetic by changing the math itself. Dense FlashAttention doesn’t do that. It keeps the math intact. The block-sparse extension from the same paper is a different story, since skipping blocks literally changes which query-key interactions get computed.&lt;sup id=&quot;fnref:4:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h3 id=&quot;12-the-attention-equation-is-not-the-implementation&quot;&gt;1.2 The Attention Equation Is Not the Implementation&lt;/h3&gt;

&lt;p&gt;The equation does not tell you where tensors live. Let’s look at a &lt;strong&gt;standard naive attention implementation&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Calculate $S$, store $S$.&lt;/li&gt;
  &lt;li&gt;Read $S$, calculate $P$, store $P$.&lt;/li&gt;
  &lt;li&gt;Read $P$, calculate $O$.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The problem is the number of HBM round trips. A simple materializing implementation conceptually does:&lt;/p&gt;

\[S \leftarrow QK^T / \sqrt{d}, \quad P \leftarrow \mathrm{softmax}(S), \quad O \leftarrow PV.\]

&lt;p&gt;Prior to FlashAttention, here is what the standard attention implementation looks like in more detail in terms of memory communication. Load $Q, K, V \in \mathbb{R}^{N \times d}$ from HBM, then:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Read $Q, K$ from HBM, compute $S$, write $S$ to HBM.&lt;/li&gt;
  &lt;li&gt;Read $S$ from HBM, compute $P$, write $P$ to HBM.&lt;/li&gt;
  &lt;li&gt;Read $P, V$ by blocks from HBM, compute $O$, write $O$ to HBM.&lt;/li&gt;
  &lt;li&gt;Return $O$.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The GPU spends significant time moving an $N^2$ object through memory. &lt;strong&gt;FlashAttention avoids repeatedly moving huge intermediates:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Calculate a small $S$ tile, apply softmax to the tile, use the tile with $V_j$, and discard the tile.&lt;/li&gt;
  &lt;li&gt;Calculate the next tile, and repeat step 1 until all tiles are completed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;strong&gt;key questions I ask when looking at the attention equation:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;How many operations are required?&lt;br /&gt;&lt;/li&gt;
  &lt;li&gt;What data must move between memory levels to perform those operations?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;FlashAttention’s move is simple: &lt;em&gt;keep the work close&lt;/em&gt;. Blocks of $Q$, $K$, and $V$ sit near the compute units, score tiles get built and consumed on the spot, and only the running row statistics plus the output survive across tiles. Nothing else needs to stick around.&lt;/p&gt;

&lt;!-- &gt; **Algorithmic lesson.** A computational graph is not a memory schedule. Writing $P = \mathrm{softmax}(QK^T)$ on paper does not require an implementation to store all of $QK^T$ or $P$ in off-chip memory at once. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Algorithmic lesson&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;A computational graph is not a memory schedule. Writing $P = \mathrm{softmax}(QK^T)$ on paper does not require an implementation to store all of $QK^T$ or $P$ in off-chip memory at once.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The pattern isn’t unique to attention. &lt;strong&gt;Fused kernels, tiling, recomputation, and operator scheduling&lt;/strong&gt; all do the same thing: spend a little extra arithmetic to avoid dragging huge intermediates through memory. On modern hardware, that’s usually a winning bet. Matmul throughput has raced ahead of the rest of the memory hierarchy, so the math is cheap and the data movement is what hurts. FlashAttention-2 (FA2), FlashAttention-3 (FA3), and FlashAttention-4 (FA4) take this further, and they’re upfront about it: the algorithm bends to the hardware, not the other way around.&lt;sup id=&quot;fnref:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:6&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:7&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h3 id=&quot;13-gpu-memory-hierarchy-and-why-io-matters&quot;&gt;1.3 GPU Memory Hierarchy and Why IO Matters&lt;/h3&gt;

&lt;p&gt;GPUs expose a hierarchy rather than one uniform pool of equally fast memory. The names and capacities vary by architecture, but the mental model is:&lt;/p&gt;

&lt;div class=&quot;mermaid&quot; style=&quot;margin: 2rem 0;&quot;&gt;
flowchart BT
    HBM[HBM/device memory]
    SRAM[on-chip shared memory/SRAM]
    Registers[Registers/very local state]
    
    HBM --&amp;gt;|load tiles| SRAM
    SRAM --&amp;gt;|feed compute| Registers
    
    classDef memoryDevice stroke:#818cf8,fill:#eef2ff
    classDef memorySRAM stroke:#2dd4bf,fill:#f0fdfa
    classDef memoryRegisters stroke:#a78bfa,fill:#f5f3ff
    
    class HBM memoryDevice
    class SRAM memorySRAM
    class Registers memoryRegisters
&lt;/div&gt;

&lt;p&gt;&lt;small class=&quot;text-muted d-block text-center&quot;&gt;&lt;strong&gt;Figure 1:&lt;/strong&gt; GPU Memory Hierarchy.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;FA1 models the HBM/SRAM asymmetry and explicitly optimizes the number of transfers between them.&lt;sup id=&quot;fnref:4:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; My breakdown of each GPU memory level in the figure above:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. HBM:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Relatively large&lt;/li&gt;
  &lt;li&gt;Relatively slower to access&lt;/li&gt;
  &lt;li&gt;Physically farther from the compute units, so data must travel further to reach them&lt;/li&gt;
  &lt;li&gt;Stores model weights, $Q/K/V$, activations, and other large tensors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. SRAM / shared memory:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Much smaller&lt;/li&gt;
  &lt;li&gt;Faster&lt;/li&gt;
  &lt;li&gt;Cheaper reuse cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Registers:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Even smaller&lt;/li&gt;
  &lt;li&gt;More local&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why tiling helps:&lt;/strong&gt; Suppose $Q_i$ (a query tile/block) needs to interact with many $K/V$ tiles/blocks prior to writing its output state. Instead of constantly moving $Q_i$ back and forth, we can keep it close to the compute units while processing:&lt;/p&gt;

\[K_1, V_1 \to K_2, V_2 \to K_3, V_3 \to \cdots\]

&lt;p&gt;Therefore, one loaded $Q_i$ can participate in lots of computation. This is called &lt;strong&gt;reuse&lt;/strong&gt;. Tiling doesn’t only make tensors smaller, it also &lt;strong&gt;increases reuse&lt;/strong&gt; while a tile is resident on chip. Conversely, $K/V$ blocks can be streamed through query blocks according to the chosen schedule.&lt;/p&gt;

&lt;!-- &gt; A kernel becomes **IO-aware when the placement and movement of data are part of the algorithm,** rather than an afterthought left to a sequence of separately launched tensor operations. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;IO-Awareness&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;A kernel becomes &lt;strong&gt;IO-aware when the placement and movement of data are part of the algorithm,&lt;/strong&gt; rather than an afterthought left to a sequence of separately launched tensor operations.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This does not mean attention is “always memory-bound.” FA3 and FA4 exist partly because, as hardware changed, the dominant bottlenecks changed too.&lt;sup id=&quot;fnref:6:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:7:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; The bottleneck depends on:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Sequence length, $N$&lt;/li&gt;
  &lt;li&gt;Head dimension, $d$&lt;/li&gt;
  &lt;li&gt;dtype&lt;/li&gt;
  &lt;li&gt;Mask pattern&lt;/li&gt;
  &lt;li&gt;GPU generation (Ampere/Hopper/Blackwell)&lt;/li&gt;
  &lt;li&gt;Forward vs. backward pass&lt;/li&gt;
  &lt;li&gt;Which kernel is running&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;14-why-materializing-the-score-and-probability-matrices-is-expensive&quot;&gt;1.4 Why Materializing the Score and Probability Matrices Is Expensive&lt;/h3&gt;

&lt;p&gt;The quadratic intermediate becomes concrete very quickly. Suppose a batch contains one sequence, with 32 attention heads, sequence length $N = 8192$, and a two-byte dtype such as FP16 or BF16. Concretely, my arithmetic:&lt;/p&gt;

\[\begin{aligned}
\text{batch} &amp;amp;= 1 \\
\text{heads} &amp;amp;= 32 \\
N &amp;amp;= 8192 \\
\text{dtype } &amp;amp;= \text{FP16/BF16} \\
\text{tensor shape } &amp;amp;= [1, 32, 8192, 8192] \\
2 &amp;amp; \text{ bytes/element} \\
\end{aligned}\]

&lt;p&gt;At two bytes per element, the total memory is:
\(32 \times 8192^2 \times 2 = 4{,}294{,}967{,}296 \text{ bytes} = 4 \text{ GiB}.\)&lt;/p&gt;

&lt;p&gt;One dense tensor with shape $[1, 32, 8192, 8192]$ contains $32 \times 8192^2$ elements. At two bytes per element, that is $4 \text{ GiB}$. That is the size of one full score- or probability-like tensor for this example. &lt;em&gt;Imagine reading and writing this much data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Naive pipeline:&lt;/strong&gt;&lt;/p&gt;

&lt;!-- 
$$
QK^T \to \text{huge } S \to \text{softmax} \to \text{huge } P \to PV
$$ --&gt;

&lt;p&gt;A naive decomposition can produce arrays of this size at several stages. That’s not to say every framework keeps $S$ and $P$ alive at once, or that compiler fusion hasn’t already cut some of the traffic. The example is there to make the problem concrete: a dense $N^2$ intermediate is big enough that rewriting and rereading it can dominate memory and bandwidth.&lt;/p&gt;

&lt;div class=&quot;mermaid&quot; style=&quot;margin: 2rem 0;&quot;&gt;
flowchart LR
    QKT[&quot;QKᵀ&quot;] --&amp;gt; S[&quot;huge S&quot;]
    S --&amp;gt; Softmax[&quot;softmax&quot;]
    Softmax --&amp;gt; P[&quot;huge P&quot;]
    P --&amp;gt; PV[&quot;PV&quot;]

    classDef boxed fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,font-size:22px
    classDef emphasized fill:none,stroke:none,font-size:26px,font-weight:bold

    class QKT,Softmax,PV boxed
    class S,P emphasized
&lt;/div&gt;

&lt;p&gt;&lt;small class=&quot;text-muted d-block text-center&quot;&gt;&lt;strong&gt;Figure 2:&lt;/strong&gt; Naive Attention Implementation Pipeline.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FlashAttention pipeline:&lt;/strong&gt;&lt;/p&gt;

&lt;!-- $$
Q\_i K\_j^T \to \text{softmax tile} \to \text{tile} \times V\_j \to \text{discard}
$$ --&gt;

&lt;p&gt;FlashAttention skips the naive decomposition entirely. Score tiles get formed, softmaxed on chip, used to accumulate their contribution to the output, then thrown away.&lt;sup id=&quot;fnref:4:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; The full matrix never touches HBM.&lt;/p&gt;

&lt;div class=&quot;mermaid&quot; style=&quot;margin: 2rem 0;&quot;&gt;
flowchart LR
    QK[&quot;QᵢKⱼᵀ&quot;] --&amp;gt; Softmax[&quot;softmax tile&quot;]
    Softmax --&amp;gt; PV[&quot;tile × Vⱼ&quot;]
    PV --&amp;gt; Discard[&quot;discard tile&quot;]

    classDef boxed fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,font-size:22px
    classDef plain fill:none,stroke:none,font-size:26px,font-weight:bold

    class QK,Softmax,PV boxed
    class Discard plain
&lt;/div&gt;

&lt;p&gt;&lt;small class=&quot;text-muted d-block text-center&quot;&gt;&lt;strong&gt;Figure 3:&lt;/strong&gt; FlashAttention Implementation Pipeline.&lt;/small&gt;&lt;/p&gt;

&lt;!-- &gt; **Important wording:** FlashAttention removes the need to materialize the full attention matrix as an off-chip intermediate. It does not remove the logical pairwise interactions required by dense attention. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important wording&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;FlashAttention removes the need to materialize the full attention matrix as an off-chip intermediate. It does not remove the logical pairwise interactions required by dense attention.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This matters most during training, where naive autograd would want those large intermediates saved for the backward pass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important distinction:&lt;/strong&gt; FlashAttention acknowledges that $N^2$ interactions exist and says: &lt;em&gt;Do not store the entire result of these interactions as a giant intermediate if we can consume each piece immediately.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;15-dense-arithmetic-is-still-quadratic&quot;&gt;1.5 Dense Arithmetic Is Still Quadratic&lt;/h3&gt;

&lt;p&gt;FlashAttention changes the memory schedule, not the math. The function itself is still dense attention. That means forming all query-key scores for $N$ tokens with head dimension $d$ still costs work proportional to $N^2 d$. There’s no way around that.&lt;/p&gt;

&lt;p&gt;Multiplying probabilities by values is another dense pairwise matrix multiplication of the same broad order. FlashAttention reshuffles these operations, but it doesn’t skip the dense set of query-key interactions. Those still happen.&lt;sup id=&quot;fnref:4:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:5:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;So three things must not get conflated:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Arithmetic complexity:&lt;/strong&gt; dense attention stays at $O(N^2 d)$.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Large intermediate storage:&lt;/strong&gt; FlashAttention avoids an $O(N^2)$ materialized score/probability tensor in HBM.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;HBM traffic:&lt;/strong&gt; the original paper proves a lower IO cost than standard materializing attention under its two-level memory model.&lt;sup id=&quot;fnref:4:6&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how a method makes much longer sequences practical without making long context “free.” Double $N$, and you roughly quadruple the number of dense query-key pairs. FlashAttention just attacks the data-movement and memory-footprint side of that.&lt;/p&gt;

&lt;!-- &gt; If you actually want to reduce the *number* of query-key pairs, you need a different mathematical structure: sparsity, a local pattern, or a different attention formulation. Those choices change model behavior, and they&apos;re conceptually separate from dense FlashAttention. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;If you actually want to reduce the &lt;em&gt;number&lt;/em&gt; of query-key pairs, you need a different mathematical structure: sparsity, a local pattern, or a different attention formulation. Those choices change model behavior, and they’re conceptually separate from dense FlashAttention.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;When you report speedups, always separate asymptotic arithmetic from measured runtime. A kernel can run several times faster at the same $O(N^2 d)$ complexity because the &lt;strong&gt;constant factors, occupancy, fusion, and memory traffic change dramatically.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Essentially, the key takeaways are summarized below:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Property&lt;/th&gt;
      &lt;th&gt;Dense FlashAttention&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Arithmetic&lt;/td&gt;
      &lt;td&gt;$O(N^2 d)$&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Full attention intermediates&lt;/td&gt;
      &lt;td&gt;Avoids $O(N^2)$ storage&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;HBM traffic&lt;/td&gt;
      &lt;td&gt;Reduced substantially&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;16-memory-efficient-exact-attention-predates-flashattention&quot;&gt;1.6 Memory-Efficient Exact Attention Predates FlashAttention&lt;/h3&gt;

&lt;p&gt;It would be historically inaccurate to say FlashAttention first discovered that exact attention can avoid quadratic memory. Earlier work by &lt;strong&gt;Rabe and Staats&lt;/strong&gt; demonstrated exact memory-efficient attention with quadratic computation but subquadratic memory. They showed that attention does not require $O(N^2)$ memory with respect to $N$.&lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; &lt;strong&gt;Milakov and Gimelshein&lt;/strong&gt; also did earlier work on online softmax, showing a memory-efficient, online recurrent way of computing the classical stable softmax normalizer.&lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;9&lt;/a&gt;&lt;/sup&gt; FlashAttention builds the same kind of running-max/running-normalizer idea into tiled attention, while also accumulating the value-weighted output.&lt;sup id=&quot;fnref:4:7&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The lineage from stable softmax to FlashAttention is shown in Figure 4 below:&lt;/p&gt;

&lt;!-- $$
\text{Stable softmax} \to \text{Online normalization} \to \text{Exact memory-efficient attention} \to \text{FlashAttention} \to \text{FlashAttention-2} \to \text{FlashAttention-3} \to \text{FlashAttention-4}
$$ --&gt;

&lt;div class=&quot;mermaid&quot; style=&quot;margin: 2rem 0;&quot;&gt;
flowchart TD
    Stable[&quot;Stable softmax&quot;] --&amp;gt; Online[&quot;Online normalization&quot;]
    Online --&amp;gt; Exact[&quot;Exact memory-efficient attention&quot;]
    Exact --&amp;gt; Flash[&quot;FlashAttention&quot;]

    classDef boxed fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,font-size:16px
    class Stable,Online,Exact,Flash boxed
&lt;/div&gt;

&lt;p&gt;&lt;small class=&quot;text-muted d-block text-center&quot;&gt;&lt;strong&gt;Figure 4:&lt;/strong&gt; Memory-Efficient Exact Attention Lineage.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;FlashAttention’s key contribution was to bring together the techniques below into a practical, high-performance algorithm:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Tiling&lt;/li&gt;
  &lt;li&gt;Online softmax&lt;/li&gt;
  &lt;li&gt;Fused computation&lt;/li&gt;
  &lt;li&gt;GPU memory hierarchy awareness&lt;/li&gt;
  &lt;li&gt;IO complexity analysis&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;This distinction matters because “memory-efficient” does not automatically mean “IO-optimized for a particular hardware model.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2-the-mathematical-trick&quot;&gt;&lt;strong&gt;2. The Mathematical Trick&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;21-tiling-queries-keys-and-values&quot;&gt;2.1 Tiling Queries, Keys, and Values&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Most important.&lt;/strong&gt; This is the section where the trick lives. Score tiles are small enough to stay near the compute units. Each tile’s contribution gets folded into the running row statistics and the output accumulator, then the tile is thrown away.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Suppose $Q \in \mathbb{R}^{N_q \times d}$, $K \in \mathbb{R}^{N_k \times d}$, and $V \in \mathbb{R}^{N_k \times d_v}$. Instead of processing all $N_q, N_k$ rows at once, divide them into blocks. For example:&lt;/p&gt;

\[Q: \boxed{Q_1} \boxed{Q_2} \boxed{Q_3} \quad K, V: \boxed{K_1, V_1} \boxed{K_2, V_2} \boxed{K_3, V_3}\]

    &lt;p&gt;For one query-key/value pair ($Q_i, K_j/V_j$), we process that score tile $S_{ij}$ locally:&lt;/p&gt;

\[S_{ij} \doteq \frac{Q_i K_j^T}{\sqrt{d}} + B_{ij},\]

    &lt;p&gt;Then, for each query block:&lt;/p&gt;

    &lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  1. Calculate score tile, S_ij
  2. Row-wise softmax and update
  3. Multiply by V_j and accumulate
  4. Update the running output
  5. Discard the tile 
  6. Move to the next tile, never writing a global S or P matrix.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Recall that softmax is defined as&lt;/p&gt;

\[\mathrm{softmax}(x_i) = \frac{e^{x_i}}{\sum_j e^{x_j}}.\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Matrix multiplication is easy to tile&lt;/strong&gt; because it is fundamentally accumulation:&lt;/p&gt;

\[AB = \sum_j A_j B_j,\]

    &lt;p&gt;so we can calculate pieces and add them.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Softmax is harder&lt;/strong&gt; because every element depends on the entire row. If we process the first block, we do not know the eventual denominator. Even worse, numerical stability requires knowing the maximum.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In the kernel, we choose tile shapes and loop order based on &lt;strong&gt;on-chip capacity, head dimension, GPU generation, causal structure, and work partitioning.&lt;/strong&gt; FA1 uses tile sizes derived from SRAM capacity $M$.&lt;sup id=&quot;fnref:4:8&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;!-- &gt; Tiling alone is not enough. Matrix multiplication tiles compose naturally because sums can be accumulated. Softmax couples every score in a row through a shared maximum and denominator, so we need a way to merge blocks without seeing the full row at once. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Mathematical Trick&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Tiling alone is not enough. Matrix multiplication tiles compose naturally because sums can be accumulated. Softmax couples every score in a row through a shared maximum and denominator, so we need a way to merge blocks without seeing the full row at once.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;22-softmax-is-the-difficult-part-of-streaming&quot;&gt;2.2 Softmax Is the Difficult Part of Streaming&lt;/h3&gt;

&lt;p&gt;A numerically stable softmax for one row $x_1, \ldots, x_N$ uses&lt;/p&gt;

\[m = \max_j x_j, \qquad \ell = \sum_j e^{x_j - m}, \qquad p_j = \frac{e^{x_j - m}}{\ell}.\]

&lt;p&gt;Subtracting $m$ keeps large logits from blowing up. However, it seems to create a streaming problem: &lt;strong&gt;how does one normalize an early block if a later block turns out to have a bigger maximum?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer is to keep running statistics and &lt;strong&gt;rescale&lt;/strong&gt; them when the maximum changes. Say the state after the earlier elements is $(m_{\text{old}}, \ell_{\text{old}})$, and the new block’s maximum is $m_b$. Then&lt;/p&gt;

\[m_{\text{new}} = \max(m_{\text{old}}, m_b)\]

&lt;p&gt;Anything accumulated under the old max converts to the new one by a single factor:&lt;/p&gt;

\[\alpha \doteq e^{m_{\text{old}} - m_{\text{new}}}\]

&lt;p&gt;The new block is then evaluated against the same $m_{\text{new}}$.&lt;/p&gt;

&lt;p&gt;None of this is an approximation. It’s just the identity&lt;/p&gt;

\[e^{x - m_{\text{old}}}\, e^{m_{\text{old}} - m_{\text{new}}} = e^{x - m_{\text{new}}}\]

&lt;!-- &gt;**Mathematical insight.** The running maximum is a change of numerical reference point. When that reference changes, previously accumulated exponentials can be rescaled exactly in real arithmetic rather than recomputed from scratch. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Mathematical Insight&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;The running maximum is a change of numerical reference point. When that reference changes, previously accumulated exponentials can be rescaled exactly in real arithmetic rather than recomputed from scratch.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The online-normalizer recurrence predates FlashAttention and is the mathematical basis for streaming stable softmax.&lt;sup id=&quot;fnref:2:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h4 id=&quot;worked-example-1-why-subtract-the-max&quot;&gt;Worked example 1: why subtract the max&lt;/h4&gt;

&lt;p&gt;Start with two extreme values to see why the max subtraction matters:&lt;/p&gt;

\[\begin{aligned}
x &amp;amp;= [1000, 999], \quad \text{Directly computing $e^{1000}$ overflows}\\
m &amp;amp;= 1000 \\
x - m &amp;amp;= [1000 - 1000, \; 999 - 1000] = [0, -1] \\
e^{[0, -1]} &amp;amp;\approx [1, 0.368] \\
\end{aligned}\]

&lt;p&gt;The exponential values $1$ and $0.368$ are perfectly manageable.&lt;/p&gt;

&lt;!-- $$
x = [1000, 999].
$$

Directly computing $e^{1000}$ overflows. Instead, set $m = 1000$, so

$$
x - m = [1000 - 1000, \; 999 - 1000] = [0, -1],
$$

and $e^{0} = 1$, $e^{-1} \approx 0.368$ are perfectly manageable. --&gt;

&lt;h4 id=&quot;worked-example-2-streaming-two-blocks&quot;&gt;Worked example 2: streaming two blocks&lt;/h4&gt;

&lt;p&gt;Now let’s work through streaming two blocks.&lt;/p&gt;

\[\begin{aligned}
\text{Block 1}: [2, 1]&amp;amp;, \text{ Block 2}:[4, 3]\\
\text{After Block 1} &amp;amp;: m_{\text{old}} = 2 \\
\text{Evaluating Block 1 at } m_{\text{old}} &amp;amp;: [e^{0}, e^{-1}] \\
\text{When Block 2 arrives} &amp;amp;: m_b = 4 \\
\text{Now in Block 2} &amp;amp;: m_{\text{new}} = \max(2, 4) = 4, \quad \alpha = e^{2 - 4} = e^{-2}\\
\text{Rescaling Block 1 at the new reference} &amp;amp;: [\alpha e^{0}, \; \alpha e^{-1}] = [e^{-2}, \; e^{-3}] \\
\text{Block 2 at the new reference} &amp;amp;: [e^{4 - 4}, \; e^{3 - 4}] = [e^{0}, \; e^{-1}] \\
\end{aligned}\]

&lt;p&gt;Putting it all together, with $x = [2, 1, 4, 3]$:&lt;/p&gt;

\[\begin{aligned}
\mathrm{softmax}(x) &amp;amp;= \frac{[e^{-2}, \; e^{-3}, \; e^{0}, \; e^{-1}]}{e^{-2} + e^{-3} + e^{0} + e^{-1}}\\
\text{Block 1 streaming computation} &amp;amp;: \ell = \sum_j e^{x_j - m} = e^{0} + e^{-1} = 1 + e^{-1}, \\
\text{Block 1 normalized output} &amp;amp;: \frac{[e^{0}, e^{-1}]}{(1 + e^{-1})} \\
\text{Block 2 running normalizer} &amp;amp;: \ell_{\text{new}} = \alpha\, \ell_{\text{old}} + \sum_{j \in \text{Block 2}} e^{x_j - m_{\text{new}}} = e^{-2}(1 + e^{-1}) + e^{0} + e^{-1} \\
\text{Block 2 unnormalized accumulator} &amp;amp;: a = [\alpha e^{0}, \alpha e^{-1}, e^{0}, e^{-1}] = [e^{-2}, e^{-3}, e^{0}, e^{-1}] \\
\text{Combined result} &amp;amp;: \frac{[e^{-2}, e^{-3}, e^{0}, e^{-1}]}{(e^{-2} + e^{-3} + 1 + e^{-1})} \\
\end{aligned}\]

&lt;p&gt;This matches the full softmax computed in one shot.&lt;/p&gt;

&lt;h3 id=&quot;23-online-softmax-from-first-principles&quot;&gt;2.3 Online Softmax from First Principles&lt;/h3&gt;

&lt;p&gt;Process scalar logits $x_1, x_2, \ldots$ one at a time. Initialize&lt;/p&gt;

\[m_0 = -\infty, \qquad \ell_0 = 0\]

&lt;p&gt;After observing $x_j$:&lt;/p&gt;

\[m_j = \max(m_{j-1}, x_j), \qquad \ell_j = \ell_{j-1} e^{m_{j-1} - m_j} + e^{x_j - m_j}\]

&lt;p&gt;Milakov and Gimelshein showed this produces the same stable softmax normalizer as the conventional safe-softmax procedure, with fewer passes over the input.&lt;sup id=&quot;fnref:2:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Attention needs a weighted value sum, so we also maintain a value-weighted, unnormalized accumulator,&lt;/p&gt;

\[a_j = \sum_{i \leq j} e^{x_i - m_j} v_i.\]

&lt;p&gt;When the maximum moves from $m_{j-1}$ to $m_j$, the accumulator is rescaled exactly like the normalizer before the new value contribution is folded in:&lt;/p&gt;

\[a_j = a_{j-1}\, e^{m_{j-1} - m_j} + e^{x_j - m_j} v_j.\]

&lt;p&gt;After the last element, $x_n$, the output is&lt;/p&gt;

&lt;p&gt;\(o = \frac{a_n}{\ell_n}.\)
&lt;!-- &gt; For attention, $x\_j$ is not a fixed input vector stored in advance. Each block of logits is generated on demand from a matrix product $QK\_j^T / \sqrt{d}$ plus mask/bias terms. The online recurrence lets the kernel consume that block immediately. --&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;For attention, $x_j$ is not a fixed input vector stored in advance. Each block of logits is generated on demand from a matrix product $QK_j^T / \sqrt{d}$ plus mask/bias terms. The online recurrence lets the kernel consume that block immediately.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The same idea works row by row and block by block, which is what makes a tiled exact softmax-attention forward pass possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Insight.&lt;/strong&gt; We don’t need the entire probability vector. We only need enough information to reconstruct its contribution to the final output.&lt;/p&gt;

&lt;!-- 
&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Mathematical insight II&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;We don’t need the entire probability vector. We only need enough information to reconstruct its contribution to the final output.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt; --&gt;

&lt;h3 id=&quot;24-the-blockwise-merge-recurrence&quot;&gt;2.4 The Blockwise Merge Recurrence&lt;/h3&gt;

&lt;p&gt;For one query row, suppose the running state after some key blocks is&lt;/p&gt;

\[(m, \ell, a),\]

&lt;p&gt;where $m$ is the running maximum score, $\ell$ is the stable softmax normalizer under that maximum, and $a \in \mathbb{R}^{d_v}$ is the unnormalized value accumulator.&lt;/p&gt;

&lt;p&gt;A new block lands with scores $s \in \mathbb{R}^b$ and values $V_b \in \mathbb{R}^{b \times d_v}$. First, find the block’s own maximum and update the running one:&lt;/p&gt;

\[m_b = \max(s), \qquad m&apos; = \max(m, m_b).\]

&lt;p&gt;The rescale factor and the block’s exponentials follow:&lt;/p&gt;

\[\alpha = e^{m - m&apos;}, \qquad p = e^{s - m&apos;}.\]

&lt;p&gt;Then update:&lt;/p&gt;

\[\ell&apos; = \alpha \ell + \sum_j p_j, \qquad a&apos; = \alpha a + p^T V_b,\]

\[m \leftarrow m&apos;, \qquad \ell \leftarrow \ell&apos;, \qquad a \leftarrow a&apos;.\]

&lt;p&gt;At the end:&lt;/p&gt;

\[o = a / \ell.\]

&lt;p&gt;When you process a block of query rows rather than a single row, the running state changes shape. $m$ and $\ell$ each become vectors with one entry per row, and $a$ becomes a matrix. If you have masks, apply them to the score tile before the exponentials. Masked positions contribute zero probability, so they drop out of the update entirely. FA1 and FA2 express equivalent running-max/running-normalizer/output updates in block form.&lt;sup id=&quot;fnref:4:9&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:5:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;!-- &gt; The recurrence is the algebraic reason tile boundaries do not change the dense softmax result. A different tiling changes the order of floating-point operations, but not the intended real-arithmetic function. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;The recurrence is the algebraic reason tile boundaries do not change the dense softmax result. A different tiling changes the order of floating-point operations, but not the intended real-arithmetic function.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;This recurrence is what makes tiled exact attention possible.&lt;/strong&gt;&lt;/p&gt;

&lt;!-- The mean of the running state:

- $m$ = running maximum score 
- $\ell$ = stable softmax normalizer
- $a$ = unnormalized value accumulator

The blockwise recurrence in my own notation:

- For one query row, maintain $(m, \ell, a)$.
- Suppose the next score block is $s = [s\_1, s\_2, \ldots, s\_b]$ with corresponding values $V\_b \in \mathbb{R}^{b \times d\_v}$.
- First calculate the block maximum: $m\_b = \max(s)$, 
- Then update the global maximum: $m&apos; = \max(m, m\_b)$.
- Define: $\alpha = e^{m - m&apos;}$, $p = e^{s - m&apos;}$.
- Then update: 
    - $\ell&apos; = \alpha \ell + \sum\_j p\_j$, $a&apos; = \alpha a + p^T V\_b$.
    - $m \leftarrow m&apos;$, $\ell \leftarrow \ell&apos;$, $a \leftarrow a&apos;$.
- Finally at the end: $o = a / \ell$. --&gt;

&lt;h3 id=&quot;25-a-complete-numerical-example&quot;&gt;2.5 A Complete Numerical Example&lt;/h3&gt;

&lt;p&gt;Take one already-scaled, unmasked attention row&lt;/p&gt;

\[s = [2, 1, 4, 3]\]

&lt;p&gt;with two-dimensional values&lt;/p&gt;

\[v_1 = [1, 0], \quad v_2 = [0, 1], \quad v_3 = [2, 0], \quad v_4 = [0, 2].\]

&lt;p&gt;The global maximum is 4, so the stable unnormalized weights are&lt;/p&gt;

\[[e^{-2}, e^{-3}, e^{0}, e^{-1}] \approx [0.135335, 0.049787, 1, 0.367879].\]

&lt;p&gt;Their sum is&lt;/p&gt;

\[\ell \approx 1.55300179,\]

&lt;p&gt;and full softmax gives&lt;/p&gt;

\[O \approx [1.37497284, 0.50582424].\]

&lt;p&gt;Now run it through in two blocks, the way a tiled kernel would: $[2, 1]$ and $[4, 3]$.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Block 1: $s = [2, 1]$.&lt;/strong&gt; Running max $m_1 = 2$, stable exponentials $[e^{0}, e^{-1}] = [1, e^{-1}]$. So&lt;/p&gt;

\[\ell_1 = 1 + e^{-1} \approx 1.36788,\]

&lt;p&gt;and the value accumulator comes out to&lt;/p&gt;

\[a_1 = 1 \cdot [1, 0] + e^{-1} \cdot [0, 1] = [1, e^{-1}].\]

&lt;p&gt;&lt;strong&gt;Block 2: $s = [4, 3]$.&lt;/strong&gt; The block max is $m_b = 4$, beating the running max, so $m’ = \max(2, 4) = 4$. The rescale factor:&lt;/p&gt;

\[\alpha = e^{2 - 4} = e^{-2}.\]

&lt;p&gt;Then&lt;/p&gt;

\[\ell_2 = \alpha \ell_1 + e^{0} + e^{-1} = e^{-2}(1 + e^{-1}) + 1 + e^{-1} = e^{-2} + e^{-3} + 1 + e^{-1} \approx 1.553,\]

&lt;p&gt;and&lt;/p&gt;

\[a_2 = e^{-2} \cdot [1, e^{-1}] + [2 e^{0}, 2 e^{-1}] = [2 + e^{-2}, 2 e^{-1} + e^{-3}] \approx [2.135, 0.7855].\]

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;

\[O_2 = a_2 / \ell_2 \approx [1.375, 0.506],\]

&lt;p&gt;matching the full-row computation.&lt;/p&gt;

&lt;!-- &gt; The old block was not revisited. Its contribution was merely rescaled when a larger maximum appeared. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;The old block was not revisited. Its contribution was merely rescaled when a larger maximum appeared.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;This rescale-instead-of-recompute is what makes streaming possible.&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;3-putting-the-mathematics-onto-the-gpu&quot;&gt;&lt;strong&gt;3. Putting the Mathematics onto the GPU&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;31-flashattention-forward-pass&quot;&gt;3.1 FlashAttention Forward Pass&lt;/h3&gt;

&lt;p&gt;The forward pass, conceptually:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Partition $Q$ into query-row tiles, and $K$ and $V$ into key/value tiles.&lt;/li&gt;
  &lt;li&gt;For each query tile, initialize row-wise running maxima, normalizers, and output accumulators: $m = -\infty$, $\ell = 0$, $a = 0$.&lt;/li&gt;
  &lt;li&gt;Load a $K_j, V_j$ tile and form the local score tile $S_{ij} = Q_i K_j^T / \sqrt{d}$.&lt;/li&gt;
  &lt;li&gt;Apply the masks or biases $B_{ij}$ belonging to this tile.&lt;/li&gt;
  &lt;li&gt;Find the tile maximum, update the running maximum $\max(m, m_b)$, and rescale the previous state.&lt;/li&gt;
  &lt;li&gt;Exponentiate the current tile relative to the updated maximum.&lt;/li&gt;
  &lt;li&gt;Update the normalizer $\ell$ and the value-weighted accumulator $a$.&lt;/li&gt;
  &lt;li&gt;Repeat for every required key tile.&lt;/li&gt;
  &lt;li&gt;Normalize the accumulator row-wise, $O = a / \ell$, then write the output.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The original algorithm picks block sizes so the tiles and running state fit in on-chip SRAM, cutting trips to HBM.&lt;sup id=&quot;fnref:4:10&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; A quick summary of the forward pass is shown below:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;scores&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;K_block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;running&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalizer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accumulator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;O&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# normalize once after the last block
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can reproduce the algebra in a few lines of PyTorch, but that’s not the same as a high-performance FlashAttention kernel. Real production implementations additionally exploit &lt;strong&gt;GPU-specific tiling, registers, shared memory, warp scheduling, tensor cores, async copies, specialized intrinsics, and occupancy optimization.&lt;/strong&gt;&lt;/p&gt;

&lt;!-- &gt; The essential algorithmic idea is independent of one CUDA kernel: generate a score tile, consume it immediately through online softmax and $V$ accumulation, and never materialize the complete score/probability matrix in HBM. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;The essential algorithmic idea is independent of one CUDA kernel: generate a score tile, consume it immediately through online softmax and $V$ accumulation, and never materialize the complete score/probability matrix in HBM.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Minimal educational PyTorch.&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;math&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;torch&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Single query block, tiled over K/V. Production kernels also
# tile Q; the outer loop is omitted here to focus on the recurrence.
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tiled_attention&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;scale&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_full&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;inf&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;kj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scale&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;m_new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;torch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maximum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;torch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m_new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;torch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m_new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vj&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m_new&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- ```python
def tiled_attention(q, k, v, block=128):
    scale = 1 / math.sqrt(q.shape[-1])
    n_q, n_kv = q.shape[0], k.shape[0]
    O = torch.zeros_like(q)

    for i in range(0, n_q, block):
        q_blk = q[i:i+block]
        m = torch.full((q_blk.shape[0],), -float(&apos;inf&apos;))
        l = torch.zeros((q_blk.shape[0],))
        a = torch.zeros((q_blk.shape[0], v.shape[-1]))

        for j in range(0, n_kv, block):
            k_blk = k[j:j+block]
            v_blk = v[j:j+block]
            s = (q_blk @ k_blk.T) * scale
            m_new = torch.maximum(m, s.max(dim=-1).values)
            p = torch.exp(s - m_new[:, None])
            alpha = torch.exp(m - m_new)
            l = alpha * l + p.sum(dim=-1)
            a = alpha[:, None] * a + p @ v_blk
            m = m_new

        O[i:i+block] = a / l[:, None]

    return O
``` --&gt;

&lt;p&gt;The gap between this educational code and a production kernel matters a lot for FA2, FA3, and FA4. Every generation keeps the same semantic structure, but schedules the work differently for newer hardware.&lt;sup id=&quot;fnref:5:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:6:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:7:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; Same math, different schedule, each tuned to the hardware of its generation.&lt;/p&gt;

&lt;h3 id=&quot;32-why-dense-flashattention-is-exact&quot;&gt;3.2 Why Dense FlashAttention Is Exact&lt;/h3&gt;

&lt;p&gt;Does FlashAttention approximate attention? For dense FlashAttention, &lt;strong&gt;NO&lt;/strong&gt;. The target function is untouched:&lt;/p&gt;

\[\mathrm{softmax}\left(\frac{QK^T}{\sqrt{d}} + B\right) V.\]

&lt;p&gt;A short checklist for why this counts as exact:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;No Q-K pairs are intentionally removed.&lt;/li&gt;
  &lt;li&gt;No low-rank approximation is introduced.&lt;/li&gt;
  &lt;li&gt;No alternative kernelized attention function replaces softmax.&lt;/li&gt;
  &lt;li&gt;Tiling just processes the same interactions in blocks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Online softmax doesn’t swap the exponential or normalization for something else either. The block recurrence only changes the order in which sufficient statistics get accumulated.&lt;/p&gt;

&lt;p&gt;But &lt;em&gt;exact&lt;/em&gt; needs a qualification. Suppose two implementations compute $a + b + c$:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;A:&lt;/strong&gt; $(a + b) + c$&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;B:&lt;/strong&gt; $a + (b + c)$&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In exact mathematics,&lt;/p&gt;

\[(a + b) + c = a + (b + c).\]

&lt;p&gt;In floating point, for some values,&lt;/p&gt;

\[(a + b) + c \neq a + (b + c).\]

&lt;p&gt;Tiling changes the reduction order. Fusion can change how rounding shakes out. So:&lt;/p&gt;

\[\text{exact algorithm} \neq \text{bitwise identical output}.\]

&lt;p&gt;Three caveats keep the word &lt;em&gt;exact&lt;/em&gt; precise:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Floating point has finite precision.&lt;/strong&gt; Reorder the additions and reductions and you’ll get small numerical differences from another implementation. PyTorch says so directly: the output of SDPA can differ depending on which backend kernel is chosen, because each backend accumulates floating-point values in a different order.&lt;sup id=&quot;fnref:10:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:10&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:13:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:13&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Dropout is random during training.&lt;/strong&gt; If you want two runs to match bit-for-bit, you have to line up the random behavior too. Attention semantics alone won’t be enough.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Sparse variants aren’t dense.&lt;/strong&gt; The block-sparse variant in the original paper skips blocks entirely, so it approximates full dense attention rather than reproducing it.&lt;sup id=&quot;fnref:4:11&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;!-- &gt; &quot;Exact&quot; doesn&apos;t mean &quot;bitwise identical to every reference kernel.&quot; It means the algorithm isn&apos;t quietly changing dense softmax attention to save work. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;em&gt;“Exact”&lt;/em&gt; doesn’t mean &lt;em&gt;“bitwise identical to every reference kernel.”&lt;/em&gt; It means the algorithm isn’t quietly changing dense softmax attention to save work.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This distinction becomes important in numerical testing. A sensible tolerance depends on &lt;strong&gt;dtype, accumulation order, sequence length, and backend, rather than requiring binary identity.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;33-io-complexity-what-the-theorem-actually-says&quot;&gt;3.3 IO Complexity: What the Theorem Actually Says&lt;/h3&gt;

&lt;p&gt;The original FlashAttention paper works in a two-level memory model: HBM plus on-chip SRAM of size $M$. Under its assumptions, which include head dimension $d$ and an SRAM size in the range $d \leq M \leq Nd$, the two attention implementations require:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Standard materializing attention:&lt;/strong&gt; $\Theta(Nd + N^2)$ HBM accesses&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FlashAttention:&lt;/strong&gt; $\Theta\left(\frac{N^2 d^2}{M}\right)$ HBM accesses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second quantity is a &lt;strong&gt;communication-complexity bound&lt;/strong&gt;, and a few things are worth keeping straight:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It holds for a specific memory model, not for every GPU.&lt;/li&gt;
  &lt;li&gt;It counts scalar elements moved between the modeled memory levels, up to asymptotic factors. It is not a byte count.&lt;/li&gt;
  &lt;li&gt;It is not the arithmetic complexity. Dense attention still performs $O(N^2 d)$ work.&lt;/li&gt;
  &lt;li&gt;More usable on-chip memory $M$ means more reuse and less modeled HBM traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The intuition: bigger on-chip memory lets larger working tiles stay resident. Bigger tiles mean more reuse and fewer HBM reloads. In the theorem’s regime, that reuse is exactly why the HBM-access bound shrinks as $M$ grows. It’s also why GPU architecture matters so much to FlashAttention performance. For typical values ($d$ of 64 to 128, $M$ around 100KB), $d^2$ is many times smaller than $M$, so FlashAttention needs many times fewer HBM accesses.&lt;sup id=&quot;fnref:4:12&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;!-- &gt; A common incorrect summary is &quot;FlashAttention reduces attention IO from $O(N^2)$ to $O(N)$.&quot; The linear quantity is the large **auxiliary memory footprint with respect to sequence length,** not the general HBM-access expression above. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;A common incorrect summary is “FlashAttention reduces attention IO from $O(N^2)$ to $O(N)$.” The linear quantity is the large &lt;strong&gt;auxiliary memory footprint with respect to sequence length,&lt;/strong&gt; not the general HBM-access expression above.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;34-memory-complexity-linear-auxiliary-state-not-linear-compute&quot;&gt;3.4 Memory Complexity: Linear Auxiliary State, Not Linear Compute&lt;/h3&gt;

&lt;p&gt;FlashAttention papers often say memory becomes linear instead of quadratic. That’s true, but it’s easy to misread, so it’s worth being precise about what’s linear and what isn’t:&lt;/p&gt;

\[\text{linear auxiliary state} \neq \text{linear compute.}\]

&lt;p&gt;A naive attention implementation creates $O(N^2)$ intermediates. FlashAttention doesn’t. It keeps only:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$Q/K/V$ tiles&lt;/li&gt;
  &lt;li&gt;row-wise $m$&lt;/li&gt;
  &lt;li&gt;row-wise $\ell$&lt;/li&gt;
  &lt;li&gt;the output accumulator $a$&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The inputs and output already occupy $O(Nd)$ memory on their own. A materializing dense attention implementation additionally creates $O(N^2)$ score and probability state. FlashAttention skips those full matrices and holds only tile-sized working state on chip, plus $O(N)$ row-wise statistics. So the extra memory tied to the attention operation grows linearly with sequence length rather than quadratically. But linear memory is not linear compute. The computation is still $O(N^2 d)$.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example.&lt;/strong&gt; For $N = 10{,}000$, there are roughly $10{,}000^2 = 100{,}000{,}000$ Q-K interactions. FlashAttention doesn’t make those disappear. It just prevents you from needing a giant intermediate holding all of them at once.&lt;/p&gt;

&lt;p&gt;Training is where this difference bites hardest. A straightforward backward pass would otherwise want the full probability matrix $P$ kept around. FlashAttention stores compact information instead, the output plus row-wise normalization statistics, and recomputes score and probability tiles during the backward pass.&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;strong&gt;Linear memory does not imply linear runtime.&lt;/strong&gt; The number of dense query-key interactions is still quadratic in $N$.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Two more things to keep in mind. First, don’t claim that total model memory is $O(N)$. Other Transformer components consume activation memory, and autoregressive serving has KV-cache memory that grows with context length. FlashAttention changes how the attention computation manages its intermediates, not the whole model’s memory profile. Second, FlashAttention and activation checkpointing can coexist, since both trade recomputation for reduced stored state, just at different scopes of the training graph.&lt;/p&gt;

&lt;h3 id=&quot;35-causal-masking-and-tile-skipping&quot;&gt;3.5 Causal Masking and Tile Skipping&lt;/h3&gt;

&lt;p&gt;In autoregressive attention, query token $i$ can’t attend to future key token $j &amp;gt; i$. The attention matrix looks like&lt;/p&gt;

\[\begin{bmatrix}
\checkmark &amp;amp; \times &amp;amp; \times &amp;amp; \times \\
\checkmark &amp;amp; \checkmark &amp;amp; \times &amp;amp; \times \\
\checkmark &amp;amp; \checkmark &amp;amp; \checkmark &amp;amp; \times \\
\checkmark &amp;amp; \checkmark &amp;amp; \checkmark &amp;amp; \checkmark
\end{bmatrix}\]

&lt;p&gt;with mask&lt;/p&gt;

\[M = \begin{bmatrix}
0 &amp;amp; -\infty &amp;amp; -\infty &amp;amp; -\infty \\
0 &amp;amp; 0 &amp;amp; -\infty &amp;amp; -\infty \\
0 &amp;amp; 0 &amp;amp; 0 &amp;amp; -\infty \\
0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 
\end{bmatrix}\]

&lt;p&gt;Apply the mask before softmax and you get&lt;/p&gt;

\[\mathrm{softmax}(S&apos;) = \begin{bmatrix}
\text{value} &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 \\
\text{value} &amp;amp; \text{value} &amp;amp; 0 &amp;amp; 0 \\
\text{value} &amp;amp; \text{value} &amp;amp; \text{value} &amp;amp; 0 \\
\text{value} &amp;amp; \text{value} &amp;amp; \text{value} &amp;amp; \text{value} 
\end{bmatrix}\]

&lt;p&gt;A materialized mask is another $N \times N$ object. But a tiled kernel can reason about the geometry of each tile directly:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Blocks strictly above the causal boundary are fully masked, so they can be skipped.&lt;/li&gt;
  &lt;li&gt;Blocks strictly below the boundary are fully valid.&lt;/li&gt;
  &lt;li&gt;Only blocks intersecting the diagonal need element-level causal masking.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s a lot of invalid tiles you never have to compute. FA2 explicitly exploits this structure,&lt;sup id=&quot;fnref:5:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; and current implementations also define alignment rules for unequal query/key lengths. The FlashAttention interface aligns the causal mask to the bottom-right corner of the attention matrix, whereas PyTorch’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_causal=True&lt;/code&gt; assumes upper-left alignment. The two only differ when $N_q \neq N_k$, so check which convention your code path uses.&lt;sup id=&quot;fnref:8&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:10:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:10&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Mask semantics are an API detail that must not be guessed. For example, current PyTorch SDPA treats a Boolean &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attn_mask&lt;/code&gt; value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt; as a position that &lt;em&gt;participates&lt;/em&gt; in attention, while other PyTorch mask APIs use different conventions.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Connection to local attention.&lt;/strong&gt; Sliding-window attention works on the same principle. If each token only attends to 128 nearby tokens, most tiles can be skipped the same way. But this is a different move: you’ve changed the mathematical attention pattern itself. The model is now doing sparse or local attention, and FlashAttention is just the kernel executing that pattern. It’s no longer the same problem as unrestricted dense attention.&lt;/p&gt;

&lt;h3 id=&quot;36-backward-pass-recompute-instead-of-save&quot;&gt;3.6 Backward Pass: Recompute Instead of Save&lt;/h3&gt;

&lt;p&gt;A naive training implementation saves $P = \mathrm{softmax}(S)$ for the backward pass. That’s an $N^2$ tensor sitting in HBM. FlashAttention doesn’t keep it. It stores compact row-wise normalization information instead, then recomputes score and probability tiles when gradients are needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forward:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Store the output and row-wise normalization statistics.&lt;/li&gt;
  &lt;li&gt;Do not store $P \in \mathbb{R}^{N \times N}$.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Backward:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;For each tile: recompute $QK^T$, reconstruct the local probabilities, compute gradients, and discard the tile.&lt;/li&gt;
  &lt;li&gt;Same trade as before: more arithmetic, less memory traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For&lt;/p&gt;

\[S = QK^T / \sqrt{d}, \quad P = \mathrm{softmax}(S), \quad O = PV,\]

&lt;p&gt;the backward pass relies on a row-wise identity, where $D_i$ is a per-row scalar:&lt;/p&gt;

\[D_i = \sum_r dO_{ir}\, O_{ir} = \sum_j P_{ij}\, dP_{ij}\]

&lt;p&gt;After recomputing a tile of $P$, the local derivatives fall out as&lt;/p&gt;

\[dV \mathrel{+}= P^T dO, \qquad dP = dO\, V^T, \qquad dS = P \odot (dP - D_i[\text{:}, \text{None}]),\]

\[dS_{ij} = P_{ij}\,(dP_{ij} - D_i),\]

&lt;p&gt;and then&lt;/p&gt;

\[dQ \mathrel{+}= dS\, K / \sqrt{d}, \qquad dK \mathrel{+}= dS^T\, Q / \sqrt{d}.\]

&lt;p&gt;Masks contribute zero probability and zero gradient. FA2 stores a row-wise log-sum-exp quantity so the probability tile can be reconstructed stably from recomputed scores.&lt;sup id=&quot;fnref:5:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;!-- &gt; Backward recomputation is intentional. It spends extra matrix-multiply work to avoid reading and writing a giant probability tensor, which is a favorable trade on GPUs. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Backward recomputation is intentional. It spends extra matrix-multiply work to avoid reading and writing a giant probability tensor, which is a favorable trade on GPUs.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This is the &lt;strong&gt;memory-compute tradeoff&lt;/strong&gt; applied to the backward pass.&lt;/p&gt;

&lt;h3 id=&quot;37-why-more-flops-can-still-be-faster&quot;&gt;3.7 Why More FLOPs Can Still Be Faster&lt;/h3&gt;

&lt;p&gt;This is one of the biggest lessons from FlashAttention. The usual assumption is that fewer FLOPs means faster. On a GPU, that’s not always true.&lt;/p&gt;

&lt;p&gt;Different operations run at completely different throughputs. Tensor cores chew through matrix multiplication. Everything else is comparatively expensive: exponentials, reductions, synchronization, shared-memory operations, and HBM transfers.&lt;sup id=&quot;fnref:4:13&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:5:6&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; &lt;strong&gt;&lt;em&gt;Therefore, doing extra arithmetic can be the right move if it eliminates expensive memory traffic.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider two options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;A:&lt;/strong&gt; compute, write huge $P$ to HBM, read $P$ back, compute&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;B:&lt;/strong&gt; compute, discard, recompute later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;B&lt;/strong&gt; does more arithmetic, but it avoids pushing a giant tensor through HBM. On modern accelerators, that trade usually wins.&lt;/p&gt;

&lt;p&gt;Every FlashAttention generation is built around this. FA2’s goal is partly to &lt;strong&gt;reduce non-matmul FLOPs,&lt;/strong&gt; since those don’t have the same throughput as tensor-core GEMMs. It also reworks the work partitioning so more of the GPU is doing useful work at once.&lt;sup id=&quot;fnref:5:7&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; FA3 pushes further on Hopper, overlapping matrix multiplication, softmax, and data movement with asynchronous hardware features.&lt;sup id=&quot;fnref:6:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; FA4 responds to Blackwell, where tensor-core throughput grew faster than other resources. That shift makes exponentials and shared-memory traffic relatively more important, and FA4 is designed around it.&lt;sup id=&quot;fnref:7:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;!-- &gt; The cost of a FLOP depends on what hardware executes it and what data movement surrounds it. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;A better performance question is not merely “How many FLOPs?” but &lt;strong&gt;“Which operations, on which units, with what data movement, reuse, parallelism, and synchronization?”&lt;/strong&gt;&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This is the systems lesson that makes FlashAttention matter beyond attention itself. Hardware efficiency doesn’t come from the math alone. It comes from co-designing the mathematical scheduling with the memory/execution hierarchy.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;4-architectural-compatibility&quot;&gt;&lt;strong&gt;4. Architectural Compatibility&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;41-mha-mqa-and-gqa-compatibility-shared-kv-heads&quot;&gt;4.1 MHA, MQA, and GQA Compatibility (Shared K/V Heads)&lt;/h3&gt;

&lt;p&gt;FlashAttention isn’t tied to standard &lt;a href=&quot;https://chizkidd.github.io/2026/04/17/transformers/#self-attention--multi-head-attention&quot;&gt;Multi-Head Attention&lt;/a&gt; (MHA). In general, the inputs have shapes&lt;/p&gt;

\[Q \in \mathbb{R}^{B \times N_q \times H_q \times d}, \quad K, V \in \mathbb{R}^{B \times N_k \times H_{kv} \times d}.\]

&lt;p&gt;Let’s look into FlashAttention’s compatibility with MHA, &lt;a href=&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/#multi-query-attention-mqa&quot;&gt;Multi-Query Attention&lt;/a&gt; (MQA), and &lt;a href=&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/#grouped-query-attention-gqa&quot;&gt;Grouped-Query Attention&lt;/a&gt; (GQA). Three head-sharing regimes are distinguished by $H_q$ versus $H_{kv}$:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;MHA:&lt;/strong&gt; Every query head has its own K/V head. \(H_q = H_{kv}\)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;MQA:&lt;/strong&gt; All query heads share a single K/V head. \(H_{kv} = 1\)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GQA:&lt;/strong&gt; Several query heads share each K/V head. \(1 &amp;lt; H_{kv} &amp;lt; H_q\)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s the distinction that matters:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;MQA/GQA defines the architecture. It decides how heads share K/V projections.&lt;/li&gt;
  &lt;li&gt;RoPE defines the position transform. It decides how position gets baked into Q and K.&lt;/li&gt;
  &lt;li&gt;FlashAttention defines the execution. It decides how the resulting attention operation gets scheduled and computed efficiently.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Any combination of these three orthogonal concepts can be used together. A kernel doesn’t care how many query heads share a K/V head, or whether the Q/K inputs were RoPE-rotated. It just evaluates attention between each query head and its assigned K/V head. Head sharing changes the architecture and the KV-memory footprint. RoPE changes the input representation. FlashAttention changes how the work gets scheduled on the hardware.&lt;/p&gt;

&lt;p&gt;Current implementations do impose one shape constraint. In the Dao-AILab kernels, the number of query heads must be divisible by the number of KV heads&lt;sup id=&quot;fnref:8:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;

\[H_q \bmod H_{kv} = 0\]

&lt;p&gt;PyTorch SDPA exposes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enable_gqa&lt;/code&gt;, but its docs have described GQA as an experimental feature with its own shape constraints, so production code should follow the exact version’s documentation rather than assuming universal fused-kernel support.&lt;sup id=&quot;fnref:10:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:10&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h3 id=&quot;42-variable-lengths-local-attention-and-dropout&quot;&gt;4.2 Variable Lengths, Local Attention, and Dropout&lt;/h3&gt;

&lt;p&gt;Real systems aren’t always the clean case of same sequence length, dense attention, and no dropout.&lt;/p&gt;

&lt;p&gt;Production implementations support a broader feature set:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;variable-length sequences&lt;/li&gt;
  &lt;li&gt;causal attention&lt;/li&gt;
  &lt;li&gt;sliding-window attention (SWA)&lt;/li&gt;
  &lt;li&gt;dropout, in training-oriented interfaces&lt;/li&gt;
  &lt;li&gt;MQA/GQA&lt;/li&gt;
  &lt;li&gt;ALiBi-style score bias&lt;/li&gt;
  &lt;li&gt;KV-cache decoding, including optional RoPE handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But these are implementation capabilities, not properties of the FlashAttention mathematical idea itself.&lt;sup id=&quot;fnref:8:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;What a given build supports depends on:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GPU&lt;/li&gt;
  &lt;li&gt;CUDA/ROCm backend (NVIDIA and AMD have separate implementations, with different support matrices)&lt;/li&gt;
  &lt;li&gt;dtype&lt;/li&gt;
  &lt;li&gt;head dimension&lt;/li&gt;
  &lt;li&gt;mask&lt;/li&gt;
  &lt;li&gt;library version&lt;/li&gt;
  &lt;li&gt;kernel generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One practical warning on dropout. PyTorch’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scaled_dot_product_attention&lt;/code&gt; applies dropout according to its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dropout_p&lt;/code&gt; argument regardless of training or eval mode, so eval code needs to pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.0&lt;/code&gt; explicitly when dropout should be off.&lt;sup id=&quot;fnref:10:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:10&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;!-- &gt; Do not infer feature support from the name &quot;FlashAttention.&quot; Check the exact library, kernel generation, device, dtype, head dimension, mask/bias, and training/inference path that will actually run. --&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Important Fact&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Do not infer feature support from the name “FlashAttention.” Check the exact library, kernel generation, device, dtype, head dimension, mask/bias, and training/inference path that will actually run.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&quot;5-summary&quot;&gt;&lt;strong&gt;5. Summary&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Let’s walk through the whole FlashAttention story in one derivation, and hit the key points to remember.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ordinary attention:&lt;/strong&gt; $QK^T \in \mathbb{R}^{N \times N}$. Materializing it creates an enormous intermediate matrix.&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

\[O = \mathrm{softmax}\left(\frac{QK^T}{\sqrt{d}}\right) V.\]

&lt;p&gt;&lt;strong&gt;Naive execution:&lt;/strong&gt; This creates huge memory traffic.&lt;/p&gt;

\[QK^T \to \text{store } N \times N \text{ scores} \to \text{softmax} \to \text{store } N \times N \text{ probabilities} \to \text{multiply by } V\]

&lt;p&gt;&lt;strong&gt;FlashAttention:&lt;/strong&gt; Tiling is used. Split $Q \to Q_i$ and $K, V \to K_j, V_j$, then calculate the score tile:&lt;/p&gt;

\[S_{ij} = \frac{Q_i K_j^T}{\sqrt{d}}.\]

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;u&gt;The problem with tiling&lt;/u&gt;: Softmax needs the maximum and the denominator over the &lt;strong&gt;&lt;em&gt;whole row.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;u&gt;The solution&lt;/u&gt;: online softmax&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;Maintain $(m, \ell, a)$ where:
        &lt;ul&gt;
          &lt;li&gt;$m$ = running maximum&lt;/li&gt;
          &lt;li&gt;$\ell$ = running sum of exponentials (the softmax denominator)&lt;/li&gt;
          &lt;li&gt;$a$ = running unnormalized value-weighted sum (accumulator)&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;When a new block has a larger maximum $m’$, calculate a rescaling factor $\alpha$ and rescale the old state ($\ell$ and $a$) by it:&lt;/li&gt;
    &lt;/ol&gt;

\[\alpha = e^{m - m&apos;}\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Therefore, we can process the full FlashAttention pipeline without ever materializing the $N \times N$ matrix:&lt;/p&gt;

\[\text{tile} \to \text{score} \to \text{online softmax} \to V \text{ accumulation} \to \text{discard tile}.\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;u&gt;Result&lt;/u&gt;: The same exact dense attention mathematics, but much better memory behavior: &lt;strong&gt;&lt;em&gt;less HBM traffic, much smaller intermediates.&lt;/em&gt;&lt;/strong&gt; Arithmetic remains $O(N^2 d)$.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;FlashAttention in one table:&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Property&lt;/th&gt;
      &lt;th&gt;Complexity / Formulation&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Memory complexity&lt;/td&gt;
      &lt;td&gt;$O(Nd)$ auxiliary attention storage&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Compute complexity&lt;/td&gt;
      &lt;td&gt;$O(N^2 d)$ dense attention compute&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Attention formulation&lt;/td&gt;
      &lt;td&gt;Exact dense softmax attention&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Key takeaway&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;strong&gt;Same exact dense attention mathematics, but much better memory behavior.&lt;/strong&gt; Less HBM traffic, much smaller intermediates, arithmetic unchanged at $O(N^2 d)$.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;appendix-1&quot;&gt;&lt;strong&gt;Appendix&lt;/strong&gt;&lt;/h2&gt;

&lt;!-- | Source § | Hierarchical |
|---|---|
| 1-6 | 1.1-1.6 |
| 7-11 | 2.1-2.5 |
| 12-18 | 3.1-3.7 |
| 19-20 | 4.1-4.2 | --&gt;

&lt;h3 id=&quot;reference-map&quot;&gt;Reference Map&lt;/h3&gt;

&lt;p&gt;The table below shows which paper to refer to for each question.&lt;sup id=&quot;fnref:15:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Question&lt;/th&gt;
      &lt;th&gt;Best starting source&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;What is the original IO-aware algorithm?&lt;/td&gt;
      &lt;td&gt;FlashAttention-1 paper &lt;sup id=&quot;fnref:4:14&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Why does online normalization work?&lt;/td&gt;
      &lt;td&gt;Milakov &amp;amp; Gimelshein &lt;sup id=&quot;fnref:2:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Was exact subquadratic-memory attention known earlier?&lt;/td&gt;
      &lt;td&gt;Rabe &amp;amp; Staats &lt;sup id=&quot;fnref:3:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;What specifically changed in FA2?&lt;/td&gt;
      &lt;td&gt;FlashAttention-2 paper &lt;sup id=&quot;fnref:5:8&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;What is Hopper-specific in FA3?&lt;/td&gt;
      &lt;td&gt;FlashAttention-3 paper &lt;sup id=&quot;fnref:6:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;What changes on Blackwell in FA4?&lt;/td&gt;
      &lt;td&gt;FlashAttention-4 paper &lt;sup id=&quot;fnref:7:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;What does the current official package support?&lt;/td&gt;
      &lt;td&gt;Dao-AILab repository &lt;sup id=&quot;fnref:8:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;How does current PyTorch select SDPA kernels?&lt;/td&gt;
      &lt;td&gt;PyTorch SDPA and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sdpa_kernel&lt;/code&gt; docs &lt;sup id=&quot;fnref:10:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:10&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; $^,$ &lt;sup id=&quot;fnref:11&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:11&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Why can exact backends differ numerically?&lt;/td&gt;
      &lt;td&gt;PyTorch reproducibility notes &lt;sup id=&quot;fnref:13:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:13&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;How is PagedAttention different?&lt;/td&gt;
      &lt;td&gt;Kwon et al. &lt;sup id=&quot;fnref:14&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:14&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;A note on how to use this map&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Implementation support changes faster than the mathematics. For deployed systems, treat the installed framework’s documentation and the exact kernel repository/release as authoritative for supported devices, dtypes, head dimensions, masks, dropout, and GQA behavior.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;references&quot;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h3&gt;

&lt;h3 id=&quot;citation&quot;&gt;&lt;strong&gt;Citation&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;If you found this blog post helpful, please consider citing it:&lt;/p&gt;

&lt;div class=&quot;language-bibtex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;@article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;obasi2026FlashAttentionPt1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;FlashAttention: Part 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Obasi, Chizoba&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;chizkidd.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;year&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2026&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;month&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Sep&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://chizkidd.github.io/2026/09/13/flashattention/&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:15&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://drive.google.com/file/d/1CLyK-9Cflcvi3fRl3qAHyzYvwJFjCyVg/view&quot;&gt;Understanding FlashAttention: How IO-Aware Attention Makes Transformers Faster Without Approximating Attention&lt;/a&gt;. Google Drive PDF. Accessed September 2026. &lt;a href=&quot;#fnref:15&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:15:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Tri Dao et al. &lt;a href=&quot;https://arxiv.org/abs/2205.14135&quot;&gt;FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness&lt;/a&gt;. NeurIPS 2022. &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:4:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;5&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;6&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:6&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;7&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:7&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;8&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:8&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;9&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:9&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;10&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:10&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;11&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:11&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;12&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:12&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;13&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:13&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;14&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:4:14&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;15&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:10&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;PyTorch. &lt;a href=&quot;https://docs.pytorch.org/docs/stable/generated/torch.nn.functional.scaled_dot_product_attention.html&quot;&gt;torch.nn.functional.scaled_dot_product_attention documentation&lt;/a&gt;. PyTorch Documentation. Accessed September 2026. &lt;a href=&quot;#fnref:10&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:10:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:10:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:10:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:10:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;5&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:10:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;6&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:13&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;PyTorch. &lt;a href=&quot;https://docs.pytorch.org/docs/stable/notes/randomness.html&quot;&gt;Reproducibility documentation, including scaled-dot-product attention backend differences&lt;/a&gt;. PyTorch Documentation. Accessed September 2026. &lt;a href=&quot;#fnref:13&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:13:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:13:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:5&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Tri Dao. &lt;a href=&quot;https://arxiv.org/abs/2307.08691&quot;&gt;FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning&lt;/a&gt;. ICLR 2024. &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:5:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;5&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;6&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5:6&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;7&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5:7&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;8&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5:8&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;9&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:6&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Jay Shah et al. &lt;a href=&quot;https://arxiv.org/abs/2407.08608&quot;&gt;FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision&lt;/a&gt;. NeurIPS 2024. &lt;a href=&quot;#fnref:6&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:6:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:6:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:6:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:6:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;5&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:7&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Ted Zadouri et al. &lt;a href=&quot;https://arxiv.org/abs/2603.05451&quot;&gt;FlashAttention-4: Algorithm and Kernel Pipelining Co-Design for Asymmetric Hardware Scaling&lt;/a&gt;. arXiv, 2026. &lt;a href=&quot;#fnref:7&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:7:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:7:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:7:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:7:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;5&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Markus N. Rabe and Charles Staats. &lt;a href=&quot;https://arxiv.org/abs/2112.05682&quot;&gt;Self-attention Does Not Need O(n²) Memory&lt;/a&gt;. arXiv, 2021. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:3:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Maxim Milakov and Natalia Gimelshein. &lt;a href=&quot;https://arxiv.org/abs/1805.02867&quot;&gt;Online normalizer calculation for softmax&lt;/a&gt;. arXiv, 2018. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:2:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:2:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:2:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:8&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Dao-AILab. &lt;a href=&quot;https://github.com/Dao-AILab/flash-attention&quot;&gt;flash-attention official repository and current implementation documentation&lt;/a&gt;. GitHub. Accessed September 2026. &lt;a href=&quot;#fnref:8&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:8:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:8:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:8:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Ashish Vaswani et al. &lt;a href=&quot;https://arxiv.org/abs/1706.03762&quot;&gt;Attention Is All You Need&lt;/a&gt;. NeurIPS 2017. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:11&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;PyTorch. &lt;a href=&quot;https://docs.pytorch.org/docs/stable/generated/torch.nn.attention.sdpa_kernel.html&quot;&gt;torch.nn.attention.sdpa_kernel documentation&lt;/a&gt;. PyTorch Documentation. Accessed September 2026. &lt;a href=&quot;#fnref:11&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:14&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Woosuk Kwon et al. &lt;a href=&quot;https://arxiv.org/abs/2309.06180&quot;&gt;Efficient Memory Management for Large Language Model Serving with PagedAttention&lt;/a&gt;. SOSP 2023. &lt;a href=&quot;#fnref:14&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate>
        <link>https://chizkidd.github.io//2026/09/13/flashattention/</link>
        <guid isPermaLink="true">https://chizkidd.github.io//2026/09/13/flashattention/</guid>
        
        
      </item>
    
      <item>
        <title>Mixture of Experts (MoE): How Transformers Scale Without Activating Everything.</title>
        <description>&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Mixture of Experts (MoE)&lt;/strong&gt; is one of the main techniques used to scale modern language models without making every token pay the full computational cost of the model.&lt;/li&gt;
  &lt;li&gt;The basic idea is surprisingly simple: instead of sending every token through one enormous feed-forward network, we split it into many smaller &lt;strong&gt;expert networks&lt;/strong&gt; and only activate a few experts for each token.&lt;/li&gt;
  &lt;li&gt;These notes walk through the intuition behind MoE, starting from the role of the &lt;strong&gt;feed-forward network (FFN)&lt;/strong&gt; inside a Transformer, then moving through routing, top-$k$ selection, fine-grained experts, shared experts, expert capacity, dropless MoE, load balancing, and router stability.&lt;/li&gt;
  &lt;li&gt;The focus is not just on &lt;em&gt;what&lt;/em&gt; MoE does, but &lt;strong&gt;why each piece exists&lt;/strong&gt; and what problem it is trying to solve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I originally learned this material from Jia-Bin Huang’s visual explanation of MoE &lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. I have rewritten the ideas here as notes for myself, with the equations and implementation details that I found useful when trying to understand how modern sparse MoE models actually work. After working through it, the name makes a lot more sense. It really &lt;em&gt;is&lt;/em&gt; a mixture of experts. The interesting part is deciding who gets called for each token, and then making sure the whole system doesn’t collapse under its own weight.&lt;/p&gt;

&lt;h2 id=&quot;table-of-contents&quot;&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#feed-forward-networks-in-transformers&quot;&gt;Feed-Forward Networks in Transformers&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#a-useful-intuition-for-ffn&quot;&gt;A Useful Intuition for FFN&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#rmsnorm&quot;&gt;RMSNorm&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-ffn-as-a-knowledge-store&quot;&gt;The FFN as a Knowledge Store&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#why-make-the-ffn-bigger&quot;&gt;Why make the FFN bigger?&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-mixture-of-experts&quot;&gt;Why Mixture of Experts?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#sparse-mixture-of-experts&quot;&gt;Sparse Mixture of Experts&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#the-router&quot;&gt;The Router&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#top-k-routing&quot;&gt;Top-$k$ Routing&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#fine-grained-experts&quot;&gt;Fine-Grained Experts&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#shared-experts&quot;&gt;Shared Experts&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#expert-capacity--token-overflow&quot;&gt;Expert Capacity &amp;amp; Token Overflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#dropless-moe&quot;&gt;Dropless MoE&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-load-balancing-problem&quot;&gt;The Load Balancing Problem&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#noisy-top-k-gating&quot;&gt;Noisy Top-$k$ Gating&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#importance-vs-load&quot;&gt;Importance vs Load&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#load-balancing-loss&quot;&gt;Load Balancing Loss&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#device-level-load-balancing&quot;&gt;Device-Level Load Balancing&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#deepseek-v3s-bias-adjustment-trick&quot;&gt;DeepSeek V3’s Bias Adjustment Trick&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#auxiliary-loss-free-load-balancing&quot;&gt;Auxiliary-Loss-Free Load Balancing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#router-stability--the-router-z-loss&quot;&gt;Router Stability &amp;amp; the Router Z-Loss&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#safe-softmax&quot;&gt;Safe Softmax&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#router-z-loss&quot;&gt;Router Z-Loss&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#putting-it-all-together&quot;&gt;Putting It All Together&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-bigger-picture&quot;&gt;The Bigger Picture&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;appendix&quot;&gt;&lt;strong&gt;Appendix&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#citation&quot;&gt;Citation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;feed-forward-networks-in-transformers&quot;&gt;&lt;strong&gt;Feed-Forward Networks in Transformers&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;A Transformer layer typically alternates between an &lt;strong&gt;attention mechanism&lt;/strong&gt; and a &lt;strong&gt;feed-forward network (FFN)&lt;/strong&gt;. Attention allows each token to incorporate information from other tokens in the sequence. The FFN then processes each token independently. Attention basically answers this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Which other tokens should this token pay attention to?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div style=&quot;text-align: center; margin-bottom: 24px; break-inside: avoid; display: inline-block; width: 100%;&quot;&gt;
  &lt;h3 style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 1.4rem; font-weight: 600; margin-bottom: 16px; color: #1a1a1a;&quot;&gt;
    The decoder-only transformer architecture. 
  &lt;/h3&gt;
  &lt;figure style=&quot;margin: 0; padding: 0;&quot;&gt;
    &lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!_Ipr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9540283e-b4bc-4c3f-943c-5df959b1733b_1656x818.png&quot; alt=&quot;The decoder-only transformer architecture&quot; style=&quot;max-width: 100%; width: 450px; height: auto; display: block; margin: 0 auto 12px;&quot; /&gt;
    &lt;figcaption style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 0.85rem; color: #666666; line-height: 1.4; max-width: 500px; margin: 0 auto; text-align: center;&quot;&gt;
      Figure 1: The decoder-only transformer architecture. 
      &lt;span style=&quot;color: #999999; font-size: 0.8rem;&quot;&gt;(Source: &lt;a href=&quot;https://cameronrwolfe.substack.com/p/moe-llms&quot; style=&quot;color: #666666; text-decoration: underline;&quot;&gt;Cameron R. Wolfe&lt;/a&gt;)&lt;/span&gt;
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The FFN is different. It operates independently on each token after attention has produced its contextual representation. A simplified Transformer block looks roughly like:&lt;/p&gt;

\[\boxed{x \rightarrow \text{Attention} \rightarrow \text{FFN} \rightarrow x&apos;}\]

&lt;p&gt;The FFN is usually a relatively large multilayer perceptron (MLP). If the input token representation has dimension $d$, the FFN typically expands it to a much larger hidden dimension $d_h$, applies a nonlinear activation, and projects it back down:&lt;/p&gt;

\[x
\rightarrow
W_{up}x
\rightarrow
\sigma(\cdot)
\rightarrow
W_{down}a\]

&lt;p&gt;For a single token embedding $x$, a simplified FFN can be written as:&lt;/p&gt;

\[\begin{aligned} 
z &amp;amp;= xW_{\text{up}} + b_{\text{up}} \\ 
a &amp;amp;= \sigma(z) \\ 
y &amp;amp;= aW_{\text{down}} + b_{\text{down}} 
\end{aligned}\]

&lt;!-- $$
z = xW_{\text{up}} + b_{\text{up}}
$$

$$
a = \sigma(z)
$$

$$
y = aW_{\text{down}} + b_{\text{down}}
$$ --&gt;

&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$W_{\text{up}}$ projects the token into a larger hidden dimension.&lt;/li&gt;
  &lt;li&gt;$\sigma$ is the nonlinear activation function.&lt;/li&gt;
  &lt;li&gt;$W_{\text{down}}$ projects the representation back to the original model dimension.&lt;/li&gt;
  &lt;li&gt;$b_{\text{up}}$ and $b_{\text{down}}$ are the biases which could be zero or nonzero.&lt;/li&gt;
  &lt;li&gt;and the result is added back through the residual connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Typically, $d_h \approx 4d$, where $d$ is the input dimension and $d_h$ is the hidden dimension of the FFN.&lt;/p&gt;

&lt;h3 id=&quot;a-useful-intuition-for-ffn&quot;&gt;&lt;strong&gt;A Useful Intuition for FFN&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;One way to think about the first projection is that each hidden dimension asks the token a different learned “question.” If a row of $W_{up}$ points strongly in some semantic direction, its dot product with the token representation tells us how strongly that feature is present. The activation function then decides which features should remain active. Finally, $W_{down}$ maps those activated features back into the model dimension. This gives us a useful mental model:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;up projection = detect features&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;activation = select features&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;down projection = combine features into an output&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is an important caveat here. It is tempting to say that the FFN literally stores one clean factual association per neuron, but that is too strong. The weights are distributed representations, and features can be entangled across dimensions. The “questions” and “facts” interpretation is a useful intuition, not a literal description of what every neuron is doing.&lt;/p&gt;

&lt;h3 id=&quot;rmsnorm&quot;&gt;&lt;strong&gt;RMSNorm&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Before the FFN, modern Transformers commonly use a normalization layer such as &lt;strong&gt;RMSNorm&lt;/strong&gt;. For an input vector $x$, given that $\gamma$ is a learnable scaling vector, the RMSNorm is:&lt;/p&gt;

\[\text{RMS}(x) = \sqrt{\frac{1}{d}\sum_{i=1}^{d}x_i^2+\epsilon}\]

\[\text{RMSNorm}(x) = \gamma \odot \frac{x}{\text{RMS}(x)}\]

&lt;p&gt;The important idea is that normalization keeps the magnitude of activations under control while allowing the model to learn a different scale for each dimension.&lt;/p&gt;

&lt;h3 id=&quot;the-ffn-as-a-knowledge-store&quot;&gt;&lt;strong&gt;The FFN as a Knowledge Store&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;The FFN is more than just a generic nonlinear transformation. One useful interpretation is that the first projection asks a collection of learned &lt;strong&gt;questions&lt;/strong&gt; about the token representation. Each row of $W_{\text{up}}$ can be thought of as a learned direction in representation space.&lt;/p&gt;

\[z_i = x \cdot W_{\text{up},i} + b_i\]

&lt;p&gt;If $z_i$ is large, the input strongly matches the feature represented by that row. The activation function then suppresses irrelevant features. For a simple ReLU example:&lt;/p&gt;

\[a_i = \max(0,z_i)\]

&lt;p&gt;The second projection maps the activated features back into the model dimension. This gives us an intuitive picture:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The FFN can be thought of as a large collection of learned feature detectors that activate different pieces of stored information depending on the input.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This interpretation is useful for understanding why increasing the FFN hidden dimension can improve model capacity.&lt;/p&gt;

&lt;h3 id=&quot;why-make-the-ffn-bigger&quot;&gt;&lt;strong&gt;Why make the FFN bigger?&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Now suppose we want a more capable model. One obvious option is to increase $d_h$, the hidden dimension of the FFN. More hidden dimensions means more learned features and more capacity. However, if we make the FFN four times larger, eight times larger, or even more, we also increase the amount of computation performed for &lt;strong&gt;every token&lt;/strong&gt;. A dense model has to use the same FFN for every token. That means the computational cost grows roughly with the total size of the network. This gives us a frustrating trade-off:&lt;/p&gt;

\[\boxed{
\text{more parameters}
\quad\Longrightarrow\quad
\text{more capacity}
}\]

&lt;!--
```bash
More Parameters ====&gt; More Capacity 
```
--&gt;

&lt;p&gt;but also:&lt;/p&gt;

\[\boxed{
\text{more parameters}
\quad\Longrightarrow\quad
\text{more computation per token}
}\]

&lt;!--
```bash
More Parameters ====&gt; More Computation per Token 
```
--&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;why-mixture-of-experts&quot;&gt;&lt;strong&gt;Why Mixture of Experts?&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;What if we could have the extra parameters without using all of them every time? That is where MoE enters. If we simply increase $d_h$, the FFN becomes larger. That gives the model more capacity, but it also increases:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;training computation,&lt;/li&gt;
  &lt;li&gt;inference computation,&lt;/li&gt;
  &lt;li&gt;parameter memory,&lt;/li&gt;
  &lt;li&gt;communication requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And there is another observation: &lt;strong&gt;A token does not need every feature in the FFN.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, a token about chemistry probably does not need every feature that might be useful for programming, mathematics, or another language. So instead of making one enormous FFN that processes every token, we can divide the FFN into multiple smaller networks. Each one becomes an &lt;strong&gt;expert&lt;/strong&gt;. The model can then choose which experts should process each token. This is the central idea behind a sparse &lt;strong&gt;Mixture of Experts (MoE)&lt;/strong&gt;.&lt;/p&gt;

&lt;!-- Let’s visualize it:

```bash

                    Token
                      |
                   Router
                      |
          +-----------+-----------+
          |           |           |
        Expert 1    Expert 2    Expert 3   ...
          |           |           |
          +-----------+-----------+
                      |
                   Combine
                      |
                    Output
``` --&gt;

&lt;div style=&quot;text-align: center; margin-bottom: 24px; break-inside: avoid; display: inline-block; width: 100%;&quot;&gt;
  &lt;figure style=&quot;margin: 0; padding: 0;&quot;&gt;
    &lt;img src=&quot;https://towardsdatascience.com/wp-content/uploads/2021/01/13DyfyeNt_Z8IUaktSp0RJQ-768x386.png&quot; alt=&quot;Mixture of Experts (MoE) routing in a Switch Transformer encoder block.&quot; style=&quot;max-width: 100%; width: 450px; height: auto; display: block; margin: 0 auto 12px;&quot; /&gt;
    &lt;figcaption style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 0.85rem; color: #666666; line-height: 1.4; max-width: 500px; margin: 0 auto; text-align: center;&quot;&gt;
      Figure 2: Mixture of Experts (MoE) routing in a Switch Transformer encoder block. The standard dense feed-forward network (FFN) layer is replaced by a sparse Switch FFN layer (light blue) that processes sequence tokens independently. As illustrated, a router independently directs two tokens (x₁ = “More”, x₂ = “Parameters”) via solid lines to specific FFN experts across 4 FFN experts. The final layer output (dotted line) is the selected FFN&apos;s response multiplied by its router gate value.
      &lt;span style=&quot;color: #999999; font-size: 0.8rem;&quot;&gt;(Source: &lt;a href=&quot;https://arxiv.org/abs/2101.03961&quot; style=&quot;color: #666666; text-decoration: underline;&quot;&gt;William Fedus, Barret Zoph, Noam Shazeer, 2022&lt;/a&gt;)&lt;/span&gt;
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;The crucial part is that we do not run every expert. The router selects only a small number of experts for each token. If we have 64 experts but activate only 8 for a token, then the model can contain a lot more total parameters than a dense model with roughly the same amount of computation per token. A simplified MoE layer can be written as:&lt;/p&gt;

\[\text{MoE}(x) =
\sum_{i \in \text{TopK}(r(x))}
p_i(x) E_i(x)\]

&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$x$ is the token representation&lt;/li&gt;
  &lt;li&gt;$E_i$ is expert $i$&lt;/li&gt;
  &lt;li&gt;$r(x)$ is the router output&lt;/li&gt;
  &lt;li&gt;$p_i(x)$ is the routing probability&lt;/li&gt;
  &lt;li&gt;$\text{TopK}$ selects the experts that actually process the token&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the key trick:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The model’s total parameter count can grow much faster than the number of parameters activated for each token.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the “sparse” in sparse MoE.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;sparse-mixture-of-experts&quot;&gt;&lt;strong&gt;Sparse Mixture of Experts&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Suppose we have $N$ experts:&lt;/p&gt;

\[E_1, E_2, \dots, E_N\]

&lt;p&gt;Each expert is itself an FFN. Instead of evaluating all $N$ experts for every token, we select only the top $k$ experts. If $k \ll N$, then the model can contain many more parameters while only activating a small fraction of them for each token. This creates an important distinction:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Total parameters&lt;/strong&gt; and &lt;strong&gt;active parameters&lt;/strong&gt; are no longer the same thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A model can therefore have a very large parameter count without requiring every token to use the entire model.&lt;/p&gt;

&lt;!-- https://emergentmind-storage-cdn-c7atfsgud9cecchk.z01.azurefd.net/ai-images/03157e12b0f6f9932938ab69125bb2aa.webp 

https://www.emergentmind.com/topics/sparse-mixture-of-experts-s-moe--&gt;

&lt;div style=&quot;text-align: center; margin-bottom: 24px; break-inside: avoid; display: inline-block; width: 100%;&quot;&gt;
  &lt;figure style=&quot;margin: 0; padding: 0;&quot;&gt;
    &lt;img src=&quot;/assets/images/2026/moe/sparse-MoE-transparent-background.png&quot; alt=&quot;Sparse Mixture-of-Experts Overview.&quot; style=&quot;max-width: 100%; width: 450px; height: auto; display: block; margin: 0 auto 12px;&quot; /&gt;
    &lt;figcaption style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 0.85rem; color: #666666; line-height: 1.4; max-width: 500px; margin: 0 auto; text-align: center;&quot;&gt;
      Figure 3: Sparse Mixture-of-Experts (s-MoE) overview. Core mechanism, motivation, training challenge and solution.
      &lt;span style=&quot;color: #999999; font-size: 0.8rem;&quot;&gt;(Source: &lt;a href=&quot;https://www.emergentmind.com/topics/sparse-mixture-of-experts-moe&quot; style=&quot;color: #666666; text-decoration: underline;&quot;&gt;Emergent Mind&lt;/a&gt;)&lt;/span&gt;
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h3 id=&quot;the-router&quot;&gt;&lt;strong&gt;The Router&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;So now we have another problem. If there are many experts, who decides which ones get used? The model needs some mechanism to decide which experts should receive each token. This is the job of the &lt;strong&gt;router,&lt;/strong&gt; a small neural network. A simple router can just be a learned linear projection.&lt;/p&gt;

&lt;p&gt;For an input token representation $x$, the router computes a score for every expert:&lt;/p&gt;

\[h(x) = xW_g\]

&lt;p&gt;where&lt;/p&gt;

\[W_g \in \mathbb{R}^{d \times N}\]

\[h(x) \in \mathbb{R}^{N}\]

&lt;p&gt;Each value in $h(x)$ is the router’s score, or &lt;strong&gt;logit&lt;/strong&gt;, for one expert. We can turn these logits into probabilities using softmax:&lt;/p&gt;

\[p_i(x) = \frac{\exp(h(x)_i)} {\sum_{j=1}^{N}\exp(h(x)_j)}\]

&lt;!-- $$
p_i(x) = \frac{\exp(h_i(x))} {\sum_{j=1}^{N}\exp(h_j(x))}
$$ --&gt;

&lt;!--
$$
p_i(x) =
\frac{e^{h_i(x)}}
{\sum_{j=1}^{N} e^{h_j(x)}}
$$
--&gt;

&lt;p&gt;The resulting vector represents the router’s preference over the experts.Then we select the top $K$ experts.&lt;/p&gt;

&lt;h3 id=&quot;top-k-routing-&quot;&gt;&lt;strong&gt;Top-$k$ Routing&lt;/strong&gt; &lt;a name=&quot;top-k-routing&quot;&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;We then select only the $k$ experts with the highest routing scores. Let&lt;/p&gt;

\[S(x) = \text{TopK}(p(x),k)\]

&lt;p&gt;Then the MoE output can be written as:&lt;/p&gt;

\[y = \sum_{i \in S(x)} p_i(x)E_i(x)\]

&lt;p&gt;In practice, implementations may renormalize the selected routing weights, but the basic idea remains the same. The process is therefore:&lt;/p&gt;

\[\boxed{x
\rightarrow
\text{Router}
\rightarrow
\text{Top-}k\text{ Experts}
\rightarrow
\text{Weighted Combination}
\rightarrow
y}\]

&lt;!--
```bash
x -&gt; Router -&gt; Top-k Experts -&gt; Weighted Combination -&gt; y
```
--&gt;

&lt;p&gt;The routing happens &lt;strong&gt;at the token level&lt;/strong&gt;. Different tokens in the same sequence can therefore be sent to completely different combinations of experts. For example, if the router produces:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;0.05, 0.10, 0.03, 0.42, 0.07, 0.33]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and $K=2$, experts &lt;strong&gt;4&lt;/strong&gt; and &lt;strong&gt;6&lt;/strong&gt; win. The token is sent only to those experts. Their outputs are then combined using the corresponding routing weights. This means routing happens per token. Two neighboring tokens can go through completely different experts.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;fine-grained-experts&quot;&gt;&lt;strong&gt;Fine-Grained Experts&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;A natural question is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Why not just have a small number of large experts?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One answer is &lt;strong&gt;flexibility&lt;/strong&gt;. Suppose we have eight large experts and select two of them. The number of possible combinations is limited.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Instead of having a few large experts, why not have many smaller experts?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can split the expert computation into many smaller experts and activate more of them. This is the idea behind &lt;strong&gt;fine-grained expert segmentation&lt;/strong&gt;. Suppose we have roughly the same total expert capacity. We could build:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;8 large experts
OR 
64 smaller experts 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and activate a subset of them.&lt;/p&gt;

&lt;p&gt;The second approach gives the router many more possible combinations to choose from. This can be useful because the model does not have to commit a token to one very large “specialist.” It can combine several smaller specialists. For example:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Token A → Expert 3 + Expert 11 + Expert 42 + Expert 57
Token B → Expert 2 + Expert 8 + Expert 19 + Expert 61
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The resulting combinations give the model a much larger routing space. OLMoE, for example, uses 64 small experts and activates 8 per token &lt;sup id=&quot;fnref:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Its released configuration has about 6.9B total parameters with about 1.3B active parameters per token. DeepSeek-V3 pushes this idea much further, using hundreds of routed experts while activating only a small subset for each token. DeepSeekMoE proposed splitting the experts into a much larger number of smaller experts and activating a correspondingly larger number of them. The goal is to allow more flexible combinations of specialized knowledge &lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. This gives the router a finer-grained set of choices:&lt;/p&gt;

\[\boxed{
\text{many small experts}
+
\text{top-}k\text{ routing}
\rightarrow
\text{more possible combinations}}\]

&lt;!--
```bash
Many Small Experts + Top-k Routing ——&gt; More Possible Combinations 
```
--&gt;

&lt;p&gt;So MoE is not just:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Let’s make several copies of the FFN.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The design of the expert pool itself matters. This is one of the important ideas behind modern DeepSeek-style MoE architectures.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;shared-experts&quot;&gt;&lt;strong&gt;Shared Experts&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;DeepSeekMoE also introduced the idea of &lt;strong&gt;shared experts&lt;/strong&gt;. A shared expert is always active rather than being selected by the router. The intuition is that some information is useful for almost every token. Instead of forcing the routed experts to repeatedly learn this common information, we can give the model a dedicated expert for broadly useful patterns. The architecture therefore contains:&lt;/p&gt;

\[\boxed{\text{Shared Experts} + \text{Routed Experts}}\]

&lt;p&gt;The shared component handles more general information, while the routed experts can specialize. The DeepSeekMoE paper found benefits from combining fine-grained experts with shared experts, although the value of shared experts is architecture-dependent and is not universally guaranteed &lt;sup id=&quot;fnref:2:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;div style=&quot;text-align: center; margin-bottom: 24px; break-inside: avoid; display: inline-block; width: 100%;&quot;&gt;
  &lt;figure style=&quot;margin: 0; padding: 0;&quot;&gt;
    &lt;img src=&quot;https://sebastianraschka.com/images/LLMs-from-scratch-images/bonus/moe-memory/3.webp?1&quot; alt=&quot;DeepSeekMoE architecture illustration showing conventional top-2 routing, fine-grained expert segmentation, and shared expert isolation&quot; style=&quot;max-width: 100%; width: 500px; height: auto; display: block; margin: 0 auto 12px;&quot; /&gt;
    &lt;figcaption style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 0.85rem; color: #666666; line-height: 1.4; max-width: 500px; margin: 0 auto; text-align: center;&quot;&gt;
      Figure 4: Illustration of DeepSeekMoE. Subfigure (a) showcases an MoE layer with the conventional top-2 routing strategy. Subfigure (b) illustrates the fine-grained expert segmentation strategy. Subsequently, subfigure (c) demonstrates the integration of the shared expert isolation strategy, constituting the complete DeepSeekMoE architecture. It is noteworthy that across these three architectures, the number of expert parameters and computational costs remain constant.
      &lt;span style=&quot;color: #999999; font-size: 0.8rem;&quot;&gt;(Source: &lt;a href=&quot;https://arxiv.org/abs/2401.06066&quot; style=&quot;color: #666666; text-decoration: underline;&quot;&gt;Damai Dai et al., 2024&lt;/a&gt;)&lt;/span&gt;
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;expert-capacity--token-overflow&quot;&gt;&lt;strong&gt;Expert Capacity &amp;amp; Token Overflow&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;There is a practical problem with routing. Suppose we have $16$ tokens and $8$ experts. If tokens were distributed perfectly evenly, each expert would receive $\frac{16}{8}=2$ tokens. So we could give every expert a capacity of 2. But routing decisions depend on the actual input. There is nothing inherently stopping the router from doing this instead:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Expert 1:  8 tokens
Expert 2:  5 tokens
Expert 3:  2 tokens
Expert 4:  1 token
Expert 5:  0 tokens
Expert 6:  0 tokens
Expert 7:  0 tokens
Expert 8:  0 tokens

OR

Expert 1:  5 tokens
Expert 2:  3 tokens
Expert 3:  2 tokens
Expert 4:  2 token
Expert 5:  1 tokens
Expert 6:  1 tokens
Expert 7:  1 tokens
Expert 8:  1 tokens
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now the first expert has received more tokens than it can process in both cases. This creates &lt;strong&gt;token overflow&lt;/strong&gt;. Some experts are overloaded while others are sitting idle in the first case. And in a distributed system, experts may live on different GPUs. So routing is not merely a modeling decision, but also a systems problem. Historically, one way to deal with this is to give every expert a fixed batch size. This allows us to define the &lt;strong&gt;expert capacity&lt;/strong&gt;, which is the maximum number of tokens in a batch that can be routed to each expert. Expert capacity allows more buffer to help mitigate token overflow during routing &lt;sup id=&quot;fnref:6&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

\[\text{expert capacity} =
\text{capacity factor}
\times
\frac{T}{N}\]

&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$T$ = number of total tokens per batch&lt;/li&gt;
  &lt;li&gt;$N$ = number of experts&lt;/li&gt;
&lt;/ul&gt;

&lt;div style=&quot;text-align: center; margin-bottom: 24px; break-inside: avoid; display: inline-block; width: 100%;&quot;&gt;
  &lt;figure style=&quot;margin: 0; padding: 0;&quot;&gt;
    &lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!vE2b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F417c5fc8-2524-48e1-a9ef-460b4476d323_1784x1184.png&quot; alt=&quot;Illustration of token routing dynamics.&quot; style=&quot;max-width: 100%; width: 450px; height: auto; display: block; margin: 0 auto 12px;&quot; /&gt;
    &lt;figcaption style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 0.85rem; color: #666666; line-height: 1.4; max-width: 500px; margin: 0 auto; text-align: center;&quot;&gt;
      Figure 5:  Illustration of token routing dynamics.
      &lt;span style=&quot;color: #999999; font-size: 0.8rem;&quot;&gt;(Source: &lt;a href=&quot;https://www.emergentmind.com/topics/sparse-mixture-of-experts-s-moe&quot; style=&quot;color: #666666; text-decoration: underline;&quot;&gt;William Fedus, Barret Zoph, Noam Shazeer, 2022&lt;/a&gt;)&lt;/span&gt;
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;If an expert receives more tokens than its capacity, something has to happen. One option is to drop the excess tokens. The token essentially skips the MoE computation and continues through the residual pathway. That avoids exceeding the allocated compute and memory budget, but now some tokens are not getting the expert computation the model intended. Another traditional solution is to increase the expert’s capacity. But then we have the opposite problem. For example, a capacity factor of $1.5$ would increase the available capacity above the ideal balanced allocation in the second case as shown above, but larger capacity means:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;more memory,&lt;/li&gt;
  &lt;li&gt;more computation,&lt;/li&gt;
  &lt;li&gt;more padding,&lt;/li&gt;
  &lt;li&gt;more communication.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suppose an expert receives only 2 tokens but we allocate space for 8. We now have six empty slots. The hardware still has to deal with those padded tensor shapes. So we have a trade-off:&lt;/p&gt;

\[\boxed{
\text{low capacity}
\rightarrow
\text{token overflow}
}\]

\[\boxed{
\text{high capacity}
\rightarrow
\text{wasted computation/memory}
}\]

&lt;p&gt;This is one reason modern MoE implementations care so much about efficient routing kernels. So can we avoid both?&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;dropless-moe&quot;&gt;&lt;strong&gt;Dropless MoE&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Instead of forcing every expert to process the same number of tokens, we can organise the expert computation around the &lt;strong&gt;actual routing assignments&lt;/strong&gt;. One way to think about this is using block-sparse matrix multiplication. Rather than padding every expert to the same token count, the computation can use blocks whose sizes correspond to the number of tokens actually assigned to each expert. Conceptually:&lt;/p&gt;

\[\boxed{
X
\rightarrow
\text{Routing}
\rightarrow
\text{Variable-sized expert batches}
\rightarrow
\text{Expert computation}
\rightarrow
\text{Scatter back}
}\]

&lt;p&gt;For example, we can use sparse/block operations that operate on the actual routed tokens:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Token routing:
Expert 1 → x1, x7, x9
Expert 2 → x2
Expert 3 → x3, x4, x5, x6

Rather than padding everything to four tokens:
Expert 1 → x1, x7, x9, PAD
Expert 2 → x2, PAD, PAD, PAD
Expert 3 → x3, x4, x5, x6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This allows us to avoid dropping tokens simply because an expert received more tokens than expected, while also avoiding unnecessary padding. This style of implementation is often referred to as &lt;strong&gt;dropless MoE&lt;/strong&gt;. The exact implementation depends heavily on the GPU kernels and distributed training system, but the underlying goal is simple:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Compute only the expert work that actually exists.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OLMoE, for example, uses token-choice routing with dropless routing. The important distinction is that MoE sparsity is not just about the neural network architecture. We need the underlying hardware and kernels to exploit that sparsity too. Otherwise, theoretical efficiency can disappear in implementation overhead.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;the-load-balancing-problem&quot;&gt;&lt;strong&gt;The Load Balancing Problem&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;There is a deeper problem with sparse routing. At the beginning of training, all experts are randomly initialised. Suppose the router happens to send many of the first tokens to experts $E_1$ and $E_2$. Those experts are updated more often so they become better. The router then has even more reason to send tokens to them, and the cycle continues. This can create a &lt;strong&gt;self-reinforcing&lt;/strong&gt; feedback loop:&lt;/p&gt;

\[\boxed{
\text{more tokens}
\rightarrow
\text{more updates}
\rightarrow
\text{better experts}
\rightarrow
\text{higher routing score}
\rightarrow
\text{more tokens}
\rightarrow
\text{same better experts}
}\]

&lt;p&gt;Eventually, some experts may receive very few tokens. These underused experts become effectively &lt;strong&gt;dead&lt;/strong&gt;. This is the &lt;strong&gt;load balancing problem&lt;/strong&gt;. The goal is not necessarily to make every expert equally good, but to prevent the router from collapsing onto a small subset of experts by distributing the processing of tokens as evenly as possible among the experts.&lt;/p&gt;

&lt;h3 id=&quot;noisy-top-k-gating-&quot;&gt;&lt;strong&gt;Noisy Top-$k$ Gating&lt;/strong&gt; &lt;a name=&quot;noisy-top-k-gating&quot;&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;One early approach is to add &lt;strong&gt;Gaussian noise&lt;/strong&gt; to the router logits:&lt;/p&gt;

\[h(x) = xW_g\]

\[h&apos;(x)_i = h(x)_i + \epsilon \cdot \text{Softplus}\big((xW_{\text{noise}})_i\big) \quad \text{where } \epsilon \sim \mathcal{N}(0, 1)\]

&lt;!-- $$
h&apos;_i(x)=h_i(x)+\epsilon_i
$$ --&gt;

&lt;p&gt;The noise encourages exploration. Instead of always selecting the same experts, the router occasionally explores other experts, giving them opportunities to receive tokens and learn. The router selects a more diverse set of experts and prevents the same experts from always being chosen. We can further improve this by adding a tunable scaling factor for each expert. This is called &lt;strong&gt;noisy top-K gating&lt;/strong&gt;. This idea appears in noisy top-$k$ gating approaches to MoE routing &lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;. But exploration alone is not enough. We still need an explicit mechanism to encourage balanced expert utilisation.&lt;/p&gt;

&lt;h3 id=&quot;importance-vs-load&quot;&gt;&lt;strong&gt;Importance vs Load&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Given N experts indexed by $i = 1 \text{ to } N$ and a batch $B$ with $T$ tokens, there are two slightly different things we can measure.&lt;/p&gt;

&lt;h4 id=&quot;importance&quot;&gt;&lt;strong&gt;Importance&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;We can measure how much routing probability an expert receives. For expert $i$, the importance $I_i$ is:&lt;/p&gt;

\[I_i = \sum_{x \in B} p_i(x)\]

&lt;p&gt;An expert can therefore have high importance even if it is not selected very often.&lt;/p&gt;

&lt;h4 id=&quot;load&quot;&gt;&lt;strong&gt;Load&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;Instead, we can count how many tokens are actually routed/assigned to the expert $i$. For expert $i$, the load $L_i$ is:&lt;/p&gt;

\[L_i = \sum_{x \in B} \mathbb{1}[i\in S(x)]\]

\[\text{where }
S(x) = \text{TopK}(p(x),k)\]

&lt;p&gt;This measures actual expert usage. These two quantities ($L_i, I_i$) are not necessarily the same. For example, the router might give eight experts reasonably balanced probabilities while repeatedly selecting only four of them through top-$k$ routing. Therefore, balancing probabilities alone does not guarantee balanced computation. Unfortunately, the number of tokens received by each expert is a &lt;strong&gt;discrete quantity&lt;/strong&gt; that cannot be used directly for backpropagation. Instead, we can define a &lt;strong&gt;smooth estimator&lt;/strong&gt; for the number of examples assigned to each expert in the batch. The smoothness enables gradients to flow through the estimator, making it possible to apply backpropagation.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;load-balancing-loss&quot;&gt;&lt;strong&gt;Load Balancing Loss&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;A commonly used loss formulation combines the fraction of tokens routed to each expert with the average routing probability assigned to that expert. Mathematically, the load balancing loss is:&lt;/p&gt;

\[P_i =
\frac{1}{T} I_i =
\frac{1}{T} \sum_{x \in B} p_i(x),
\qquad
f_i =
\frac{1}{T} L_i =
\frac{1}{T} \sum_{x \in B} \mathbb{1}[i \in S(x)]\]

\[\mathcal{L}_{\text{LB}} = N
\sum_{i=1}^{N}
f_i P_i\]

&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$f_i$ is the fraction of tokens routed to expert $i$.&lt;/li&gt;
  &lt;li&gt;$P_i$ is the average router probability assigned to expert $i$.&lt;/li&gt;
  &lt;li&gt;$N$ is the number of experts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The factor $N$ keeps the scale of the loss comparable as the number of experts changes. Essentially, it keeps the scale of the loss from shrinking simply because we increased the number of experts. Under perfectly uniform routing where $f_i = P_i = \frac{1}{N}$, the scaling factor $N$ cancels out the splitting effect of the cross-expert summation:&lt;/p&gt;

&lt;!--
$$
f_i = p_i = \frac{1}{N}
$$
--&gt;

&lt;!-- and therefore: --&gt;

\[N \sum_{i=1}^{N} f_i P_i = 
N \sum_{i=1}^{N} \left( \frac{1}{N} \cdot \frac{1}{N} \right) =
N \sum_{i=1}^{N} \frac{1}{N^2} = N \cdot \frac{N}{N^2} = 1\]

&lt;!--
$$
N \sum_i f_i p_i = 1
$$
--&gt;
&lt;p&gt;A common approach is to add an auxiliary load balancing loss to the language modeling objective. Conceptually:&lt;/p&gt;

\[\mathcal{L} =
\mathcal{L}_{\text{CE}}
+
\alpha \mathcal{L}_{\text{LB}}\]

&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$\mathcal{L}_{\text{CE}}$ is the normal next-token prediction loss (cross-entropy loss).&lt;/li&gt;
  &lt;li&gt;$\mathcal{L}_{\text{LB}}$ encourages more uniform expert usage (load-balancing loss).&lt;/li&gt;
  &lt;li&gt;$\alpha$ is a hyperparameter that controls the influence of the load balancing loss during training. It controls how strongly the balancing objective affects training.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important idea is that the balancing loss gives the router a reason to spread tokens across experts instead of collapsing onto a few. However, there is an obvious downside.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;If $\alpha$ is too large, the model may optimise for balanced routing at the expense of the actual language modeling objective. The router is forced to care too much about uniformity, and uniform routing is not necessarily what we actually want. The model should learn to specialize.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If $\alpha$ is too small, the balancing loss may have almost no effect.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So:&lt;/p&gt;

\[\boxed{
\alpha \uparrow
\rightarrow
\text{better balance, potentially worse model objective}
}\]

\[\boxed{
\alpha \downarrow
\rightarrow
\text{less interference, potentially worse balance}
}\]

&lt;p&gt;From experimental results, we see that a clear advantage of using load balancing loss is that it produces lower training and validation loss. Without load balancing, two out of eight experts receive all the training tokens while the remaining six experts are poorly trained. With load balancing loss, the tokens are distributed much more evenly across all experts.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;device-level-load-balancing&quot;&gt;&lt;strong&gt;Device-Level Load Balancing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;In a real distributed MoE model, experts are often spread across different GPUs. This introduces another problem, a systems problem. Even if the &lt;strong&gt;experts&lt;/strong&gt; are balanced globally, the &lt;strong&gt;devices&lt;/strong&gt; might not be. For example:&lt;/p&gt;

\[\boxed{
\text{GPU}_1
\rightarrow
\text{many tokens}
}\]

\[\boxed{
\text{GPU}_2
\rightarrow
\text{few tokens}
}\]

&lt;p&gt;The model is still bottlenecked by the overloaded GPU. Therefore, large-scale MoE systems may also consider load at the device level. These experts may live on different devices. Therefore, in addition to expert-level balance, we can further distribute the computational load evenly across devices and alleviate bottlenecks. The computation of device-level balance loss follows a similar formulation but aggregates token counts and router probabilities at the device level. The same general principle applies:&lt;/p&gt;

\[\boxed{
\text{balanced experts}
+
\text{balanced devices}
\rightarrow
\text{better hardware utilization }
}\]

&lt;p&gt;This matters because MoE introduces communication between devices whenever tokens need to be dispatched to experts located on different GPUs.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;deepseek-v3s-bias-adjustment-trick&quot;&gt;&lt;strong&gt;DeepSeek V3’s Bias Adjustment Trick&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;DeepSeek V3 uses a simple trick to balance the load. Instead of using softmax, the router in DeepSeek V3 directly applies a &lt;strong&gt;sigmoid&lt;/strong&gt; function on the predicted logits, one for each expert. Suppose the router is slightly biased towards selecting the first two experts. To overcome this imbalance, we add a small &lt;strong&gt;bias&lt;/strong&gt; to each expert depending on whether the number of tokens routed to the expert is larger or smaller than the average. This simple bias adjustment increases the likelihood of tokens being routed to a broader range of experts. By dynamically adjusting the bias for overloaded or underloaded experts, we keep balanced expert load while achieving better performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The bias adjustments were &lt;strong&gt;only&lt;/strong&gt; used for determining the top-K routing. The final output still uses the original probabilities as weights for combining expert outputs.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;auxiliary-loss-free-load-balancing&quot;&gt;&lt;strong&gt;Auxiliary-Loss-Free Load Balancing&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;DeepSeek-V3 introduced an interesting alternative to the traditional auxiliary balancing loss. Instead of adding another loss term that directly competes with the language modeling objective, DeepSeek-V3 uses a &lt;strong&gt;dynamic expert-level bias&lt;/strong&gt; to influence which experts are selected &lt;sup id=&quot;fnref:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. Conceptually:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CASE 1:
overloaded expert
      ↓
lower routing bias
      ↓
less likely to be selected 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CASE 2:
underloaded expert
      ↓
higher routing bias
      ↓
more likely to be selected
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The idea is roughly:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Measure how many tokens each expert is receiving.&lt;/li&gt;
  &lt;li&gt;Compare each expert’s load with the average.&lt;/li&gt;
  &lt;li&gt;Increase the routing bias for under-utilised experts.&lt;/li&gt;
  &lt;li&gt;Decrease the routing bias for overloaded experts.&lt;/li&gt;
  &lt;li&gt;Use the adjusted scores for top-$k$ selection.&lt;/li&gt;
  &lt;li&gt;Keep the original router scores for weighting the selected expert outputs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Mathematically:&lt;/p&gt;

\[h&apos;(x)_i = h(x)_i + b_i\]

&lt;p&gt;where $b_i$ is a dynamically updated expert-specific bias.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If an expert is overloaded $b_i \downarrow$&lt;/li&gt;
  &lt;li&gt;If an expert is underloaded $b_i \uparrow$&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bias is used to influence the selection of the top-$K$ experts. But the original routing probabilities are still used when combining the expert outputs. That means the mechanism can encourage balanced routing without directly adding a load-balancing penalty to the main optimization objective. This is why it is often described as loss-free load balancing. It is a nice example of a recurring theme in large-scale ML:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Sometimes the best way to fix an optimization problem is to change the algorithm around the objective rather than adding another term to the objective.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Essentially, the key detail is that this bias is used for &lt;strong&gt;routing selection&lt;/strong&gt;, rather than changing the probabilities used to combine the final expert outputs. This allows DeepSeek-V3 to perform load balancing without introducing the same auxiliary-loss trade-off. The DeepSeek-V3 technical report describes this as an &lt;strong&gt;auxiliary-loss-free load balancing strategy&lt;/strong&gt; &lt;sup id=&quot;fnref:4:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. Results demonstrate that this &lt;strong&gt;auxiliary-loss-free&lt;/strong&gt; load balancing strategy avoids the trade-off between load balance and model performance. There is also an important nuance here. DeepSeek-V3 does not completely eliminate the need to monitor routing balance. Sequence-level imbalance can still matter, especially when individual sequences produce skewed routing patterns. DeepSeek-V3 uses a &lt;strong&gt;sequence-wise balance loss&lt;/strong&gt; to prevent severe imbalance within individual sequences; so it’s not completely loss-free, but it avoids a global auxiliary loss that competes directly with the main objective.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;router-stability--the-router-z-loss&quot;&gt;&lt;strong&gt;Router Stability &amp;amp; the Router Z-Loss&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Load balancing is not the only issue with the router. Recall that the router begins with logits $h(x)$ and converts them to probabilities using softmax:&lt;/p&gt;

\[p_i(x) = \frac{e^{h(x)_i}}{\sum_{j=1}^{N} e^{h(x)_j}}\]

&lt;!-- $$
p_i =
\frac{e^{h_i(x)}}
{\sum_j e^{h_j(x)}}
$$ --&gt;

&lt;p&gt;An interesting property of softmax is that it is &lt;strong&gt;shift invariant&lt;/strong&gt;. For any constant $c$:&lt;/p&gt;

\[\text{Softmax}(h) =
\text{Softmax}(h+c)\]

&lt;p&gt;because:&lt;/p&gt;

\[\frac{e^{h_i+c}}
{\sum_j e^{h_j+c}} =
\frac{e^c e^{h_i}}
{e^c\sum_j e^{h_j}} =
\frac{e^{h_i}}
{\sum_j e^{h_j}}\]

&lt;p&gt;This creates a subtle problem. The router can increase all of its logits by the same amount without changing its output probabilities. So the probabilities remain stable while the underlying logits can grow arbitrarily large. This becomes dangerous when using reduced-precision arithmetic such as FP16. For example:&lt;/p&gt;

\[[2, 1, 0] \text{ and } [102, 101, 100]\]

&lt;p&gt;produce exactly the same softmax distribution. The model therefore has no incentive, from the softmax probabilities alone, to keep the absolute magnitude of the logits small. This poses a numerical problem because large logits can interact badly with exponentiation especially in modern LLMs that are trained using reduced-precision arithmetic (BF16 or FP16) for memory reduction and training speed increase.&lt;/p&gt;

&lt;h3 id=&quot;safe-softmax&quot;&gt;&lt;strong&gt;Safe Softmax&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;A standard numerical trick is to subtract the maximum logit before exponentiation:&lt;/p&gt;

\[p_i(x) = \text{Softmax}(h)_i = \frac{e^{h(x)_i - \max(h(x))}}{\sum_{j=1}^{N} e^{h(x)_j - \max(h(x))}}\]

&lt;!-- $$
\text{Softmax}(h)_i =
\frac{e^{h_i-\max(h)}}
{\sum_j e^{h_j-\max(h)}}
$$ --&gt;

&lt;p&gt;This prevents excessively large exponentials from overflowing. However, it only treats the numerical symptom. The underlying logits can still drift to extremely large values. Using safe softmax is like wearing a seat belt while driving. It does provide important protections, but it does not mean we should press the accelerator recklessly and allow the logits to grow without constraints.&lt;/p&gt;

&lt;h3 id=&quot;router-z-loss&quot;&gt;&lt;strong&gt;Router Z-Loss&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;To prevent the logits from becoming excessively large, a straightforward approach is to add a loss term that penalizes the sum of their square values. Unfortunately, this hurts model performance because it interferes with the router probability from the softmax. A more direct approach is to regularise the normalisation term. The idea is to penalize large values of the router’s log-normalizer. This ensures that the gradient from this loss does not change the router probabilities. Let’s define:&lt;/p&gt;

\[Z(x) =
\log
\sum_{i=1}^{N} e^{h_i(x)}\]

&lt;p&gt;We square the log normalizer to penalize both positive and negative shifts and average them across the batch. This is known as the &lt;strong&gt;router Z-loss&lt;/strong&gt;. The router Z-loss can be written as:&lt;/p&gt;

\[\mathcal{L}_Z =
\frac{1}{T}
\sum_{x \in B}
Z(x)^2\]

&lt;p&gt;The logarithm prevents the regularisation term from growing exponentially, while the squared term penalises large positive or negative shifts (large values of the log-normalizer). The router Z-loss keeps the logits centered and prevents them from drifting to arbitrarily large values, ensuring training stability is crucial for reliable scaling of mixture of experts to larger and more powerful models. The intuition is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Keep the router logits numerically well-behaved without unnecessarily changing which experts the router prefers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is particularly important when scaling MoE training to large models reliably. Scaling the number of experts is not enough. We also have to scale the stability machinery around them. From controlled experiments in &lt;a href=&quot;https://arxiv.org/abs/2409.02060&quot;&gt;OLMoE&lt;/a&gt; &lt;sup id=&quot;fnref:5:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, without Z-loss, the training curve has &lt;strong&gt;many loss spikes&lt;/strong&gt;. In contrast, adding router Z-loss leads to more stable training and better performance.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;putting-it-all-together&quot;&gt;&lt;strong&gt;Putting It All Together&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;At this point, the architecture looks something like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                    Token representation
                           |
                         RMSNorm
                           |
                      Transformer
                        Attention
                           |
                      MoE / FFN
                           |
                    +------+------+
                    |             |
                  Router       Experts
                    |             |
             score every      Expert 1
               expert         Expert 2
                    |          ...
                 Top-K         Expert N
                    |             |
                    +------+------+
                           |
                    Weighted combine
                           |
                      Residual add
                           |
                         Output
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And during training, we are optimizing more than just next-token prediction. A simplified objective is:&lt;/p&gt;

\[\boxed{
\mathcal{L}_{\text{CE}} + \alpha\mathcal{L}_{\text{LB}} + \beta\mathcal{L}_{\text{Z}}
}\]

&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$\mathcal{L}_{\text{CE}}$ teaches the model to predict the next token&lt;/li&gt;
  &lt;li&gt;$\mathcal{L}_{\text{LB}}$ encourages healthy expert utilization&lt;/li&gt;
  &lt;li&gt;$\mathcal{L}_{\text{Z}}$ keeps the router numerically stable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some modern architectures replace the explicit load-balancing loss with other routing strategies, such as DeepSeek-V3’s loss-free bias adjustment. So there is not one universal MoE recipe. There is a family of design decisions around:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;number of experts&lt;/li&gt;
  &lt;li&gt;expert size&lt;/li&gt;
  &lt;li&gt;number of active experts&lt;/li&gt;
  &lt;li&gt;routing function&lt;/li&gt;
  &lt;li&gt;load balancing&lt;/li&gt;
  &lt;li&gt;capacity&lt;/li&gt;
  &lt;li&gt;communication&lt;/li&gt;
  &lt;li&gt;numerical stability&lt;/li&gt;
  &lt;li&gt;shared experts&lt;/li&gt;
  &lt;li&gt;hardware kernels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is what makes modern MoE systems much more interesting than the simple diagram suggests. The entire sparse MoE pipeline can now be summarised as:&lt;/p&gt;

\[x
\rightarrow
\boxed{\text{Router}}
\rightarrow
\boxed{\text{Top-}k\text{ Selection}}
\rightarrow
\boxed{\text{Expert Dispatch}}
\rightarrow
\boxed{\text{Expert FFNs}}
\rightarrow
\boxed{\text{Weighted Combination}}
\rightarrow
y\]

&lt;p&gt;But making this work at scale requires solving several different problems:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Problem&lt;/th&gt;
      &lt;th&gt;Technique&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Too many FFN parameters&lt;/td&gt;
      &lt;td&gt;Sparse expert activation&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Limited expert specialisation&lt;/td&gt;
      &lt;td&gt;Fine-grained experts&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Common information repeated across experts&lt;/td&gt;
      &lt;td&gt;Shared experts&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Uneven token assignments&lt;/td&gt;
      &lt;td&gt;Capacity management&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Token dropping / padding&lt;/td&gt;
      &lt;td&gt;Dropless MoE&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Expert collapse&lt;/td&gt;
      &lt;td&gt;Load balancing&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GPU bottlenecks&lt;/td&gt;
      &lt;td&gt;Device-level balancing&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Balancing hurts the main objective&lt;/td&gt;
      &lt;td&gt;Auxiliary-loss-free routing&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Router logits grow uncontrollably&lt;/td&gt;
      &lt;td&gt;Router Z-loss&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The thing I find most interesting about MoE is that the main idea is actually very simple:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Build a much larger network, but only use a small part of it for each token.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The difficult part is everything that comes afterward. The router has to learn where each token should go. The experts have to specialise without some of them becoming useless. The tokens have to be distributed across GPUs without creating communication bottlenecks. And the whole routing system has to remain numerically stable while the model is being trained. That is what makes modern MoE architectures more than simply “a bunch of FFNs with a router.”&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;the-bigger-picture&quot;&gt;&lt;strong&gt;The Bigger Picture&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;The thing I found most interesting about MoE is that it changes how we think about scaling neural networks.&lt;/p&gt;

&lt;p&gt;A dense model says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Make the whole network bigger, and every token pays for the bigger network.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An MoE model says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Make the network much bigger, but only activate the parts that matter for this token.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That gives us two different notions of model size:&lt;/p&gt;

\[\boxed{\text{Total Parameters}}\]

&lt;p&gt;and:&lt;/p&gt;

\[\boxed{\text{Active Parameters per Token}}\]

&lt;p&gt;Those numbers can be dramatically different. This distinction is now fundamental when comparing large MoE models. A model with 1T total parameters is not necessarily performing 1T parameters’ worth of computation for every token. Only a subset of those parameters may participate in a given forward pass. But there is an important caveat that is easy to miss:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Sparse compute does not mean sparse memory.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The expert weights still have to exist somewhere. During training and inference, the system has to store and distribute those parameters across devices. And the router has to move tokens to the devices that contain the selected experts. That introduces communication overhead, especially the all-to-all communication used in distributed MoE systems. So MoE does not magically make a trillion-parameter model equivalent to a tiny model. Instead, it changes the trade-off:&lt;/p&gt;

\[\boxed{
\text{more total capacity}
\quad\text{vs.}\quad
\text{roughly controlled active compute}
}\]

&lt;p&gt;while introducing additional memory, communication, and systems complexity. That trade-off is the real reason MoE is powerful. The easiest way I’ve found to remember MoE is shown below.&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Key Takeaways&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;ul&gt;
  &lt;li&gt;The &lt;strong&gt;FFN&lt;/strong&gt; gives the model &lt;strong&gt;capacity.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;The &lt;strong&gt;experts&lt;/strong&gt; split that capacity into &lt;strong&gt;specialists.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;The &lt;strong&gt;router&lt;/strong&gt; decides &lt;strong&gt;which specialists a token needs.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Load balancing&lt;/strong&gt; prevents the specialists from &lt;strong&gt;becoming unused or overloaded.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Router Z-loss&lt;/strong&gt; keeps the routing mechanism &lt;strong&gt;numerically stable.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;And underneath all of this, the &lt;strong&gt;hardware has to make the sparse computation actually efficient.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;So the architecture is really two problems intertwined:&lt;/p&gt;

\[\boxed{
\text{Machine Learning} + \text{Systems Engineering}
}\]

&lt;p&gt;The &lt;em&gt;&lt;strong&gt;machine learning problem&lt;/strong&gt;&lt;/em&gt; is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How do we learn useful routing and specialization?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;em&gt;&lt;strong&gt;systems problem&lt;/strong&gt;&lt;/em&gt; is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How do we execute that routing efficiently across accelerators?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That combination is what makes Mixture of Experts such a powerful scaling strategy. And honestly, after working through it, the name makes a lot more sense. It really is a mixture of experts. The interesting part is deciding who gets called for each token.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;appendix-1&quot;&gt;&lt;strong&gt;Appendix&lt;/strong&gt;&lt;/h2&gt;

&lt;h3 id=&quot;citation&quot;&gt;&lt;strong&gt;Citation&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;If you found this blog post helpful, please consider citing it:&lt;/p&gt;

&lt;div class=&quot;language-bibtex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;@article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;obasi2026mixtureOfExperts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Mixture of Experts (MoE): How Transformers Scale Without Activating Everything&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Obasi, Chizoba&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;chizkidd.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;year&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2026&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;month&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Aug&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://chizkidd.github.io/2026/08/10/mixture-of-experts/&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;references&quot;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h3&gt;

&lt;!-- https://cameronrwolfe.substack.com/p/moe-llms 

https://substack.com/@maartengrootendorst/p-148217245

https://open.substack.com/pub/maartengrootendorst/p/a-visual-guide-to-mixture-of-experts?utm_campaign=post&amp;utm_medium=web--&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Jia-Bin Huang. &lt;a href=&quot;https://www.youtube.com/watch/0QQlYR1r6pQ&quot;&gt;Mixture of Experts (MoE), Visually Explained&lt;/a&gt;. YouTube. Accessed August 2026. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:5&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Muennighoff et al. &lt;a href=&quot;https://arxiv.org/abs/2409.02060&quot;&gt;OLMoE: Open Mixture-of-Experts Language Models&lt;/a&gt;. arXiv, 2024. &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:5:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Damai Dai et al. &lt;a href=&quot;https://aclanthology.org/2024.acl-long.70/&quot;&gt;DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models&lt;/a&gt;. ACL 2024. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:2:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:6&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;William Fedus et al. &lt;a href=&quot;https://arxiv.org/abs/2101.03961&quot;&gt;Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity&lt;/a&gt;. arXiv, 2021. &lt;a href=&quot;#fnref:6&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Noam Shazeer et al. &lt;a href=&quot;https://arxiv.org/abs/1701.06538&quot;&gt;Sparsely-Gated Mixture-of-Experts&lt;/a&gt;. arXiv, 2017. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;DeepSeek-AI et al. &lt;a href=&quot;https://arxiv.org/abs/2412.19437&quot;&gt;DeepSeek-V3 Technical Report&lt;/a&gt;. arXiv, 2024. &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:4:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Mon, 10 Aug 2026 00:00:00 +0000</pubDate>
        <link>https://chizkidd.github.io//2026/08/10/mixture-of-experts/</link>
        <guid isPermaLink="true">https://chizkidd.github.io//2026/08/10/mixture-of-experts/</guid>
        
        
      </item>
    
      <item>
        <title>How Attention Became Efficient &amp; Scalable: KV Caching, MQA, GQA, MLA, and Sparse Attention.</title>
        <description>&lt;ul&gt;
  &lt;li&gt;Attention mechanisms have evolved considerably to make transformer inference faster and more memory-efficient.&lt;/li&gt;
  &lt;li&gt;These notes trace that evolution: from vanilla &lt;strong&gt;self-attention&lt;/strong&gt;, through &lt;strong&gt;KV caching&lt;/strong&gt;, to memory-saving variants like &lt;strong&gt;MQA&lt;/strong&gt;, &lt;strong&gt;GQA&lt;/strong&gt;, and &lt;strong&gt;MLA&lt;/strong&gt;, and finally to &lt;strong&gt;sparse attention&lt;/strong&gt; methods like &lt;strong&gt;SWA&lt;/strong&gt; and &lt;strong&gt;Deepseek Sparse Attention (DSA)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#self-attention&quot;&gt;Self-Attention&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#masked-causal-self-attention&quot;&gt;Masked (Causal) Self-Attention&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#multi-head-attention&quot;&gt;Multi-Head Attention&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#key-value-caching-kv-caching&quot;&gt;Key-Value Caching (KV Caching)&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#deepseek-r1v3-memory-example&quot;&gt;Deepseek R1/V3 memory example&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#multi-query-attention-mqa&quot;&gt;Multi-Query Attention (MQA)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#grouped-query-attention-gqa&quot;&gt;Grouped Query Attention (GQA)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#multihead-latent-attention-mla&quot;&gt;Multihead Latent Attention (MLA)&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#mla-at-inference-time&quot;&gt;MLA at Inference Time&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#sliding-window-attention-swa&quot;&gt;Sliding Window Attention (SWA)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#deepseek-sparse-attention-dsa&quot;&gt;Deepseek Sparse Attention (DSA)&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#quantization--rotation-in-dsa&quot;&gt;Quantization &amp;amp; Rotation in DSA&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#dsa-training&quot;&gt;DSA Training&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;appendix&quot;&gt;Appendix&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#citation&quot;&gt;Citation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;self-attention&quot;&gt;Self-Attention&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;See detailed notes on transformer and attention &lt;a href=&quot;https://chizkidd.github.io/2026/04/04/muon-muonclip/#multihead-latent-attention-mla&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

\[Q = XW_Q, \quad K = XW_K, \quad V = XW_V\]

\[\text{Attention}(Q,K,V) = \text{Softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V = O\]

&lt;hr /&gt;

&lt;h2 id=&quot;masked-causal-self-attention&quot;&gt;Masked (Causal) Self-Attention&lt;/h2&gt;

\[\text{Masked Attention}(Q,K,V) = \text{Softmax}\left(\frac{QK^T}{\sqrt{d_k}} + M\right)V\]

\[\begin{aligned}
\text{where} \\
M &amp;amp;\equiv \text{lookahead mask}
\end{aligned}\]

&lt;hr /&gt;

&lt;h2 id=&quot;multi-head-attention&quot;&gt;Multi-Head Attention&lt;/h2&gt;

\[\text{head}_i = \text{Attention}(Q,K,V)\]

\[\text{MHA} = \text{multi-head attention}(Q,K,V) = \text{Concat}(\text{head}_1, \text{head}_2, \dots, \text{head}_H)W_O = Z\]

\[\text{OR}\]

\[\begin{aligned}
O_i &amp;amp;= \text{Attention}(Q_i, K_i, V_i) \\
O &amp;amp;= \text{MultiheadAttention}(Q,K,V) \\
O &amp;amp;= \text{Concat}(O_1, O_2, \dots) \\
\Delta X &amp;amp;= OW_O
\end{aligned}\]

&lt;hr /&gt;

&lt;h2 id=&quot;key-value-caching-kv-caching&quot;&gt;Key-Value Caching (KV Caching)&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Due to masking, the transformer model can compute attention for all the tokens at the same time. This is called the &lt;strong&gt;prefilling stage&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;During inference, the key and value vectors from the previous tokens &lt;strong&gt;DO NOT&lt;/strong&gt; change at all across multiple attention heads &amp;amp; all attention layers. It is therefore wasteful to recompute these vectors every time for each new token.&lt;/li&gt;
  &lt;li&gt;A simple solution is to store the key &amp;amp; value vectors from previous tokens in memory, and reuse them when needed. This is known as &lt;strong&gt;KV caching&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;KV caching helps avoid unnecessary computation &amp;amp; therefore speeds up the decoding process.&lt;/li&gt;
  &lt;li&gt;In KV caching, we &lt;strong&gt;DO NOT&lt;/strong&gt; need to cache any of the previous query vectors because of the masked out entries, which have no effect on the attention output.&lt;/li&gt;
  &lt;li&gt;KV caching is very effective but memory intensive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;deepseek-r1v3-memory-example&quot;&gt;Deepseek R1/V3 memory example&lt;/h3&gt;

\[\text{Required memory} = (2)(\text{KV dim})(\text{Precision})(\#\text{Heads})(\#\text{Layers})(\text{Sequence length})\]

\[= (2)(128)(2\ \text{bytes/element})(128\ \text{heads})(61\ \text{layers})(32{,}768\ \text{tokens})\]

\[= 131\ \text{GB} \quad (\text{A lot of memory!!!})\]

&lt;hr /&gt;

&lt;h2 id=&quot;multi-query-attention-mqa&quot;&gt;Multi-Query Attention (MQA)&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;How do we reduce the memory requirements?&lt;/li&gt;
  &lt;li&gt;In the MHA mechanism, we cannot adjust the KV dim, precision, or the number of layers, but maybe we can look at the number of heads.&lt;/li&gt;
  &lt;li&gt;One simple idea is to reduce the number of heads for key and value matrices from $h$ to $1$. Therefore, there is only a single copy of key &amp;amp; value vectors for each token in a layer. This single copy of KV vectors is then &lt;strong&gt;shared&lt;/strong&gt; across all attention heads. This is known as &lt;strong&gt;multi-query attention&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

\[\begin{aligned}
W_Q &amp;amp;\in \mathbb{R}^{d \times (d_k \times h)} \\
W_K &amp;amp;\in \mathbb{R}^{d \times (d_k \times 1)} \\
W_V &amp;amp;\in \mathbb{R}^{d \times (d_v \times 1)}
\end{aligned}\]

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;KV cache per token&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;MHA&lt;/td&gt;
      &lt;td&gt;4 MB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MQA&lt;/td&gt;
      &lt;td&gt;31 KB&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;128x reduction in memory usage&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;An issue with MQA, however, is that it sacrifices the ability to capture complex relationships between tokens. As a result, MQA’s performance degrades considerably compared to the original MHA.&lt;/li&gt;
  &lt;li&gt;The drastic reduction of number of heads from $128$ to $1$ is likely the reason for the performance degradation of MQA. We can instead reduce the number of heads to a smaller value denoted as $n_g$.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;grouped-query-attention-gqa&quot;&gt;Grouped Query Attention (GQA)&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Instead of the total reduction of number of heads to 1 for the key and value matrices as in MQA, GQA reduces the number of heads for the key and value matrices to a smaller value, $n_g$.&lt;/li&gt;
  &lt;li&gt;GQA maintains the same overall pattern of attention as MHA, but collapses the number of key-value heads by sharing them across multiple query heads.&lt;/li&gt;
&lt;/ul&gt;

&lt;div style=&quot;text-align: center; margin-bottom: 24px; break-inside: avoid; display: inline-block; width: 100%;&quot;&gt;
  &lt;h3 style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 1.4rem; font-weight: 600; margin-bottom: 16px; color: #1a1a1a;&quot;&gt;
    Multi-head attention (MHA) vs Grouped query attention (GQA)
  &lt;/h3&gt;
  &lt;figure style=&quot;margin: 0; padding: 0;&quot;&gt;
    &lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!h6wM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f39923c-8357-487d-9e69-40ee18a902e8_2523x1248.png&quot; alt=&quot;Multi-Head Attention vs Group Query Attention&quot; style=&quot;max-width: 100%; width: 450px; height: auto; display: block; margin: 0 auto 12px;&quot; /&gt;
    &lt;figcaption style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 0.85rem; color: #666666; line-height: 1.4; max-width: 500px; margin: 0 auto; text-align: center;&quot;&gt;
      Figure 1: Multi-Head Attention vs Group Query Attention. 
      &lt;span style=&quot;color: #999999; font-size: 0.8rem;&quot;&gt;(Source: &lt;a href=&quot;https://magazine.sebastianraschka.com/p/visual-attention-variants?r=2u5avh&amp;amp;utm_medium=ios&amp;amp;triedRedirect=true&quot; style=&quot;color: #666666; text-decoration: underline;&quot;&gt;Sebastian Raschka&lt;/a&gt;)&lt;/span&gt;
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;&lt;u&gt;MHA&lt;/u&gt;&lt;/strong&gt;: each of head₁, head₂, head₃, head₄ has its own Q, K, V.&lt;br /&gt;
&lt;strong&gt;&lt;u&gt;GQA&lt;/u&gt;&lt;/strong&gt;: head₁ and head₂ share one K/V pair; head₃ and head₄ share another K/V pair.&lt;br /&gt;
&lt;strong&gt;&lt;u&gt;MQA&lt;/u&gt;&lt;/strong&gt;: head₁, head₂, head₃, head₄ all share a single K/V pair.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;GQA strikes a balance between the memory efficiency of MQA and the expressive power of MHA.&lt;/li&gt;
  &lt;li&gt;GQA is a popular choice in modern LLMs, including Llama 3 8B (Meta), Qwen 3 4B (Alibaba), Gemma 3 27B (Google), Mistral Small 3.1 24B (Mistral), SmolLM3 3B (Hugging Face), etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;KV cache per token&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;MHA&lt;/td&gt;
      &lt;td&gt;4 MB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MQA&lt;/td&gt;
      &lt;td&gt;31 KB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GQA ($n_g = 16$)&lt;/td&gt;
      &lt;td&gt;500 KB&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;8x reduction in memory usage from MHA to GQA ($n_g = 16$)&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;multihead-latent-attention-mla&quot;&gt;Multihead Latent Attention (MLA)&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;See detailed notes on MLA &lt;a href=&quot;https://chizkidd.github.io/2026/04/04/muon-muonclip/#multihead-latent-attention-mla&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;MLA reduces memory usage &amp;amp; slightly improves model performance compared to others using MHA.&lt;/p&gt;

    &lt;p&gt;&lt;!-- $$O = \text{Multihead Latent Attention}(Q, K, V)$$ --&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div style=&quot;text-align: center; margin-bottom: 24px; break-inside: avoid; display: inline-block; width: 100%;&quot;&gt;
  &lt;figure style=&quot;margin: 0; padding: 0;&quot;&gt;
    &lt;img src=&quot;/assets/images/2026/muon/MLA.png&quot; alt=&quot;Multihead Latent Attention&quot; style=&quot;max-width: 100%; width: 450px; height: auto; display: block; margin: 0 auto 12px;&quot; /&gt;
    &lt;figcaption style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 0.85rem; color: #666666; line-height: 1.4; max-width: 500px; margin: 0 auto; text-align: center;&quot;&gt;
      Figure 2: Multihead Latent Attention.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;MLA compresses $Q, K, V$ representations into a low-rank space to reduce the size of the KV cache using a &lt;strong&gt;down-projection matrix&lt;/strong&gt; which produces latent representations:&lt;/p&gt;

\[C^Q = XW^Q_\downarrow \qquad C^{KV} = XW^{KV}_\downarrow\]
  &lt;/li&gt;
  &lt;li&gt;These compressed latent vectors are then mapped back to $W^Q_\uparrow$, $W^K_\uparrow$, $W^V_\uparrow$ for each attention head using the corresponding &lt;strong&gt;up-projection matrices&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Issue:&lt;/strong&gt; This low-rank KV compression fails with rotary position embedding (RoPE).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fix:&lt;/strong&gt; A decoupled RoPE technique which introduces extra multi-head queries $W^{QR}$ and a shared key $W^{KR}$ to encode positional information.&lt;/li&gt;
  &lt;li&gt;For MLA, the Query, Key &amp;amp; Values are regrouped for each head:
    &lt;ul&gt;
      &lt;li&gt;The &lt;strong&gt;Query&lt;/strong&gt; is constructed by concatenating the compressed query $Q^C$ with the rotated query $Q^R$.&lt;/li&gt;
      &lt;li&gt;The &lt;strong&gt;Key&lt;/strong&gt; is constructed similarly by concatenating the compressed key $K^C$ with the rotated key $K^R$.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Multihead Latent Attention (MLA)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;MLA compresses $Q, K, V$ representations into a low-rank latent space via down-projection matrices $W^Q_\downarrow$ and $W^{KV}_\downarrow$, then maps back up via up-projection matrices. A decoupled RoPE technique adds rotary queries $W^{QR}$ and a shared rotary key $W^{KR}$.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;KV cache per token&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;MHA&lt;/td&gt;
      &lt;td&gt;4 MB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MQA&lt;/td&gt;
      &lt;td&gt;31 KB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GQA ($n_g = 16$)&lt;/td&gt;
      &lt;td&gt;500 KB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MLA&lt;/td&gt;
      &lt;td&gt;70 KB&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;57x reduction in memory usage from MHA to MLA&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;mla-at-inference-time&quot;&gt;MLA at Inference Time&lt;/h3&gt;

\[\Delta X = OW_O = [O_1, O_2, \dots, O_h]W_O\]

\[= \left[\dots, \text{Softmax}\left(\frac{Q_iK_i^T}{\sqrt{d_k}} + M\right)V_i, \dots\right]W_O\]

\[\begin{aligned}
Q_i &amp;amp;= XW_{\downarrow}^Q W_{\uparrow,i}^Q \\
K_i &amp;amp;= C^{KV}W_{\uparrow,i}^K = XW_{\downarrow}^{KV}W_{\uparrow,i}^K \\
V_i &amp;amp;= C^{KV}W_{\uparrow,i}^V
\end{aligned}\]

\[\begin{aligned}
\text{where} \\
C^{KV} &amp;amp;\equiv \text{cached latent}
\end{aligned}\]

\[\Delta X = \left[\dots, \underbrace{\text{Softmax}\left(\frac{XW_{\downarrow}^QW_{\uparrow,i}^QW_{\uparrow,i}^{K^T}C^{KV^T}}{\sqrt{d_k}} + M\right)}_{A_i}V_i, \dots\right]W_O\]

\[\Delta X = [A_1V_1, A_2V_2, \dots, A_hV_h]W_O\]

\[\Delta X = \left[A_1C^{KV}W_{\uparrow,1}^V, \dots, A_hC^{KV}W_{\uparrow,h}^V\right]W_O\]

\[= [A_1C^{KV}, A_2C^{KV}, A_3C^{KV}, \dots, A_hC^{KV}]
\begin{bmatrix}
W_{\uparrow,1}^V &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 \\
0 &amp;amp; W_{\uparrow,2}^V &amp;amp; 0 &amp;amp; \cdots &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; W_{\uparrow,3}^V &amp;amp; \cdots &amp;amp; 0 \\
\vdots &amp;amp; \vdots &amp;amp; \vdots &amp;amp; \ddots &amp;amp; \vdots \\
0 &amp;amp; 0 &amp;amp; 0 &amp;amp; \cdots &amp;amp; W_{\uparrow,h}^V
\end{bmatrix}W_O\]

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note: the up-projection matrix for the value can be incorporated into the output matrix.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Due to the associative property, we can merge $W_{\downarrow}^QW_{\uparrow,i}^QW_{\uparrow,i}^{K^T}$ into a single matrix, which means we &lt;strong&gt;do not&lt;/strong&gt; need to explicitly compute the key vectors during inference.&lt;/li&gt;
  &lt;li&gt;Also, $W_{\uparrow}^V$ can be absorbed into $W^O$. By incorporating the up-projection matrix into the query and output matrices, we can compute attention efficiently &lt;strong&gt;without incurring extra computation.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;MLA has a fundamental limitation: it is &lt;strong&gt;incompatible&lt;/strong&gt; with Rotary Positional Embedding (RoPE):&lt;/li&gt;
&lt;/ul&gt;

\[XW_{\downarrow}^QW_{\uparrow,i}^Q\underbrace{R_mR_n^T}_{\text{hinders merge}}W_{\uparrow,i}^{K^T}C^{KV^T} \quad (\text{RoPE})\]

&lt;ul&gt;
  &lt;li&gt;We can no longer absorb the up-projection matrix $W_{\uparrow}^K$ during inference. Therefore, &lt;strong&gt;during inference,&lt;/strong&gt; we must recompute the keys for all preceding tokens, which decreases inference efficiency.&lt;/li&gt;
  &lt;li&gt;To fix this, we introduce additional multihead queries &amp;amp; a shared key in order to effectively encode position information when using RoPE.&lt;/li&gt;
  &lt;li&gt;We concatenate the original query/key vectors and the rotated ones as input to the MHA computation. This approach is known as &lt;strong&gt;decoupled RoPE.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;MLA is used in recent LLMs such as Deepseek V3 and R1, Kimi K2, GLM-5, Ling 2.5, Mistral Large 3, Sarvam 105B, etc.&lt;/li&gt;
  &lt;li&gt;However, as we generate more tokens, we must calculate attention between the current token and all preceding tokens. This results in &lt;strong&gt;slower token-per-second throughput.&lt;/strong&gt; To fix this, Deepseek proposed a &lt;strong&gt;lightning indexer.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;sliding-window-attention-swa&quot;&gt;Sliding Window Attention (SWA)&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Instead of attending to the entire prefix, each token only attends to a &lt;strong&gt;fixed window&lt;/strong&gt; of recent tokens around its position.&lt;/li&gt;
  &lt;li&gt;SWA reduces the memory &amp;amp; compute cost of long-context inference (slower tok/s throughput) by limiting how many previous tokens each position can attend to.&lt;/li&gt;
  &lt;li&gt;Because attention is restricted to a local token neighborhood, this mechanism is often referred to as &lt;strong&gt;local attention.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div style=&quot;text-align: center; margin-bottom: 24px; break-inside: avoid; display: inline-block; width: 100%;&quot;&gt;
  &lt;!-- Main Title Style --&gt;
  &lt;h3 style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 1.4rem; font-weight: 600; margin-bottom: 16px; color: #1a1a1a;&quot;&gt;
    Regular (causal) self-attention mask vs Sliding window attention
  &lt;/h3&gt;

  &lt;!-- Figure and Image Container --&gt;
  &lt;figure style=&quot;margin: 0; padding: 0;&quot;&gt;
    &lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!edFZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48a9cd31-24a5-47d9-ae37-506763ebc67d_1292x704.png&quot; alt=&quot;Causal Self Attention and Sliding Window Attention&quot; style=&quot;max-width: 100%; width: 450px; height: auto; display: block; margin: 0 auto 12px;&quot; /&gt;
    &lt;!-- Captions with Light-Muted Source Styling --&gt;
    &lt;figcaption style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 0.85rem; color: #666666; line-height: 1.4; max-width: 500px; margin: 0 auto; text-align: center;&quot;&gt;
      Figure 3: Causal Self Attention vs Sliding Window Attention (Window = 3). 
      &lt;span style=&quot;color: #999999; font-size: 0.8rem;&quot;&gt;(Source: &lt;a href=&quot;https://magazine.sebastianraschka.com/p/visual-attention-variants?r=2u5avh&amp;amp;utm_medium=ios&amp;amp;triedRedirect=true&quot; style=&quot;color: #666666; text-decoration: underline;&quot;&gt;Sebastian Raschka&lt;/a&gt;)&lt;/span&gt;
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Regular (causal) self-attention mask:&lt;/u&gt;&lt;/strong&gt; each token attends to all preceding tokens.&lt;br /&gt;
&lt;strong&gt;&lt;u&gt;Sliding Window Attention mask (window = 3):&lt;/u&gt;&lt;/strong&gt; each token attends only to itself and the previous two tokens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;deepseek-sparse-attention-dsa&quot;&gt;Deepseek Sparse Attention (DSA)&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Deepseek proposed a &lt;strong&gt;lightning indexer&lt;/strong&gt; to address the slower token-per-second throughput of MLA.&lt;/li&gt;
  &lt;li&gt;The key concept of the lightning indexer is to quickly assess the relevance of each token and select only the most relevant ones for attention computation.&lt;/li&gt;
  &lt;li&gt;The selected tokens are not determined by a fixed-width local window like in SWA, but by &lt;strong&gt;an indexer-plus-selector setup&lt;/strong&gt; (top-k selector), where a lightning indexer computes index relevance scores, and a token selector keeps only a smaller set of high-scoring past positions &lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;

\[I^{t,s} = \sum_{j=1}^{n_h} w_j^t \, \text{ReLU}(q_j^t \cdot k_j^s) \equiv \text{Index score}\]

&lt;ul&gt;
  &lt;li&gt;Apply partial RoPE to $W^Q$ and $W^K$ before rotation &amp;amp; quantization, then lightning indexing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;quantization--rotation-in-dsa&quot;&gt;Quantization &amp;amp; Rotation in DSA&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;How is the attention computation of the indexer between the query vector and all the key vectors made efficient?&lt;/li&gt;
  &lt;li&gt;The key step is to first &lt;strong&gt;quantize&lt;/strong&gt; the query &amp;amp; key vectors into 8-bit (BF16/FP32 → FP8) representation.&lt;/li&gt;
  &lt;li&gt;Quantization offers a coarse approximation, greatly speeding up the calculation of index scores, and is fine for use since we only want to find the most relevant tokens rather than compute the exact attention scores.&lt;/li&gt;
  &lt;li&gt;However, naive quantization of the query &amp;amp; key vectors may result in inaccuracy. To mitigate this issue, we need to apply a &lt;strong&gt;rotation&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Floating point formats with higher precision (float16 or above) can accurately represent the full dynamic range of values of $q^t$ and $k^s$ ($q^t = 0.85, -0.000000017$; $k^s = -0.99, 0.000001$). But this is problematic when we quantize these vectors with lower precision.&lt;/li&gt;
&lt;/ul&gt;

\[S_{orig} = q^t \cdot k^s \quad (\text{in FP32/FP16/BF16})\]

\[S_{direct} = \text{Quantize}(q^t) \cdot \text{Quantize}(k^s)\]

&lt;ul&gt;
  &lt;li&gt;Multiplying the query &amp;amp; key vectors with a &lt;strong&gt;random orthogonal matrix&lt;/strong&gt; $R$ reduces the range of values, but this requires another matrix multiplication: $q^tR, k^sR$.&lt;/li&gt;
  &lt;li&gt;A simpler &amp;amp; even more effective approach is to use the &lt;strong&gt;Hadamard transform&lt;/strong&gt; $H$, which yields deterministically computed output vectors that exhibit uniform mixing across all coordinates.&lt;/li&gt;
  &lt;li&gt;The Hadamard transform effectively spreads out large spikes across all coordinates, allowing the quantizer to retain more information.&lt;/li&gt;
  &lt;li&gt;The Hadamard transform can be implemented efficiently with highly optimized GPU kernels.&lt;/li&gt;
  &lt;li&gt;For implementation, a &lt;strong&gt;Fast Walsh-Hadamard Transform (FWHT)&lt;/strong&gt; is used instead of a dense Hadamard matrix, $H_D$ &lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;

\[H_D = \begin{bmatrix} 1 &amp;amp; 1 &amp;amp; 1 &amp;amp; 1 \\ 1 &amp;amp; -1 &amp;amp; 1 &amp;amp; -1 \\ 1 &amp;amp; 1 &amp;amp; -1 &amp;amp; -1 \\ 1 &amp;amp; -1 &amp;amp; -1 &amp;amp; 1 \end{bmatrix}\frac{1}{\sqrt{4}} \quad \text{for a 4-head DSA}\]

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

\[q^t = [0.999, -0.001, 0.001, -0.001]\]

\[k^s = [-0.001, 0.897, -0.001, 0.001]\]

\[\begin{aligned}
S_{orig} &amp;amp;= q^t \cdot k^s \\
S_{direct} &amp;amp;= \text{Quantize}(q^t) \cdot \text{Quantize}(k^s) \\
S_{rotated-R} &amp;amp;= \text{Quantize}(q^tR) \cdot \text{Quantize}(k^sR) \\
S_{rotated-H} &amp;amp;= \text{Quantize}(q^tH) \cdot \text{Quantize}(k^sH)
\end{aligned}\]

\[\text{MAE}(S_{orig}, S_{direct}) = 0.0112, \quad \text{std} = 0.0085\]

\[\text{MAE}(S_{orig}, S_{rotated-R}) = 0.0074, \quad \text{std} = 0.0061\]

\[\text{MAE}(S_{orig}, S_{rotated-H}) = 0.0025, \quad \text{std} = 0.0020\]

&lt;ul&gt;
  &lt;li&gt;DSA’s use of a lightning indexer to efficiently identify the most relevant tokens allows for 2 to 3x faster processing of long sequences, while reducing memory consumption by ~30 to 40%. This efficiency boost maintains the same performance level as previous models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;dsa-training&quot;&gt;DSA Training&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;I) Dense warm-up stage&lt;/strong&gt;: First, we freeze the main MLA layer &amp;amp; only update the parameters of the lightning indexer.&lt;/p&gt;

\[L^I = \sum_t D_{KL}\left(p^{t,:}, \text{Softmax}(I^{t,:})\right)\]

\[\begin{aligned}
\text{where} \\
p^{t,:} &amp;amp;\equiv \text{target distribution} \\
\text{Softmax}(I^{t,:}) &amp;amp;\equiv \text{prediction distribution}
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;The target distribution is obtained by summing the mean attention scores across all heads for each query token, followed by applying L1 normalization along the sequence dimension.&lt;/li&gt;
  &lt;li&gt;This dense warm-up stage aligns the indexer’s outputs with the main attention distribution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;II) Sparse stage&lt;/strong&gt;: After warming up the indexer, we add fine-grained token selection and train all the model parameters so that the model can learn the sparse attention patterns of DSA.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;During this stage, we still encourage the indexer to match the main attention distribution, but only for the selected tokens.&lt;/li&gt;
  &lt;li&gt;We also detach the indexer inputs from the computational graph, allowing us to optimize it separately.&lt;/li&gt;
  &lt;li&gt;The indexer is trained only on its own loss, while the main model is optimized solely on the language modeling loss.&lt;/li&gt;
&lt;/ul&gt;

\[L^I = \sum_t D_{KL}\left(p^{t,S^t}, \text{Softmax}(I^{t,S^t})\right)\]

&lt;hr /&gt;

&lt;h2 id=&quot;appendix-1&quot;&gt;Appendix&lt;/h2&gt;

&lt;h3 id=&quot;citation&quot;&gt;Citation&lt;/h3&gt;

&lt;p&gt;If you found this blog post helpful, please consider citing it:&lt;/p&gt;

&lt;div class=&quot;language-bibtex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;@article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;obasi2026attentionEfficientScalable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;How Attention Became Efficient &amp;amp; Scalable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Obasi, Chizoba&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;chizkidd.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;year&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2026&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;month&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Aug&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://chizkidd.github.io/2026/08/05/attention-efficient-scalable/&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Sebastian Raschka. &lt;a href=&quot;https://magazine.sebastianraschka.com/p/visual-attention-variants&quot;&gt;A Visual Guide to Attention Variants in Modern LLMs&lt;/a&gt;. magazine.sebastianraschka.com. 2026. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://youtu.be/Y-o545eYjXM?is=XhXOUKird-8i7ZKb&quot;&gt;How Attention Got So Efficient (YouTube Video)&lt;/a&gt;. YouTube. 2025. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Wed, 05 Aug 2026 00:00:00 +0000</pubDate>
        <link>https://chizkidd.github.io//2026/08/05/attention-efficient-scalable/</link>
        <guid isPermaLink="true">https://chizkidd.github.io//2026/08/05/attention-efficient-scalable/</guid>
        
        
      </item>
    
      <item>
        <title>Policy Gradient Methods: REINFORCE, Actor-Critic, and the Policy Gradient Theorem (S&amp;B Ch. 13)</title>
        <description>&lt;ul&gt;
  &lt;li&gt;Almost all the algorithms/methods covered so far have been &lt;strong&gt;action-value methods&lt;/strong&gt; (except gradient-bandit algorithms, &lt;a href=&quot;https://chizkidd.github.io/RL-Sutton-Barto-notes/chapters/ch02-multi-armed-bandits.html#sec-ch02-2-8&quot;&gt;Section 2.8&lt;/a&gt;).&lt;/li&gt;
  &lt;li&gt;Action-value methods learn the values of actions and then derive the policy thereafter to select actions based on their estimated action values.&lt;/li&gt;
  &lt;li&gt;Here, we explicitly learn a &lt;strong&gt;parametrized policy&lt;/strong&gt; that can select actions without consulting a value function.&lt;/li&gt;
  &lt;li&gt;A value function is not required for action selection, but still could be used to learn the policy parameters $\boldsymbol{\theta}$.&lt;/li&gt;
  &lt;li&gt;The parametrized policy $\pi(a \vert s, \boldsymbol{\theta})$ is now the probability that action $a$ is taken at time $t$ given that the environment is in state $s$ at time $t$ with parameter $\boldsymbol{\theta} \in \mathbb{R}^{d’}$:&lt;/li&gt;
&lt;/ul&gt;

\[\pi(a \vert s, \boldsymbol{\theta}) = \Pr\{A_t = a \vert S_t = s,\ \boldsymbol{\theta}_t = \boldsymbol{\theta}\}\]

&lt;ul&gt;
  &lt;li&gt;We will consider methods for learning $\boldsymbol{\theta}$ based on the gradient of some scalar performance measure $J(\boldsymbol{\theta})$ w.r.t. $\boldsymbol{\theta}$. The goal is to &lt;strong&gt;maximize&lt;/strong&gt; performance, hence the use of gradient ascent:&lt;/li&gt;
&lt;/ul&gt;

\[\boldsymbol{\theta}_{t+1} = \boldsymbol{\theta}_t + \alpha \widehat{\nabla J(\boldsymbol{\theta}_t)}\]

\[\begin{aligned}
\text{where} \\
\widehat{\nabla J(\boldsymbol{\theta}_t)} &amp;amp;\in \mathbb{R}^{d&apos;} \equiv \text{a stochastic estimate whose expectation approximates} \\
&amp;amp;\phantom{{}\equiv{}} \text{the gradient of the performance measure } J \text{ w.r.t. } \boldsymbol{\theta}_t
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;This general methodology applies to &lt;strong&gt;policy gradient methods&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Methods that learn approximations to both policy &amp;amp; value functions are often called &lt;strong&gt;Actor-Critic methods&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Actor&lt;/strong&gt; refers to the learned policy.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Critic&lt;/strong&gt; refers to the learned value function (state-value function).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#131-policy-approximation--its-advantages&quot;&gt;13.1 Policy Approximation &amp;amp; Its Advantages&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#132-the-policy-gradient-theorem&quot;&gt;13.2 The Policy Gradient Theorem&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#133-reinforce-monte-carlo-policy-gradient&quot;&gt;13.3 REINFORCE: Monte Carlo Policy Gradient&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#134-reinforce-with-baseline&quot;&gt;13.4 REINFORCE with Baseline&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#135-actor-critic-methods&quot;&gt;13.5 Actor-Critic Methods&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#136-policy-gradient-for-continuing-problems&quot;&gt;13.6 Policy Gradient for Continuing Problems&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#137-policy-parametrization-for-continuous-actions&quot;&gt;13.7 Policy Parametrization for Continuous Actions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#138-summary&quot;&gt;13.8 Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;appendix&quot;&gt;Appendix&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#citation&quot;&gt;Citation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;131-policy-approximation--its-advantages&quot;&gt;13.1 Policy Approximation &amp;amp; Its Advantages&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;In policy gradient methods, the policy can be parametrized in any way, as long as $\pi(a \vert s, \boldsymbol{\theta})$ is differentiable w.r.t. $\boldsymbol{\theta}$ (essentially the partial derivatives’ column vector exists and is finite):&lt;/li&gt;
&lt;/ul&gt;

\[\nabla \pi(a \vert s, \boldsymbol{\theta}) \text{ exists and is finite for all } s \in S,\ a \in A(s),\ \boldsymbol{\theta} \in \mathbb{R}^{d&apos;}\]

&lt;ul&gt;
  &lt;li&gt;If the action-space is &lt;strong&gt;discrete&lt;/strong&gt; and not too large, then a natural &amp;amp; common kind of parametrization is to form parametrized &lt;strong&gt;numerical preferences&lt;/strong&gt; $h(s, a, \boldsymbol{\theta}) \in \mathbb{R}$ for each $(s, a)$ pair.&lt;/li&gt;
  &lt;li&gt;The actions with the highest preferences in each state are given the highest probabilities of being selected, for example, according to an exponential soft-max distribution:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\pi(a \vert s, \boldsymbol{\theta}) \doteq \frac{e^{h(s,a,\boldsymbol{\theta})}}{\sum_b e^{h(s,b,\boldsymbol{\theta})}}}\]

&lt;ul&gt;
  &lt;li&gt;This kind of policy parametrization is called &lt;strong&gt;softmax in action preferences&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;The action preferences can be parametrized arbitrarily, they can be computed by a deep artificial neural network (ANN) or could simply be &lt;strong&gt;linear&lt;/strong&gt; in features:&lt;/li&gt;
&lt;/ul&gt;

\[h(s, a, \boldsymbol{\theta}) = \boldsymbol{\theta}^T \mathbf{x}(s, a)\]

&lt;p&gt;&lt;strong&gt;Advantages of softmax in action preferences policy parametrization:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The approx. policy can approach a &lt;strong&gt;deterministic policy&lt;/strong&gt;, unlike $\varepsilon$-greedy action selection.&lt;/li&gt;
  &lt;li&gt;It enables the selection of actions with &lt;strong&gt;arbitrary probabilities&lt;/strong&gt;, which is useful for cases that require near-optimal stochastic policy (e.g. significant function approximation).
    &lt;ul&gt;
      &lt;li&gt;E.g. useful in environments with imperfect information (e.g., card games) where it is optimal to act stochastically, such as when &lt;strong&gt;bluffing in Poker&lt;/strong&gt;; it is important to do so randomly to unnerve/confuse an opponent.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The policy from policy parametrization may be a &lt;strong&gt;simpler function to approximate&lt;/strong&gt; than the action-value function.&lt;/li&gt;
  &lt;li&gt;Often the most important reason for using a policy-based learning method is that it is a good way to &lt;strong&gt;inject prior knowledge&lt;/strong&gt; about the desired form of the policy into the RL system.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;132-the-policy-gradient-theorem&quot;&gt;13.2 The Policy Gradient Theorem&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Policy-gradient methods have stronger convergence guarantees compared to action-value methods due to &lt;strong&gt;smooth continuity&lt;/strong&gt; in change of action probabilities as a function of the learned parameter (gradient ascent).&lt;/li&gt;
  &lt;li&gt;Let’s consider the &lt;strong&gt;episodic&lt;/strong&gt; performance measure, which is the value of the start state of the episode:&lt;/li&gt;
&lt;/ul&gt;

\[J(\boldsymbol{\theta}) \doteq v_{\pi_{\boldsymbol{\theta}}}(s_0)\]

\[\begin{aligned}
\text{where} \\
v_{\pi_{\boldsymbol{\theta}}} &amp;amp;\equiv \text{the true value function for } \pi_{\boldsymbol{\theta}}\text{, the policy determined by } \boldsymbol{\theta} \\
s_0 &amp;amp;\equiv \text{some non-random state}
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;How can we estimate the performance gradient w.r.t. the policy parameter when the gradient depends on the unknown effect of policy changes on the state distribution?
    &lt;ul&gt;
      &lt;li&gt;The theoretical answer is the &lt;strong&gt;policy gradient theorem&lt;/strong&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The policy gradient theorem, which provides an analytic expression for the performance gradient w.r.t. the policy parameter, for the episodic case establishes that:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\nabla J(\boldsymbol{\theta}) \propto \sum_s \mu(s) \sum_a q_\pi(s,a)\, \nabla\pi(a \vert s, \boldsymbol{\theta})}\]

\[\begin{aligned}
  \text{where} \quad &amp;amp;\\
  \nabla J(\boldsymbol{\theta}) &amp;amp;\equiv \text{column vectors of partial derivatives w.r.t. the components of } \boldsymbol{\theta} \\
  \mu &amp;amp;\equiv \text{the on-policy distribution under the policy } \pi \\
  &amp;amp;\phantom{{}\equiv{}} \text{(in the episodic case, proportionality constant is the average length of an episode;} \\
  &amp;amp;\phantom{{}\equiv{}} \text{in the continuing case it is 1)}
  \end{aligned}\]

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Proof: Policy Gradient Theorem (Episodic Case)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Let’s prove the policy gradient theorem from first principles using elementary calculus.&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To keep the notation simple, we leave it implicit in all cases that $\pi = f(\boldsymbol{\theta})$ and all gradients $\nabla[…]$ are also implicit w.r.t. $\boldsymbol{\theta}$.&lt;/p&gt;

\[\begin{align*}
\nabla v_\pi(s) &amp;amp;= \nabla \!\left[\sum_a \pi(a \vert s)\, q_\pi(s,a)\right], \quad \text{for all } s \in \mathcal{S} \\
&amp;amp;= \sum_a \!\Biggl[\nabla\pi(a \vert s)\, q_\pi(s,a) + \pi(a \vert s)\, \nabla q_\pi(s,a)\Biggr] \\
&amp;amp;= \sum_a \!\left[\nabla\pi(a \vert s)\, q_\pi(s,a) + \pi(a \vert s)\, \nabla \sum_{s&apos;,r} p(s&apos;,r \vert s,a)\!\left(r + v_\pi(s&apos;)\right)\right] \\
&amp;amp;= \sum_a \!\left[\nabla\pi(a \vert s)\, q_\pi(s,a) + \pi(a \vert s) \sum_{s&apos;} p(s&apos; \vert s,a)\, \nabla v_\pi(s&apos;)\right] \\
&amp;amp;= \sum_a \Biggl[\nabla\pi(a \vert s)\, q_\pi(s,a) + \pi(a \vert s) \sum_{s&apos;} p(s&apos; \vert s,a) \sum_{a&apos;} \Biggl[\nabla\pi(a&apos; \vert s&apos;)\, q_\pi(s&apos;,a&apos;) \\
&amp;amp;\qquad\qquad + \pi(a&apos; \vert s&apos;) \sum_{s&apos;&apos;} p(s&apos;&apos; \vert s&apos;,a&apos;)\, \nabla v_\pi(s&apos;&apos;)\Biggr]\Biggr]
\end{align*}\]

&lt;p&gt;Unrolling this recursion:&lt;/p&gt;

\[\boxed{\nabla v_\pi(s) = \sum_{x \in \mathcal{S}} \sum_{k=0}^{\infty} \Pr(s \to x, k, \pi) \sum_a \nabla\pi(a \vert x)\, q_\pi(x,a)}\]

\[\begin{aligned}
\Pr(s \to x, k, \pi) &amp;amp;\equiv \text{probability of transitioning from state } s \text{ to state } x \text{ in } k \text{ steps under policy } \pi
\end{aligned}\]

&lt;p&gt;Then:&lt;/p&gt;

\[\begin{align*}
\nabla J(\boldsymbol{\theta}) &amp;amp;= \nabla v_\pi(s_0) \\
&amp;amp;= \sum_s \!\left(\sum_{k=0}^{\infty} \Pr(s_0 \to s, k, \pi)\right) \sum_a \nabla\pi(a \vert s)\, q_\pi(s,a) \\
&amp;amp;= \sum_s \eta(s) \sum_a \nabla\pi(a \vert s)\, q_\pi(s,a) \\
&amp;amp;= \sum_{s&apos;} \eta(s&apos;) \sum_s \frac{\eta(s)}{\sum_{s&apos;} \eta(s&apos;)} \sum_a \nabla\pi(a \vert s)\, q_\pi(s,a) \\
&amp;amp;= \sum_{s&apos;} \eta(s&apos;) \sum_s \mu(s) \sum_a \nabla\pi(a \vert s)\, q_\pi(s,a)
\end{align*}\]

\[\boxed{\nabla J(\boldsymbol{\theta}) \propto \sum_s \mu(s) \sum_a \nabla\pi(a \vert s)\, q_\pi(s,a)}\]

  &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;133-reinforce-monte-carlo-policy-gradient&quot;&gt;13.3 REINFORCE: Monte Carlo Policy Gradient&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;REINFORCE is our first policy gradient algorithm.&lt;/li&gt;
  &lt;li&gt;The goal/strategy is to find a way to get samples such that the expectation of the sample gradient is proportional to the actual performance gradient as a function of the parameters.&lt;/li&gt;
  &lt;li&gt;The sample gradients need to only be proportional to the performance gradient because any proportionality constant can be absorbed into the step size $\alpha$.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The policy gradient theorem gives &lt;strong&gt;an exact expression proportional to the gradient&lt;/strong&gt;; so all that is needed is a way of sampling whose expectation equals or approximates this expression.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Recall the RHS of the policy gradient theorem is a sum over states weighted by how often the states occur under the target policy $\pi$:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
\nabla J(\boldsymbol{\theta}) &amp;amp;\propto \sum_s \mu(s) \sum_a q_\pi(s,a)\, \nabla\pi(a \vert s, \boldsymbol{\theta}) \\
&amp;amp;= \mathbb{E}_\pi \!\left[\sum_a q_\pi(S_t, a)\, \nabla\pi(a \vert S_t, \boldsymbol{\theta})\right]
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;We get REINFORCE by replacing the sum over the random variable’s possible values by an expectation under $\pi$, and then sampling the expectation:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
\nabla J(\boldsymbol{\theta}) &amp;amp;\propto \mathbb{E}_\pi \!\left[\sum_a \pi(a \vert S_t, \boldsymbol{\theta})\, q_\pi(S_t, a)\, \frac{\nabla\pi(a \vert S_t, \boldsymbol{\theta})}{\pi(a \vert S_t, \boldsymbol{\theta})}\right] \\
&amp;amp;= \mathbb{E}_\pi \!\left[q_\pi(S_t, A_t)\, \frac{\nabla\pi(A_t \vert S_t, \boldsymbol{\theta})}{\pi(A_t \vert S_t, \boldsymbol{\theta})}\right] \quad \text{(replacing } a \text{ by the sample } A_t \sim \pi\text{)} \\
&amp;amp;= \mathbb{E}_\pi \!\left[G_t\, \frac{\nabla\pi(A_t \vert S_t, \boldsymbol{\theta})}{\pi(A_t \vert S_t, \boldsymbol{\theta})}\right] \quad \text{(because } \mathbb{E}_\pi[G_t \vert S_t, A_t] = q_\pi(S_t, A_t)\text{)}
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;The last expression is the required expression; a quantity that can be sampled on each time step whose expectation is proportional to the gradient. This leads to the &lt;strong&gt;REINFORCE update&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\boldsymbol{\theta}_{t+1} \doteq \boldsymbol{\theta}_t + \alpha G_t \frac{\nabla\pi(A_t \vert S_t, \boldsymbol{\theta}_t)}{\pi(A_t \vert S_t, \boldsymbol{\theta}_t)}}\]

&lt;ul&gt;
  &lt;li&gt;This update is intuitively coherent:
    &lt;ul&gt;
      &lt;li&gt;The gradient term represents the direction in parameter space that most increases the probability of repeating/taking again the same action $A_t$ in the future.&lt;/li&gt;
      &lt;li&gt;The update moves the parameter the most in the directions that favour actions that yield the highest return.&lt;/li&gt;
      &lt;li&gt;The update ensures a balancing act (lower impact) of frequently selected actions with lower returns.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;REINFORCE has good convergence properties but may be of high variance and slow learning as a MC method.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;reinforce-monte-carlo-policy-gradient-control-episodic-for-pi_&quot;&gt;REINFORCE: Monte Carlo Policy Gradient Control (Episodic) for $\pi_*$&lt;/h4&gt;

\[\boxed{
\begin{aligned}
&amp;amp;\textbf{Input: } \text{a differentiable policy parametrization } \pi(a \vert s, \boldsymbol{\theta}) \\
&amp;amp;\textbf{Algorithm parameter: } \text{step size } \alpha &amp;gt; 0 \\
&amp;amp;\textbf{Initialize } \text{policy parameter } \boldsymbol{\theta} \in \mathbb{R}^{d&apos;} \text{ (e.g., to } \mathbf{0}\text{)} \\
&amp;amp;\textbf{Loop forever } \text{(for each episode):} \\
&amp;amp;\quad \text{Generate an episode } S_0, A_0, R_1, \ldots, S_{T-1}, A_{T-1}, R_T \text{ following } \pi(\cdot \vert \cdot, \boldsymbol{\theta}) \\
&amp;amp;\quad \textbf{Loop for each step of the episode } t = 0, 1, 2, \ldots, T-1\text{:} \\
&amp;amp;\qquad G \leftarrow \sum_{k=t+1}^{T} \gamma^{k-t-1} R_k \\
&amp;amp;\qquad \boldsymbol{\theta} \leftarrow \boldsymbol{\theta} + \alpha\gamma^t G\, \nabla \ln \pi(A_t \vert S_t, \boldsymbol{\theta})
\end{aligned}
}\]

&lt;hr /&gt;

&lt;h2 id=&quot;134-reinforce-with-baseline&quot;&gt;13.4 REINFORCE with Baseline&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;The policy gradient theorem can be generalized to include a comparison of the action value to an arbitrary &lt;strong&gt;baseline&lt;/strong&gt; $b(s)$:&lt;/li&gt;
&lt;/ul&gt;

\[\nabla J(\boldsymbol{\theta}) \propto \sum_s \mu(s) \sum_a \!\left[q_\pi(s,a) - b(s)\right] \nabla\pi(a \vert s, \boldsymbol{\theta})\]

&lt;ul&gt;
  &lt;li&gt;The baseline can be any function that is not dependent on the action $a$:&lt;/li&gt;
&lt;/ul&gt;

\[\sum_a b(s)\, \nabla\pi(a \vert s, \boldsymbol{\theta}) = b(s)\, \nabla \sum_a \pi(a \vert s, \boldsymbol{\theta}) = b(s)\, \nabla 1 = 0\]

&lt;ul&gt;
  &lt;li&gt;Now the REINFORCE update with baseline is:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\boldsymbol{\theta}_{t+1} \doteq \boldsymbol{\theta}_t + \alpha \!\left[G_t - b(S_t)\right] \frac{\nabla\pi(A_t \vert S_t, \boldsymbol{\theta}_t)}{\pi(A_t \vert S_t, \boldsymbol{\theta}_t)}}\]

&lt;ul&gt;
  &lt;li&gt;The Baseline REINFORCE update &lt;strong&gt;reduces the variance&lt;/strong&gt; despite leaving the expected update value unchanged, which speeds up learning.&lt;/li&gt;
  &lt;li&gt;One natural choice for the baseline is an estimate of the state value $\hat{v}(S_t, \mathbf{w})$.&lt;/li&gt;
  &lt;li&gt;Since REINFORCE is a Monte Carlo method for learning the policy parameter $\boldsymbol{\theta}$, it’s natural to also use a Monte Carlo method to learn the state-value weights $\mathbf{w}$.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;reinforce-with-baseline-episodic-for-estimating-pi_boldsymboltheta-approx-pi_&quot;&gt;REINFORCE with Baseline (Episodic), for estimating $\pi_{\boldsymbol{\theta}} \approx \pi_*$&lt;/h4&gt;

\[\boxed{
\begin{aligned}
&amp;amp;\textbf{Input: } \text{a differentiable policy parametrization } \pi(a \vert s, \boldsymbol{\theta}) \\
&amp;amp;\textbf{Input: } \text{a differentiable state-value function parametrization } \hat{v}(s, \mathbf{w}) \\
&amp;amp;\textbf{Algorithm parameters: } \text{step sizes } \alpha^{\boldsymbol{\theta}} &amp;gt; 0,\ \alpha^{\mathbf{w}} &amp;gt; 0 \\
&amp;amp;\textbf{Initialize } \text{policy parameter } \boldsymbol{\theta} \in \mathbb{R}^{d&apos;} \text{ and state-value weights } \mathbf{w} \in \mathbb{R}^d \text{ (e.g., to } \mathbf{0}\text{)} \\
&amp;amp;\textbf{Loop forever } \text{(for each episode):} \\
&amp;amp;\quad \text{Generate an episode } S_0, A_0, R_1, \ldots, S_{T-1}, A_{T-1}, R_T \text{ following } \pi(\cdot \vert \cdot, \boldsymbol{\theta}) \\
&amp;amp;\quad \textbf{Loop for each step of the episode } t = 0, 1, \ldots, T-1\text{:} \\
&amp;amp;\qquad G \leftarrow \sum_{k=t+1}^{T} \gamma^{k-t-1} R_k \\
&amp;amp;\qquad \delta \leftarrow G - \hat{v}(S_t, \mathbf{w}) \\
&amp;amp;\qquad \mathbf{w} \leftarrow \mathbf{w} + \alpha^{\mathbf{w}}\, \delta\, \nabla \hat{v}(S_t, \mathbf{w}) \\
&amp;amp;\qquad \boldsymbol{\theta} \leftarrow \boldsymbol{\theta} + \alpha^{\boldsymbol{\theta}} \gamma^t\, \delta\, \nabla \ln \pi(A_t \vert S_t, \boldsymbol{\theta})
\end{aligned}
}\]

&lt;ul&gt;
  &lt;li&gt;This algorithm has 2 step sizes $\alpha^{\boldsymbol{\theta}}$ and $\alpha^{\mathbf{w}}$.&lt;/li&gt;
  &lt;li&gt;Choosing $\alpha^{\mathbf{w}}$ is relatively easy; in the linear case a good rule of thumb is (see &lt;a href=&quot;https://chizkidd.github.io/RL-Sutton-Barto-notes/chapters/ch09-on-policy-prediction-approximation.html#sec-ch09-9-6&quot;&gt;Section 9.6&lt;/a&gt;):&lt;/li&gt;
&lt;/ul&gt;

\[\alpha^{\mathbf{w}} = \frac{0.1}{\mathbb{E}\!\left[\|\nabla \hat{v}(S_t, \mathbf{w})\|_\mu^2\right]}\]

&lt;ul&gt;
  &lt;li&gt;Choosing $\alpha^{\boldsymbol{\theta}}$ is much less clear since its best value depends on the range of variation of the rewards and on the policy parametrization.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;135-actor-critic-methods&quot;&gt;13.5 Actor-Critic Methods&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;REINFORCE with baseline cannot be used to evaluate actions because its state-value function &lt;strong&gt;only estimates the value of the 1st state&lt;/strong&gt; of each state transition (1st state to 2nd state), which serves as a baseline of what to expect for the subsequent return to be, prior to the actual transition’s action.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Actor-critic methods, however, apply the state-value function to the 2nd state&lt;/strong&gt; of the transition thereby estimating its value and thus &lt;strong&gt;evaluating the action&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;The policy is the &lt;strong&gt;actor&lt;/strong&gt; that maps states to actions, while the state-value function used to assess actions in this way is the &lt;strong&gt;critic&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;The estimated value of the 2nd state, when discounted &amp;amp; added to the reward, yields the &lt;strong&gt;one-step return&lt;/strong&gt;, $G_{t:t+1}$.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;1351-one-step-actor-critic-methods&quot;&gt;13.5.1 One-Step Actor-Critic Methods&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;One-step actor-critic methods replace the REINFORCE full return with the one-step return and use a learned state-value function as the baseline as follows:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
\boldsymbol{\theta}_{t+1} &amp;amp;\doteq \boldsymbol{\theta}_t + \alpha \!\left[G_{t:t+1} - \hat{v}(S_t, \mathbf{w})\right] \frac{\nabla\pi(A_t \vert S_t, \boldsymbol{\theta}_t)}{\pi(A_t \vert S_t, \boldsymbol{\theta}_t)} \\
&amp;amp;= \boldsymbol{\theta}_t + \alpha \!\left[R_{t+1} + \gamma \hat{v}(S_{t+1}, \mathbf{w}) - \hat{v}(S_t, \mathbf{w})\right] \frac{\nabla\pi(A_t \vert S_t, \boldsymbol{\theta}_t)}{\pi(A_t \vert S_t, \boldsymbol{\theta}_t)} \\
&amp;amp;= \boldsymbol{\theta}_t + \alpha\, \delta_t\, \frac{\nabla\pi(A_t \vert S_t, \boldsymbol{\theta}_t)}{\pi(A_t \vert S_t, \boldsymbol{\theta}_t)}
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;The semi-gradient TD(0) could serve as the natural state-value function learning method.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PROS:&lt;/strong&gt; simple, fully online &amp;amp; incremental.&lt;/li&gt;
  &lt;li&gt;It is analogous to TD(0), Sarsa(0) &amp;amp; Q-learning.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;one-step-actor-critic-episodic-for-estimating-pi_boldsymboltheta-approx-pi_&quot;&gt;One-Step Actor-Critic (Episodic), for estimating $\pi_{\boldsymbol{\theta}} \approx \pi_*$&lt;/h4&gt;

\[\boxed{
\begin{aligned}
&amp;amp;\textbf{Inputs: } \text{a differentiable policy } \pi(a \vert s, \boldsymbol{\theta}) \text{ and state-value function } \hat{v}(s, \mathbf{w}) \text{ parametrization} \\
&amp;amp;\textbf{Parameters: } \text{step sizes } \alpha^{\boldsymbol{\theta}} &amp;gt; 0,\ \alpha^{\mathbf{w}} &amp;gt; 0 \\
&amp;amp;\textbf{Initialize } \text{policy parameter } \boldsymbol{\theta} \in \mathbb{R}^{d&apos;} \text{ and state-value weights } \mathbf{w} \in \mathbb{R}^d \text{ (e.g., to } \mathbf{0}\text{)} \\
&amp;amp;\textbf{Loop forever } \text{(for each episode):} \\
&amp;amp;\quad \text{Initialize } S \text{ (1st state of episode)} \\
&amp;amp;\quad I \leftarrow 1 \\
&amp;amp;\quad \textbf{Loop while } S \text{ is not terminal (for each time step):} \\
&amp;amp;\qquad A \sim \pi(\cdot \vert S, \boldsymbol{\theta}) \\
&amp;amp;\qquad \text{Take action } A\text{, observe } S&apos;, R \\
&amp;amp;\qquad \delta \leftarrow R + \gamma \hat{v}(S&apos;, \mathbf{w}) - \hat{v}(S, \mathbf{w}) \quad \text{(if } S&apos; \text{ is terminal, then } \hat{v}(S&apos;, \mathbf{w}) \doteq 0\text{)} \\
&amp;amp;\qquad \mathbf{w} \leftarrow \mathbf{w} + \alpha^{\mathbf{w}}\, \delta\, \nabla \hat{v}(S, \mathbf{w}) \\
&amp;amp;\qquad \boldsymbol{\theta} \leftarrow \boldsymbol{\theta} + \alpha^{\boldsymbol{\theta}} I\, \delta\, \nabla \ln \pi(A \vert S, \boldsymbol{\theta}) \\
&amp;amp;\qquad I \leftarrow \gamma I \\
&amp;amp;\qquad S \leftarrow S&apos;
\end{aligned}
}\]

&lt;h3 id=&quot;1352-actor-critic-with-eligibility-traces&quot;&gt;13.5.2 Actor-Critic with Eligibility Traces&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;We generalize to the forward view of $n$-step methods and then to a $\lambda$-return algorithm.&lt;/li&gt;
  &lt;li&gt;We replace the one-step return $G_{t:t+1}$ by $G_{t:t+n}$ or $G_t^\lambda$ respectively for either $n$-step or $\lambda$-return.&lt;/li&gt;
  &lt;li&gt;The backward view of the $\lambda$-return algorithm uses &lt;strong&gt;eligibility traces&lt;/strong&gt; for the actor and the critic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;actor-critic-with-eligibility-traces-episodic-for-estimating-pi_boldsymboltheta-approx-pi_&quot;&gt;Actor-Critic with Eligibility Traces (Episodic), for estimating $\pi_{\boldsymbol{\theta}} \approx \pi_*$&lt;/h4&gt;

\[\boxed{
\begin{aligned}
&amp;amp;\textbf{Inputs: } \text{a differentiable policy and state-value function parametrization: } \pi(a \vert s, \boldsymbol{\theta}),\ \hat{v}(s, \mathbf{w}) \\
&amp;amp;\textbf{Parameters: } \text{trace-decay rates } \lambda^{\boldsymbol{\theta}} \in [0,1],\ \lambda^{\mathbf{w}} \in [0,1]\text{; step sizes } \alpha^{\boldsymbol{\theta}} &amp;gt; 0,\ \alpha^{\mathbf{w}} &amp;gt; 0 \\
&amp;amp;\textbf{Initialize } \text{policy parameter } \boldsymbol{\theta} \in \mathbb{R}^{d&apos;} \text{ and state-value weights } \mathbf{w} \in \mathbb{R}^d \text{ (e.g., to } \mathbf{0}\text{)} \\
&amp;amp;\textbf{Loop forever } \text{(for each episode):} \\
&amp;amp;\quad \text{Initialize } S \text{ (1st state of episode)} \\
&amp;amp;\quad \mathbf{z}^{\boldsymbol{\theta}} \leftarrow \mathbf{0} \quad \text{($d&apos;$-component eligibility trace vector)} \\
&amp;amp;\quad \mathbf{z}^{\mathbf{w}} \leftarrow \mathbf{0} \quad \text{($d$-component eligibility trace vector)} \\
&amp;amp;\quad \textbf{Loop while } S \text{ is not terminal (for each time step):} \\
&amp;amp;\qquad A \sim \pi(\cdot \vert S, \boldsymbol{\theta}) \\
&amp;amp;\qquad \text{Take action } A\text{, observe } S&apos;, R \quad \text{(if } S&apos; \text{ is terminal, then } \hat{v}(S&apos;, \mathbf{w}) \doteq 0\text{)} \\
&amp;amp;\qquad \delta \leftarrow R + \gamma \hat{v}(S&apos;, \mathbf{w}) - \hat{v}(S, \mathbf{w}) \\
&amp;amp;\qquad \mathbf{z}^{\mathbf{w}} \leftarrow \gamma\lambda^{\mathbf{w}} \mathbf{z}^{\mathbf{w}} + \nabla \hat{v}(S, \mathbf{w}) \\
&amp;amp;\qquad \mathbf{z}^{\boldsymbol{\theta}} \leftarrow \gamma\lambda^{\boldsymbol{\theta}} \mathbf{z}^{\boldsymbol{\theta}} + I\, \nabla \ln \pi(A \vert S, \boldsymbol{\theta}) \\
&amp;amp;\qquad \mathbf{w} \leftarrow \mathbf{w} + \alpha^{\mathbf{w}}\, \delta\, \mathbf{z}^{\mathbf{w}} \\
&amp;amp;\qquad \boldsymbol{\theta} \leftarrow \boldsymbol{\theta} + \alpha^{\boldsymbol{\theta}}\, \delta\, \mathbf{z}^{\boldsymbol{\theta}} \\
&amp;amp;\qquad I \leftarrow \gamma I \\
&amp;amp;\qquad S \leftarrow S&apos;
\end{aligned}
}\]

&lt;hr /&gt;

&lt;h2 id=&quot;136-policy-gradient-for-continuing-problems&quot;&gt;13.6 Policy Gradient for Continuing Problems&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;From &lt;a href=&quot;https://chizkidd.github.io/RL-Sutton-Barto-notes/chapters/ch10-on-policy-control-approximation.html#sec-ch10-10-3&quot;&gt;Section 10.3&lt;/a&gt; on continuing problems, lack of episode boundaries requires a new performance measure definition in terms of the &lt;strong&gt;average rate of reward per time step&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
J(\boldsymbol{\theta}) \doteq r(\pi) &amp;amp;\doteq \lim_{h \to \infty} \frac{1}{h} \sum_{t=1}^{h} \mathbb{E}\!\left[R_t \vert S_0, A_{0:t-1} \sim \pi\right] \\
&amp;amp;= \lim_{t \to \infty} \mathbb{E}\!\left[R_t \vert S_0, A_{0:t-1} \sim \pi\right] \\
&amp;amp;= \sum_s \mu(s) \sum_a \pi(a \vert s) \sum_{s&apos;,r} p(s&apos;,r \vert s,a)\, r
\end{align*}\]

\[\begin{aligned}
\text{where} \\
\mu(s) &amp;amp;\doteq \lim_{t \to \infty} \Pr\{S_t = s \vert A_{0:t} \sim \pi\} \equiv \text{steady state distribution under } \pi\text{,} \\
&amp;amp;\phantom{{}\doteq{}} \text{assumed to exist and be independent of } S_0 \textbf{ [ergodicity assumption]}
\end{aligned}\]

\[\sum_s \mu(s) \sum_a \pi(a \vert s, \boldsymbol{\theta})\, p(s&apos; \vert s,a) = \mu(s&apos;) \quad \text{for all } s&apos; \in S \quad \text{(ergodicity)}\]

&lt;ul&gt;
  &lt;li&gt;In the continuing case, we define values $v_{\pi}(s) \doteq \mathbb{E}_{\pi}[G_t \vert S_t = s]$ and $q_{\pi}(s,a) \doteq \mathbb{E}_{\pi}[G_t \vert S_t = s, A_t = a]$ w.r.t. the &lt;strong&gt;differential return&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

\[G_t \doteq R_{t+1} - r(\pi) + R_{t+2} - r(\pi) + R_{t+3} - r(\pi) + \ldots\]

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Proof: Policy Gradient Theorem (Continuing Case)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Leave the notation implicit in all cases that $\pi = f(\boldsymbol{\theta})$ and that the gradients $\nabla[…]$ are w.r.t. $\boldsymbol{\theta}$. In the continuing case $J(\boldsymbol{\theta}) = r(\pi)$, and $v_\pi$ &amp;amp; $q_\pi$ denote values w.r.t. the &lt;strong&gt;differential return&lt;/strong&gt;.&lt;/p&gt;

\[\begin{align*}
\nabla v_\pi(s) &amp;amp;= \nabla \!\left[\sum_a \pi(a \vert s)\, q_\pi(s,a)\right], \quad \text{for all } s \in \mathcal{S} \\
&amp;amp;= \sum_a \!\left[\nabla\pi(a \vert s)\, q_\pi(s,a) + \pi(a \vert s)\, \nabla q_\pi(s,a)\right] \\
&amp;amp;= \sum_a \!\left[\nabla\pi(a \vert s)\, q_\pi(s,a) + \pi(a \vert s)\, \nabla \sum_{s&apos;,r} p(s&apos;,r \vert s,a)\!\left(r - r(\boldsymbol{\theta}) + v_\pi(s&apos;)\right)\right] \\
&amp;amp;= \sum_a \!\left[\nabla\pi(a \vert s)\, q_\pi(s,a) + \pi(a \vert s)\!\left[-\nabla r(\boldsymbol{\theta}) + \sum_{s&apos;} p(s&apos; \vert s,a)\, \nabla v_\pi(s&apos;)\right]\right]
\end{align*}\]

\[\nabla r(\boldsymbol{\theta}) = \sum_a \!\left[\nabla\pi(a \vert s)\, q_\pi(s,a) + \pi(a \vert s) \sum_{s&apos;} p(s&apos; \vert s,a)\, \nabla v_\pi(s&apos;)\right] - \nabla v_\pi(s)\]

&lt;p&gt;Since $\nabla J(\boldsymbol{\theta})$ does not depend on $s$, we sum over all $s \in \mathcal{S}$ weighted by $\mu(s)$ (because $\sum_s \mu(s) = 1$):&lt;/p&gt;

\[\begin{align*}
\nabla J(\boldsymbol{\theta}) &amp;amp;= \sum_s \mu(s) \Bigl(\sum_a \Bigl[\nabla\pi(a \vert s)\, q_\pi(s,a) + \pi(a \vert s) \sum_{s&apos;} p(s&apos; \vert s,a)\, \nabla v_\pi(s&apos;)\Bigr] - \nabla v_\pi(s)\Bigr) \\
&amp;amp;= \sum_s \mu(s) \sum_a \nabla\pi(a \vert s)\, q_\pi(s,a) \\
&amp;amp;\quad + \sum_{s&apos;} \underbrace{\sum_s \mu(s) \sum_a \pi(a \vert s)\, p(s&apos; \vert s,a)}_{\mu(s&apos;)} \nabla v_\pi(s&apos;) - \sum_s \mu(s)\, \nabla v_\pi(s) \\
&amp;amp;= \sum_s \mu(s) \sum_a \nabla\pi(a \vert s)\, q_\pi(s,a) + \sum_{s&apos;} \mu(s&apos;)\, \nabla v_\pi(s&apos;) - \sum_s \mu(s)\, \nabla v_\pi(s) \\
&amp;amp;= \sum_s \mu(s) \sum_a \nabla\pi(a \vert s)\, q_\pi(s,a) \qquad \text{Q.E.D.}
\end{align*}\]

  &lt;/div&gt;
&lt;/div&gt;

&lt;h4 id=&quot;actor-critic-with-eligibility-traces-continuing-for-estimating-pi_boldsymboltheta-approx-pi_&quot;&gt;Actor-Critic with Eligibility Traces (Continuing), for estimating $\pi_{\boldsymbol{\theta}} \approx \pi_*$&lt;/h4&gt;

\[\boxed{
\begin{aligned}
&amp;amp;\textbf{Inputs: } \pi(a \vert s, \boldsymbol{\theta}),\ \hat{v}(s, \mathbf{w}) \\
&amp;amp;\textbf{Parameters: } \lambda^{\mathbf{w}} \in [0,1],\ \lambda^{\boldsymbol{\theta}} \in [0,1],\ \alpha^{\mathbf{w}} &amp;gt; 0,\ \alpha^{\boldsymbol{\theta}} &amp;gt; 0,\ \alpha^{\bar{R}} &amp;gt; 0 \\
&amp;amp;\textbf{Initialize } \bar{R} \in \mathbb{R} \text{ (e.g., to 0)},\ \mathbf{w} \in \mathbb{R}^d\ \&amp;amp;\ \boldsymbol{\theta} \in \mathbb{R}^{d&apos;} \text{ (e.g., to } \mathbf{0}\text{)},\ S \in \mathcal{S} \text{ (e.g., to } s_0\text{)} \\
&amp;amp;\mathbf{z}^{\mathbf{w}} \leftarrow \mathbf{0};\ \mathbf{z}^{\boldsymbol{\theta}} \leftarrow \mathbf{0} \\
&amp;amp;\textbf{Loop forever } \text{(for each time step):} \\
&amp;amp;\quad A \sim \pi(\cdot \vert S, \boldsymbol{\theta}) \\
&amp;amp;\quad \text{Take action } A\text{, observe } S&apos;, R \\
&amp;amp;\quad \delta \leftarrow R - \bar{R} + \hat{v}(S&apos;, \mathbf{w}) - \hat{v}(S, \mathbf{w}) \\
&amp;amp;\quad \bar{R} \leftarrow \bar{R} + \alpha^{\bar{R}}\, \delta \\
&amp;amp;\quad \mathbf{z}^{\mathbf{w}} \leftarrow \lambda^{\mathbf{w}} \mathbf{z}^{\mathbf{w}} + \nabla \hat{v}(S, \mathbf{w}) \\
&amp;amp;\quad \mathbf{z}^{\boldsymbol{\theta}} \leftarrow \lambda^{\boldsymbol{\theta}} \mathbf{z}^{\boldsymbol{\theta}} + \nabla \ln \pi(A \vert S, \boldsymbol{\theta}) \\
&amp;amp;\quad \mathbf{w} \leftarrow \mathbf{w} + \alpha^{\mathbf{w}}\, \delta\, \mathbf{z}^{\mathbf{w}} \\
&amp;amp;\quad \boldsymbol{\theta} \leftarrow \boldsymbol{\theta} + \alpha^{\boldsymbol{\theta}}\, \delta\, \mathbf{z}^{\boldsymbol{\theta}} \\
&amp;amp;\quad S \leftarrow S&apos;
\end{aligned}
}\]

&lt;hr /&gt;

&lt;h2 id=&quot;137-policy-parametrization-for-continuous-actions&quot;&gt;13.7 Policy Parametrization for Continuous Actions&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Policy gradient methods enable us to handle large (and even continuous) action spaces by learning the statistics of the probability distribution instead of computing learned probabilities for each of the many actions.&lt;/li&gt;
  &lt;li&gt;The probability density function for the normal distribution is conventionally written as:&lt;/li&gt;
&lt;/ul&gt;

\[p(x) \doteq \frac{1}{\sigma\sqrt{2\pi}} \exp\!\left(-\frac{(x-\mu)^2}{2\sigma^2}\right)\]

\[\begin{aligned}
\text{where} \\
\mu &amp;amp;\equiv \text{mean of the normal distribution} \\
\sigma &amp;amp;\equiv \text{standard deviation of the normal distribution}
\end{aligned}\]

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/rl-sutton-barto/ch13-13-7-prob-density-function.png&quot; alt=&quot;Probability Density Function&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Probability Density Function (PDF)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;The probability density functions for several different means and standard deviations are shown above.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;$p(x)$ is the &lt;strong&gt;density&lt;/strong&gt; of the probability at $x$, and not the probability. It can be greater than 1; it is the total area under the graph that must sum to 1.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To get the probability of $x$ falling within a range, take the integral under $p(x)$ for that specific range of $x$ values.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;The policy parametrization, defining the policy as the normal probability density over a real-valued scalar action $a$ with mean &amp;amp; standard deviation given by state-dependent, parametric function approximators, is as follows:&lt;/li&gt;
&lt;/ul&gt;

\[\pi(a \vert s, \boldsymbol{\theta}) \doteq \frac{1}{\sigma(s, \boldsymbol{\theta})\sqrt{2\pi}} \exp\!\left(-\frac{(a - \mu(s, \boldsymbol{\theta}))^2}{2\sigma(s, \boldsymbol{\theta})^2}\right)\]

\[\begin{aligned}
\text{where} \\
\mu &amp;amp;: S \times \mathbb{R}^{d&apos;} \to \mathbb{R} \equiv \text{mean function approximator} \\
\sigma &amp;amp;: S \times \mathbb{R}^{d&apos;} \to \mathbb{R}^{+} \equiv \text{standard deviation function approximator}
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;The approximators need a representation form, so we split the policy’s parameter vector into 2 parts, $\boldsymbol{\theta} = [\boldsymbol{\theta}_{\mu}, \boldsymbol{\theta}_{\sigma}]^T$, one for mean approximation and the other for standard deviation approximation.&lt;/li&gt;
  &lt;li&gt;The mean can be approximated as a linear function while the standard deviation can be approximated as the exponential of a linear function:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{aligned}
\mu(s, \boldsymbol{\theta}) &amp;amp;\doteq \boldsymbol{\theta}_\mu^T \mathbf{x}_\mu(s) \\
\sigma(s, \boldsymbol{\theta}) &amp;amp;\doteq \exp\!\left(\boldsymbol{\theta}_\sigma^T \mathbf{x}_\sigma(s)\right)
\end{aligned}\]

&lt;hr /&gt;

&lt;h2 id=&quot;138-summary&quot;&gt;13.8 Summary&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Going from action-value methods to &lt;strong&gt;parametrized policy&lt;/strong&gt; methods that take actions without consulting action-value estimates.&lt;/li&gt;
  &lt;li&gt;More specifically, policy gradient methods update the policy parameter on each step in the direction of an estimate of the performance gradient w.r.t. the policy parameter.&lt;/li&gt;
  &lt;li&gt;Advantages of &lt;strong&gt;parametrized policy&lt;/strong&gt; methods over $\varepsilon$-greedy &amp;amp; action-value methods:
    &lt;ul&gt;
      &lt;li&gt;They can learn specific probabilities for taking actions.&lt;/li&gt;
      &lt;li&gt;They can learn appropriate levels of exploration &amp;amp; approach deterministic policies asymptotically.&lt;/li&gt;
      &lt;li&gt;They can naturally handle continuous action spaces.&lt;/li&gt;
      &lt;li&gt;Important theoretical advantage over action-value methods in the form of the &lt;strong&gt;policy gradient theorem&lt;/strong&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;REINFORCE&lt;/strong&gt; uses the policy gradient theorem with Monte Carlo returns.&lt;/li&gt;
  &lt;li&gt;Addition of a state-value function as a &lt;strong&gt;baseline&lt;/strong&gt; with REINFORCE reduces its variance without introducing bias and speeds up learning.&lt;/li&gt;
  &lt;li&gt;If the state-value function is used to criticize/assess the policy’s action selections, then the value function is called a &lt;strong&gt;critic&lt;/strong&gt; and the policy is called an &lt;strong&gt;actor&lt;/strong&gt;. Overall this is referred to as the &lt;strong&gt;actor-critic method&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;The critic introduces &lt;strong&gt;bias&lt;/strong&gt; into the actor’s gradient estimates, but is still often desirable for the same reason that bootstrapping TD methods are superior to Monte Carlo methods (significant variance reduction).&lt;/li&gt;
  &lt;li&gt;Overall, policy-gradient methods provide a significantly different set of strengths &amp;amp; weaknesses than action-value methods.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;citation&quot;&gt;Citation&lt;/h2&gt;

&lt;p&gt;If you found this blog post helpful, please consider citing it:&lt;/p&gt;

&lt;div class=&quot;language-bibtex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;@article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;obasi2026RLsuttonBartoCh13notes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Sutton &amp;amp; Barto, Ch. 13: Policy Gradient Methods (Personal Notes)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Obasi, Chizoba&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;chizkidd.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;year&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2026&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;month&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;May&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://chizkidd.github.io/2026/05/07/rl-sutton-barto-notes-ch013/&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;
</description>
        <pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate>
        <link>https://chizkidd.github.io//2026/05/07/rl-sutton-barto-notes-ch013/</link>
        <guid isPermaLink="true">https://chizkidd.github.io//2026/05/07/rl-sutton-barto-notes-ch013/</guid>
        
        
      </item>
    
      <item>
        <title>Transformer Architecture Explained: Self-Attention, Encoders, and Decoders</title>
        <description>&lt;ul&gt;
  &lt;li&gt;Transformers are a &lt;strong&gt;sequence-to-sequence model&lt;/strong&gt;: given an input sequence, produce an output sequence.&lt;/li&gt;
  &lt;li&gt;Architecture: an &lt;strong&gt;Encoder&lt;/strong&gt; processes the input; a &lt;strong&gt;Decoder&lt;/strong&gt; generates the output autoregressively.&lt;/li&gt;
&lt;/ul&gt;

\[\text{(En) &quot;I am sorry&quot;} \xrightarrow{\text{Encoder}} \xrightarrow{\text{Decoder}} \texttt{&amp;lt;start&amp;gt;}\ \text{Je suis désolé}\ \texttt{&amp;lt;end&amp;gt;}\]

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Autoregressive&lt;/strong&gt;: the decoder generates one token at a time, conditioning on all previously generated tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#input-text-sequence-representation&quot;&gt;Input Text Sequence Representation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#encoders&quot;&gt;Encoders&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#from-mlp-to-attention&quot;&gt;From MLP to Attention&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#self-attention--multi-head-attention&quot;&gt;Self-Attention &amp;amp; Multi-Head Attention&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#decoders&quot;&gt;Decoders&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#masked-attention&quot;&gt;Masked Attention&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#encoder-decoder-cross-attention&quot;&gt;Encoder-Decoder Cross Attention&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;appendix&quot;&gt;Appendix&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#citation&quot;&gt;Citation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;div style=&quot;text-align: center; margin-bottom: 24px; break-inside: avoid; display: inline-block; width: 100%;&quot;&gt;
  &lt;h3 style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 1.4rem; font-weight: 600; margin-bottom: 16px; color: #1a1a1a;&quot;&gt;
    Transformer architecture
  &lt;/h3&gt;
  &lt;figure style=&quot;margin: 0; padding: 0;&quot;&gt;
    &lt;img src=&quot;https://machinelearningmastery.com/wp-content/uploads/2025/05/Full_Transformer-704x1024.png&quot; alt=&quot;Transformer architecture from the paper “Attention is All You Need”&quot; style=&quot;max-width: 100%; width: 450px; height: auto; display: block; margin: 0 auto 12px;&quot; /&gt;
    &lt;figcaption style=&quot;font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Helvetica, Arial, sans-serif; font-size: 0.85rem; color: #666666; line-height: 1.4; max-width: 500px; margin: 0 auto; text-align: center;&quot;&gt;
      Transformer architecture
      &lt;span style=&quot;color: #999999; font-size: 0.8rem;&quot;&gt;(Source: &lt;a href=&quot;https://arxiv.org/abs/1706.03762&quot; style=&quot;color: #666666; text-decoration: underline;&quot;&gt;Attention is all you need&lt;/a&gt;)&lt;/span&gt;
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h2 id=&quot;input-text-sequence-representation&quot;&gt;Input Text Sequence Representation&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/transformers/enc-dec.png&quot; alt=&quot;encoder-decoder&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;tokenization&quot;&gt;Tokenization&lt;/h3&gt;

&lt;p&gt;Two approaches to representing input text:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. One-hot encoding&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;No semantic similarity or meaning of words encoded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Token embedding&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Encodes semantic similarity between words.&lt;/li&gt;
  &lt;li&gt;Embedding matrix is &lt;strong&gt;learned&lt;/strong&gt; (Lookup Table).&lt;/li&gt;
  &lt;li&gt;Each token embedding is stored as a &lt;strong&gt;column vector&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/transformers/token-embedding.png&quot; alt=&quot;token embedding&quot; /&gt;&lt;/p&gt;

&lt;!-- $$
\underbrace{\begin{bmatrix} 0.5 \\ 2.7 \\ 1.2 \\ -0.2 \end{bmatrix}}_{d} = W_E \underbrace{\begin{bmatrix} 1 \\ 0 \\ \vdots \\ 0 \end{bmatrix}}_{\text{\# tokens}}
$$ --&gt;

\[\begin{aligned}
\text{where} \\
W_E &amp;amp;\equiv \text{embedding matrix, } d \times \text{\#tokens} \\
d &amp;amp;\equiv \text{embedding dimension}
\end{aligned}\]

&lt;h3 id=&quot;why-we-need-context&quot;&gt;Why We Need Context&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Many words have different meanings in different contexts:
    &lt;ul&gt;
      &lt;li&gt;“I bought an &lt;strong&gt;apple&lt;/strong&gt; &amp;amp; an orange”&lt;/li&gt;
      &lt;li&gt;“I bought an &lt;strong&gt;apple&lt;/strong&gt; watch”&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;We need to rely on &lt;strong&gt;context&lt;/strong&gt; to resolve the ambiguity.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;encoders&quot;&gt;Encoders&lt;/h2&gt;

&lt;p&gt;The encoder pipeline:&lt;/p&gt;

\[\text{Input} \to \text{token} + \text{POS embed} \to \text{Norm} \to \text{MHA(self)} \to \text{Add} \to \text{Norm} \to \text{FFN} \to \text{Add} \to \text{Output}\]

&lt;ul&gt;
  &lt;li&gt;Input tokens are embedded using $W_E$ and combined with &lt;strong&gt;positional encodings&lt;/strong&gt; to produce the input matrix $X$.&lt;/li&gt;
  &lt;li&gt;The architecture stacks: Multi-Head Self-Attention + residual, then FeedForward Network + residual.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/transformers/encoder.png&quot; alt=&quot;encoder&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;from-mlp-to-attention&quot;&gt;From MLP to Attention&lt;/h2&gt;

&lt;h3 id=&quot;mlp-only&quot;&gt;MLP only&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;MLP stands for Multilayer Perceptron&lt;/li&gt;
  &lt;li&gt;No contextual information; each token is processed independently.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;concatenation-of-nearby-token-embeddings-before-mlp&quot;&gt;Concatenation of nearby token embeddings before MLP&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Need a sufficiently large window to cover the entire input sequence.&lt;/li&gt;
  &lt;li&gt;Cannot handle variable sequence lengths.&lt;/li&gt;
  &lt;li&gt;Requires many model parameters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;attention&quot;&gt;Attention&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Use &lt;strong&gt;token similarity&lt;/strong&gt; to determine the relevance of each token to every other token by performing a dot product.&lt;/li&gt;
  &lt;li&gt;Allows the model to dynamically weight which parts of the input are relevant for each position.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;self-attention--multi-head-attention&quot;&gt;Self-Attention &amp;amp; Multi-Head Attention&lt;/h2&gt;

&lt;h3 id=&quot;self-attention&quot;&gt;Self-Attention&lt;/h3&gt;

\[Q = XW_Q, \quad K = XW_K, \quad V = XW_V\]

\[\text{head}_i = \text{Softmax}\!\left(\frac{QK^T}{\sqrt{d_k}}\right)V = \text{Attention}(Q, K, V)\]

&lt;h3 id=&quot;multi-head-attention-mha&quot;&gt;Multi-Head Attention (MHA)&lt;/h3&gt;

\[\text{MHA}(X) = \text{multi-head}(Q, K, V) = \text{Concat}(h_1, h_2, \ldots, h_H)\, W_O = Z\]

\[\begin{aligned}
\text{where} \\
h_i &amp;amp;\equiv i\text{-th attention head} \\
W_O &amp;amp;\equiv \text{output projection matrix} \\
Z &amp;amp;\equiv \text{final output}
\end{aligned}\]

&lt;hr /&gt;

&lt;h2 id=&quot;decoders&quot;&gt;Decoders&lt;/h2&gt;

&lt;p&gt;The decoder pipeline:&lt;/p&gt;

\[\text{Masked MHA} \to \text{Cross-Attn} \to \text{FFN} \to \text{Linear} \to \text{Softmax} \to \text{Output}\]

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/transformers/decoder.png&quot; alt=&quot;Decoder&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The decoder takes as input the previously generated tokens $(z_i)$ along with their positional encodings $(p_i)$, and at each step attends both to itself (masked) and to the encoder output (cross attention).&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;masked-attention&quot;&gt;Masked Attention&lt;/h2&gt;

&lt;p&gt;In the decoder, we use &lt;strong&gt;masked&lt;/strong&gt; (causal) self-attention to prevent the decoder from attending to future tokens:&lt;/p&gt;

\[\text{Masked Attn}(Q, K, V) = \text{Softmax}\!\left(\frac{QK^T}{\sqrt{d_k}} + M\right)V\]

\[\begin{aligned}
\text{where} \\
M &amp;amp;\equiv \text{lookahead mask}
\end{aligned}\]

\[M = \begin{bmatrix}
0 &amp;amp; -\infty &amp;amp; -\infty &amp;amp; -\infty \\
0 &amp;amp; 0 &amp;amp; -\infty &amp;amp; -\infty \\
0 &amp;amp; 0 &amp;amp; 0 &amp;amp; -\infty \\
0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0
\end{bmatrix}\]

&lt;p&gt;Adding $-\infty$ to future positions drives their softmax weights to zero, ensuring position $i$ can only attend to positions $\leq i$.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;encoder-decoder-cross-attention&quot;&gt;Encoder-Decoder Cross Attention&lt;/h2&gt;

&lt;p&gt;The decoder queries the encoder output $E$ (encoder output) to incorporate source context:&lt;/p&gt;

\[\begin{aligned}
Y&apos; &amp;amp;= Y + \text{Masked MHA}(\text{Norm}(Y)) \\[6pt]
Q &amp;amp;= Y&apos; W_Q^{\text{dec}} \\
K^{\text{enc}} &amp;amp;= E W_K^{\text{enc}} \\
V^{\text{enc}} &amp;amp;= E W_V^{\text{enc}}
\end{aligned}\]

\[\begin{aligned}
\text{where} \\
E &amp;amp;\equiv \text{encoder output}
\end{aligned}\]

\[\text{Cross Attn}(Y&apos;, E) = \text{Softmax}\!\left(\frac{Q K_{\text{enc}}^T}{\sqrt{d_k}}\right)V^{\text{enc}}\]

&lt;p&gt;Then the rest of the decoder:&lt;/p&gt;

\[\begin{aligned}
Y&apos;&apos; &amp;amp;= Y&apos; + \text{Cross Attn}(\text{Norm}(Y&apos;),\ E) \\
Y^O &amp;amp;= Y&apos;&apos; + \text{FFN}(\text{Norm}(Y&apos;&apos;)) \\
D &amp;amp;= Y^O \\
\text{logits} &amp;amp;= DW^{\text{out}} + b \\
P(y_t) &amp;amp;= \text{Softmax}(\text{logits}_t)
\end{aligned}\]

\[\begin{aligned}
\text{where} \\
D &amp;amp;\equiv \text{decoder output} \\
W^{\text{out}} &amp;amp;\equiv \text{output projection to vocabulary} \\
P(y_t) &amp;amp;\equiv \text{probability distribution over next token}
\end{aligned}\]

&lt;hr /&gt;

&lt;h2 id=&quot;citation&quot;&gt;Citation&lt;/h2&gt;

&lt;p&gt;If you found this blog post helpful, please consider citing it:&lt;/p&gt;

&lt;div class=&quot;language-bibtex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;@article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;obasi2026transformers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Transformers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Obasi, Chizoba&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;chizkidd.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;year&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2026&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;month&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Apr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://chizkidd.github.io/2026/04/17/transformers/&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;
</description>
        <pubDate>Fri, 17 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://chizkidd.github.io//2026/04/17/transformers/</link>
        <guid isPermaLink="true">https://chizkidd.github.io//2026/04/17/transformers/</guid>
        
        
      </item>
    
      <item>
        <title>SAM 2 Explained: Meta&apos;s Promptable Visual Segmentation Model</title>
        <description>&lt;ul&gt;
  &lt;li&gt;Meta’s unified model for promptable image and video segmentation.
    &lt;ul&gt;
      &lt;li&gt;A foundation model for solving promptable visual segmentation in images &amp;amp; &lt;strong&gt;videos&lt;/strong&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Built a data engine to collect the largest video segmentation dataset to date.&lt;/li&gt;
  &lt;li&gt;&lt;u&gt;Model&lt;/u&gt;: Simple transformer architecture with &lt;strong&gt;streaming memory&lt;/strong&gt; for real-time video processing.&lt;/li&gt;
  &lt;li&gt;Trained on a wide range of tasks: video segmentation and image segmentation.&lt;/li&gt;
  &lt;li&gt;The paper can be found &lt;a href=&quot;https://arxiv.org/pdf/2408.00714&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#1-introduction&quot;&gt;1. Introduction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#2-related-work&quot;&gt;2. Related Work&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#3-task-promptable-visual-segmentation-pvs&quot;&gt;3. Task: Promptable Visual Segmentation (PVS)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#4-model&quot;&gt;4. Model&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#5-data&quot;&gt;5. Data&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#6-zero-shot-experiments&quot;&gt;6. Zero-Shot Experiments&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#7-comparison-to-sota-in-semi-supervised-vos&quot;&gt;7. Comparison to SOTA in Semi-Supervised VOS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#8-conclusion&quot;&gt;8. Conclusion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#9-discussion&quot;&gt;9. Discussion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;appendix&quot;&gt;Appendix&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#citation&quot;&gt;Citation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1-introduction&quot;&gt;1. Introduction&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why video and not image?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Image is only a static snapshot of the real world; lacks motion information (temporal).&lt;/li&gt;
  &lt;li&gt;Video captures temporal information.&lt;/li&gt;
  &lt;li&gt;Many vital applications (robotics, AR/VR, autonomous vehicles) require temporal localization beyond image-level segmentation.&lt;/li&gt;
  &lt;li&gt;A universal visual segmentation system should be applicable to both images &amp;amp; videos.&lt;/li&gt;
  &lt;li&gt;Video segmentation aims to determine the &lt;strong&gt;spatio-temporal extent&lt;/strong&gt; of entities, which presents unique challenges beyond those in images.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Significant changes in appearance&lt;/strong&gt; encountered by entities &amp;amp; &lt;strong&gt;lower quality nature of videos&lt;/strong&gt; than images present challenges for video segmentation.&lt;/li&gt;
  &lt;li&gt;SAM successfully solves image segmentation, but existing video segmentation models &amp;amp; datasets fall short in providing a comparable capability to “segment anything in videos.”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SAM 2&lt;/strong&gt;: A unified model for &lt;strong&gt;video &amp;amp; image&lt;/strong&gt; segmentation.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Promptable Visual Segmentation (PVS)&lt;/strong&gt;: Task that generalizes image segmentation to the video domain.&lt;/li&gt;
  &lt;li&gt;A &lt;strong&gt;data engine&lt;/strong&gt; that generates training data via an in-the-loop model with annotators and produces the &lt;strong&gt;Segment Anything Video (SA-V) dataset&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2-related-work&quot;&gt;2. Related Work&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Video Object Segmentation (VOS)&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Video augmentation datasets&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Interactive Video Object Segmentation (iVOS)&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Image Segmentation&lt;/strong&gt; task, model and dataset
    &lt;ul&gt;
      &lt;li&gt;Research Paper: &lt;a href=&quot;https://arxiv.org/pdf/2304.02643&quot;&gt;Segment Anything (SA)&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Segment Anything (Adapted from the Paper)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;We aim to build a foundation model for segmentation by introducing three interconnected components: a promptable segmentation &lt;strong&gt;task&lt;/strong&gt;, a segmentation &lt;strong&gt;model&lt;/strong&gt; (SAM) that powers data annotation and enables zero-shot transfer to a range of tasks via prompt engineering, and a &lt;strong&gt;data&lt;/strong&gt; engine for collecting SA-1B, our dataset of over 1 billion masks.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;3-task-promptable-visual-segmentation-pvs&quot;&gt;3. Task: Promptable Visual Segmentation (PVS)&lt;/h2&gt;

\[\text{PVS} \longrightarrow \text{SAM 2} \longrightarrow \text{SA-V dataset}\]

&lt;ul&gt;
  &lt;li&gt;PVS task allows providing prompts to the model on &lt;strong&gt;any frame&lt;/strong&gt; of a video.&lt;/li&gt;
  &lt;li&gt;The interactive segmentation with SAM2 involves the steps below:
    &lt;ul&gt;
      &lt;li&gt;SAM 2 is prompted on a single frame and responds instantly with a valid segmentation mask of the target object on this frame.&lt;/li&gt;
      &lt;li&gt;SAM 2 then propagates the target object’s segment to multiple frames to form a &lt;strong&gt;masklet&lt;/strong&gt;.&lt;/li&gt;
      &lt;li&gt;Multiple initial prompts are received and propagated by the model to obtain the masklet of the object &lt;strong&gt;across the entire video&lt;/strong&gt;, which leads to localization of the segmentation mask of the target on every single video frame.&lt;/li&gt;
      &lt;li&gt;Additional prompts on &lt;strong&gt;any&lt;/strong&gt; frame can be added to SAM 2 for segmentation mask refinement.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;SAM 2 is applied as a data collection tool to the PVS task for building the SA-V dataset.&lt;/li&gt;
  &lt;li&gt;Model evaluation is done via simulation of interactive video segmentation scenarios across multiple frames in the conventional first-frame, limited, semi-supervised VOS setting, and for image segmentation on the SA benchmarks.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;4-model&quot;&gt;4. Model&lt;/h2&gt;

&lt;p&gt;SAM 2 is a generalization of SAM to the video (&amp;amp; image) domain. Essentially, it employs taking point, box &amp;amp; mask prompts on individual frames to define the &lt;strong&gt;spatial extent&lt;/strong&gt; of the object to be segmented &lt;strong&gt;spatio-temporally&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/sam-2/sam-2.png&quot; alt=&quot;SAM 2 Architecture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small class=&quot;text-muted d-block text-center&quot;&gt;&lt;strong&gt;Figure 1:&lt;/strong&gt; The SAM 2 architecture. For a given frame, the segmentation prediction is conditioned on the current prompt and/or on previously observed memories. Video frames are processed in a streaming fashion by the image encoder, cross-attended to memories of the target object from previous frames stored in the memory bank, and decoded via the mask decoder (optionally prompted by the prompt encoder) to predict the segmentation mask for that frame. Finally, a memory encoder transforms the prediction and image encoder embeddings for use in future frames.&lt;/small&gt;&lt;/p&gt;

&lt;!-- 
&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;SAM 2 Architecture&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;For a given frame, the segmentation prediction is conditioned on the current prompt and/or on previously observed memories. Video frames are processed in a streaming fashion by the image encoder, cross-attended to memories of the target object from previous frames stored in the memory bank, and decoded via the mask decoder (optionally prompted by the prompt encoder) to predict the segmentation mask for that frame. Finally, a memory encoder transforms the prediction and image encoder embeddings for use in future frames.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt; --&gt;

&lt;p&gt;&lt;strong&gt;Components:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Image encoder&lt;/strong&gt;: For real-time processing of arbitrarily long videos via a streaming, hierarchical approach.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Memory attention&lt;/strong&gt;: &lt;em&gt;Condition&lt;/em&gt; the current frame features on the past frames features and predictions or any new prompts.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Prompt encoder &amp;amp; mask decoder&lt;/strong&gt;: Encode and send the input prompts to the mask decoder to predict the segmentation mask for the current frame.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Memory encoder&lt;/strong&gt;: Generates a memory by downsampling, element-wise summation and light-weight convolution of the output mask which fuses the information.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Memory bank&lt;/strong&gt;: Stores spatial memory information (from prompts) about past predictions for the target object in the video, and high-level semantic information of the object to segment based on each frame’s mask decoder output tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Training:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The model is trained &lt;strong&gt;jointly&lt;/strong&gt; on image and video data via interactive prompting simulation of the model, sequential sampling of 8 frames and random selection of 2 frames to prompt, with the goal of sequential and interactive prediction of the gound-truth masklet.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;5-data&quot;&gt;5. Data&lt;/h2&gt;

&lt;p&gt;The data engine was built, by an interactive in-the-loop model setup with human annotators, to collect a large and diverse video segmentation dataset to develop the “segemnt anything” capability in video. The data engine underwent 3 phases, each based on the level of model assistance provided to annotators.&lt;/p&gt;

&lt;h3 id=&quot;51-data-engine&quot;&gt;5.1 Data Engine&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Phase 1:&lt;/strong&gt; SAM per frame&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Phase 2:&lt;/strong&gt; SAM + SAM 2&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Phase 3:&lt;/strong&gt; SAM 2&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Quality verification&lt;/strong&gt; including a separate set of QA annotators for the masklets (satisfactory or unsatisfactory)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Auto masklet generation&lt;/strong&gt; enables the &lt;em&gt;“anything capability”&lt;/em&gt; of the model by ensuring annotation diversity.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Analysis 1:&lt;/strong&gt; Comparison in each data engine phase through a controlled experiment of the average annotation time per frame, the average percentage of manually edited frames per masklet, and the average number of clicks per clicked frame. For QA, the defined metric is called &lt;strong&gt;Phase 1 Mask Alignment Score&lt;/strong&gt;, the percentage of masks whose IoU compared to the corresponding masks in Phase 1 (highest quality manual annotations) exceeds 0.75.
&lt;!-- &gt; Phase 3 is **8.4×** faster than Phase 1, has the lowest edited frame percentage and clicks per frame, and results in better alignment. --&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Analysis 2:&lt;/strong&gt; Performance comparison of SAM 2 trained on the available data at the end of each phase keeping the number of iterations fixed, therefore measuring solely the impact of the additional data. Evaluation is done on the SA-V val dataset and 9 zero-shot benchmarks using the standard &lt;em&gt;$J\&amp;amp;F$&lt;/em&gt; accuracy metric. 
&lt;!-- &gt;Consistent segmentation accuracy improvement from iteratively adding data from each data engine phase (1, 2 &amp; 3) for both SA-V val set and 9 zero-shot benchmarks is observed. --&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Analysis Results&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;strong&gt;Phase 3 is 8.4× faster than Phase 1,&lt;/strong&gt; has the lowest edited frame percentage and clicks per frame, and results in better alignment.&lt;br /&gt; 
&lt;strong&gt;Consistent segmentation accuracy improvement&lt;/strong&gt; from iteratively adding data from each data engine phase (1, 2 &amp;amp; 3) for both SA-V val set and 9 zero-shot benchmarks is observed.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;52-sa-v-dataset&quot;&gt;5.2 SA-V Dataset&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Videos (&lt;strong&gt;50.9K total, 54% indoor + 46% outdoor scenes, average duration of 14 secs&lt;/strong&gt;)&lt;/li&gt;
  &lt;li&gt;Masklets (&lt;strong&gt;190.9K manual + 451.7K automatic, 642.6K total&lt;/strong&gt;)&lt;/li&gt;
  &lt;li&gt;SA-V training, validation (&lt;strong&gt;293 masklets &amp;amp; 155 videos&lt;/strong&gt;) &amp;amp; test splits (&lt;strong&gt;278 masklets &amp;amp; 150 videos&lt;/strong&gt;)&lt;/li&gt;
  &lt;li&gt;Internal dataset (&lt;strong&gt;62.9K videos &amp;amp; 69.6K masklets&lt;/strong&gt; annotated in Phase &lt;em&gt;2 &amp;amp; 3&lt;/em&gt; for training; &lt;strong&gt;96 videos &amp;amp; 189 masklets&lt;/strong&gt; annotated using Phase &lt;em&gt;1&lt;/em&gt; for testing)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;6-zero-shot-experiments&quot;&gt;6. Zero-Shot Experiments&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Compare SAM 2 with previous work on zero-shot video &amp;amp; image tasks using the &lt;em&gt;$J\&amp;amp;F$&lt;/em&gt; accuracy metric.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;61-promptable-video-segmentation-pvs&quot;&gt;6.1 Promptable Video Segmentation (PVS)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;First evaluate PVS, which involves simulating an interactive setting that is akin to the user experience. Both evaluation settings below are done on 9 densely annotated zero-shot video datasets using $N_{click} = 3$ clicks per frame and are compared to 2 strong baselines based on 2 SOTA VOS models (&lt;strong&gt;XMem++ &amp;amp; Cutie&lt;/strong&gt;)
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Offline evaluation:&lt;/strong&gt; multiple passes made through a video for frame selection via the largest model error.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Online evaluation:&lt;/strong&gt; single forward pass for video frames’ annotation.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;62-semi-supervised-video-object-segmentation&quot;&gt;6.2 Semi-Supervised Video Object Segmentation&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Evaluate the semi-supervised VOS setting with click, box or mask prompts only on the &lt;strong&gt;1st frame&lt;/strong&gt; of the video.&lt;/li&gt;
  &lt;li&gt;For click prompts, try either 1, 3, or 5 clicks on the 1st video frame (mIoU).&lt;/li&gt;
  &lt;li&gt;Comparison is done via &lt;em&gt;$J\&amp;amp;F$&lt;/em&gt; accuracy between SAM + XMem++, SAM + Cutie and SAM 2.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;63-image-segmentation&quot;&gt;6.3 Image Segmentation&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Evaluate SAM 2 on the Segment Anything task across &lt;strong&gt;37 zero-shot datasets&lt;/strong&gt; using 1-click and 5-click mIoUs.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;7-comparison-to-sota-in-semi-supervised-vos&quot;&gt;7. Comparison to SOTA in Semi-Supervised VOS&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Evaluate 2 versions of SAM 2 with varying image encoder sizes with different speed-vs-accuracy tradeoffs.&lt;/li&gt;
  &lt;li&gt;Comparison with existing SOTA models/methods via accuracy (&lt;em&gt;$J\&amp;amp;F, G$&lt;/em&gt;) using standard protocols.
&lt;!-- &gt;SAM 2 performs well in accuracy for video segmentation based on first-frame ground-truth mask prompts.  --&gt;&lt;/li&gt;
  &lt;li&gt;Evaluate existing work on the SA-V val &amp;amp; test sets which measure performance for open-world segments of &lt;strong&gt;“any”&lt;/strong&gt; object class via &lt;em&gt;$J\&amp;amp;F$&lt;/em&gt; accuracy metric.
&lt;!-- &gt;SAM 2 performs significantly better on SA-V val/test. --&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Comparison Results&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;SAM 2 &lt;strong&gt;performs well&lt;/strong&gt; in accuracy for video segmentation based on first-frame ground-truth mask prompts.&lt;br /&gt; 
SAM 2 performs &lt;strong&gt;significantly better&lt;/strong&gt; on SA-V val/test.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;8-conclusion&quot;&gt;8. Conclusion&lt;/h2&gt;

&lt;p&gt;Present a natural extension of Segment Anything into the video domain, based on 3 key aspects:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Extending the promptable segmentation task to video.&lt;/li&gt;
  &lt;li&gt;Equipping the SAM architecture to use memory when applied to video.&lt;/li&gt;
  &lt;li&gt;The diverse SA-V dataset for training &amp;amp; benchmarking video segmentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The authors believed SAM 2 marked a significant advancement in visual perception, positioning their contributions as milestones that will propel further research &amp;amp; applications.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;9-discussion&quot;&gt;9. Discussion&lt;/h2&gt;

&lt;p&gt;When I first read about SAM 2’s memory bank component, my immediate thought was: &lt;em&gt;this feels familiar&lt;/em&gt;. Not because it copies prior work, but because it sits at an intersection I’ve been circling for a while: &lt;em&gt;how do neural systems remember what matters, without remembering everything?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;SAM 2 maintains object identity across video frames by storing two kinds of information in its memory bank:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Spatial feature maps&lt;/strong&gt; from up to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;N&lt;/code&gt; recent frames and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M&lt;/code&gt; prompted frames (stored in FIFO queues)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Object pointers&lt;/strong&gt;: lightweight semantic vectors derived from the mask decoder’s output tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Memory attention then cross-attends over &lt;em&gt;both&lt;/em&gt; when predicting the current frame. This design elegantly sidesteps the sequential bottleneck that plagued RNNs. Instead of compressing history into a single fixed-length hidden state, SAM 2 gives the model direct, position-invariant access to past representations. The “distance” between frame 1 and frame 200 is functionally the same as between frame 199 and 200. In that sense, attention acts as a &lt;strong&gt;temporal superhighway&lt;/strong&gt;.&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Distance vs. Capacity&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;&lt;strong&gt;Key distinction&lt;/strong&gt;: RNNs encode history into a single vector (making long-range dependencies hard). SAM 2’s attention mechanism bypasses that bottleneck entirely, so distance becomes irrelevant. But attention solves the &lt;em&gt;access&lt;/em&gt; problem, not the &lt;em&gt;retention&lt;/em&gt; problem.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;And that’s where the FIFO design matters. The paper explicitly states that the memory bank evicts the oldest frame once the queue is full, &lt;em&gt;regardless of semantic importance&lt;/em&gt;.&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; This validates a subtle but critical observation: SAM 2’s forgetting mechanism is a &lt;strong&gt;fixed heuristic&lt;/strong&gt;, not a learned one. The model doesn’t decide what to remember based on future tracking utility; it drops frames based on arrival time.&lt;/p&gt;

&lt;p&gt;This creates a tangible trade-off between &lt;strong&gt;memory availability&lt;/strong&gt; and &lt;strong&gt;diagnostic utility&lt;/strong&gt;. Consider an object that:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Undergoes dramatic lighting change&lt;/li&gt;
  &lt;li&gt;Is occluded for dozens of frames&lt;/li&gt;
  &lt;li&gt;Reappears with significant appearance deformation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The frames that best capture its initial identity might be evicted long before it reappears. Meanwhile, newer frames with degraded or ambiguous representations linger in the queue simply because they arrived later. The “object pointers” partially mitigate this by storing lightweight semantic summaries, but they’re still bound by the same FIFO eviction policy. If the pointer for frame &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt; is overwritten, the model loses not just the spatial map but also its high-level anchor.&lt;/p&gt;

&lt;div class=&quot;mermaid&quot; style=&quot;margin: 2rem 0;&quot;&gt;
flowchart LR
    A[FIFO Queue&lt;br /&gt;&lt;small&gt;time-based eviction&lt;/small&gt;] --&amp;gt; B[Eviction Policy&lt;br /&gt;&lt;small&gt;fixed heuristic&lt;/small&gt;]
    B --&amp;gt; C[Memory Attention&lt;br /&gt;&lt;small&gt;position-invariant access&lt;/small&gt;]
    C --&amp;gt; D{Tracking Outcome}
    D --&amp;gt;|Object reappears&lt;br /&gt;with useful memory| E[✓ Success]
    D --&amp;gt;|Key frame evicted&lt;br /&gt;or pointer overwritten| F[✗ Failure]
    style A fill:#e3f2fd
    style B fill:#fff3e0
    style C fill:#e8f5e9
    style E fill:#c8e6c9,stroke:#2e7d32
    style F fill:#ffcdd2,stroke:#c62828
&lt;/div&gt;

&lt;p&gt;&lt;small class=&quot;text-muted d-block text-center&quot;&gt;&lt;strong&gt;Figure 2:&lt;/strong&gt; The memory bank’s fixed eviction policy (FIFO) interacts with attention’s position-invariant access. When evicted frames contain critical identity information, tracking fails; even if attention could theoretically retrieve them.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;The paper’s handling of temporal position encoding reinforces this pragmatic trade-off. Temporal embeddings are injected into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;N&lt;/code&gt; recent frames to capture short-term motion, but deliberately &lt;em&gt;omitted&lt;/em&gt; from prompted frames due to sparse training signals and inference generalization concerns. This is a sound engineering decision, but it reveals a boundary: SAM 2 optimizes for stable, short-to-medium horizon tracking, not open-ended temporal reasoning.&lt;/p&gt;

&lt;h3 id=&quot;where-this-fits-in-the-broader-literature&quot;&gt;Where This Fits in the Broader Literature&lt;/h3&gt;

&lt;p&gt;SAM 2’s memory bank isn’t operating in a vacuum. It shares conceptual DNA with several prior lines of work:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Neural Turing Machines&lt;/strong&gt; introduced differentiable external memory with read/write heads, allowing networks to learn &lt;em&gt;what&lt;/em&gt; to store and &lt;em&gt;where&lt;/em&gt; to retrieve from &lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. SAM 2’s memory attention is a specialized, non-differentiable cousin: it retrieves, but doesn’t learn the eviction policy.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;RETRO&lt;/strong&gt; demonstrated that retrieval-augmented transformers can scale knowledge without scaling parameters, by querying a frozen corpus at inference &lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. SAM 2 does something analogous for video: query a frozen buffer of past frames. The open question is whether that buffer should be learned, not fixed.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TimeSformer&lt;/strong&gt; showed that spatiotemporal attention alone can handle video understanding without recurrent components &lt;sup id=&quot;fnref:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. SAM 2 extends this by adding explicit memory, and also inherits TimeSformer’s assumption that all frames are equally worth attending to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What SAM 2 adds is a &lt;em&gt;practical&lt;/em&gt; instantiation of these ideas for promptable segmentation. It’s not trying to solve general memory-augmented reasoning; it’s solving “keep this object tracked, please.” That focus is its strength; and its limitation.&lt;/p&gt;

&lt;div class=&quot;callout callout--danger&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Counter Argument to the Paper&apos;s Memory Design&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;About memory design, I’d say: &lt;em&gt;“FIFO is computationally cheap and training-stable, which makes sense for a production model. But from a research standpoint, it hardcodes a failure mode: important frames get evicted by time, not relevance. A differentiable memory controller or retrieval-augmented eviction policy could close that gap.”&lt;/em&gt;&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;/h3&gt;

&lt;p&gt;There’s a tendency in ML to treat architectural choices as purely engineering decisions. But memory management isn’t just about compute budgets, it’s also about what the model &lt;em&gt;values&lt;/em&gt;. When SAM 2 drops a frame because it’s old, not because it’s uninformative, it’s making a silent claim: &lt;em&gt;recency matters more than relevance&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That claim works well for many tracking scenarios. But it breaks down when objects reappear after long occlusions, or when appearance changes dramatically. In those cases, the model isn’t failing because attention is weak; it’s failing because the &lt;em&gt;right information was never kept around to attend to&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This isn’t unique to SAM 2. It’s a fundamental tension in any system that must balance finite memory against infinite context. But because SAM 2 is a foundation model positioned for broad adoption, its design choices will influence how thousands of downstream applications handle temporal reasoning. Getting the memory story right matters.&lt;/p&gt;

&lt;p&gt;So where does this leave us? SAM 2 is undoubtedly a milestone in promptable video segmentation. But its memory bank inadvertently frames a deeper research problem: &lt;strong&gt;attention removes the barrier of temporal distance, but leaves the bottleneck of memory management wide open&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;open-questions-id-want-to-explore&quot;&gt;Open Questions I’d Want to Explore&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Learnable eviction&lt;/strong&gt;: Could we replace FIFO with a lightweight, content-aware, learnable memory eviction mechanistic network that predicts which frames to retain based on tracking confidence, appearance stability, or semantic salience? Would SAM 2 maintain robust identity over arbitrarily long horizons? What’s the compute trade-off from an engineering system to a long-context reasoning model?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pointer robustness&lt;/strong&gt;: The object pointers are a clever compression trick, but they’re still overwritten by FIFO. Could we decouple pointer retention from spatial memory eviction?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Cross-video retrieval&lt;/strong&gt;: RETRO retrieves from a corpus of documents; could SAM 2 retrieve from a corpus of &lt;em&gt;past videos&lt;/em&gt; to bootstrap tracking of familiar objects?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Failure diagnostics&lt;/strong&gt;: Can we design a probe that predicts &lt;em&gt;when&lt;/em&gt; SAM 2 is likely to lose an object, based on memory bank state? That would be valuable for safety-critical applications.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I don’t have answers to these yet. But they feel like the right questions to ask if we want video models that don’t just see, but remember. SAM 2 shows we’ve mastered &lt;em&gt;access&lt;/em&gt; to the past. The next step is mastering &lt;em&gt;retention&lt;/em&gt; of what matters.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;citation&quot;&gt;Citation&lt;/h2&gt;

&lt;p&gt;If you found this blog post helpful, please consider citing it:&lt;/p&gt;

&lt;div class=&quot;language-bibtex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;@article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;obasi2026sam2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SAM 2: Segment Anything in Images &amp;amp; Videos&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Obasi, Chizoba&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;chizkidd.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;year&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2026&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;month&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Apr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://chizkidd.github.io/2026/04/17/sam2/&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;hr /&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Ravi N, Gabeur V, Hu Y-T, et al. &lt;a href=&quot;https://arxiv.org/pdf/2408.00714&quot;&gt;SAM 2: Segment Anything in Images and Videos&lt;/a&gt;. arXiv. 2024. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Graves A, Wayne G, Danihelka I. &lt;a href=&quot;https://arxiv.org/abs/1410.5401&quot;&gt;Neural Turing Machines&lt;/a&gt;. arXiv. 2014. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Borgeaud S, Mensch A, Hoffmann J, et al. &lt;a href=&quot;https://arxiv.org/abs/2112.04426&quot;&gt;Improving Language Models by Retrieving from Trillions of Tokens&lt;/a&gt;. arXiv. 2022. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Bertasius G, Wang H, Torresani L. &lt;a href=&quot;https://arxiv.org/abs/2102.05095&quot;&gt;Is Space-Time Attention All You Need for Video Understanding?&lt;/a&gt;. arXiv. 2021. &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Fri, 17 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://chizkidd.github.io//2026/04/17/sam-2/</link>
        <guid isPermaLink="true">https://chizkidd.github.io//2026/04/17/sam-2/</guid>
        
        
      </item>
    
      <item>
        <title>Muon Optimizer Explained: Newton-Schulz Orthogonalization Beyond Adam</title>
        <description>&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Muon&lt;/strong&gt; stands for &lt;strong&gt;M&lt;/strong&gt;oment&lt;strong&gt;U&lt;/strong&gt;m &lt;strong&gt;O&lt;/strong&gt;rthogonalized by &lt;strong&gt;N&lt;/strong&gt;ewton-Schulz and was invented by &lt;a href=&quot;https://kellerjordan.github.io/&quot;&gt;Keller Jordan&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;&lt;strong&gt;The key idea:&lt;/strong&gt;&lt;/em&gt; Instead of applying Adam-style per-element adaptive updates to model parameters, Muon orthogonalizes the momentum matrix before using it as the update direction.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#adam-optimizer&quot;&gt;Adam Optimizer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#matrix-orthogonalization&quot;&gt;Matrix Orthogonalization&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#newton-schulz-5-iteration&quot;&gt;Newton-Schulz 5 Iteration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#muon&quot;&gt;Muon&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#qk-clip&quot;&gt;QK-Clip&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#multihead-latent-attention-mla&quot;&gt;Multihead Latent Attention (MLA)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#muonclip&quot;&gt;MuonClip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;appendix&quot;&gt;Appendix&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#citation&quot;&gt;Citation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://x.com/Kimi_Moonshot&quot;&gt;Moonshot AI&lt;/a&gt;, creator of Kimi, pioneered the improvements to Muon (MuonClip) which I dive into in detail in this post. But they have an article on X with more academic details on why they chose Muon titled &lt;em&gt;“Why We Chose Muon: Our Chain of Thought”&lt;/em&gt;&lt;sup id=&quot;fnref:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; by &lt;a href=&quot;https://x.com/Jianlin_S&quot;&gt;Jianlin Su&lt;/a&gt;, the first author of &lt;a href=&quot;https://arxiv.org/abs/2104.09864&quot;&gt;RoPE&lt;/a&gt; (Rotary Position Embedding).&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;zxx&quot; dir=&quot;ltr&quot;&gt;&lt;a href=&quot;https://t.co/dxZnLxvPae&quot;&gt;https://t.co/dxZnLxvPae&lt;/a&gt;&lt;/p&gt;&amp;mdash; Kimi.ai (@Kimi_Moonshot) &lt;a href=&quot;https://twitter.com/Kimi_Moonshot/status/1897929976948965870?ref_src=twsrc%5Etfw&quot;&gt;March 7, 2025&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;This blog post is directly from my personal handwritten notes on studying Muon &amp;amp; MuonClip. I posted those &lt;a href=&quot;https://x.com/latentchiz/status/2040617828856803690?s=20&quot;&gt;notes on X&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;My notes on the Muon optimizer by &lt;a href=&quot;https://twitter.com/kellerjordan0?ref_src=twsrc%5Etfw&quot;&gt;@kellerjordan0&lt;/a&gt; and MuonClip by &lt;a href=&quot;https://twitter.com/Kimi_Moonshot?ref_src=twsrc%5Etfw&quot;&gt;@Kimi_Moonshot&lt;/a&gt; which integrates Muon with weight decay, RMS matching, &amp;amp; QK-Clip.&lt;br /&gt;&lt;br /&gt;&amp;quot;Using MuonClip, we successfully pre-trained Kimi K2 on 15.5 trillion tokens without a single loss spike.&amp;quot; - &lt;a href=&quot;https://twitter.com/Kimi_Moonshot?ref_src=twsrc%5Etfw&quot;&gt;@Kimi_Moonshot&lt;/a&gt; &lt;a href=&quot;https://t.co/KW84xOi51E&quot;&gt;pic.twitter.com/KW84xOi51E&lt;/a&gt;&lt;/p&gt;&amp;mdash; Chiz (@latentchiz) &lt;a href=&quot;https://twitter.com/latentchiz/status/2040617828856803690?ref_src=twsrc%5Etfw&quot;&gt;April 5, 2026&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;adam-optimizer&quot;&gt;Adam Optimizer&lt;/h2&gt;
&lt;p&gt;Let’s start by looking at Adam optimizer, the most common optimizer for training neural networks, which I cover in &lt;a href=&quot;https://chizkidd.github.io/2026/01/22/neural-net-optimizers/#8-muon-momentum-orthogonalized-by-newton-schulz&quot;&gt;this article guide&lt;/a&gt; on common deep learning optimizers. &lt;strong&gt;Adam&lt;/strong&gt; stands for &lt;strong&gt;Ada&lt;/strong&gt;ptive &lt;strong&gt;M&lt;/strong&gt;oment Estimation.&lt;/p&gt;

&lt;p&gt;It combines momentum and adaptive learning rates so the model not only remembers the direction it has been moving in, but also adjusts how big each step should be for every parameter. More specifcally, it combines momentum (first moment) and RMSProp (second moment) with bias corrections to handle noisy gradients and early training instability.&lt;sup id=&quot;fnref:7&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; The forward pass looks like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/muon/model-learning.png&quot; alt=&quot;model learning&quot; /&gt;&lt;/p&gt;

&lt;!-- $$x \longrightarrow \boxed{\Theta} \longrightarrow \hat{y} \longrightarrow \boxed{\text{Loss},\ L} \longrightarrow y_{gt}$$

$${\text{input}} \quad {\text{model}} \quad {\text{prediction}} \hspace{2em} \quad  {\text{ground truth}}$$ --&gt;

&lt;h3 id=&quot;gradient-descent&quot;&gt;Gradient Descent&lt;/h3&gt;

&lt;p&gt;Gradient descent updates the model parameters by stepping in the direction that reduces the loss, using the gradient as a guide for how to adjust each parameter. The momentum and velocity terms refine this process by smoothing past gradients and scaling updates adaptively, which helps stabilize training and converge faster, especially in noisy or complex loss landscapes.&lt;/p&gt;

\[\begin{aligned}
\text{simply} \quad &amp;amp; \Theta_i \leftarrow \Theta_{i-1} - \alpha \frac{\partial L}{\partial \Theta_i} \\[6pt]
\text{momentum} \quad &amp;amp; M_i \leftarrow \beta_1 M_{i-1} + (1 - \beta_1) \frac{\partial L}{\partial \Theta_i} \\[6pt]
\text{velocity} \quad &amp;amp; V_i \leftarrow \beta_2 V_{i-1} + (1 - \beta_2) \left(\frac{\partial L}{\partial \Theta_i}\right)^2 \\[6pt]
&amp;amp; \Theta_i \leftarrow \Theta_{i-1} - \alpha \left(\frac{M_i}{\sqrt{V_i} + \varepsilon}\right)
\end{aligned}\]

\[\begin{aligned}
\text{where} \\
M_i &amp;amp;\equiv \text{momentum (1st moment)} \\
V_i &amp;amp;\equiv \text{velocity: gradient squared (2nd moment)} \\
\beta_1, \beta_2 &amp;amp;\equiv \text{decay hyperparameters} \\
\varepsilon &amp;amp;\equiv \text{small constant for numerical stability}
\end{aligned}\]

&lt;h3 id=&quot;cons-of-adam&quot;&gt;Cons of Adam&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Memory intensive.&lt;/li&gt;
  &lt;li&gt;Challenging hyper-parameter tuning.&lt;/li&gt;
  &lt;li&gt;Independent update of each value in a single, long, parameter vector without considering any internal structure (vector-based optimizer behavior) of the model parameters.&lt;sup id=&quot;fnref:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; Can we explicitly account for the underlying matrix structure of the model parameters?&lt;/p&gt;

&lt;h3 id=&quot;the-linear-layer--matrix-momentum&quot;&gt;The Linear Layer &amp;amp; Matrix Momentum&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;For a linear layer with 3 inputs $(x_1, x_2, x_3)$ and 4 outputs $(z_1, z_2, z_3, z_4)$:&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/2026/muon/linear-layer.png&quot; alt=&quot;Linear layer&quot; /&gt;&lt;/p&gt;

\[z_i = \Theta_{i1} x_1 + \Theta_{i2} x_2 + \Theta_{i3} x_3, \quad \forall\, i = 1, 2, 3, 4\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In matrix form:&lt;/p&gt;

\[\begin{bmatrix} z_1 \\ z_2 \\ z_3 \\ z_4 \end{bmatrix}
  =
  \begin{bmatrix}
  \Theta_{11} &amp;amp; \Theta_{12} &amp;amp; \Theta_{13} \\
  \Theta_{21} &amp;amp; \Theta_{22} &amp;amp; \Theta_{23} \\
  \Theta_{31} &amp;amp; \Theta_{32} &amp;amp; \Theta_{33} \\
  \Theta_{41} &amp;amp; \Theta_{42} &amp;amp; \Theta_{43}
  \end{bmatrix}
  \begin{bmatrix} x_1 \\ x_2 \\ x_3 \end{bmatrix}\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Since $\Theta$ is now a matrix, we need a &lt;strong&gt;matrix momentum&lt;/strong&gt; $\hat{M}$ for $\hat{\Theta}$:&lt;/p&gt;

\[\hat{M} =
  \begin{bmatrix}
  m_{11} &amp;amp; m_{12} &amp;amp; m_{13} \\
  m_{21} &amp;amp; m_{22} &amp;amp; m_{23} \\
  m_{31} &amp;amp; m_{32} &amp;amp; m_{33} \\
  m_{41} &amp;amp; m_{42} &amp;amp; m_{43}
  \end{bmatrix}\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;So the matrix momentum update is:&lt;/p&gt;

\[\hat{M}_i \leftarrow \beta \hat{M}_i + \frac{\partial L}{\partial \hat{\Theta}_i}\]
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-problem-with-vector-based-optimizers&quot;&gt;The Problem with Vector-Based Optimizers&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;With vector-based optimizers like Adam, the momentum for a linear layer (a 2D matrix) tends to become &lt;strong&gt;almost low rank&lt;/strong&gt; in practice.&lt;/li&gt;
  &lt;li&gt;Essentially, only a small number of dominant directions really drive the update, while the many remaining other directions contribute very little.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; How can we tackle this update direction imbalance and what makes a good optimizer?&lt;/p&gt;

&lt;p&gt;From fundamental first principles, a good optimizer possesses two characteristics: &lt;strong&gt;stability&lt;/strong&gt; and &lt;strong&gt;speed.&lt;/strong&gt; The goal of each update of a good optimizer is to minimize model variance and maximize loss reduction contribution, which correspond to stability and speed respectively.&lt;sup id=&quot;fnref:5:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;matrix-orthogonalization&quot;&gt;Matrix Orthogonalization&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;We can fix the imbalance of update directions via orthogonalization.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Orthogonalize&lt;/strong&gt; the momentum matrix. This is where &lt;strong&gt;Muon&lt;/strong&gt; comes in.&lt;/li&gt;
  &lt;li&gt;It amplifies the effect of &lt;strong&gt;rare&lt;/strong&gt; directions – the directions that typically receive small or infrequent updates.&lt;/li&gt;
  &lt;li&gt;Even though these rare directions seem minor, they are often essential for effective learning and can help capture more nuance patterns in the data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;orthogonalization-via-svd&quot;&gt;Orthogonalization via SVD&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;We want the orthogonal matrix $O$ closest to $M$:&lt;/p&gt;

\[\text{Ortho}(M) = \arg\min_{O} \left\{\| O - M \|_F\right\} \quad \text{subject to } OO^T = I \text{ or } O^TO = I\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Using &lt;strong&gt;SVD&lt;/strong&gt; (Singular Value Decomposition): $M = USV^T$, where:&lt;/p&gt;

\[\begin{aligned}
  UU^T &amp;amp;= U^TU = I \\
  VV^T &amp;amp;= V^TV = I
  \end{aligned}
  \quad \bigg\} \text{ orthonormal matrices}\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Since $OO^T = O^TO = I$:&lt;/p&gt;

\[\therefore\quad O = USV^T \quad \text{where } S = \begin{bmatrix} 1 &amp;amp; &amp;amp; 0 \\ &amp;amp; \ddots &amp;amp; \\ 0 &amp;amp; &amp;amp; 1 \end{bmatrix} \equiv \text{unit diagonal matrix}\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; SVD on a matrix is computationally expensive.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use an odd polynomial matrix:&lt;/p&gt;

\[p(X) = aX + b(XX^T)X\]
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;odd-polynomial-matrix&quot;&gt;Odd Polynomial Matrix&lt;/h3&gt;

\[\begin{align*}
p(M) &amp;amp;= a(M) + b(MM^T)M \\
&amp;amp;= \left(aI + b(MM^T)\right)M \\
&amp;amp;= \left(aI + b(USV^T VS U^T)\right)USV^T \\
&amp;amp;= \left(aI + b(US^2 U^T)\right)USV^T \\
&amp;amp;= aUSV^T + bUS^2 U^T USV^T \\
p(M) &amp;amp;= aUSV^T + bUS^3V^T \\
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;This applies to any odd polynomial, so in general:&lt;/p&gt;

\[p(M) = U\!\left(aS + bS^3 + cS^5 + \ldots + (\text{const})\, S^{2n+1}\right)V^T, \quad \forall\, n \geq 0,\ n \to \infty\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Sticking to the &lt;strong&gt;5th order polynomial&lt;/strong&gt;:&lt;/p&gt;

\[\boxed{p(M) = U\!\left(aS + bS^3 + cS^5\right)V^T}\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We need to determine the coefficients $(a, b, c)$. The goal is to get $S$ to a unit diagonal matrix – i.e., get the diagonal values as close to 1 as possible.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Plot $y = p(x)$ against $x$. If $(a, b, c) = (1.5,\ -0.5,\ 0)$:&lt;/p&gt;

\[y = 1.5x - 0.5x^3\]

    &lt;p&gt;&lt;img src=&quot;/assets/images/2026/muon/newton-schulz5.png&quot; alt=&quot;Newton-Schulz5-1&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Applying this repeatedly via &lt;strong&gt;Newton-Schulz iteration&lt;/strong&gt;:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

\[\begin{aligned}
y_1 &amp;amp;= p(x) \\
y_2 &amp;amp;= p(p(x)) \\
y_3 &amp;amp;= p(p(p(x))) \\
y_4 &amp;amp;= p(p(p(p(x)))) \\
y_5 &amp;amp;= p(p(p(p(p(x))))) \\
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;Each $y_k$ represents one more composition:  $y_1 \to y_5$ are multiple iterations aimed at converging the singular values toward 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;newton-schulz-5-iteration&quot;&gt;Newton-Schulz-5 Iteration&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;After 5 iterations, almost all input values end up very close to 1.&lt;/li&gt;
  &lt;li&gt;We can change $(a, b, c)$ to see the effect on convergence of $y$ to 1.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;$(a, b, c) = (2,\ -1.5,\ 0.5)$ speeds up the convergence to 1.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/2026/muon/newton-schulz5-converged.png&quot; alt=&quot;Netwon-Schulz5-2&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Empirically, we don’t need the singular values to converge to exactly 1.&lt;/li&gt;
  &lt;li&gt;Let’s set an upper &amp;amp; lower bound, e.g. $(0.7,\ 1.3)$, which is basically $[1 - \varepsilon, 1 + \varepsilon]$.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Tuned coefficients:&lt;/p&gt;

\[\boxed{(a,\ b,\ c) = (3.4445,\ -4.775,\ 2.0315)}\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Each Newton-Schulz 5 iteration involves only &lt;strong&gt;matrix multiplication&lt;/strong&gt;:&lt;/p&gt;

\[X \leftarrow aX + b(XX^T)X + c(XX^T)^2 X\]
  &lt;/li&gt;
  &lt;li&gt;With GPUs, no need to use SVD since GPUs can efficiently compute matrix multiplication.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newtonschulz5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1e-7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ndim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.4445&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;4.7750&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.0315&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bfloat16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;norm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Retrieved from https://kellerjordan.github.io/posts/muon/
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;

&lt;h2 id=&quot;muon&quot;&gt;Muon&lt;/h2&gt;

&lt;p&gt;Muon is designed specifically for 2D weight matrices in neural network hidden layers.&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; Unlike traditional optimizers that treat each parameter independently, Muon leverages the geometric structure of weight matrices by orthogonalizing gradients using the Newton-Schulz iteration. Muon is specifically designed for linear neural network layers, which aligns with ongoing research that argues that different layer types require different optimizers due to their varying geometry.&lt;sup id=&quot;fnref:6&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The optimizer formulates weight updates as a constrained optimization problem in the RMS-to-RMS operator norm space:&lt;/p&gt;

\[\text{Ortho}(M) = \arg\min_{O} \left\{\| O - M \|_F\right\} \quad \text{subject to } OO^T = I \text{ or } O^TO = I\]

&lt;p&gt;Where $M$ is the gradient matrix. The solution involves projecting the gradient onto the set of orthogonal matrices, which aims to standardize all singular values to 1 while preserving gradient directions.&lt;/p&gt;

&lt;h3 id=&quot;pseudo-algorithm-for-muon&quot;&gt;Pseudo-Algorithm for Muon&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;for t = 1, 2, ..., do:
    Compute gradient      G_t  ←  ∇L_t(θ_{t-1})
    Compute momentum      M_t  ←  βM_{t-1} + G_t
    Normalize             M&apos;_t ←  M_t / ||M_t||_F
    Orthogonalization     O_t  ←  NewtonSchutz5(M&apos;_t)
    Update parameter      θ_t  ←  θ_{t-1} - α O_t
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;With &lt;strong&gt;weight decay&lt;/strong&gt; $(+)$:&lt;/p&gt;

\[\Theta_t \leftarrow \Theta_{t-1} - \alpha\!\left(O_t + \lambda\Theta_{t-1}\right)\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;With &lt;strong&gt;adjusted learning rate&lt;/strong&gt; $(+)$:&lt;/p&gt;

\[\Theta_t \leftarrow \Theta_{t-1} - \alpha\!\left[\left(0.2\sqrt{\max(n,m)}\right) O_t + \lambda\Theta_{t-1}\right]\]

\[\begin{aligned}
  \text{where} \\
  \lambda &amp;amp;\equiv \text{weight decay coefficient} \\
  n, m &amp;amp;\equiv \text{dimensions of the 2D parameter matrix}
  \end{aligned}\]
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;muon--weight-decay--rms-alignment&quot;&gt;Muon + Weight Decay + RMS Alignment&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Weight decay is used to address the diminished performance gains of Muon over AdamW when scaling up to train a larger model.&lt;/li&gt;
  &lt;li&gt;The learning rate also gets adjusted by taking into account the &lt;strong&gt;size of the 2D matrix&lt;/strong&gt;. This is the underlying principle behind the &lt;strong&gt;RMS (Root Mean Squared) Alignment.&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;The scaling factor used to scale the Muon update for each matrix to ensure per-matrix parameter update RMS alignment of around 1 of matrices of different shapes is $\sqrt(\max(A, B))$ for a full-rank weight matrix of shape $[A,\ B]$.&lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
      &lt;li&gt;The $0.2$ factor is used to match Muon’s update RMS to that of AdamW. From empirical observations, AdamW’s update RMS is usually around $0.2$ to $0.4$.&lt;sup id=&quot;fnref:2:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, &lt;sup id=&quot;fnref:5:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;These 2 improvements (weight decay &amp;amp; adjusting the per-parameter update scale) help to stabilize the training of large models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-exploding-attention-logit-crisis&quot;&gt;The Exploding Attention Logit Crisis&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Attention logits can grow larger &amp;amp; larger as training continues, which may cause the training process to become unstable.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Consider a sequence of 4 tokens &amp;amp; assume self-attention for simplicity. Each token is mapped to an embedding vector of dimension $d$. Let the embedding matrix be $X$.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/2026/muon/self-attention.png&quot; alt=&quot;Self-attention architecture diagram&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Self-Attention&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;$O = \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;In self attention above, $Q = XW^Q$, $K = XW^K$, and $V = XW^V$.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;strong&gt;attention logits&lt;/strong&gt; $S$:&lt;/p&gt;

\[\begin{align*}
  S &amp;amp;= QK^T \\
  &amp;amp;= (XW^Q)(XW^K)^T \\
  &amp;amp;= X\!\left(W^Q W^{K^T}\right)X^T
  \end{align*}\]

    &lt;p&gt;where $X$ and $X^T$ denote the embedding vectors, which are typically normalized to have unit norms.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To prevent the attention logits from becoming excessively large, we must control the scale of $W^Q$ and $W^K$:&lt;/p&gt;

\[S = X\underbrace{\left(W^Q W^{K^T}\right)}_{\text{scale control}}X^T\]
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;qk-clip&quot;&gt;QK-Clip&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;A common strategy is to apply a scaling vector to these matrices.&lt;/li&gt;
  &lt;li&gt;During training, monitor the maximum value of the attention logits, $S_{\max}$. If it exceeds a certain threshold $\tau$, calculate a scaling ratio $\gamma$:&lt;/li&gt;
&lt;/ul&gt;

\[\text{if } S_{\max} &amp;gt; \tau: \quad \gamma = \frac{\tau}{S_{\max}} \implies \frac{\tau}{S_{\max}} &amp;lt; 1\]

&lt;ul&gt;
  &lt;li&gt;Directly constrains attention logits, ensuring they stay within a safe range by rescaling the query &amp;amp; key projection weights.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Idea:&lt;/strong&gt; Scale the relevant model parameters by $\gamma$ when the attention logits surpass the threshold. Scale both $W^Q$, $W^K$ by $\sqrt{\gamma}$:&lt;/p&gt;

\[\begin{align*}
S &amp;amp;= X\!\left(\gamma W^Q W^{K^T}\right)X^T \\
&amp;amp;= X\!\left(\sqrt{\gamma}\, W^Q\ \sqrt{\gamma}\, W^{K^T}\right)X^T
\end{align*}\]

&lt;p&gt;&lt;strong&gt;Revised pseudo-algorithm (QK-Clip):&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;θ_t ← MuonOptimizer(θ_{t-1}, G_t)
if S_max &amp;gt; τ:
    W^Q ← √γ W^Q
    W^K ← √γ W^K
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;qk-clip-for-multi-head-attention&quot;&gt;QK-Clip for Multi-Head Attention&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;In practice, self-attention consists of multiple heads ($n_{\text{heads}} = h$).&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;When the maximum attention logits exceed the threshold, instead of rescaling all heads in the same way (which doesn’t make sense), we introduce an &lt;strong&gt;individual scaling factor for each head&lt;/strong&gt; to control their logits separately.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/2026/muon/MHA.png&quot; alt=&quot;Multi-head attention architecture diagram&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Multi-Head Attention&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;$O = \text{MultiheadAttention}(Q, K, V) = \text{Concat}(head_0, head_1, .., head_h) W_o$&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Attention logits per head:&lt;/p&gt;

\[S = X\!\left(\sqrt{\gamma_h}\, W_h^Q\ \sqrt{\gamma_h}\, W_h^{K^T}\right)X^T\]

\[\text{if } S_{\max}^h &amp;gt; \tau \quad \text{then} \quad \gamma_h = \frac{\tau}{S_{\max}}\]
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Algorithm for Muon + QK-Clip for MHA:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;θ_t ← MuonOptimizer(θ_{t-1}, G_t)
if S^h_max &amp;gt; τ:
    W^Q_h ← √γ_h W^Q_h
    W^K_h ← √γ_h W^K_h
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;multihead-latent-attention-mla&quot;&gt;Multihead Latent Attention (MLA)&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;For multihead Latent Attention (MLA) by DeepSeek, things get tricky.&lt;/p&gt;

    &lt;p&gt;&lt;!-- $$O = \text{Multihead Latent Attention}(Q, K, V)$$ --&gt;&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/images/2026/muon/MLA.png&quot; alt=&quot;MLA architecture diagram&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;MLA compresses $Q, K, V$ representations into a low-rank space to reduce the size of the KV cache using a &lt;strong&gt;down-projection matrix&lt;/strong&gt; which produces latent representations:&lt;/p&gt;

\[C^Q = XW^Q_\downarrow \qquad C^{KV} = XW^{KV}_\downarrow\]
  &lt;/li&gt;
  &lt;li&gt;These compressed latent vectors are then mapped back to $W^Q_\uparrow$, $W^K_\uparrow$, $W^V_\uparrow$ for each attention head using the corresponding &lt;strong&gt;up-projection matrices&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Issue:&lt;/strong&gt; This low-rank KV compression fails with rotary position embedding (RoPE).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fix:&lt;/strong&gt; A decoupled RoPE technique which introduces extra multi-head queries $W^{QR}$ and a shared key $W^{KR}$ to encode positional information.&lt;/li&gt;
  &lt;li&gt;For MLA, the Query, Key &amp;amp; Values are regrouped for each head:
    &lt;ul&gt;
      &lt;li&gt;The &lt;strong&gt;Query&lt;/strong&gt; is constructed by concatenating the compressed query $Q^C$ with the rotated query $Q^R$.&lt;/li&gt;
      &lt;li&gt;The &lt;strong&gt;Key&lt;/strong&gt; is constructed similarly by concatenating the compressed key $K^C$ with the rotated key $K^R$.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Multihead Latent Attention (MLA)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;MLA compresses $Q, K, V$ representations into a low-rank latent space via down-projection matrices $W^Q_\downarrow$ and $W^{KV}_\downarrow$, then maps back up via up-projection matrices. A decoupled RoPE technique adds rotary queries $W^{QR}$ and a shared rotary key $W^{KR}$.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;muonclip&quot;&gt;MuonClip&lt;/h2&gt;

&lt;p&gt;In MLA, it is important to carefully decide how to rescale these 4 matrices: $W^Q_\uparrow,\ W^{QR},\ W^{KR},\ W^K_\uparrow$.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;For the &lt;strong&gt;up-projection matrices&lt;/strong&gt; $W^Q_\uparrow$, $W^K_\uparrow$: rescale these parameters for each head individually.&lt;/li&gt;
  &lt;li&gt;The &lt;strong&gt;RoPE components&lt;/strong&gt; $W^{QR}$, $W^{KR}$ rescaling is more tricky:
    &lt;ul&gt;
      &lt;li&gt;Each head has its &lt;strong&gt;own&lt;/strong&gt; rotary query $W^{QR}$.&lt;/li&gt;
      &lt;li&gt;But all heads &lt;strong&gt;share&lt;/strong&gt; a single rotary key matrix $W^{KR}$.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Applying the same per-head scaling for both RoPE components leads to the shared $W^{KR}$ being rescaled multiple times, which is undesirable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Rescale only the head-specific rotary query $W^{QR}$ by their respective $\gamma^h$, while leaving the shared rotary key matrix $W^{KR}$ unchanged. This technique is called &lt;strong&gt;MuonClip&lt;/strong&gt;. MuonClip improves upon Muon with the QK-Clip technique to handle training instability while benefiting from Muon’s advanced token efficiency.&lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MuonClip Algorithm:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;θ_t ← MuonOptimizer(θ_{t-1}, G_t)
if S^h_max &amp;gt; τ:
    γ_h = τ / S^h_max
    W^Q_{h,↑}  ← √γ_h  W^Q_{h,↑}
    W^K_{h,↑}  ← √γ_h  W^K_{h,↑}
    W^{QR}_h   ← γ_h   W^{QR}_h
    W^{KR}     ← W^{KR}   (unchanged)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In math:&lt;/p&gt;

\[\begin{aligned}
\gamma_h &amp;amp;= \frac{\tau}{S_{\max}^h} \\[6pt]
W_{h,\uparrow}^Q &amp;amp;\leftarrow \sqrt{\gamma_h}\, W_{h,\uparrow}^Q \\
W_{h,\uparrow}^K &amp;amp;\leftarrow \sqrt{\gamma_h}\, W_{h,\uparrow}^K \\
W_h^{QR} &amp;amp;\leftarrow \gamma_h\, W_h^{QR} \\
W^{KR} &amp;amp;\leftarrow W^{KR} \quad \text{(unchanged)}
\end{aligned}\]

&lt;hr /&gt;

&lt;h2 id=&quot;citation&quot;&gt;Citation&lt;/h2&gt;

&lt;p&gt;If you found this blog post helpful, please consider citing it:&lt;/p&gt;

&lt;div class=&quot;language-bibtex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;@article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;obasi2026muonmuonclip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Muon &amp;amp; MuonClip Optimizers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Obasi, Chizoba&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;chizkidd.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;year&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2026&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;month&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Apr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://chizkidd.github.io/2026/04/04/muon-muonclip/&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;hr /&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:5&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Kimi (Moonshot AI). &lt;a href=&quot;https://x.com/Kimi_Moonshot/status/1897929976948965870&quot;&gt;Why We Chose Muon: Our Chain of Thought&lt;/a&gt;. X (Twitter). 2025. &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:5:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:7&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Chizoba Obasi. &lt;a href=&quot;https://chizkidd.github.io/2026/01/22/neural-net-optimizers/&quot;&gt;A Complete Guide to Neural Network Optimizers&lt;/a&gt;. chizkidd.github.io. 2026. &lt;a href=&quot;#fnref:7&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Jia-Bin Huang &lt;a href=&quot;https://youtu.be/bO5nvE289ec&quot;&gt;This Simple Optimizer Is Revolutionizing How We Train AI [Muon] (YouTube Video)&lt;/a&gt;. YouTube. &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Keller Jordan. &lt;a href=&quot;https://kellerjordan.github.io/posts/muon/&quot;&gt;Muon: An optimizer for hidden layers in neural networks&lt;/a&gt;. kellerjordan.github.io. 2024. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:6&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Jeremy Bernstein. &lt;a href=&quot;https://jeremybernste.in/writing/deriving-muon&quot;&gt;Deriving Muon&lt;/a&gt;. jeremybernste.in. 2025. &lt;a href=&quot;#fnref:6&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://arxiv.org/pdf/2502.16982&quot;&gt;Muon is Scalable for LLM Training&lt;/a&gt;. arXiv. 2025. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:2:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://arxiv.org/pdf/2507.20534&quot;&gt;Kimi K2: Open Agentic Intelligence&lt;/a&gt;. arXiv. 2025. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Sat, 04 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://chizkidd.github.io//2026/04/04/muon-muonclip/</link>
        <guid isPermaLink="true">https://chizkidd.github.io//2026/04/04/muon-muonclip/</guid>
        
        
      </item>
    
      <item>
        <title>Inkcast: Turn Any EPUB or PDF into an Audiobook in Your Browser</title>
        <description>&lt;p&gt;Earlier this year, I decided to force myself to read more. Not a New Year’s resolution, because those never last. The reason is that growing up as a child and young teenager, reading often felt like punishment. My mum required my siblings and me to read a certain number of pages from a designated book every day throughout elementary school. Missing a day meant mandatory punishment. In boarding secondary school, this eventually led to a stubborn, subconscious resistance to non-essential reading. Over the six years I spent there, I probably read only five to ten non-academic fiction books (though &lt;em&gt;Artemis Fowl&lt;/em&gt; was a delight). So it is not hard to see where my indifference to reading came from.&lt;/p&gt;

&lt;p&gt;During the COVID-19 pandemic, however, I fell deep into podcasts. As an avid sports fan and TV show buff, I listened to everything: sports recaps, tech podcasts, expert interviews, the works (meeting Walter White at Anfield would be the ultimate dream). So when I decided to read more this year, audiobooks felt like the natural bridge. I already had a few EPUBs in the Apple Books app on my reading list and wondered: &lt;em&gt;Can I listen to these EPUBs using Apple Dictation’s two-finger swipe-down feature?&lt;/em&gt; Unfortunately, it only works for the current page. It is quite janky, not very user-friendly, and frankly does not work well for my use case.&lt;/p&gt;

&lt;p&gt;Recently, I worked on &lt;a href=&quot;https://chizkidd.github.io/2026/03/01/tonal-fidelity-multilingual-asr/&quot;&gt;evaluating how a Facebook state-of-the-art (SOTA) automatic speech recognition (ASR) model handles Igbo tones&lt;/a&gt;, trying to see whether it actually “listens” properly. So I have been dabbling with audio quite a bit this year. You could say I have been thinking about listening a lot. In the past, I also experimented with WaveNet (a generative model for raw audio) and its fundamental building block, &lt;a href=&quot;https://chizkidd.github.io/Karpathy-Neural-Networks-Zero-to-Hero/006_makemore_WaveNet/makemore_WaveNet.html&quot;&gt;the dilated causal convolution&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With these experiences in mind, I wondered: &lt;em&gt;Can I build an iPhone Shortcut that lets me listen to EPUBs properly?&lt;/em&gt; That question eventually led to &lt;a href=&quot;https://test-inkcast.pages.dev/&quot;&gt;&lt;strong&gt;Inkcast&lt;/strong&gt;&lt;/a&gt;. The goal was not to build a Speechify competitor. I simply wanted to solve a personal problem. My aim was to create a low-effort, frictionless tool for personal use, so I made a GitHub repository and started building. Within a few days, I had a working website that could take EPUBs and PDFs and let users listen to the content organized by chapters in a sidebar with one-tap navigation. It included basic controls such as play/pause, rewind (15 seconds), forward (30 seconds), playback speed control (0.75× to 2×), and voice selection.&lt;/p&gt;

&lt;p&gt;It worked well on desktop, so I used the URL to create a Shortcut on my iPhone. On mobile it also worked, but there was one problem: the reader voices sounded robotic and monotone, which is not ideal for long-form listening. The irony was that only weeks earlier I had been evaluating how machines handle speech. So I went back to the drawing board to figure out how to get more natural-sounding reader voices on Inkcast.&lt;/p&gt;

&lt;p&gt;While researching audiobook-quality text-to-speech, I came across several APIs (OpenAI, ElevenLabs, Google Cloud). None were free, and for a personal project I wanted something that required no subscriptions or API keys. Most resources suggested that human-quality narration requires a dedicated TTS service. Eventually I discovered that the Web Speech API can access premium voices already installed on the device. These voices are free, require no API keys, and remain available offline. They are not state of the art, but they are surprisingly good. Many people do not realize that higher-quality Siri voices can be downloaded. Since then I have also added optional HD voices that run on-device via Kokoro-82M (a one-time download of about 90 to 330 MB, then they work offline) for the highest quality. The voice quality improved, but the project also started evolving in another direction.&lt;/p&gt;

&lt;p&gt;I have always wanted to work through Paul Graham’s essays properly. There are 229 of them, and they read almost like long-form podcasts. But they live on webpages, which raised another question: Why limit the input to EPUBs and PDFs? So I added URL support. I pasted Paul Graham’s archive page into Inkcast, and it automatically pulled all 200+ essays into the sidebar. That was the moment I realized the idea actually worked.&lt;/p&gt;

&lt;p&gt;The app itself lives in a single HTML file. There are no accounts and no installations, and your EPUBs and PDFs never leave your device. The one exception is web articles: when you paste a URL, a small proxy fetches the page for you, since browsers do not let a page fetch other sites directly. Everything else runs locally, so it ended up functioning as a privacy-preserving tool by default. In a way, I started the year studying whether machines listen well. Along the way, I realized that humans do not have many good free tools for listening either. Speechify costs $139 per year and Audible requires a subscription, so I built something that worked for me.&lt;/p&gt;

&lt;p&gt;Feel free to &lt;a href=&quot;https://test-inkcast.pages.dev/&quot;&gt;try it&lt;/a&gt;, and if you find it useful or it saves you a Speechify subscription, you can &lt;a href=&quot;https://buymeacoffee.com/cobasi&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Mon, 16 Mar 2026 00:00:00 +0000</pubDate>
        <link>https://chizkidd.github.io//2026/03/16/inkcast/</link>
        <guid isPermaLink="true">https://chizkidd.github.io//2026/03/16/inkcast/</guid>
        
        
      </item>
    
      <item>
        <title>Eligibility Traces Explained: TD(λ), Sarsa(λ), and the λ-Return (S&amp;B Ch. 12)</title>
        <description>&lt;ul&gt;
  &lt;li&gt;Eligibility traces are one of the basic mechanisms of RL that unify and generalize TD and Monte Carlo (MC) methods.&lt;/li&gt;
  &lt;li&gt;TD methods augmented with eligibility traces produce a family of methods spanning a range from MC methods at one end ($\lambda = 1$) to one-step TD (TD(0)) methods at the other end ($\lambda = 0$).&lt;/li&gt;
  &lt;li&gt;With eligibility traces, MC methods can be implemented online and on continuing problems.&lt;/li&gt;
  &lt;li&gt;$n$-step methods also unify TD and MC methods but are not as elegant algorithmically as eligibility traces (ET).&lt;/li&gt;
  &lt;li&gt;The eligibility traces algorithm entails:
    &lt;ul&gt;
      &lt;li&gt;First, we have a short-term memory vector, the &lt;strong&gt;eligibility trace&lt;/strong&gt; $\mathbf{z}_t \in \mathbb{R}^d$, that parallels the long-term weight vector $\mathbf{w}_t \in \mathbb{R}^d$.&lt;/li&gt;
      &lt;li&gt;Then when a component of $\mathbf{w}_t$ participates in producing an estimated value, the corresponding component of $\mathbf{z}_t$ is bumped up and then begins to fade away.&lt;/li&gt;
      &lt;li&gt;Learning occurs in that component of $\mathbf{w}_t$ if a non-zero TD error occurs before the trace falls back to zero (fades away).&lt;/li&gt;
      &lt;li&gt;The trace-decay parameter $\lambda \in [0,1)$ determines the rate at which the trace falls.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Advantages of ET over $n$-step methods:
    &lt;ul&gt;
      &lt;li&gt;Requires only a single trace vector $\mathbf{z}_t$ rather than storing the last $n$ feature vectors.&lt;/li&gt;
      &lt;li&gt;Learning occurs continually and uniformly in time rather than being delayed and playing “catch up” at episode end. This leads to immediate behavior effect from learning rather than being delayed.&lt;/li&gt;
      &lt;li&gt;ET is a &lt;strong&gt;backward view&lt;/strong&gt; algorithm, unlike $n$-step methods that are &lt;strong&gt;forward view&lt;/strong&gt; algorithms, which is less complex to implement.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Forward view&lt;/strong&gt; algorithms are based on looking forward from the updated state, and the updated state depends on all the future rewards.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Backward view&lt;/strong&gt; algorithms use the current TD error, looking backward to recently visited states, to achieve nearly the same updates as forward view.&lt;/li&gt;
  &lt;li&gt;We start with ideas for state values and prediction, then extend them to action values and control, then do on-policy, then extend to off-policy learning. The field of focus is linear function approximation (covers tabular and state aggregation cases).&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#121-the--return&quot;&gt;12.1 The $\lambda$-return&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#122-td&quot;&gt;12.2 TD($\lambda$)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#123--step-truncated--return-methods&quot;&gt;12.3 $n$-step Truncated $\lambda$-return Methods&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#124-redoing-updates-online--return-algorithm&quot;&gt;12.4 Redoing Updates: Online $\lambda$-return Algorithm&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#125-true-online-td&quot;&gt;12.5 True Online TD($\lambda$)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#126-dutch-traces-in-monte-carlo-learning&quot;&gt;12.6 Dutch Traces in Monte Carlo Learning&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#127-sarsa&quot;&gt;12.7 Sarsa($\lambda$)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#128-variable--and&quot;&gt;12.8 Variable $\lambda$ and $\gamma$&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#129-off-policy-traces-with-control-variates&quot;&gt;12.9 Off-Policy Traces with Control Variates&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#1210-watkinss-q-to-tree-backup&quot;&gt;12.10 Watkins’s Q($\lambda$) to Tree-Backup($\lambda$)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#1211-stable-off-policy-methods-with-traces&quot;&gt;12.11 Stable Off-Policy Methods with Traces&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#1212-implementation-issues&quot;&gt;12.12 Implementation Issues&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#1213-conclusions&quot;&gt;12.13 Conclusions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;appendix&quot;&gt;Appendix&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#citation&quot;&gt;Citation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;121-the-lambda-return-&quot;&gt;12.1 The $\lambda$-return &lt;a name=&quot;121-the--return&quot;&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Recall in Chapter 7 we defined an $n$-step return as the sum of the first $n$ rewards plus the estimated value of the state reached in $n$ steps, each appropriately discounted:&lt;/li&gt;
&lt;/ul&gt;

\[G_{t:t+n} \doteq R_{t+1} + \gamma R_{t+2} + \ldots + \gamma^{n-1} R_{t+n} + \gamma^n \hat{v}(S_{t+n}, \mathbf{w}_{t+n-1}), \quad\quad 0 \leq t \leq T - n\]

&lt;ul&gt;
  &lt;li&gt;A valid update can be done not just towards any $n$-step return, but also towards any average of $n$-step returns.
    &lt;ul&gt;
      &lt;li&gt;E.g. average the 2-step and 4-step return: $\frac{1}{2} G_{t:t+2} + \frac{1}{2} G_{t:t+4}$&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/rl-sutton-barto/ch12-12-1-the-2-and-4-step-returns.png&quot; alt=&quot;compound update of 2-step and 4-step&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Backup Diagram for Compound Update&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;The compound update mixing half of a two-step return and half of a four-step return.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Any set of $n$-step returns can be averaged, even an infinite set, as long as the weights on the component returns are positive and sum to $1$.&lt;/li&gt;
  &lt;li&gt;What if instead of using one $n$-step return, we use a weighted average of all $n$-step returns? This leads to averaging which produces a substantial new range of algorithms.E.g.,
    &lt;ol&gt;
      &lt;li&gt;Averaging one-step and infinite-step returns to interrelate TD and MC methods.&lt;/li&gt;
      &lt;li&gt;Averaging experience-based updates with Dynamic Programming (DP) updates to obtain a single combination of experience-based and model-based methods.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;An update that averages simpler component updates is called a &lt;strong&gt;compound update&lt;/strong&gt; or the &lt;strong&gt;$\lambda$-return&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;The TD($\lambda$) algorithm is one way of averaging $n$-step updates, each weighted proportionally by $\lambda^{n-1}$ (where $\lambda \in [0,1]$) and normalized by a factor of $(1-\lambda)$ to ensure that the weights sum to $1$.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/rl-sutton-barto/ch12-12-1-td-lambda.png&quot; alt=&quot;TD(lambda)&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Backup Diagram for TD($\lambda$)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;If $\lambda = 0$, then the overall update reduces to its first component, the &lt;strong&gt;TD(0)&lt;/strong&gt; update, whereas if $\lambda = 1$, then the overall update reduces to its last component, the &lt;strong&gt;MC&lt;/strong&gt; update.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Essentially, $\lambda$-return, $G_t^\lambda$, combines all $n$-step returns $G_{t:t+n}$ in a weighted average manner, $(1-\lambda)\lambda^{n-1}$, and is defined in its state-based form by:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{G_t^\lambda \doteq (1-\lambda) \sum_{n=1}^{\infty} \lambda^{n-1} G_{t:t+n}}\]

&lt;h3 id=&quot;tdlambda-weighting&quot;&gt;TD($\lambda$) Weighting&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;The TD($\lambda$) weighting function diagram illustrates the weighting on the sequence of $n$-step returns in the $\lambda$-return:
    &lt;ul&gt;
      &lt;li&gt;$1$-step return gets the largest weight, $1-\lambda$&lt;/li&gt;
      &lt;li&gt;$2$-step return gets the next (2nd) largest weight, $(1-\lambda)\lambda$&lt;/li&gt;
      &lt;li&gt;$3$-step return gets the 3rd largest weight, $(1-\lambda)\lambda^2$&lt;/li&gt;
      &lt;li&gt;$n$-step return gets the $n$-th largest (smallest) weight, $(1-\lambda)\lambda^{n-1}$&lt;/li&gt;
      &lt;li&gt;The weight fades by $\lambda$ with each additional step.&lt;/li&gt;
      &lt;li&gt;After a terminal state has been reached, all subsequent $n$-step returns are equal to the conventional return $G_t$.&lt;/li&gt;
      &lt;li&gt;So essentially, we can decompose $G_t^\lambda$ based on the TD($\lambda$) weighting function diagram into the main sum and post-termination terms:&lt;/li&gt;
    &lt;/ul&gt;

\[\begin{array}{l}
G_t^\lambda = (1-\lambda) \sum\nolimits_{n=1}^{T-t-1} \lambda^{n-1} G_{t:t+n} + \lambda^{T-t-1} G_t \\
\hspace{3em} \underbrace{\hspace{11em}}_{\text{pre-termination}} \kern{0.5em}\underbrace{\hspace{4em}}_{\text{post-termination}}
\end{array}\]

    &lt;ul&gt;
      &lt;li&gt;So now we can see the impact of $\lambda$ more clearly:&lt;/li&gt;
    &lt;/ul&gt;

\[\begin{aligned}
\text{if } \lambda = 1: \quad &amp;amp; G_t^\lambda = G_t \hspace{18em} \text{(MC)} \\[6pt]
\text{if } \lambda = 0: \quad &amp;amp; G_t^\lambda = \left\{ \begin{array}{ll} \sum_{n=1}^{T-t-1} G_{t:t+n} &amp;amp; \text{for } n=1 \\ 0 &amp;amp; \text{for } n &amp;gt; 1 \end{array} \right\} = G_{t:t+1} \quad \text{(TD(0))}
\end{aligned}\]
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/rl-sutton-barto/ch12-12-1-td-lambda-weighting-function.png&quot; alt=&quot;TD(lambda) weighting&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;TD($\lambda$) Weighting&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;Weighting given in the $\lambda$-return to each of the $n$-step returns.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Our first learning algorithm based on the $\lambda$-return is the &lt;strong&gt;off-line $\lambda$-return algorithm&lt;/strong&gt;, which waits until the end of an episode to make updates. Its semi-gradient, $\lambda$-return, target update for $t = 0, 1, 2, \ldots, T-1$ is:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\mathbf{w}_{t+1} \doteq \mathbf{w}_t + \alpha \!\left[G_t^\lambda - \hat{v}(S_t, \mathbf{w}_t)\right] \nabla \hat{v}(S_t, \mathbf{w}_t)}\]

&lt;ul&gt;
  &lt;li&gt;The $\lambda$-return allows us to smoothly move between MC and TD(0) methods, comparable to $n$-step returning.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;forward-view&quot;&gt;Forward View&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;This approach is called the theoretical, forward view of a learning algorithm:
    &lt;ul&gt;
      &lt;li&gt;Update value function towards the $\lambda$-return.&lt;/li&gt;
      &lt;li&gt;Look forward in time to all the future rewards to compute $G_t^\lambda$.&lt;/li&gt;
      &lt;li&gt;Like MC, can only be computed from complete return.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/rl-sutton-barto/ch12-12-1-forward-view-td-lambda.png&quot; alt=&quot;Forward view&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Forward View&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;We decide how to update each state by looking forward to future rewards and states.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;122-tdlambda-&quot;&gt;12.2 TD($\lambda$) &lt;a name=&quot;122-td&quot;&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;TD($\lambda$) was the first algorithm that showed a formal relationship between a forward view and backward view using eligibility traces.&lt;/li&gt;
  &lt;li&gt;TD($\lambda$) improves over the off-line $\lambda$-return algorithm in 3 ways:
    &lt;ul&gt;
      &lt;li&gt;Updates the weight vector on every step of an episode and not just the end.&lt;/li&gt;
      &lt;li&gt;Equal distributions in time of its computations rather than at an episode’s end.&lt;/li&gt;
      &lt;li&gt;Can be applied to continuing problems and not just episodic problems.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Let’s focus on the &lt;strong&gt;semi-gradient version of TD($\lambda$)&lt;/strong&gt; with function approximation:
    &lt;ul&gt;
      &lt;li&gt;The &lt;strong&gt;eligibility trace $\mathbf{z}_t$&lt;/strong&gt; has the same number of components as $\mathbf{w}_t$.&lt;/li&gt;
      &lt;li&gt;$\mathbf{z}$ is initialized to $\mathbf{0}$, incremented on each time step by the value gradient, and then fades away by $\gamma\lambda$:&lt;/li&gt;
    &lt;/ul&gt;

\[\begin{align*}
\mathbf{z}_{-1} &amp;amp;\doteq \mathbf{0} \\
\mathbf{z}_t &amp;amp;\doteq \gamma\lambda \mathbf{z}_{t-1} + \nabla \hat{v}(S_t, \mathbf{w}_t), \quad 0 \leq t \leq T
\end{align*}\]

\[\text{where } \lambda \equiv \text{trace decay parameter and }  \gamma \equiv \text{discount rate}\]
  &lt;/li&gt;
  &lt;li&gt;The eligibility trace keeps track of which $\mathbf{w}_t$ components have contributed, positively or negatively, to recent state valuations.&lt;/li&gt;
  &lt;li&gt;This is the &lt;strong&gt;recency heuristic&lt;/strong&gt; used for &lt;strong&gt;credit assignment,&lt;/strong&gt; where more credit is assigned to the most recent states. &lt;strong&gt;Recent&lt;/strong&gt; is defined in terms of $\gamma\lambda$.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The TD error for state-value prediction is:&lt;/p&gt;

\[\delta_t \doteq R_{t+1} + \gamma \hat{v}(S_{t+1}, \mathbf{w}_t) - \hat{v}(S_t, \mathbf{w}_t)\]

    &lt;p&gt;and the weight vector update in TD($\lambda$) is proportional to the scalar TD error and the vector eligibility trace:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\mathbf{w}_{t+1} \doteq \mathbf{w}_t + \alpha\, \delta_t\, \mathbf{z}_t}\]

&lt;h3 id=&quot;backward-view&quot;&gt;Backward View&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Forward view provides theory but backward view provides mechanism (practical) where we update online, every step, from incomplete sequences.&lt;/li&gt;
  &lt;li&gt;Keep an eligibility trace for every state $s$.&lt;/li&gt;
  &lt;li&gt;Update value $V(s)$ for every state $s$ in proportion to TD-error $\delta_t$ and eligibility trace $\mathbf{z}_t$:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{aligned}
\delta_t &amp;amp;= R_{t+1} + \gamma V(S_{t+1}) - V(S_t) \\
V(s) &amp;amp;\leftarrow V(s) + \alpha\, \delta_t\, \mathbf{z}_t
\end{aligned}\]

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/rl-sutton-barto/ch12-12-2-backward-view-td-lambda.png&quot; alt=&quot;Backward TD(lambda)&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Backward View of TD($\lambda$)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;In the backward or mechanistic view of TD($\lambda$), each update depends on the 
current TD error combined with the current eligibility traces of past events.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Let’s look at the effect of $\lambda$ to understand the backward view of TD($\lambda$):&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
\text{if } \lambda = 0: \quad &amp;amp; \mathbf{z}_t = \nabla \hat{v}(S_t, \mathbf{w}_t) \\
&amp;amp; \mathbf{w}_{t+1} = \mathbf{w}_t + \alpha\, \delta_t \nabla \hat{v}(S_t, \mathbf{w}_t) \quad \longrightarrow \quad \text{TD(0)} \\[6pt]
\text{if } 0 &amp;lt; \lambda &amp;lt; 1: \quad &amp;amp; \text{earlier states are given less credit for the TD error} \\[6pt]
\text{if } \lambda = 1: \quad &amp;amp; \mathbf{z}_t = \gamma \mathbf{z}_{t-1} + \nabla \hat{v}(S_t, \mathbf{w}_t) \quad \longrightarrow \quad \text{credit for earlier states falls by } \gamma \text{ per step} \\[6pt]
\text{if } \lambda = 1,\ \gamma = 1: \quad &amp;amp; \mathbf{z}_t = \mathbf{z}_{t-1} + \nabla \hat{v}(S_t, \mathbf{w}_t) \quad \longrightarrow \quad \text{MC-like behavior (no time decay for ET)} \\[6pt]
\text{if } \lambda = 1: \quad &amp;amp; \text{we get TD(1)}
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;In summary, $\lambda = 1$ yields TD(1).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;TD(1) implements MC algorithms in a more general way and for a wider range of applicability:
    &lt;ul&gt;
      &lt;li&gt;Not limited to episodic tasks, can be applied to discounted continuing tasks.&lt;/li&gt;
      &lt;li&gt;Can be performed &lt;strong&gt;incrementally and online.&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Learns &lt;strong&gt;immediately&lt;/strong&gt; and alters behavior during an episode if something good or bad happens, for control methods.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Linear TD($\lambda$) converges in the on-policy case if the step-size parameter $\alpha$ is reduced over time according to stochastic approximation theory conditions.&lt;/li&gt;
  &lt;li&gt;The convergence of linear TD($\lambda$) is not to the minimum-error weight vector but to a nearby weight vector that depends on $\lambda$.&lt;/li&gt;
  &lt;li&gt;The bound on solution quality generalized for any $\lambda$, for the continuing, discounted case is:&lt;/li&gt;
&lt;/ul&gt;

\[\overline{\text{VE}}(\mathbf{w}_\infty) \leq \frac{1 - \gamma\lambda}{1 - \gamma} \min_\mathbf{w} \overline{\text{VE}}(\mathbf{w})\]

&lt;ul&gt;
  &lt;li&gt;That is, the asymptotic error is no more than $\dfrac{1-\gamma\lambda}{1-\gamma}$ times the smallest possible error for TD($\lambda$):&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
\text{as } \lambda \to 1: \quad &amp;amp; \overline{\text{VE}}(\mathbf{w}_\infty) \to \min_\mathbf{w} \overline{\text{VE}}(\mathbf{w}) \\[6pt]
\text{as } \lambda \to 0: \quad &amp;amp; \overline{\text{VE}}(\mathbf{w}_\infty) \to \frac{1}{1-\gamma} \min_\mathbf{w} \overline{\text{VE}}(\mathbf{w}) = \overline{\text{VE}}(\mathbf{w}_\text{TD}) \quad \text{(TD(0))}
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;However, $\lambda = 1$ is often the poorest choice.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;123-n-step-truncated-lambda-return-methods-&quot;&gt;12.3 $n$-step Truncated $\lambda$-return Methods &lt;a name=&quot;123--step-truncated--return-methods&quot;&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;The off-line $\lambda$-return is of limited use because the $\lambda$-return is not known until the episode ends.&lt;/li&gt;
  &lt;li&gt;The off-line $\lambda$-return approximation is to truncate the sequence after a &lt;strong&gt;fixed&lt;/strong&gt; number of steps.&lt;/li&gt;
  &lt;li&gt;Hence, a natural approximation is to truncate the sequence where $\lambda$-return cannot be calculated for an arbitrarily large $n$. This handles the continuing case.&lt;/li&gt;
  &lt;li&gt;The truncated $\lambda$-return for time $t$, given data only up to some later horizon $h$, is:&lt;/li&gt;
&lt;/ul&gt;

\[G_{t:h}^\lambda \doteq (1-\lambda) \sum_{n=1}^{h-t-1} \lambda^{n-1} G_{t:t+n} + \lambda^{h-t-1} G_{t:h}, \quad 0 \leq t \leq h \leq T\]

\[\begin{aligned}
\text{where } h &amp;amp;\equiv \text{horizon (plays same role as time of termination } T\text{)}
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;Here the &lt;strong&gt;residual weighting&lt;/strong&gt; is given to the longest available $n$-step return $G_{t:h}$.&lt;/li&gt;
  &lt;li&gt;The truncated $\lambda$-return gives rise to a family of $n$-step $\lambda$-return algorithms, known in the state-value case as &lt;strong&gt;Truncated TD($\lambda$)&lt;/strong&gt; or &lt;strong&gt;TTD($\lambda$)&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;TTD($\lambda$) is defined for $0 \leq t &amp;lt; T$ by:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\mathbf{w}_{t+n} \doteq \mathbf{w}_{t+n-1} + \alpha \!\left[G_{t:t+n}^\lambda - \hat{v}(S_t, \mathbf{w}_{t+n-1})\right] \nabla \hat{v}(S_t, \mathbf{w}_{t+n-1})}\]

&lt;ul&gt;
  &lt;li&gt;Efficient implementation of TTD($\lambda$) relies on the $k$-step $\lambda$-return:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{G_{t:t+k}^\lambda = \hat{v}(S_t, \mathbf{w}_{t-1}) + \sum_{i=t}^{t+k-1} (\delta\lambda)^{i-t} \delta_i&apos;}\]

\[\begin{aligned}
\text{where } \delta_i&apos; &amp;amp;\equiv R_{t+1} + \gamma \hat{v}(S_{t+1}, \mathbf{w}_{t-1}) - \hat{v}(S_t, \mathbf{w}_{t-1})
\end{aligned}\]

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/rl-sutton-barto/ch12-12-3-truncated-td-lambda.png&quot; alt=&quot;TTD(lambda)&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Backup Diagram for Truncated TD($\lambda$)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;The truncated $\lambda$-return gives rise to a family of $n$-step $\lambda$-return algorithms called &lt;strong&gt;TTD($\lambda$)&lt;/strong&gt;.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;124-redoing-updates-online-lambda-return-algorithm-&quot;&gt;12.4 Redoing Updates: Online $\lambda$-return Algorithm &lt;a name=&quot;124-redoing-updates-online--return-algorithm&quot;&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;How do we choose the truncation parameter $n$ in TTD($\lambda$)?&lt;/li&gt;
  &lt;li&gt;It involves a tradeoff:
    &lt;ul&gt;
      &lt;li&gt;$n$ should be large so that TTD($\lambda$) closely approximates off-line $\lambda$-return, but&lt;/li&gt;
      &lt;li&gt;$n$ should also be small so that the updates can be made sooner and can influence behavior sooner.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;In principle, we can achieve both cases via the &lt;strong&gt;online $\lambda$-return algorithm&lt;/strong&gt;, but at the cost of computational complexity.&lt;/li&gt;
  &lt;li&gt;Essentially at each time step, we go back and redo all the updates since the beginning of the episode as we gather new increment of data:
    &lt;ul&gt;
      &lt;li&gt;The new updates are better than the old ones because now they account for the time step’s new data.&lt;/li&gt;
      &lt;li&gt;Basically this conceptual algorithm involves multiple passes over the episode, one at each horizon, each generating a different sequence of weight vectors.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Let’s distinguish between the weight vectors computed at the different horizons by seeing the first 3 sequences:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
h=1: \quad &amp;amp; \mathbf{w}_1^1 \doteq \mathbf{w}_0^1 + \alpha \!\left[G_{0:1}^\lambda - \hat{v}(S_0, \mathbf{w}_0^1)\right] \nabla \hat{v}(S_0, \mathbf{w}_0^1) \\[6pt]
h=2: \quad &amp;amp; \mathbf{w}_0^2 \doteq \mathbf{w}_0^2 + \alpha \!\left[G_{0:2}^\lambda - \hat{v}(S_0, \mathbf{w}_0^2)\right] \nabla \hat{v}(S_0, \mathbf{w}_0^2) \\
&amp;amp; \mathbf{w}_2^2 \doteq \mathbf{w}_1^2 + \alpha \!\left[G_{1:2}^\lambda - \hat{v}(S_1, \mathbf{w}_1^2)\right] \nabla \hat{v}(S_1, \mathbf{w}_1^2) \\[6pt]
h=3: \quad &amp;amp; \mathbf{w}_1^3 \doteq \mathbf{w}_0^3 + \alpha \!\left[G_{0:3}^\lambda - \hat{v}(S_0, \mathbf{w}_0^3)\right] \nabla \hat{v}(S_0, \mathbf{w}_0^3) \\
&amp;amp; \mathbf{w}_2^3 \doteq \mathbf{w}_1^3 + \alpha \!\left[G_{1:3}^\lambda - \hat{v}(S_1, \mathbf{w}_1^3)\right] \nabla \hat{v}(S_1, \mathbf{w}_1^3) \\
&amp;amp; \mathbf{w}_3^3 \doteq \mathbf{w}_2^3 + \alpha \!\left[G_{2:3}^\lambda - \hat{v}(S_2, \mathbf{w}_2^3)\right] \nabla \hat{v}(S_2, \mathbf{w}_2^3)
\end{align*}\]

\[\begin{aligned}
\text{where} \\
\mathbf{w}_t^h &amp;amp;\equiv \text{weights used to generate the value at time } t \text{ in the sequence up to horizon } h \\
\mathbf{w}_0^h &amp;amp;\equiv \text{1st weight vector in each sequence that is inherited from the previous episode} \\
\mathbf{w}_n^h &amp;amp;\equiv \text{last weight vector in each sequence or the ultimate weight-vector sequence}
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;The general form of the &lt;strong&gt;online $\lambda$-return update&lt;/strong&gt; for $0 \leq t &amp;lt; h \leq T$ is:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\mathbf{w}_{t+1}^h \doteq \mathbf{w}_t^h + \alpha \!\left[G_{t:h}^\lambda - \hat{v}(S_t, \mathbf{w}_t^h)\right] \nabla \hat{v}(S_t, \mathbf{w}_t^h)}\]

\[\mathbf{w}_t \doteq \mathbf{w}_t^t\]

&lt;hr /&gt;

&lt;h2 id=&quot;125-true-online-tdlambda-&quot;&gt;12.5 True Online TD($\lambda$) &lt;a name=&quot;125-true-online-td&quot;&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;The original ideal online $\lambda$-return algorithm shown in Section 12.4 is very complex so we use online TD($\lambda$) to approximate it.&lt;/li&gt;
  &lt;li&gt;We’ll use eligibility trace to invert the forward view, online $\lambda$-return to an efficient backward view algorithm. This is called the &lt;strong&gt;True Online TD($\lambda$)&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;It uses a simple strategy trick with the weight matrix, where we only need to keep the last weight vector from all the updates at the last time step (the diagonals of online $\lambda$-return weight matrix).&lt;/li&gt;
&lt;/ul&gt;

\[\begin{aligned}
\begin{bmatrix}
\mathbf{w}_0^0 &amp;amp; &amp;amp; &amp;amp; &amp;amp; \\
\mathbf{w}_0^1 &amp;amp; \mathbf{w}_1^1 &amp;amp; &amp;amp; &amp;amp; \\
\mathbf{w}_0^2 &amp;amp; \mathbf{w}_1^2 &amp;amp; \mathbf{w}_2^2 &amp;amp; &amp;amp; \\
\mathbf{w}_0^3 &amp;amp; \mathbf{w}_1^3 &amp;amp; \mathbf{w}_2^3 &amp;amp; \mathbf{w}_3^3 &amp;amp; \\
\vdots &amp;amp; \vdots &amp;amp; \vdots &amp;amp; \vdots &amp;amp; \ddots \\
\mathbf{w}_0^T &amp;amp; \mathbf{w}_1^T &amp;amp; \mathbf{w}_2^T &amp;amp; \mathbf{w}_3^T &amp;amp; \cdots &amp;amp; \mathbf{w}_T^T
\end{bmatrix}
&amp;amp;\longrightarrow
\begin{bmatrix}
\mathbf{w}_0^0 \\
&amp;amp; \mathbf{w}_1^1 \\
&amp;amp; &amp;amp; \mathbf{w}_2^2 \\
&amp;amp; &amp;amp; &amp;amp; \mathbf{w}_3^3 \\
&amp;amp; &amp;amp; &amp;amp; &amp;amp; \ddots \\
&amp;amp; &amp;amp; &amp;amp; &amp;amp; &amp;amp; \mathbf{w}_T^T
\end{bmatrix} \\
\end{aligned}\]

\[\text{Online } \lambda\text{-return} \hspace{8em} \text{True Online TD}(\lambda)\]

&lt;ul&gt;
  &lt;li&gt;For the linear case in which $\hat{v}(s, \mathbf{w}) = \mathbf{w}^T \mathbf{x}(s)$, the true online TD($\lambda$) algorithm is:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\mathbf{w}_{t+1} \doteq \mathbf{w}_t + \alpha\, \delta_t\, \mathbf{z}_t + \alpha \!\left(\mathbf{w}_t^T \mathbf{x}_t - \mathbf{w}_{t-1}^T \mathbf{x}_t\right)\!\left(\mathbf{z}_t - \mathbf{x}_t\right)}\]

\[\begin{aligned}
\text{where} \\
\mathbf{w}_t &amp;amp;\doteq \mathbf{w}_t^t \\
\mathbf{x}_t &amp;amp;\doteq \mathbf{x}(S_t) \\
\mathbf{z}_t &amp;amp;\doteq \gamma\lambda \mathbf{z}_{t-1} + (1 - \alpha\gamma\lambda\, \mathbf{z}_{t-1}^T \mathbf{x}_t)\, \mathbf{x}_t
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;The per-step computational complexity of true online TD($\lambda$) is the same as TD($\lambda$), $O(d)$.&lt;/li&gt;
  &lt;li&gt;$\mathbf{z}_t$ used in true online TD($\lambda$) is called a &lt;strong&gt;dutch trace&lt;/strong&gt;, unlike that of TD($\lambda$) which is called an &lt;strong&gt;accumulating trace&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Earlier work used a 3rd kind of trace called the &lt;strong&gt;replacing trace&lt;/strong&gt;, defined only for the tabular case or for binary feature vectors (tile coding). It is defined:&lt;/li&gt;
&lt;/ul&gt;

\[\tilde{z}_{i,t} \doteq \left\{ \begin{array}{ll} 1 &amp;amp; \text{if } x_{i,t} = 1 \\ \gamma\lambda\, z_{i,t-1} &amp;amp; \text{otherwise} \end{array} \right\}\]

&lt;ul&gt;
  &lt;li&gt;Nowadays, dutch traces usually perform better than replacing traces and have a clearer theoretical basis.&lt;/li&gt;
  &lt;li&gt;Accumulating traces remain of interest for nonlinear function approximations where dutch traces are unavailable.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;126-dutch-traces-in-monte-carlo-learning&quot;&gt;12.6 Dutch Traces in Monte Carlo Learning&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Eligibility traces have nothing to do with TD learning despite their close historical association.&lt;/li&gt;
  &lt;li&gt;Eligibility traces arise even in Monte Carlo learning.&lt;/li&gt;
  &lt;li&gt;Using dutch traces, we can invert the forward view MC algorithm to an equivalent, yet computationally cheaper backward view algorithm.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;This is the only equivalence of forward and backward view that is explicitly demonstrated in this book.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;The linear, gradient MC prediction algorithm makes the following sequence of updates, one for each time step of the episode:&lt;/li&gt;
&lt;/ul&gt;

\[\mathbf{w}_{t+1} \doteq \mathbf{w}_t + \alpha \!\left[G - \mathbf{w}_t^T \mathbf{x}_t\right] \mathbf{x}_t, \quad 0 \leq t &amp;lt; T\]

&lt;ul&gt;
  &lt;li&gt;For simplicity, assume that the return $G$ is a single reward at the end of the episode (hence no subscript by time) and that there is no discounting.&lt;/li&gt;
  &lt;li&gt;This is known as the &lt;strong&gt;Least Mean Square (LMS)&lt;/strong&gt; rule.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We introduce an additional vector memory, the &lt;strong&gt;eligibility trace,&lt;/strong&gt; that keeps in it a summary of all the feature vectors seen so far. The overall update will be the same as the MC updates’ sequence shown above and is:&lt;/p&gt;

\[\begin{align*}
\mathbf{w}_T &amp;amp;= \mathbf{w}_{T-1} + \alpha \!\left(G - \mathbf{w}_{T-1}^T \mathbf{x}_{T-1}\right) \mathbf{x}_{T-1} \\
&amp;amp;= \mathbf{w}_{T-1} + \alpha \mathbf{x}_{T-1}\!\left(-\mathbf{x}_{T-1}^T \mathbf{w}_{T-1}\right) + \alpha G \mathbf{x}_{T-1} \\
&amp;amp;= \!\left(\mathbf{I} - \alpha \mathbf{x}_{T-1} \mathbf{x}_{T-1}^T\right) \mathbf{w}_{T-1} + \alpha G \mathbf{x}_{T-1} \\
&amp;amp;= \mathbf{F}_{T-1}\, \mathbf{w}_{T-1} + \alpha G \mathbf{x}_{T-1}
\end{align*}\]

\[\begin{aligned}
\text{where} \\
\mathbf{F}_t &amp;amp;\doteq \mathbf{I} - \alpha \mathbf{x}_t \mathbf{x}_t^T \equiv \text{a forgetting or fading matrix}
\end{aligned}\]

\[\therefore\quad \mathbf{w}_{T-1} = \mathbf{F}_{T-2}\, \mathbf{w}_{T-2} + \alpha G \mathbf{x}_{T-2}\]

    &lt;p&gt;Now recursing:&lt;/p&gt;

\[\begin{align*}
\mathbf{w}_T &amp;amp;= \mathbf{F}_{T-1}\, \mathbf{w}_{T-1} + \alpha G \mathbf{x}_{T-1} \\
&amp;amp;= \mathbf{F}_{T-1}\!\left(\mathbf{F}_{T-2}\, \mathbf{w}_{T-2} + \alpha G \mathbf{x}_{T-2}\right) + \alpha G \mathbf{x}_{T-1} \\
&amp;amp;= \mathbf{F}_{T-1} \mathbf{F}_{T-2}\, \mathbf{w}_{T-2} + \alpha G\!\left(\mathbf{F}_{T-1} \mathbf{x}_{T-2} + \mathbf{x}_{T-1}\right) \\
&amp;amp;= \mathbf{F}_{T-1} \mathbf{F}_{T-2}\!\left(\mathbf{F}_{T-3}\, \mathbf{w}_{T-3} + \alpha G\, \mathbf{x}_{T-3}\right) + \alpha G\!\left(\mathbf{F}_{T-1}\, \mathbf{x}_{T-2} + \mathbf{x}_{T-1}\right) \\
&amp;amp;= \mathbf{F}_{T-1} \mathbf{F}_{T-2} \mathbf{F}_{T-3}\, \mathbf{w}_{T-3} + \alpha G\!\left(\mathbf{F}_{T-1} \mathbf{F}_{T-2}\, \mathbf{x}_{T-3} + \mathbf{F}_{T-1}\, \mathbf{x}_{T-2} + \mathbf{x}_{T-1}\right) \\
&amp;amp;\quad \vdots \\
&amp;amp;= \underbrace{\mathbf{F}_{T-1} \mathbf{F}_{T-2} \cdots \mathbf{F}_0\, \mathbf{w}_0}_{\mathbf{a}_{T-1}} + \alpha G \underbrace{\sum\nolimits_{k=0}^{T-1} \mathbf{F}_{T-1} \mathbf{F}_{T-2} \cdots \mathbf{F}_{k+1}\, \mathbf{x}_k}_{\mathbf{z}_{T-1}} \\
&amp;amp;= \mathbf{a}_{T-1} + \alpha G\, \mathbf{z}_{T-1}
\end{align*}\]

\[\begin{aligned}
\text{where} \\
\mathbf{a}_{T-1}\ \&amp;amp;\ \mathbf{z}_{T-1} &amp;amp;\equiv \text{values at time } T-1 \text{ of 2 auxiliary memory vectors that can be updated} \\
&amp;amp;\phantom{{}\equiv{}} \text{incrementally w/o knowledge of } G \text{ and with } O(d) \text{ complexity per time step} \\
\mathbf{z}_t &amp;amp;\equiv \text{dutch-style eligibility trace, initialized to } \mathbf{z}_0 = \mathbf{x}_0
\end{aligned}\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The $\mathbf{z}_t$ vector is in fact a dutch-style eligibility trace, initialized to $\mathbf{z}_0 = \mathbf{x}_0$, that can be updated according to:&lt;/p&gt;

\[\begin{align*}
\mathbf{z}_t &amp;amp;= \sum\nolimits_{k=0}^{t} \mathbf{F}_t \mathbf{F}_{t-1} \cdots \mathbf{F}_{k+1}\, \mathbf{x}_k, \quad 1 \leq t &amp;lt; T \\
&amp;amp;= \sum\nolimits_{k=0}^{t-1} \mathbf{F}_t \mathbf{F}_{t-1} \cdots \mathbf{F}_{k+1}\, \mathbf{x}_k + \mathbf{x}_t \\
&amp;amp;= \mathbf{F}_t \sum\nolimits_{k=0}^{t-1} \mathbf{F}_{t-1} \mathbf{F}_{t-2} \cdots \mathbf{F}_{k+1}\, \mathbf{x}_k + \mathbf{x}_t \\
&amp;amp;= \mathbf{F}_t\, \mathbf{z}_{t-1} + \mathbf{x}_t \\
&amp;amp;= \!\left(\mathbf{I} - \alpha \mathbf{x}_t \mathbf{x}_t^T\right) \mathbf{z}_{t-1} + \mathbf{x}_t \\
&amp;amp;= \mathbf{z}_{t-1} - \alpha\!\left(\mathbf{z}_{t-1}^T \mathbf{x}_t\right) \mathbf{x}_t + \mathbf{x}_t \\
&amp;amp;\boxed{= \mathbf{z}_{t-1} + \!\left(1 - \alpha\, \mathbf{z}_{t-1}^T \mathbf{x}_t\right) \mathbf{x}_t}
\end{align*}\]

    &lt;p&gt;which is the dutch trace for $\gamma\lambda = 1$.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The $\mathbf{a}_t$ auxiliary vector is initialized to $\mathbf{a}_0 = \mathbf{w}_0$ and then updated according to:&lt;/p&gt;

\[\begin{align*}
\mathbf{a}_t &amp;amp;= \mathbf{F}_t \mathbf{F}_{t-1} \cdots \mathbf{F}_0\, \mathbf{w}_0, \quad 1 \leq t &amp;lt; T \\
&amp;amp;= \mathbf{F}_t\, \mathbf{a}_{t-1} \\
&amp;amp;= \mathbf{a}_{t-1} - \alpha \mathbf{x}_t \mathbf{x}_t^T \mathbf{a}_{t-1} \\
&amp;amp;\boxed{= \mathbf{a}_{t-1}\!\left(\mathbf{I} - \alpha \mathbf{x}_t \mathbf{x}_t^T\right)}
\end{align*}\]
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;The auxiliary vectors, $\mathbf{a}_t$ and $\mathbf{z}_t$, are updated on each time step $t &amp;lt; T$ and then, at time $T$ when $G$ is observed, they are used to compute:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\mathbf{w}_T = \mathbf{a}_{T-1} + \alpha G\, \mathbf{z}_{T-1}}\]

&lt;ul&gt;
  &lt;li&gt;The time and memory complexity per step is $O(d)$.&lt;/li&gt;
  &lt;li&gt;This is surprising and intriguing since ET is working in a non-TD setting (ET arise where long-term predictions are needed to be learned efficiently).&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;127-sarsalambda-&quot;&gt;12.7 Sarsa($\lambda$) &lt;a name=&quot;127-sarsa&quot;&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Now let’s extend eligibility traces to action-value methods.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;First, let’s recall the action-value form of the &lt;strong&gt;$n$-step&lt;/strong&gt; return:&lt;/p&gt;

\[G_{t:t+n} \doteq R_{t+1} + \ldots + \gamma^{n-1} R_{t+n} + \gamma^n \hat{q}(S_{t+n}, A_{t+n}, \mathbf{w}_{t+n-1}), \quad t+n &amp;lt; T\]

\[\text{with} \quad G_{t:t+n} = G_t \quad \text{ if } t+n \geq T.\]
  &lt;/li&gt;
  &lt;li&gt;With this, for $t = 0, \ldots, T-1,$ let’s form the action-value form of the &lt;strong&gt;off-line $\lambda$-return&lt;/strong&gt; algorithm which uses $\hat{q}$ rather than $\hat{v}$:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\mathbf{w}_{t+1} \doteq \mathbf{w}_t + \alpha \!\left[G_t^\lambda - \hat{q}(S_t, A_t, \mathbf{w}_t)\right] \nabla \hat{q}(S_t, A_t, \mathbf{w}_t)}\]

\[\begin{aligned}
\text{where} \quad G_t^\lambda &amp;amp;\doteq G_{t:\infty}^\lambda
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;For the forward view shown in the figure below, which is similar to TD($\lambda$), the updates are:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;1st update:&lt;/strong&gt; one full-step lookahead&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;2nd update:&lt;/strong&gt; two-step lookahead&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Final update:&lt;/strong&gt; complete return.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/rl-sutton-barto/ch12-12-7-sarsa-lambda.png&quot; alt=&quot;Sarsa(lambda)&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Backup Diagram for Sarsa($\lambda$)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;The first update looks ahead one full step, to the next state–action pair, the second looks
ahead two steps, to the second state–action pair, and so on. A final update is based on
the complete return.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;The weighting of each $n$-step update in the $\lambda$-return is same as in TD($\lambda$) and $\lambda$-return.&lt;/li&gt;
  &lt;li&gt;The forward view TD for action values, Sarsa($\lambda$), has the same update rule as TD($\lambda$):&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\mathbf{w}_{t+1} \doteq \mathbf{w}_t + \alpha\, \delta_t\, \mathbf{z}_t}\]

&lt;ul&gt;
  &lt;li&gt;The action-value form of the TD error is used:&lt;/li&gt;
&lt;/ul&gt;

\[\delta_t \doteq R_{t+1} + \gamma \hat{q}(S_{t+1}, A_{t+1}, \mathbf{w}_t) - \hat{q}(S_t, A_t, \mathbf{w}_t)\]

&lt;ul&gt;
  &lt;li&gt;The action-value form of the eligibility trace:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
\mathbf{z}_{-1} &amp;amp;\doteq \mathbf{0} \\
\mathbf{z}_t &amp;amp;\doteq \gamma\lambda \mathbf{z}_{t-1} + \nabla \hat{q}(S_t, A_t, \mathbf{w}_t), \quad 0 \leq t \leq T
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;There exists an action-value version of our ideal TD method, the online $\lambda$-return algorithm and its efficient implementation as true online TD($\lambda$):
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Section &lt;a href=&quot;#124-redoing-updates-online--return-algorithm&quot;&gt;12.4&lt;/a&gt;:&lt;/strong&gt; Everything there holds here except for using the action-value form of the $n$-step return, $G_{t:t+n}$.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Sections &lt;a href=&quot;#125-true-online-td&quot;&gt;12.5&lt;/a&gt; &amp;amp; &lt;a href=&quot;#126-dutch-traces-in-monte-carlo-learning&quot;&gt;12.6&lt;/a&gt;:&lt;/strong&gt; Everything holds here except for using state-action feature vectors $\mathbf{x}_t = \mathbf{x}(S_t, A_t)$ instead of state feature vectors $\mathbf{x}_t = \mathbf{x}(S_t)$.&lt;/li&gt;
      &lt;li&gt;The resulting efficient backward algorithm obtained from using the eligibility trace to invert the action-value form of the forward view, online $\lambda$-return is called the &lt;strong&gt;True Online Sarsa($\lambda$)&lt;/strong&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;There is also a truncated version of Sarsa($\lambda$), called &lt;strong&gt;Forward Sarsa($\lambda$)&lt;/strong&gt;, which appears to be a model-free, control method for use in conjunction with multi-layer ANNs.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;128-variable-lambda-and-gamma-&quot;&gt;12.8 Variable $\lambda$ and $\gamma$ &lt;a name=&quot;128-variable--and&quot;&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;To get the most general forms of the final TD algorithms, it is vital to generalize the degree of bootstrapping and discounting beyond constant parameters to functions dependent on the state and action:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
\lambda_t &amp;amp;\doteq \lambda(S_t, A_t), \quad &amp;amp; \lambda &amp;amp;: S \times A \to [0,1] \\
\gamma_t &amp;amp;\doteq \gamma(S_t), \quad &amp;amp; \gamma &amp;amp;: S \to [0,1]
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;$\lambda_t$ is the termination function and is significant because it changes the return $G_t$, which is now more generally defined as:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
G_t &amp;amp;\doteq R_{t+1} + \gamma_{t+1} G_{t+1} \\
&amp;amp;= R_{t+1} + \gamma_{t+1} R_{t+2} + \gamma_{t+1} \gamma_{t+2} R_{t+3} + \gamma_{t+1} \gamma_{t+2} \gamma_{t+3} R_{t+4} + \ldots \\
&amp;amp;= \sum_{k=t}^{\infty} \left(\prod_{i=t+1}^{k} \gamma_i\right) R_{k+1}
\end{align*}\]

\[\begin{aligned}
\text{where } \prod_{k=t}^{\infty} \gamma_k &amp;amp;= 0 \text{ with probability 1 for all } t, \text{ to assure the sums are finite}
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;This general return $G_t$ definition enables episodic settings to become a single stream of experience, without special terminal state, start distributions or termination times
    &lt;ul&gt;
      &lt;li&gt;A terminal state just becomes a state with $\gamma(s) = 0$ that transitions to the start distribution.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Generalization to variable bootstrapping yields a new state-based $\lambda$-return:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{G_t^{\lambda s} \doteq R_{t+1} + \gamma_{t+1}\!\left[(1 - \lambda_{t+1})\, \hat{v}(S_{t+1}, \mathbf{w}_t) + \lambda_{t+1}\, G_{t+1}^{\lambda s}\right]}\]

&lt;ul&gt;
  &lt;li&gt;Action-based $\lambda$-return is either the &lt;strong&gt;Sarsa form&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{G_t^{\lambda a} \doteq R_{t+1} + \gamma_{t+1}\!\left[(1 - \lambda_{t+1})\, \hat{q}(S_{t+1}, A_{t+1}, \mathbf{w}_t) + \lambda_{t+1}\, G_{t+1}^{\lambda a}\right]}\]

&lt;ul&gt;
  &lt;li&gt;or the &lt;strong&gt;Expected Sarsa form&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{G_t^{\lambda a} \doteq R_{t+1} + \gamma_{t+1}\!\left[(1 - \lambda_{t+1})\, \bar{V}_t(S_{t+1}) + \lambda_{t+1}\, G_{t+1}^{\bar{\lambda}a}\right]}\]

\[\begin{aligned}
\text{where } \bar{V}_t(s) \doteq \sum_a \Pi(a \vert s)\, \hat{q}(s, a, \mathbf{w}_t) \\
\end{aligned}\]

&lt;h3 id=&quot;superscripts-notation-for-i-in-g_tlambda-i&quot;&gt;Superscripts notation for $i$ in $G_t^{\lambda i}$&lt;/h3&gt;

\[\begin{aligned}
\text{&quot;s&quot;} &amp;amp;: \text{bootstraps from state values} \\
\text{&quot;a&quot;} &amp;amp;: \text{bootstraps from action values}
\end{aligned}\]

&lt;hr /&gt;

&lt;h2 id=&quot;129-off-policy-traces-with-control-variates&quot;&gt;12.9 Off-Policy Traces with Control Variates&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;To generalize to off-policy, we need to incorporate importance sampling using eligibility traces.&lt;/li&gt;
  &lt;li&gt;Let’s focus on the bootstrapping generalization of per-decision importance sampling with control variates &lt;strong&gt;(Section 7.4).&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;The new state-based $\lambda$-return in &lt;strong&gt;Section &lt;a href=&quot;#128-variable--and&quot;&gt;12.8&lt;/a&gt;&lt;/strong&gt; generalizes, after the off-policy, control variate, $n$-step return (ending at horizon $h$) model to:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{G_t^{\lambda s} \doteq \rho_t \!\left(R_{t+1} + \gamma_{t+1}\!\left[(1 - \lambda_{t+1})\, \hat{v}(S_{t+1}, \mathbf{w}_t) + \lambda_{t+1}\, G_{t+1}^{\lambda s}\right]\right) + (1 - \rho_t)\, \hat{v}(S_t, \mathbf{w}_t)}\]

\[\begin{aligned}
\text{where } \rho_t &amp;amp;= \frac{\pi(A_t \vert S_t)}{b(A_t \vert S_t)}
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;The final $\lambda$-return can be approximated in terms of sums of the state-based TD error $\delta_t^s$, with the approximation becoming exact if the approximate value function does not change:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
G_t^{\lambda s} &amp;amp;\approx \hat{v}(S_t, \mathbf{w}_t) + \rho_t \sum_{k=t}^{\infty} \delta_k^s \prod_{i=t+1}^{k} \gamma_i \lambda_i \rho_i \\
\delta_t^s &amp;amp;\doteq R_{t+1} + \gamma_{t+1}\, \hat{v}(S_{t+1}, \mathbf{w}_t) - \hat{v}(S_t, \mathbf{w}_t)
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;The forward view update of the approximate $\lambda$-return is:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
\mathbf{w}_{t+1} &amp;amp;= \mathbf{w}_t + \alpha \!\left[G_t^{\lambda s} - \hat{v}(S_t, \mathbf{w}_t)\right] \nabla \hat{v}(S_t, \mathbf{w}_t) \\
&amp;amp;\boxed{\approx \mathbf{w}_t + \alpha \rho_t \!\left(\sum_{k=t}^{\infty} \delta_k^s \prod_{i=t+1}^{k} \gamma_i \lambda_i \rho_i\right) \nabla \hat{v}(S_t, \mathbf{w}_t)}
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;We’re interested in the equivalence (approximately) between the forward-view update summed over time and a backward-view update summed over time. The equivalence is approximate because we ignore changes in the value function.&lt;/li&gt;
  &lt;li&gt;The sum of the forward-view update over time is:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
\sum_{t=0}^{\infty} \!\left(\mathbf{w}_{t+1} - \mathbf{w}_t\right) &amp;amp;\approx \sum_{t=0}^{\infty} \sum_{k=t}^{\infty} \alpha \rho_t\, \delta_k^s \nabla \hat{v}(S_t, \mathbf{w}_t) \prod_{i=t+1}^{k} \gamma_i \lambda_i \rho_i \\
&amp;amp;= \sum_{k=0}^{\infty} \sum_{t=0}^{k} \alpha \rho_t \nabla \hat{v}(S_t, \mathbf{w}_t)\, \delta_k^s \prod_{i=t+1}^{k} \gamma_i \lambda_i \rho_i \\
&amp;amp;\quad \left(\text{using the summation rule: } \sum_{t=x}^{y} \sum_{k=t}^{y} = \sum_{k=x}^{y} \sum_{t=x}^{k}\right) \\
&amp;amp;= \sum_{k=0}^{\infty} \alpha\, \delta_k^s \sum_{t=0}^{k} \rho_t \nabla \hat{v}(S_t, \mathbf{w}_t) \prod_{i=t+1}^{k} \gamma_i \lambda_i \rho_i
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;If the entire expression from the 2nd sum on could be written and updated incrementally as an eligibility trace, then the sum of the forward-view update over time would be in the form of the sum of a backward-view TD update.
    &lt;ul&gt;
      &lt;li&gt;That is, if this expression was the trace at time $k$, then we could update it from its value at time $k-1$ by:&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
\mathbf{z}_k &amp;amp;= \sum_{t=0}^{k} \rho_t \nabla \hat{v}(S_t, \mathbf{w}_t) \prod_{i=t+1}^{k} \gamma_i \lambda_i \rho_i \\
&amp;amp;= \sum_{t=0}^{k-1} \rho_t \nabla \hat{v}(S_t, \mathbf{w}_t) \prod_{i=t+1}^{k} \gamma_i \lambda_i \rho_i + \rho_k \nabla \hat{v}(S_k, \mathbf{w}_k) \\
&amp;amp;= \gamma_k \lambda_k \rho_k \underbrace{\sum_{t=0}^{k-1} \rho_t \nabla \hat{v}(S_t, \mathbf{w}_t) \prod_{i=t+1}^{k-1} \gamma_i \lambda_i \rho_i}_{\mathbf{z}_{k-1}} + \rho_k \nabla \hat{v}(S_k, \mathbf{w}_k)
\end{align*}\]

\[\boxed{\mathbf{z}_k = \rho_k \!\left[\gamma_k \lambda_k\, \mathbf{z}_{k-1} + \nabla \hat{v}(S_k, \mathbf{w}_k)\right]}\]

&lt;ul&gt;
  &lt;li&gt;If we change the index from $k$ to $t$ of the $\mathbf{z}_k$ equation above, we get the &lt;strong&gt;general accumulating trace&lt;/strong&gt; update for state values:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\mathbf{z}_t \doteq \rho_t \!\left[\gamma_t \lambda_t\, \mathbf{z}_{t-1} + \nabla \hat{v}(S_t, \mathbf{w}_t)\right]}\]

&lt;ul&gt;
  &lt;li&gt;This eligibility trace combined with the usual semi-gradient TD($\lambda$) parameter-update rule &lt;strong&gt;(Section &lt;a href=&quot;#122-td&quot;&gt;12.2&lt;/a&gt;)&lt;/strong&gt; forms a &lt;strong&gt;general TD($\lambda$)&lt;/strong&gt; algorithm that can be applied to either on-policy or off-policy data:
    &lt;ul&gt;
      &lt;li&gt;In on-policy, the algorithm is exactly TD($\lambda$) because $\rho_t = 1$ always and the ET above becomes the usual accumulating trace for variable $\lambda$ and $\gamma$:&lt;/li&gt;
    &lt;/ul&gt;

\[\mathbf{z}_t \doteq \gamma_t \lambda_t\, \mathbf{z}_{t-1} + \nabla \hat{v}(S_t, \mathbf{w}_t)\]

    &lt;ul&gt;
      &lt;li&gt;In off-policy, the algorithm stays as it is, although not guaranteed to be stable as a semi-gradient method.&lt;/li&gt;
      &lt;li&gt;For off-policy, we’ll consider extensions that guarantee stability in the next few sections.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Let’s derive the off-policy ET for &lt;strong&gt;action-value&lt;/strong&gt; methods and corresponding general Sarsa($\lambda$) algorithms.
    &lt;ul&gt;
      &lt;li&gt;Starting with either recursive general action-based $\lambda$-return of Sarsa or Expected Sarsa, $G_t^{\lambda a}$, in &lt;strong&gt;Section &lt;a href=&quot;#128-variable--and&quot;&gt;12.8&lt;/a&gt;&lt;/strong&gt; (Expected Sarsa works out to be simpler), we can extend the Expected Sarsa $G_t^{\lambda a}$ to the off-policy case after the off-policy model of action-based, off-policy, control variate, $n$-step return:&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\begin{align*}
G_t^{\lambda a} &amp;amp;\doteq R_{t+1} + \gamma_{t+1}\!\left(\!\left[1 - \lambda_{t+1}\right] \bar{V}_t(S_{t+1}) + \lambda_{t+1}\!\left[\rho_{t+1} G_{t+1}^{\lambda a} + \bar{V}_t(S_{t+1}) - \rho_{t+1}\, \hat{q}(S_{t+1}, A_{t+1}, \mathbf{w}_t)\right]\right) \\
&amp;amp;= R_{t+1} + \gamma_{t+1}\!\left(\bar{V}_t(S_{t+1}) + \lambda_{t+1} \rho_{t+1} \!\left[G_{t+1}^{\lambda a} - \hat{q}(S_{t+1}, A_{t+1}, \mathbf{w}_t)\right]\right)
\end{align*}}\]

\[\begin{aligned}
\text{where } \bar{V}_t(S_{t+1}) &amp;amp;= \sum_a \Pi(a \vert S_{t+1})\, \hat{q}(S_{t+1}, a, \mathbf{w}_t)
\end{aligned}\]

&lt;ul&gt;
  &lt;li&gt;The $\lambda$-return, approximately as the sum of TD errors, is:&lt;/li&gt;
&lt;/ul&gt;

\[\begin{align*}
G_t^{\lambda a} &amp;amp;\approx \hat{q}(S_t, A_t, \mathbf{w}_t) + \sum_{k=t}^{\infty} \delta_k^a \prod_{i=t+1}^{k} \gamma_i \lambda_i \rho_i \\
\delta_t^a &amp;amp;= R_{t+1} + \gamma_{t+1} \bar{V}(S_{t+1}) - \hat{q}(S_t, A_t, \mathbf{w}_t)
\end{align*}\]

&lt;ul&gt;
  &lt;li&gt;Using steps analogous to those for the state case earlier in this section, write a forward-view update based on action-based $\lambda$-return $G_t^{\lambda a}$ above, then transform the sum of the updates using the summation rule and finally derive the eligibility trace for action values:&lt;/li&gt;
&lt;/ul&gt;

\[\boxed{\mathbf{z}_t \doteq \gamma_t \lambda_t \rho_t\, \mathbf{z}_{t-1} + \nabla \hat{q}(S_t, A_t, \mathbf{w}_t)}\]

&lt;ul&gt;
  &lt;li&gt;This ET combined with the action-based, expected TD error $\delta_t^a$ and the usual semi-gradient TD($\lambda$) parameter-update rule &lt;strong&gt;(Section &lt;a href=&quot;#122-td&quot;&gt;12.2&lt;/a&gt;)&lt;/strong&gt; forms an elegant, efficient &lt;strong&gt;Expected Sarsa($\lambda$)&lt;/strong&gt; algorithm that can be applied to either on-policy or off-policy data:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;&lt;u&gt;On-policy case&lt;/u&gt;:&lt;/strong&gt; The algorithm becomes the Sarsa($\lambda$) algorithm given constant $\lambda$ and $\gamma$, and the usual state-action TD error:&lt;/li&gt;
    &lt;/ul&gt;

\[\begin{aligned}
&amp;amp;\quad \rho_t = 1, \quad \nabla\lambda_t = \nabla\gamma_t = 0 \\
&amp;amp;\boxed{\mathbf{z}_t \doteq \gamma\lambda\, \mathbf{z}_{t-1} + \nabla \hat{q}(S_t, A_t, \mathbf{w}_t)}
\end{aligned}\]
  &lt;/li&gt;
  &lt;li&gt;At $\lambda = 1$, these algorithms become closely related to corresponding Monte Carlo algorithms.&lt;/li&gt;
  &lt;li&gt;No episode-by-episode equivalence of updates exist, only of their expectations, even under the most favorable conditions.
    &lt;ul&gt;
      &lt;li&gt;Methods have been proposed recently &lt;strong&gt;[Sutton, Mahmood, Precup &amp;amp; van Hasselt, 2014]&lt;/strong&gt; that do achieve an exact equivalence.&lt;/li&gt;
      &lt;li&gt;These methods require an additional vector of &lt;strong&gt;“provisional weights”&lt;/strong&gt; that keep track of executed updates but may need to be retracted/emphasized depending on future actions taken.&lt;/li&gt;
      &lt;li&gt;The state and state-action versions of these methods are called &lt;strong&gt;PTD($\lambda$) and PQ($\lambda$)&lt;/strong&gt; respectively, where the ‘P’ stands for Provisional.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If $\lambda &amp;lt; 1$, then all these off-policy algorithms involve bootstrapping and &lt;strong&gt;the deadly triad&lt;/strong&gt; applies, meaning that they can be guaranteed stable only for the tabular case, state aggregation and other limited forms of function approximation.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Recall the challenge of off-policy learning has 2 parts. Off-policy eligibility traces deal effectively with the 1st part, correcting for the expected value of the targets, but not with the 2nd part that has to do with distribution of updates (matching off-policy to on-policy).&lt;/li&gt;
  &lt;li&gt;Algorithmic strategies for handling the 2nd part of the off-policy learning challenge with eligibility traces are summarized in &lt;strong&gt;Section &lt;a href=&quot;#1211-stable-off-policy-methods-with-traces&quot;&gt;12.11&lt;/a&gt;.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1210-watkinss-qlambda-to-tree-backuplambda-&quot;&gt;12.10 Watkins’s Q($\lambda$) to Tree-Backup($\lambda$) &lt;a name=&quot;1210-watkinss-q-to-tree-backup&quot;&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h3 id=&quot;watkinss-qlambda&quot;&gt;Watkins’s Q($\lambda$)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Watkins’s Q($\lambda$) is the original method for extending Q-learning to eligibility traces.&lt;/li&gt;
  &lt;li&gt;It involves decaying the ET in the usual way as long as a greedy action was taken, then cuts the traces to 0 after the 1st non-greedy action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/rl-sutton-barto/ch12-12-10-watkins-q-lambda.png&quot; alt=&quot;Watkins&apos;s Q(lambda)&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Backup Diagram for Watkins&apos;s Q($\lambda$)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;The series of component updates ends
either with the end of the episode or with the first nongreedy action, whichever comes first.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;tree-backuplambda&quot;&gt;Tree-Backup($\lambda$)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Let’s look at the eligibility trace version of Tree Backup, which is called &lt;strong&gt;Tree-Backup($\lambda$)&lt;/strong&gt; or &lt;strong&gt;TB($\lambda$)&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;TB($\lambda$) is the &lt;strong&gt;true successor&lt;/strong&gt; to Q-learning because it has no importance sampling.&lt;/li&gt;
  &lt;li&gt;TB($\lambda$) concept is straightforward:
    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;The tree-backup updates of each length (Section 7.5) are weighted dependent on the bootstrapping parameter $\lambda$.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Using the recursive form of the action-based $\lambda$-return for Expected Sarsa and then expanding the bootstrapping target case after the model of tree-backup $n$-step return (Section 7.5):&lt;/p&gt;

\[\boxed{\begin{align*}
G_t^{\lambda a} &amp;amp;\doteq R_{t+1} + \gamma_{t+1}\!\left(\!\left[1 - \lambda_{t+1}\right] \bar{V}_t(S_{t+1}) + \lambda_{t+1}\!\left[\sum_{a \neq A_{t+1}} \pi(a \vert S_{t+1})\, \hat{q}(S_{t+1}, a, \mathbf{w}_t) + \pi(A_{t+1} \vert S_{t+1}) G_{t+1}^{\lambda a}\right]\right) \\
&amp;amp;= R_{t+1} + \gamma_{t+1}\!\left(\bar{V}_t(S_{t+1}) + \lambda_{t+1} \pi(A_{t+1} \vert S_{t+1}) \!\left[G_{t+1}^{\lambda a} - \hat{q}(S_{t+1}, A_{t+1}, \mathbf{w}_t)\right]\right)
\end{align*}}\]
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;$G_t^{\lambda a}$ can be approximated (ignoring changes in approx. value function) as a sum of TD errors:&lt;/p&gt;

\[\begin{align*}
G_t^{\lambda a} &amp;amp;\approx \hat{q}(S_t, A_t, \mathbf{w}_t) + \sum_{k=t}^{\infty} \delta_k^a \prod_{i=t+1}^{k} \gamma_i \lambda_i \pi(A_i \vert S_i) \\
\delta_t^a &amp;amp;= R_{t+1} + \gamma_{t+1} \bar{V}_t(S_{t+1}) - \hat{q}(S_t, A_t, \mathbf{w}_t)
\end{align*}\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;As always, using same steps as in the previous section, we get a special eligibility trace update involving the target-policy probabilities of the selected actions:&lt;/p&gt;

\[\boxed{\mathbf{z}_t \doteq \gamma_t \lambda_t \pi(A_t \vert S_t)\, \mathbf{z}_{t-1} + \nabla \hat{q}(S_t, A_t, \mathbf{w}_t)}\]
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026/rl-sutton-barto/ch12-12-10-tree-backup-q-lambda.png&quot; alt=&quot;Tree Backup (lambda)&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;callout callout--note&quot;&gt;
  &lt;div class=&quot;callout__title&quot;&gt;
    &lt;strong&gt;Backup Diagram for Tree Backup($\lambda$)&lt;/strong&gt;
  &lt;/div&gt;
  &lt;div class=&quot;callout__body&quot;&gt;
    
&lt;p&gt;The tree-backup updates of each length are weighted in the
usual way dependent on the bootstrapping parameter $\lambda$&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;The ET above combined with the usual semi-gradient TD($\lambda$) parameter-update rule defines the TB($\lambda$) algorithm.&lt;/li&gt;
  &lt;li&gt;Like all semi-gradient algorithms, TB($\lambda$) is not guaranteed to be stable when used with off-policy data and a powerful function approximator &lt;strong&gt;(the deadly triad).&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1211-stable-off-policy-methods-with-traces&quot;&gt;12.11 Stable Off-Policy Methods with Traces&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Let’s look at 4 of the most important methods that achieve stable off-policy methods/training using eligibility traces.&lt;/li&gt;
  &lt;li&gt;All 4 are based on either the &lt;strong&gt;Gradient-TD or Emphatic TD&lt;/strong&gt; and linear function approximation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;gtdlambda&quot;&gt;GTD($\lambda$)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Analogous to TDC, and aims to learn a parameter $\mathbf{w}_{t}$ such that $\hat{v}(s, \mathbf{w}) \doteq \mathbf{w}_{t}^T \mathbf{x}(s) \approx v_{\pi}(s)$ even from data that is due to following another policy $b$. Its update is:&lt;/p&gt;

\[\begin{aligned}
\mathbf{w}_{t+1} &amp;amp;\doteq \mathbf{w}_t + \alpha\, \delta_t^s\, \mathbf{z}_t - \alpha \gamma_{t+1}(1 - \lambda_{t+1})\!\left(\mathbf{z}_t^T \mathbf{v}_t\right) \mathbf{x}_{t+1} \\
\mathbf{v}_{t+1} &amp;amp;\doteq \mathbf{v}_t + \beta\, \delta_t^s\, \mathbf{z}_t - \beta\!\left(\mathbf{v}_t^T \mathbf{x}_t\right) \mathbf{x}_t
\end{aligned}\]

\[\begin{aligned}
\text{where} \\
\mathbf{v} &amp;amp;\in \mathbb{R}^d \equiv \text{a vector of the same dimension as } \mathbf{w}, \text{ initialized to } \mathbf{v}_0 = \mathbf{0} \\
\beta &amp;amp;&amp;gt; 0 \equiv \text{a 2nd step-size parameter}
\end{aligned}\]
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;gqlambda&quot;&gt;GQ($\lambda$)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Gradient-TD algorithm for action values with eligibility traces.&lt;/li&gt;
  &lt;li&gt;GQ($\lambda$) aims to learn $\mathbf{w}_{t}$ such that $\hat{q}(s, a, \mathbf{w}_{t}) \doteq \mathbf{w}_{t}^T \mathbf{x}(s,a) \approx q_{\pi}(s,a)$ from off-policy data.&lt;/li&gt;
  &lt;li&gt;If the target policy is $\varepsilon$-greedy, or otherwise biased towards the greedy policy for $\hat{q}$, then GQ($\lambda$) can be used as a control algorithm.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;GQ($\lambda$) update is:&lt;/p&gt;

\[\begin{aligned}
\mathbf{w}_{t+1} &amp;amp;\doteq \mathbf{w}_t + \alpha\, \delta_t^a\, \mathbf{z}_t - \alpha \gamma_{t+1}(1 - \lambda_{t+1})\!\left(\mathbf{z}_t^T \mathbf{v}_t\right) \bar{\mathbf{x}}_{t+1} \\
\bar{\mathbf{x}}_t &amp;amp;\doteq \sum_a \pi(a \vert S_t)\, \mathbf{x}(S_t, a) \\
\delta_t^a &amp;amp;\doteq R_{t+1} + \gamma_{t+1}\, \mathbf{w}_t^T \bar{\mathbf{x}}_{t+1} - \mathbf{w}_t^T \mathbf{x}_t \\
\mathbf{z}_t &amp;amp;\doteq \gamma_t \lambda_t \rho_t\, \mathbf{z}_{t-1} + \nabla \hat{q}(S_t, A_t, \mathbf{w}_t)
\end{aligned}\]

\[\begin{aligned}
\text{where} \\
\bar{\mathbf{x}}_t &amp;amp;\equiv \text{average feature vector for } S_t \text{ under the target policy} \\
\delta_t^a &amp;amp;\equiv \text{expectation form of the TD error}
\end{aligned}\]
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;htdlambda&quot;&gt;HTD($\lambda$)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Hybrid TD($\lambda$) state-value algorithm combines aspects of GTD($\lambda$) and TD($\lambda$).&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;HTD($\lambda$) is a strict generalization of TD($\lambda$) to the off-policy setting, meaning it reduces exactly to TD($\lambda$) when the behavior and target policies coincide; a property GTD($\lambda$) does not share:&lt;/p&gt;

\[b(A_t \vert S_t) = \pi(A_t \vert S_t), \quad \rho_t = 1 \implies \text{HTD}(\lambda) = \text{TD}(\lambda)\]
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;HTD($\lambda$) is defined by:&lt;/p&gt;

\[\begin{aligned}
\mathbf{w}_{t+1} &amp;amp;\doteq \mathbf{w}_t + \alpha\, \delta_t^s\, \mathbf{z}_t + \alpha\!\left[\!\left(\mathbf{z}_t - \mathbf{z}_t^b\right)^T \mathbf{v}_t\right]\!\left(\mathbf{x}_t - \gamma_{t+1} \mathbf{x}_{t+1}\right) \\
\mathbf{v}_{t+1} &amp;amp;\doteq \mathbf{v}_t + \beta\, \delta_t^s\, \mathbf{z}_t - \beta\!\left(\mathbf{z}_t^T \mathbf{v}_t\right)\!\left(\mathbf{x}_t - \gamma_{t+1} \mathbf{x}_{t+1}\right), \quad &amp;amp; \mathbf{v}_0 \doteq \mathbf{0} \\
\mathbf{z}_t &amp;amp;\doteq \rho_t \!\left(\gamma_t \lambda_t\, \mathbf{z}_{t-1} + \mathbf{x}_t\right), \quad &amp;amp; \mathbf{z}_{-1} \doteq \mathbf{0} \\
\mathbf{z}_t^b &amp;amp;\doteq \gamma_t \lambda_t\, \mathbf{z}_{t-1}^b + \mathbf{x}_t, \quad &amp;amp; \mathbf{z}_{-1}^b \doteq \mathbf{0}
\end{aligned}\]
  &lt;/li&gt;
  &lt;li&gt;We get
    &lt;ul&gt;
      &lt;li&gt;a 2nd set of weights, $\mathbf{v}_t$.&lt;/li&gt;
      &lt;li&gt;a 2nd set of eligibility traces, $\mathbf{z}_t^b$, &lt;strong&gt;conventional accumulating traces&lt;/strong&gt; for the behavior policy.&lt;/li&gt;
    &lt;/ul&gt;

\[\begin{aligned}
\mathbf{z}_t^b = \mathbf{z}_t \text{ if all } \rho_t = 1 &amp;amp;\implies \left(\mathbf{z}_t - \mathbf{z}_t^b\right)^T = \mathbf{0} \\
&amp;amp;\implies \mathbf{w}_{t+1} = \mathbf{w}_t + \alpha\, \delta_t^s\, \mathbf{z}_t \quad \text{(TD(}\lambda\text{))}
\end{aligned}\]
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;emphatic-tdlambda&quot;&gt;Emphatic TD($\lambda$)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Extension of one-step Emphatic TD (Sections 9.11 &amp;amp; 11.8) to eligibility traces.&lt;/li&gt;
  &lt;li&gt;The resulting algorithm:
    &lt;ul&gt;
      &lt;li&gt;(+) retains strong off-policy convergence guarantees&lt;/li&gt;
      &lt;li&gt;(+) enables any degree of bootstrapping&lt;/li&gt;
      &lt;li&gt;(-) has high variance&lt;/li&gt;
      &lt;li&gt;(-) potentially slow convergence.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Emphatic TD($\lambda$) is defined by:&lt;/p&gt;

\[\begin{aligned}
\mathbf{w}_{t+1} &amp;amp;\doteq \mathbf{w}_t + \alpha\, \delta_t\, \mathbf{z}_t \\
\delta_t &amp;amp;\doteq R_{t+1} + \gamma_{t+1}\, \mathbf{w}_t^T \mathbf{x}_{t+1} - \mathbf{w}_t^T \mathbf{x}_t \\
\mathbf{z}_t &amp;amp;\doteq \rho_t \!\left(\gamma_t \lambda_t\, \mathbf{z}_{t-1} + M_t \mathbf{x}_t\right), \quad &amp;amp; \mathbf{z}_{-1} \doteq \mathbf{0} \\
M_t &amp;amp;\doteq \lambda_t \mathcal{I}_t + (1 - \lambda_t) F_t \\
F_t &amp;amp;\doteq \rho_{t-1} \gamma_t F_{t-1} + \mathcal{I}_t, \quad &amp;amp; F_0 \doteq \mathcal{i}(S_0)
\end{aligned}\]

\[\begin{aligned}
\text{where} \\
M_t &amp;amp;\geq 0 \equiv \text{emphasis} \\
F_t &amp;amp;\geq 0 \equiv \text{followon trace} \\
\mathcal{I}_t &amp;amp;\geq 0 \equiv \text{interest}
\end{aligned}\]
  &lt;/li&gt;
  &lt;li&gt;In the on-policy case ($\rho_t = 1$ for all $t$), Emphatic TD($\lambda$) is similar to conventional TD($\lambda$), but still significantly different:
    &lt;ul&gt;
      &lt;li&gt;Emphatic TD($\lambda$) is guaranteed to converge for all state-dependent $\lambda$ functions; TD($\lambda$) is not (TD($\lambda$) is guaranteed only for constant $\lambda$).&lt;/li&gt;
      &lt;li&gt;See Yu’s counterexample &lt;strong&gt;[Ghassian, Rafiee &amp;amp; Sutton, 2016].&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1212-implementation-issues&quot;&gt;12.12 Implementation Issues&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Naive implementation seems expensive:&lt;/strong&gt; Updating eligibility traces for every state at every time step appears computationally costly on serial computers.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Practical optimization:&lt;/strong&gt; Most ET are nearly 0; only recently visited states have significant traces, so implementations can track and update only these few states.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Computational cost:&lt;/strong&gt; With this optimization, tabular methods with traces are only a few times more expensive than one-step methods.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Function approximation reduces overhead:&lt;/strong&gt; When using neural networks, ET typically only double memory and computation per step (much less overhead than in tabular case).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tabular is the worst case:&lt;/strong&gt; The tabular setting represents the highest computational complexity for ET relative to simpler methods.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1213-conclusions&quot;&gt;12.13 Conclusions&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Eligibility traces&lt;/strong&gt; provide an efficient, incremental way to interpolate between TD and MC methods.&lt;/li&gt;
  &lt;li&gt;ET offer advantages over $n$-step methods in terms of &lt;strong&gt;generality and computational trade-offs.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Empirically, &lt;strong&gt;an intermediate mix works best:&lt;/strong&gt; ET should move towards MC but not all the way since pure MC performance degrades sharply.&lt;/li&gt;
  &lt;li&gt;ET are the &lt;strong&gt;first defense against long-delayed rewards and non-Markov tasks,&lt;/strong&gt; used with TD methods to make them behave more like MC methods without full bootstrapping.&lt;/li&gt;
  &lt;li&gt;Use traces &lt;strong&gt;when data is scarce and online learning is required,&lt;/strong&gt; as they provide faster learning per sample despite higher computational cost per step.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Avoid traces in offline settings&lt;/strong&gt; with cheap abundant data (maximum data processing speed matters more than learning efficiency per sample).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;True online methods&lt;/strong&gt; achieve ideal $\lambda$-return performance while maintaining $O(d)$ computational efficiency.&lt;/li&gt;
  &lt;li&gt;Forward-to-backward view derivations provide &lt;strong&gt;computationally efficient, mechanistic,&lt;/strong&gt; practical implementations of theory.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;citation&quot;&gt;Citation&lt;/h2&gt;

&lt;p&gt;If you found this blog post helpful, please consider citing it:&lt;/p&gt;

&lt;div class=&quot;language-bibtex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;@article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;obasi2026RLsuttonBartoCh12notes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Sutton &amp;amp; Barto, Ch. 12: Eligibility Traces (Personal Notes)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Obasi, Chizoba&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;journal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;chizkidd.github.io&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;year&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2026&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;month&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Mar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://chizkidd.github.io/2026/03/13/rl-sutton-barto-notes-ch012/&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;
</description>
        <pubDate>Fri, 13 Mar 2026 00:00:00 +0000</pubDate>
        <link>https://chizkidd.github.io//2026/03/13/rl-sutton-barto-notes-ch012/</link>
        <guid isPermaLink="true">https://chizkidd.github.io//2026/03/13/rl-sutton-barto-notes-ch012/</guid>
        
        
      </item>
    
  </channel>
</rss>
