Skip to content

New Record: Untie Value Embeddings (-0.4s, -25 steps)#209

Merged
ClassicLarry merged 1 commit into
KellerJordan:masterfrom
photomz:master
Jan 29, 2026
Merged

New Record: Untie Value Embeddings (-0.4s, -25 steps)#209
ClassicLarry merged 1 commit into
KellerJordan:masterfrom
photomz:master

Conversation

@photomz

@photomz photomz commented Jan 26, 2026

Copy link
Copy Markdown
Contributor

Updates in PR, building off latest #201

  • Untie value embeddings to replace pattern of VE[.12...012] with VE[.01...345].

Also suggest we close #198 by pinning typing-extensions==4.15.0 for housekeeping.

Ideas

The latest idea is inspired by the principle of Bigram/Engram Hash embeddings to allow more uncontextualised information into the architecture. I believe there's further room in adding sparsity to be explored, i.e. Meta's STEM and optimizing the bwd to not communicate unaccessed indices in sparse embeddings.

I tried these ablations:

  • Same weights but more tied layers: ve[012...012] (1)
  • Fewer VE tied layers: ve[01…01]
  • Different layers: ve[.01…01.]
  • Apply VE to all layers
    Surprisingly, the old finding in New Record: Partition Residual Dims and Symmetric Value Embeddings (-0.7s) #194 that adding VE to the first layer no longer held up -- there was no loss delta at all compared to before. I hypothesize that improvements in between (Partial Key Offset, Bigram Hash) removed the need for this somehow, but I'm not quite sure why.

Unrelated to this VE idea, I tried to optimize the bwd with torch.Embedding(..., sparse=True), but I'm running into 2 issues: 1) the metadata overhead of keeping count of the accessed indices complicates the Adam step, especially when distributed, and 2) Full CUDA graphs aren't preserved under sparse embeddings in 2.10.

Timing

The timings (-25 steps) hold up to the latest PR, and are significant (p<0.001).

  • H0: Control is latest WR at 1600 steps.
  • H1: Untie VE at 1575 steps.
                   Runs   Time μ  Time σ  Time +/-  Time p  Loss μ  Loss σ  Loss +/-      p
H0                   10  98.1066  0.0904    0.0000     NaN  3.2778  0.0010    0.0000 0.0000
H1                   10  97.7141  0.0533   -0.3925  0.0000  3.2784  0.0009    0.0006 0.0002

H0:
  losses = [3.2776, 3.2785, 3.277, 3.2772, 3.2788, 3.2764, 3.2763, 3.2784, 3.2788, 3.2791]
  times  = [98.081, 98.053, 97.895, 98.193, 98.136, 98.093, 98.11, 98.124, 98.156, 98.225]
  (n = 10)

H1:
  losses = [3.2784, 3.2778, 3.2792, 3.2799, 3.2776, 3.2782, 3.2771, 3.2786, 3.2775, 3.2794]
  times  = [97.715, 97.704, 97.725, 97.666, 97.725, 97.632, 97.755, 97.831, 97.697, 97.691]
  (n = 10)

The 3 extra embedding weights in H1 added to the average step time, compared to H0. I suggest we time this improvement time delta = 98.1-97.7 = 0.4s.

* train script

* add to record

* typing ext

* fix record

* add commentary

---------

Co-authored-by: Your Name <[email protected]>
@photomz photomz changed the title Untie ve (#1) New Record: Untie Value Embeddings (-0.4s, -25 steps) Jan 26, 2026
@varunneal

Copy link
Copy Markdown
Contributor

Did you experiment with value embeddings on every other layer, or skipping some layers? I think that is what Karpathy is doing in his Nanochat model
https://github.com/karpathy/nanochat/blob/master/dev/LOG.md#2026-01-17-various-experiments

@ClassicLarry

Copy link
Copy Markdown
Collaborator

Cool, I'll merge later if the timing/loss holds up. When I consider the loss difference, the time change here is pretty marginal but the step count decrease is significant and interesting.

@photomz

photomz commented Jan 27, 2026

Copy link
Copy Markdown
Contributor Author

Did you experiment with value embeddings on every other layer, or skipping some layers?

@varunneal Thanks for the tip! Glad that Karpathy is interested in Value Embeddings, but I did a light ablation and couldn't replicate his alternate pattern. I suspect our config for each Attn blocks is too custom, or maybe his approach worked at different scales -- can't say for sure.

Compared to SOTA of H1 (3.278, 97.7s)
- H2: 3.30, 98.9s. Alternating from Karpathy, ve[01.2.3.4.5.]

the time change here is pretty marginal but the step count decrease is significant and interesting.

I think the marginal time change just emphasizes the need for someone to find an efficient sparse gradient update. Most of our params are now in Embeddings, that apart from lm_head and embed, are mostly zero'd due to being unaccessed. I tried but the implementation work was too heavy.

@chrisjmccormick

Copy link
Copy Markdown
Contributor

It would be interesting to measure how much time one embedding table costs us over the course of training. There's probably some ratio there, like a table needs 10-15 steps to justify itself.

Also, on the pattern, there's actually someone over in nanochat who played around and found that VEs on "deeper layers" worked best (here), but was experimenting at our scale. The default / target model for nanochat is a 20-layer / 1280-dim model, though, so it could definitely be a scale thing.

Here's some thoughts I had / conjecture on why our particular pattern might be working well:

We believe that the first 8 layers of our model are where it's building its compute graph--laying down the logic (via keys and values) that will dictate how it processes future tokens.

And then the last three layers are for the actual token prediction, and that's where we have our VEs.

So maybe the value embeddings are more 'semantic'? That might also help explain why the same set of VEs are useful on the first two layers?

@ClassicLarry

Copy link
Copy Markdown
Collaborator

Merging at 99.0s (-0.3s) based on:
step:1600/1600 val_loss:3.2766 train_time:98650ms step_avg:61.66ms
step:1600/1600 val_loss:3.2768 train_time:98674ms step_avg:61.67ms
step:1600/1600 val_loss:3.2802 train_time:98718ms step_avg:61.70ms

step:1575/1575 val_loss:3.2795 train_time:98292ms step_avg:62.41ms
step:1575/1575 val_loss:3.2793 train_time:98514ms step_avg:62.55ms
step:1575/1575 val_loss:3.2763 train_time:98450ms step_avg:62.51ms

sum([98650, 98674, 98718])/3-sum([98292, 98514, 98450])/3

@ClassicLarry ClassicLarry merged commit 5040a96 into KellerJordan:master Jan 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Speed run is currently broken?

4 participants