Colossus, Part 3: Generative AI is neat

Once again I am not writing the Colossus post I thought I was going to (yet). As I started to write up the work I’ve done to make my local models useful, I realized that a lot of it depends on an intuition about generative AI that I only loosely had myself, and that many in my audience might not have at all. So let’s take a not-so-brief side quest into the things that make generative AI work. It’s fun stuff, I promise!

Classic Neural Networks

I often say that today’s AI models are just like the ones we had back in my college days — they’re just way, way bigger. And that’s kind of true, but it’s also totally not true. Some really specific innovations changed the game, and understanding them is important if you want to develop the right intuitions for productive, modern generative work.

But it’s still helpful to start with the basics. I wrote Fake Neurons are Cool more than four years ago but it still does that job pretty well — so if you could use a refresher or a baseline, click on over there and then come on back. I’ll wait.

Tokens

OK, the first idea here is the simplest, but since “token” is a shoo-in for Webster’s Word of the Year, it’s good to know what they really are and how they’re created. (Honestly, if you just think about them as “words” you’ll probably do just fine, but humor me anyways).

Tokens are the “vocabulary” of a model. Each token is a word or a fragment of a word that carries, hopefully, some independent meaning. The vocabulary can’t be infinite — its size has a direct impact on the size of the model, because layers at the bottom and top of the network have nodes for every token in the vocabulary — that gets pretty wide / tall!

Generally (and we’re going to see this approach a lot) model designers just pick a target vocabulary size they think will work well. There’s a ton of art to it. Too large and the model becomes too-big-to-run, or worse the tokens become meaningless and the patterns just aren’t there. Too small and you can’t differentiate enough to be useful. To give you a sense of scale, the Gemma 4 series vocabulary is about a quarter of a million tokens.

Once you have a target vocabulary size and your training inputs, you can create the token space. This is pretty neat, actually:

  1. Create a starting vocabulary by assigning a token to each unique character (or byte) in the training set. E.g., if your input training set is limited to ASCII text, you’ll start with something like 128 tokens (maybe less if, say, the BEL or tilde characters don’t appear).
  2. Count the occurrences of every unique adjacent token pair. Merge the most common pair into a new token and substitute that for the pairs in the training set.
  3. Keep doing this until you have either reached your target vocabulary size OR remaining pairs aren’t prevalent enough to be considered significant (by another mostly arbitrary, empirical definition of “significant”).

Of course there’s more to it than this — but that’s way more than enough for government work. That’s the easy one down.

Iterative Generation

Classic neural networks are great at lots of things — but generating long-form content isn’t one of them. A set of inputs goes in the bottom layer, and out the top comes a prediction score on every output node. For a yes/no answer, that’s a single output node with a score that you translate into something binary. For a classifier, you have one output node for every class and the model scores how likely it is that your inputs match each one.

The key is that the output nodes are fixed and finite. If I want to predict who will win the World Series this year, I can create a net with 30 output nodes, one for every team. But if I want to ask when the Mariners will finally win the World Series, I’d need an infinite number of output nodes — one for every year in the future, forever (stop laughing at me).

Conversational (aka “generative”) responses are by definition infinite — There is no output node in any network for the sentence “The truth is that the Mariners may never win the World Series, bro” and all of the other possible replies. So what are we supposed to do?

Well, it turns out that, given a training set of sequences to learn on, and a sequence of input tokens (aka a “prompt”), a neural network can get pretty good at predicting the next single token that would appear based on the “training set” — and that problem actually looks pretty familiar:

  1. We present our input prompt as a sequence of tokens. This is our context, so if the model context size is 256k, there are 256k nodes in the bottom layer of our network.
  2. (A lot to unpack here and we’ll get there; for now just think about it as network layers, except to say that this is where the model parameter count lives.)
  3. The fixed output layer is our token vocabulary; one score for every possible token.
  4. We pick the “best” token based on the scores. This is called decoding.

A fun side note: decoding is where a bunch of LLM settings work their magic. For example, “Temperature” controls how random the choice is — at 0.0 the model always picks the token with the highest score; at 2.0 it’s a crap shoot. And the “Top-k” and “Top-p” configurations define how many tokens go into the pool to pick from (using absolute numbers and/or percentages respectively). It all starts to make sense!

All this for one token?

Well, yeah. But here’s the cool part. If we add the token we just generated to the end of our prompt, we can do it again! And again! And again! Until the model spits back a special “stop” token and that, boys and girls, is Generative AI.

Except, not really. This model does work, sort of, but it has two enormous flaws that basically make it impractical for anything real.

First of all, the training effort is absurd. You have to train every single “next token” individually. So a single pass for the sentence “I can’t believe it’s not butter” is five distinct trainings (let’s assume words are tokens):

  • I → can’t
  • I can’t → believe
  • I can’t believe → it’s
  • I can’t believe it’s → not
  • I can’t believe it’s not → butter

The other killer is positionality. Because each input node is distinct and feeds its own weights up through the network, token position really matters. We may be able to complete the sentence “I can’t believe it’s not butter,” but almost for sure we’ll crash and burn presented with “I absolutely can’t believe it’s not…” — because the four tokens after “absolutely” have been shifted to the right, and those nodes weren’t trained for this!

But take heart — some folks at Google in 2017 solved both of these at the same time. Seriously, these folks should headline the Topps Amazing Scientists All-Stars collectible card set: Attention Is All You Need. The Wikipedia article about the paper is also excellent.

Transformers

The invention of the “transformer” shakes up the fully-connected-layers concept we (I) know and love from classic neural networks, replacing them with a whole bunch of transformations designed to pick out important meaning and to do so largely in parallel. I’ll do my best to walk through it with enough detail to build solid intuition, but not so much that we lose everyone along the way. We’ll see.

Before we do this, I want to reinforce something I said earlier, because it was really important in my journey to getting comfortable with it. Many of the parameters we’ll encounter are empirical, made up, try-stuff-and-see-what-sticks. Although their general scale sometimes tracks in the rear view, nobody really has any precise insight into why “4” is better than “7” in some particular scenario. On the other hand, some matter a lot, and I’ll try to differentiate — it helps to know when to keep digging for the “why,” and when to let it slide.

Phase 1: Embedding

The first step is to take our sequence of input tokens and turn it into a 2D matrix. The mechanics are simple — look up each token identifier in a learned matrix that has one column for every token in the vocabulary and concatenate the selected columns together. The number of rows in the “embedding matrix” is called d_model and it very roughly corresponds to the number of features the model can learn for each token.

This result is called the “residual stream;” it will travel with us from layer to layer up the machine until we reach the top.

One thing we’ll encounter in later steps is that processing is largely non-positional — by default each token learns to “attend to” the tokens around it but can’t tell the difference between “man bites dog” and “dog bites man.” Obviously this needs to be dealt with.

The 2017 paper handled it here at the beginning, by defining a T x d_model matrix and adding it to the residual stream, so everything downstream included positional echoes. Later approaches did the same thing but used a learned matrix rather than defining it mechanically. Current models don’t do this at all — they handle position in the “attention” layers with a technique called RoPE that we’ll encounter later.

Phase 2: Transformer Blocks

After embedding is a stack of “transformer blocks;” each takes the residual stream as input and delivers a modified residual stream — same shape — as output. We’ll refer to the count of blocks as num_layers; it’s typically on the order of 12-100, not thousands.

Blocks execute in two stages, which you can broadly think about as “attention” and “consolidation.” Attention runs a number (num_heads) of transformations in parallel, each trained the same way but (thanks to random initial weights) magically assuming a distinct semantic role. Sometimes we can perceive these roles and sometimes not, but it’s cool regardless. “Consolidation” brings the attention output back together into a single matrix and does some classic NN stuff on that.

Phase 2a: Attention Heads

OK, there’s a lot going on here so buckle up — first we’re going to figure out how much each token should “attend” to the others in context (where attention from T1 to T2 is represented by a number stored at those coordinates within a T x T matrix).

Each head uses three learned matrices W_Q, W_K and W_V. Each is d_model x d_head in shape, where d_head is just d_model / num_heads. Step one is to multiply the residual stream by each of these, cancelling out d_model to get three interim matrices of shape T x d_head: Q, K and V:

The order of operations here was designed to give each matrix a distinct role:

  • Q = “Query”: what “kinds” of tokens are relevant to each token (“I need a date!”)
  • K = “Keys”: what “kind” of token each token is (“I’m a date!”)
  • V = “Values”: the actual “stuff” each token has (“August 5, 1969”)

For models that don’t add token position information during embedding (i.e., most current models), it’s mixed in at this stage, usually with rotary positional embeddings. The idea of RoPE is that the token (horizontal) vectors in Q and K are rotated in proportion to their positions in the sequence, so that the angular difference between Q and K is a measure of relative position. This really deserves an entire article — for now just know that we’ve injected positional information into Q and K, so the model can see the difference between man-eating sharks and shark-eating men.

K is transposed, a math trick that lets us multiply it by Q to cancel d_head and end up with a T x T “attention matrix.” This one is pretty easy to understand — it represents, for each token, how important every other token is to its in-context meaning. With this framing what we do next also makes sense — apply a “causal mask” to each slot that is “in the future” in relation to its (horizontal) context. In the first row, we only know the first token in the stream and want to predict the second, so we set all of the tokens to the right with the value -∞. For the next row we know a bit more, and so on down the matrix.

Believe it or not, there are a couple of more things to do before the attention matrix is fit for use. First we divide its values by the square root of d_head, which is one of those “this seems to help” kind of things. Then we apply softmax to juice the machine with non-linearity (the equivalent of our activation function in classic neural networks).

And, finally, we turn the “attention” into actual output for this head; a simple multiplication with V that lands us back into a T x d_head matrix. Whew.

A fun fact — notice the light shading I added to the SW corner of the attention matrix above. The state of the art here is moving fast, and there a ton of tweaks that attempt to save space or time. A neat one is “sliding window attention.” In this model some heads are configured to ignore old as well as future context — a tradeoff of some long-term memory for less computation and cache.

Anyways, there’s more to do — but let’s take a breath because we’ve already seen the two game changers and they’re useful to reiterate:

  1. There is a TON of parallelism in this machine — first of all we’re running num_head heads at the same time, fully independently. Second, and this continues up the stack too, we’re able to generate predictions for every position in a single run. Our output matrix has a vector for every token! That is pretty sweet.
  2. We’ve integrated positionality into the machine in an extremely robust way. Rather than depending on the position of the token in the network layers themselves, we teach each one where it sits relative to the others — and allow that to influence learning.

Home stretch (for attention). Once all the parallel heads finish their work, we just stack their outputs together, giving us a T x d_model matrix. We multiply this by another learned matrix W_O and add the results back into the residual stream.

Phase 2b: Consolidation

The nice thing about this section is that it’s going to be short. After the head outputs are brought together and added to the residual stream, a normalized version of the stream is fed through a much more classic single-hidden-layer network (“MLP” or a Multilayer Perceptron because we’re fancy). Where the attention heads helped the token vectors figure out how they’re impacted by other tokens, this phase is all about refining the tokens themselves.

The hidden layer widens the matrix to a width of d_ff and then brings it back to d_model so we end up with the same dimensions. d_ff is — you guessed it — an empirically defined value, usually set by rule of thumb to d_model x 4. Different activation functions are used, but GELU (and it’s “gated” versions) tends to be the most popular at the moment.

The residual stream, which has been accumulating additions over these last two phases, is finally normalized in place so we have solid footing for the last mile.

Phase 3: Unembedding and Decoding

Remember way back during embedding, we turned the token identifiers into vectors. Now we’ve got to get back to vocabulary space, generating possible “next token” scores for each of the tokens in the stream.

In a lot of models, this translation is done using the original embedding matrix, transposed to cancel out d_model and end up where we want to be:

This reuse isn’t a hard-and-fast rule; sometimes (especially larger) models train it separately. But reuse kind of makes sense — the way it has been described to me is: if the embedding matrix maps a token to a “direction in space”, it follows that the closer the residual stream is to that direction, the more “likely” that token should be. Honestly, I’m not sure my brain stretches to really understand that, but another argument is “it seems to work, and it saves a ton of parameter size because this matrix is enormous.” I like that one.

During training, making predictions for every position is hugely valuable. OTOH, during generation we only really care about that last column — what’s the prediction for the next token given all the tokens in context?

That same softmax is applied to the scores in this column, and then one is picked as the winner. We’ve already talked about how we do that part, using parameters like Temperature, Top_k and Top_p to dial up or down the randomness.

Now append that token to the end of context, fire up the GPUs and turn the crank again.

Mic Drop

That was a lot. I hope the pictures are useful to some folks; no way could I understand any of it just looking at equations. PowerPoint was pretty overwhelmed with the sheer number of boxes involved, but we made it happen.

The good news is that now we’re armed with some vocabulary to help us talk about local models — what does “parameter count” really mean; how do attention schemes impact context size and cache space; why can they seem so smart and so stupid at the same time? And much more.

Next time — honest — we’ll start diving into that.

How the $TRUMP scam works

So much corruption sails through American headlines these days, it’s become hard to pay appropriate attention to any one outrage. And of course that’s the point — shock and awe until it’s completely normalized and we just let it go. So in the spirit of not letting it go, let’s talk about one example that I actually can speak to in some detail: the $TRUMP memecoin.

You’ve probably heard about it in the news. Just before taking office in January, Trump owned and affiliated companies (basically the same folks selling his shoes and bibles and other shlock) launched a crypto “coin” branded $TRUMP and promoted by the jacka** himself. Its value quickly soared before steadily dropping to the $14 or so it is today, still with a market cap in the billions.

So just what is a crypto “memecoin” anyway, and why did he bother? The TLDR is at the end — but hopefully you’ll find the longer story illuminating too. Let’s dig in.

Tokens and “Coins”

Crypto “coins” are just crypto tokens, so we have to start there. If you want to go even deeper, I’ve written about crypto and blockchain stuff more generally; see here, here and here.

It’s useful to start by thinking about tokens like baseball cards. At the beginning of the season, Topps (or Fleer or whoever) prints up a bunch of cards that make up the “supply.” The cards themselves don’t have any intrinsic value, they’re just cardboard. People can buy the cards from Topps, they can trade or sell them to other individuals, and the price goes up or down based on how much people want them. Easy peasy.

In this case it’s better to think about it as if every card in the supply was just Cal Raleigh — so it doesn’t matter which specific physical card you have, they’re all exactly the same. That’s the difference between “fungible” (every instance is the same) and “non-fungible” (every instance is unique) tokens.

Fungible tokens are everything in the world of crypto finance. One of the most popular platforms on which to deploy them these days is Solana, which in all the ways that matter is the same as the OG Ethereum I’ve talked about before. Solana includes a core “program” that makes it super-easy for anyone to define a token and “mint” instances of it, with no need to write their own code.

$TRUMP is one of these. Trump’s merch companies used the Solana Token Program to define a token/coin with one billion instances (the supply). No intrinsic value, just data they made up on the Solana blockchain. Anybody holding $TRUMP tokens can interact with the program to move them into other wallets in return for a small transaction fee that goes to the Solana stakers (not the Trump organizations yet, stay tuned).

The Meme in Memecoin

Tokens are actually a pretty neat little tool. They can help broker access to limited resources, track rights in voting organizations, be used as currency in virtual (or physical worlds), and a ton more. The “memecoin” use case, however — at best it’s a toy, and honestly it’s just a scam.

Memecoins” don’t have a use, value or other reason to exist beyond amplifying some trend or capturing news cycles. Except they’re really good for stealing money from people, as in the uniquely American coin $HAWK promoted by the “Hawk Tuah” girl. Minters use viral techniques to con people into “pumping up” the value of their memecoin, “dump” their own holdings at a profit, and leave everybody else holding the bag.

What could be a more appropriate vehicle for the President of the United States to score some quick cash? To wit: 58 wallets have made millions from $TRUMP, 764,000 have lost money.

Trading Liquidity and Fees

But the grift goes way deeper than that. Sure, by holding 80% of the coins, even at $14 a pop they’ve “created” staggering wealth on paper. But there’s a great side game here too — liquidity fees.

Remember we said that anybody who holds a token can give it to somebody else by paying a small fee to the Solana stakers (the same fee that any transaction incurs). But that’s not the way markets typically work — I don’t go hunting for somebody holding Microsoft shares and ask to buy from them directly. Instead, “market makers” sit between buyers and sellers and grease the wheels. This basically happens in two ways (simplifying for my own sanity):

Centralized Exchanges (e.g. Coinbase)

Sites like Coinbase are “custodial” exchanges, meaning that they abstract away all of the crypto/blockchain complexity by holding users’ tokens for them in one big centralized pot.

The exchange then keeps a trading “order book” — lists of users that want to buy or sell tokens. The book matches up these users to fulfill orders automatically, floating the price up or down as demand indicates. No tokens actually move on the blockchain as part of these trades; it all stays in the Coinbase pot and they just remember who owns what.

Of course this relies on trust in the exchange, which isn’t always well-founded. Still, as crypto becomes ever more mainstream (for better or worse), exchanges are incented to behave conservatively.

Exchanges only do this for tokens with significant demand — most memecoins don’t make the cut. $TRUMP is an exception because of its “unique” brand advantage.

Decentralized Liquidity Pools (e.g., Raydium)

Here’s where things get more interesting. Centralized Exchanges are increasingly regulated and require users to prove their identity, report to the IRS, and so on. This is fine for most people most of the time, but can be unattractive to folks that want to trade anonymously (or more generously, without placing trust in a custodial exchange).

These users can instead trade via a “Decentralized Exchange” (DEX) like Raydium that uses “liquidity pools” and its own order book to facilitate exchange.

Any user can create a liquidity pool on Raydium by registering equivalent dollar values of two tokens into an account there. For example, I might create a pool that has $1,000 each of $TRUMP and USDT. Right now that’d be about 71 $TRUMP ($1,000 / $14) and 1,000 USDT tokens.

My pool is now available to the Raydium order book to fulfill orders. This gets a bit complicated, but bear with me. If somebody wants to buy 10 $TRUMP tokens from my pool, the system computes a price that will keep the product of the token count (71,000) constant:

  • Initial pool: 71 * 1000 = 71,000
  • Extracting 10 $TRUMP: (71 – 10) * (1000 + y) = 71,000
  • y in this case equals about 164 USDT, or $16.40 / $TRUMP

The platform adds a fee of around 0.3% to that $164, makes the trade on the blockchain, and shares a portion of the fee back to the owner of the liquidity pool (me). In short, I’m using my personal holdings to create market liquidity, and I get paid for it. Cool!

A side note: while Raydium isn’t a custodian of tokens in the same sense as Coinbase, in any real sense they are acting as one. When you commit your tokens to a liquidity pool, Raydium’s smart contract can move them at will. So there’s still trust involved — just a different kind.

Now remember that the Trump companies still hold about 80% of all $TRUMP tokens. They’ve used 10% of their holdings to create liquidity pools largely on the Meteora platform (equivalent to Raydium). And since they are such a disproportionate holder, their pools are party to many, many DEX transactions. Again, to wit: Trump’s meme coin business racks up fees as buyers jump at the chance for access to the president. Crypto data company Chainalysis estimates $320 million. Yikes.

“Buy my coin, meet me for dinner!”

OK, so we’ve established that the President is using the power of the United States to shake down naïve users for millions. But of course there’s no bottom for these people, so they’ve upped the ante even more.

A couple of weeks ago, the Trump companies announced that the top 220 holders of $TRUMP would be invited to a private dinner with the president at his club in DC. The top 25 will have a private reception with the jacka**. And of course, since that announcement the price of the coin has gone up significantly as people vie for access.

Now of course politicians sell access for funds all the time — hey, just last Monday Trump pulled in $1.5M a plate despite the fact that he can’t even run again. That’s its own huge problem of course, but at least there are some rules around disclosure and how the funds are supposed to be used.

Not so for the $TRUMP contest. The increased value and transaction fees that result from people vying for access here go directly to Trump’s companies and to Trump personally. It is the most obvious, blatant, unbelievable act of corruption one could imagine.

And remember how DEX-based transactions are completely anonymous? I wonder who is currently pumping up the value of $TRUMP so they can show up to dinner? Shockingly: Top $TRUMP buyers vying for dinner seats are likely foreign.

“Corruption Three Ways”

The mechanics are fascinating; unwinding it all is a game I typically enjoy. But the actuality of what is happening is just so craven, it ruins the fun:

  1. Trump is using his office to inflate the value of a meaningless asset for his own benefit.
  2. Trump is also profiting from fees incurred on almost every trade of the asset.
  3. Trump is openly advertising untraceable access in return for dollars.

We are so screwed.