Summary
This paper proposes "Cut cross-entropy" (CCE), a method that reduces the memory footprint of computing the cross-entropy loss during LM training dramatically, by never materializing the full matrix of logits/probabilities (which can be huge: batch * sequence_length * vocab_size). To accomplish this, it realizes that the cross-entropy loss can be broken down into two components: (1) the logit for the correct next token, and (2) the log-sum-exp (log of softmax denominator) --- both of these terms are scalars, and can be computed without materializing the full logit tensor. In particular, (1) is computed via simple vector dot-products (Algorithm 1), while (2) can be computed by accumulating partial sums of the log-sum-exp, without every materializing all elements of the sum at once (Algorithm 2). For the backward pass (Algorithm 3), the paper proposes two methods --- gradient filtering and vocabulary sorting --- that reduce the backward pass time by skipping gradient computations for blocks of the softmax matrix where all values are < 2^(-12).
Putting all these pieces together, CCE is able to match the speed and quality of existing implementations of the cross-entropy loss, while only requiring a very small percentage of HBM memory (e.g., 1 MB instead of 1GB->24 GB).
Strengths
- Reducing the memory requirement for computing the CE loss in LLMs is a strong contribution, especially as the vocabulary sizes, batch sizes, and sequence lengths of LLMs continue to grow. This custom kernel could save many people lots of time trying to get around OOM errors during training, and make it easier to train models with larger sequence lengths/batch sizes/vocab sizes.
- The algorithm is clever and elegant, taking inspiration from FlashAttention, which avoids materializing full attention score matrix during attention computation.
- The experiments demonstrate that CCE can reduce training memory requirements without impacting quality/convergence during training, or training speed, relative to strong baselines (e.g., torch.compile).
Weaknesses
- I think the section about the backward pass could be explained more clearly (see my questions below for points of confusion that could be clarified).
- I think there could have been additional experiments to explore how CCE performs relative to baselines as different hyperparameters vary (e.g., relative size of vocabulary vs sequence length vs. hidden dim, sparsity of S, etc.).
Questions
- Are there regimes where CCE is meaningfully slower than the torch.compile method?
- There were a few elements of the backward computation that I think could be explained more clearly:
- What is the "v" index in the lines 339-341 (Algorithm 3)?
- Why doesn't recomputing the large $C^T E$ matrix multiplication in the backward pass (Algorithm 3) lead to slow-downs? If I understand correctly, this is because although much extra time is spent on this recomputation, less time is spent on the subsequent matrix multiplications, due to the gradient filtering/vocab sorting? Can you break down more granularly how much time each component of CCE (especially the backward pass) takes, and compare this to the naive implementations, so that it becomes clear what is happening here?
- Can you explain this paragraph in more detail please: "We implement the second matrix multiplication in the main memory of the GPU, as a blockwise implementation would require storing or synchronizing S..."? Here, is "main memory" HBM?
- I think there may be mistakes in the backward pass equations at the bottom of page 5 (lines 266-269). Letting V be vocab size, L be sequence length, and D be hidden dimension, we can see that C is [D,V], E is [D,L], and S is [V,L]. Then for the matrix shapes to be correct, shouldn't it be:
- $d/dE = C (S \cdot LSE_{grad})$ --- which is a [D,V] * [V,L] multiplication, which gives [D,L], which is the correct shape of E,
- $d/dC = E (S \cdot LSE_{grad})^T$ --- which is a [D,L] * [L,V] multiplication, which gives [D,V], which is the correct shape of C.
- Can you include, at least in the appendix, a version of algorithm 3 that also includes the backward pass of the indexed matrix multiplication?
- NIT: I think it could be clearer to update the notation to be something like the following, to make Algorithms 2 and 3 easier to follow. For example, you could use $V$, $L$, $D$ to denote vocab size, sequence length, and hidden dim, and correspondingly $V_B$, $L_B$, and $D_B$ to denote the dimensions of the blocks, and $B_V = V/V_B$, $B_L = L/L_B$, $B_D = D/D_B$ to denote the number of blocks, and v, l, d to index into these blocks.
- Can you add a discussion around sequence parallelism approaches, which can also reduce logit memory per GPU by splitting logits along sequence dimensions?