DEV Community

Cover image for What Actually Happens When an LLM Generates a Token? A Deep Dive into Softmax
Nicole I.
Nicole I.

Posted on Originally published at younic.de

What Actually Happens When an LLM Generates a Token? A Deep Dive into Softmax

Euler's night

21:38. Berlin. Tuesday evening.

I had just come home from a meetup with a question in my head.

Is an LLM probabilistic or not?

Because, well... somehow yes. But also somehow no.

What actually happens between "the model predicts a token" and "the model produces a token"?

In summary, an LLM produces a probability distribution over possible next tokens. A decoding strategy then turns that distribution into an actual token. But that explanation hides most of the interesting engineering and actually very interesting mathematics. To fully understand what happens (and to rediscover the beauty of ee !), it helps to follow the process one step at a time.


So, what happens when an LLM generates a token?

When an LLM processes a prompt, it does not simply decide:

"The next token is Paris."
Enter fullscreen mode Exit fullscreen mode

First of all, the model takes the input and produces a score for every possible next token, based on its learned weights.

Suppose the context is:

The capital of France is ...

Conceptually, the model might produce something like:

Paris      8.2
London     6.1
Berlin     5.4
Pizza     -2.3
Enter fullscreen mode Exit fullscreen mode

These scores are called logits.

A logit is the model's raw score

A logit is a raw score produced by the model for a possible next token.

  • It is not yet a probability.
  • It doesn't have to be between 0 and 1. It doesn't have to add up to 1. It can be positive or negative.
  • It is simply the model's numerical representation before it turns those representation into a probability distribution.

How do those scores become probabilities? That's where Softmax enters.


Softmax turns logits into probabilities

The problem with raw logits is that they are not probabilities. They can be positive, negative, large, or small. Within a probability distribution, all probabilities are positive, and all probabilities add up to 100%.

For example:

Paris      70 %
London     20 %
Berlin     10 %
Enter fullscreen mode Exit fullscreen mode

Our logits don't do that. So we need a transformation that turns them into something that can be interpreted as probabilities. That's what Softmax does.

The Softmax function is:

pi=ezijezjp_i = \frac{e^{z_i}} {\sum_j e^{z_j}}

At first glance, the formula might look a little bit intimidating. But it's actually pretty simple, so stay with me. Simply put, the formula does two things in order to transform the logits into probabilities:

First, it transforms every logit into a positive value. In addition, it amplifies the relative differences between the values so that the probability distribution gets clearer. That's what the exponential function e^x is for. Finally, the fraction normalizes these values by dividing each one by the sum of all exponentiated logits.

The result is a probability distribution where all values are positive and add up to 1.

Let's go down the rabbit hole step by step.


ii identifies the token

The little ii simply identifies the token we're currently looking at.

pip_i is the token's probability

pip_i means the probability assigned to token ii .

If we're calculating the probability of Paris, we write: pi=pParisp_i = p_{\text{Paris}}

Nothing mysterious. It's just what we're interested in - the probability.

ziz_i is the token's logit

ziz_i is the corresponding logit for token ii .

So if the logit of Paris is 2, we write: zParis=2z_{\text{Paris}}=2

Still straightforward.

jj indexes the tokens being summed

Look at:

jezj\sum_j e^{z_j}

The symbol \sum simply means: Add things together. And jj is the index we use to go through all the tokens we're summing over.

In this case:

Take every possible token, calculate ezje^{z_j} , and add all the results together.

In engineering language this would be:

Start with zero, iterate over all possible next tokens, calculate ezje^{z_j} for each one, and add the result to the total.

total = 0

for j in tokens:
    total += exp(z[j])
Enter fullscreen mode Exit fullscreen mode

Suppose we have only three tokens:

Paris    z = 2
London   z = 1
Berlin   z = 0
Enter fullscreen mode Exit fullscreen mode

Then:

jezj=e2+e1+e0\sum_j e^{z_j} = e^2+e^1+e^0

That's it.

The denominator in the Softmax formula is simply the sum of all exponentiated logits.

In our example, the result would be

jezj=e2+e1+e011.11\sum_j e^{z_j} = e^2 + e^1 + e^0 \approx 11.11

Calculating the Probability using Softmax

Now calculate the probability of Paris (z=2):

pParis=ezParisjezj=ezParisezParis+ezLondon+ezBerlin=e2e2+e1+e07.3911.110.665p_{\text{Paris}} = \frac{e^{z_{\text{Paris}}}} {\sum_j e^{z_j}} = \frac{e^{z_{\text{Paris}}}} {e^{z_{\text{Paris}}}+e^{z_{\text{London}}}+e^{z_{\text{Berlin}}}} = \frac{e^2}{e^2 + e^1 + e^0} \approx \frac{7.39}{11.11} \approx 0.665

So the probability of Paris is approximately 66.5%.

Then London (z=1):

pLondon=e1e2+e1+e02.7211.110.245p_{\text{London}} = \frac{e^1}{e^2 + e^1 + e^0} \approx \frac{2.72}{11.11} \approx 0.245

So the probability of London is approximately 24.5%.

Then Berlin (z=0):

pBerlin=e0e2+e1+e0111.110.090p_{\text{Berlin}} = \frac{e^0}{e^2 + e^1 + e^0} \approx \frac{1}{11.11} \approx 0.090

So the probability of Berlin is approximately 9.0%.

Notice what happens: You get pretty differentiated results and at the same time, the scores turned to a probability. That is Softmax.

Exponentiate every score. Add all those values together. Then divide each individual value by the total.

And now the results form a probability distribution.

The formula only looks intimidating because mathematics is extremely good at packing an entire paragraph into one line.


ee creates the required probability weights

Look at the following table:

Token / probability With exponential pi=ezijezjp_i = \frac{e^{z_i}}{\sum_j e^{z_j}} Without exponential pi=zijzjp_i = \frac{z_i}{\sum_j z_j}
pParisp_{\text{Paris}} ParseError: KaTeX parse error: Unexpected end of input in a macro argument, expected '}' at end of input: …\mathbf{66.5%} ParseError: KaTeX parse error: Unexpected end of input in a macro argument, expected '}' at end of input: …\mathbf{66.7%}
pLondonp_{\text{London}} ParseError: KaTeX parse error: Unexpected end of input in a macro argument, expected '}' at end of input: …\mathbf{24.5%} ParseError: KaTeX parse error: Unexpected end of input in a macro argument, expected '}' at end of input: …\mathbf{33.3%}
pBerlinp_{\text{Berlin}} ParseError: KaTeX parse error: Unexpected end of input in a macro argument, expected '}' at end of input: … \mathbf{9.0%} ParseError: KaTeX parse error: Unexpected end of input in a macro argument, expected '}' at end of input: …=\mathbf{0.0%}
Sum 100.0% 100.0%

In this particular example, Paris and London still look relatively similar, but Berlin immediately drops to 0% without exe^x . More importantly, directly normalizing logits only works as a valid probability construction when the logits are non-negative. Real logits can also be negative.

So the exponential isn't just decoration. It gives us a transformation with the properties we need: positive values and a way of amplifying relative differences before normalization. And that brings us to ee .

Meet ee

You might remember that e2.718281828...e \approx 2.718281828...

You might also remembered that it had something to do with exponential functions and logarithms. Let's explore why it matters.

ee naturally appears when we describe continuous exponential growth. And it has one remarkably convenient mathematical property:

ddxex=ex\frac{d}{dx}e^x=e^x

The rate at which exe^x changes - f(x)f'(x) - is always and exactly its current value f(x)f(x) . Crazy.

Take this table with xx , the values f(x)f(x) and their exponential growth f(x)f'(x) :

xx f(x)=exf(x)=e^x f(x)=exf'(x)=e^x
00 f(0)=1f(0)=1 f(0)=1f'(0)=1
11 f(1)=2.718f(1)=2.718 f(1)=2.718f'(1)=2.718
22 f(2)=7.389f(2)=7.389 f(2)=7.389f'(2)=7.389

The larger the function becomes, the faster it grows — at a rate proportional to its current value.

Side Quest: One practical problem: e10000e^{10000}

Did you notice? The equation is mathematically elegant. Computers, however, have finite-precision numbers. But exponentials grow fast. Very fast. If a logit is 10000, just calculating e10000e^{10000} is not a nice thing to do to your processor with floating-point arithmetic.

So real implementations use a mathematically equivalent, numerically stable form of Softmax:

pi=ezimax(z)jezjmax(z)p_i = \frac{e^{z_i-\max(z)}} {\sum_j e^{z_j-\max(z)}}

Why is this allowed?

Because subtracting the same constant from every logit does not change the resulting Softmax probabilities:

softmax(z)=softmax(zc)\operatorname{softmax}(z)= \operatorname{softmax}(z-c)

for any constant (c).

Let's explain this from an engineering perspective: In practice, we want to keep the numbers inside the exponential function as small as possible. Large exponents can cause numerical overflow and make the calculation unnecessarily difficult. So we subtract the largest logit from every logit.

If the largest logit is max(z)\max(z) , we calculate:

zi=zimax(z)z_i' = z_i - \max(z)

This makes the largest logit exactly zero:

max(z)max(z)=0\max(z) - \max(z) = 0

And because the largest logit was subtracted from all the others, every other adjusted logit is negative:

zimax(z)0z_i - \max(z) \leq 0

Now look at what happens when we exponentiate them.

The largest exponent is:

e0=1e^0 = 1

And all the other exponentials are between 0 and 1:

0<ezimax(z)<10 < e^{z_i-\max(z)} < 1

So instead of calculating potentially huge numbers like e1000e^{1000} , we are always working with values between 0 and 1.

But why are we allowed to do this?

Because - and now we come to the already mentioned mathematical background - subtracting the same constant from every logit does not change the resulting Softmax probabilities:

softmax(z)=softmax(zc)\operatorname{softmax}(z) = \operatorname{softmax}(z-c)

for any constant cc .

In our case, we simply choose:

c=max(z)c = \max(z)

So we haven't changed the probabilities we're calculating.

We've just shifted all logits down so that the calculation stays numerically small and safe.

Side Quest: lnln

And since we had already come this far down the rabbit hole: lnln is - simply spoken - the mathematical opposite of ee : If exe^x is the operation that takes us from xx to exponential growth, ln(x)\ln(x) takes us back again.

For example, if:

e27.389e^2\approx7.389

then:

ln(7.389)2\ln(7.389)\approx2

Back to the LLM. Back to Softmax.


Recap

Let's do a quick recap. We started with:

Paris       2
London      1
Berlin      0
Enter fullscreen mode Exit fullscreen mode

Softmax applies exe^x :

e27.389e^2\approx7.389
e12.718e^1\approx2.718
e0=1e^0=1

Then it normalizes:

pParis7.3897.389+2.718+166.5p_{\text{Paris}} \frac{7.389}{7.389+2.718+1} \approx66.5%
pLondon2.7187.389+2.718+124.5p_{\text{London}} \frac{2.718}{7.389+2.718+1} \approx24.5%
pBerlin17.389+2.718+19.0p_{\text{Berlin}} \frac{1}{7.389+2.718+1} \approx9.0%

And there it is:

Paris      66.5 %
London     24.5 %
Berlin      9.0 %
Enter fullscreen mode Exit fullscreen mode

The model started with scores (logits). Softmax transformed them. The exponential function turned the relative differences into positive weights. Normalization turned those weights into proportions. And now we have a probability distribution.

The LLM hasn't randomly chosen anything yet.

We have only calculated the distribution.


Temperature reshapes the probability distribution

Temperature doesn't itself introduce randomness. It changes the shape of the probability distribution before we sample from it.

The temperature-adjusted Softmax is:

pi=ezi/Tjezj/Tp_i= \frac{e^{z_i/T}} {\sum_j e^{z_j/T}}

The TT stands for temperature. It modifies the logits before the exponential transformation.

Suppose we have:

Paris     2
London    1
Enter fullscreen mode Exit fullscreen mode

The effect of different temperature values is:

Temperature TT Paris: 2/T2/T London: 1/T1/T Difference
0.50.5 44 22 22
11 22 11 11
22 11 0.50.5 0.50.5

* T=1T=1 means that the logits are not changed by temperature: zi/T=ziz_i/T=z_i .

Then exe^x acts on these values. Low temperature amplifies the differences between logits. High temperature reduces them. The resulting distribution becomes respectively sharper or flatter.

The lower the temperature, the sharper the probability distribution

The lower the temperature, the larger the difference between the adjusted logits becomes. The exponential transformation then amplifies this difference, resulting in a sharper probability distribution. In other words, the probability becomes more concentrated on the token with the higher logit.

The higher the temperature, the flatter the probability distribution

Vice versa, the higher the temperature, the smaller the difference between the adjusted logits becomes. After the exponential transformation, this results in a flatter probability distribution, meaning that the probability is distributed more evenly across the possible tokens.

And this is where the role of ee becomes especially useful: Temperature doesn't directly manipulate the final percentages. It just changes the scores before the exponential transformation, where differences can be amplified or compressed.


Side Quest: What happens if the Temperature drops to zero?

The formula says: zi/Tz_i/T

So if (T=0), aren't we dividing by zero?

Yes. We are. But remember Limes, your friend and helper when it comes to division by zero. Because the thing is, the formula is not defined at (T=0). Mathematically, the interesting thing is the limit as (T) approaches zero from above:

T0+T\rightarrow0^+

Again, suppose we have

Paris     2
London    1
Enter fullscreen mode Exit fullscreen mode

The effect of temperature values approaching zero is:

Temperature TT Paris: 2/T2/T London: 1/T1/T Difference Paris probability London probability
11^* 22 11 11 73.173.1% 26.926.9%
0.50.5 44 22 22 88.188.1% 11.911.9%
0.10.1 2020 1010 1010 99.99599.995% 0.0050.005%
0.010.01 200200 100100 100100 100\approx100% 0\approx0%

T=1T=1 means that the logits are not changed by temperature: zi/T=ziz_i/T=z_i .

So the table makes the effect of temperature very visible:

  • As TT decreases, the difference between the adjusted logits increases.
  • The exponential function amplifies that difference.
  • The resulting probability distribution becomes increasingly sharp.
  • At T0+T\rightarrow0^+ , the token with the highest logit approaches a probability of 11 , while the other approaches 00

The values become enormous. But Softmax doesn't care about their absolute size. It cares about their relative size.

Step-by-Step

z1=2,z2=1z_1=2,\qquad z_2=1

For p1p_1

For Softmax with temperature TT :

p1=e2/Te2/T+e1/Tp_1= \frac{e^{2/T}} {e^{2/T}+e^{1/T}}

We divide the numerator and denominator by e2/Te^{2/T} . This gives:

p1=11+e1/Tp_1= \frac{1} {1+e^{-1/T}}

Now consider the limit: T0+T\rightarrow0^+

Then: 1T\frac{1}{T}\rightarrow\infty and therefore: 1T-\frac{1}{T}\rightarrow-\infty

Explanation: As TT approaches zero, the value of TT becomes smaller and smaller. And because there is a minus sign in front, the exponent becomes increasingly negative.

Therefore: e1/T0e^{-1/T}\rightarrow0

Explanation: For a negative exponent, exe^x produces a value between 00 and 11 . The more negative the exponent becomes, the closer the result gets to zero.

which leaves:

p111+0=1p_1\rightarrow \frac{1}{1+0} =1

For p2p_2

For Softmax with temperature TT :

p2=e1/Te2/T+e1/Tp_2= \frac{e^{1/T}} {e^{2/T}+e^{1/T}}

We divide the numerator and denominator by e1/Te^{1/T} . This gives:

p2=1e1/T+1p_2= \frac{1} {e^{1/T}+1}

Now consider the limit: T0+T\rightarrow0^+

Then: 1T\frac{1}{T}\rightarrow\infty

Explanation: As TT approaches zero, the value of TT becomes smaller and smaller. The exponent therefore becomes increasingly positive.

Therefore:

e1/Te^{1/T}\rightarrow\infty

Explanation: For a positive exponent, exe^x produces a value greater than 11 . The larger the exponent becomes, the larger the result gets. As the exponent approaches infinity, the value of exe^x also approaches infinity.

which leaves:

p21+1=0p_2\rightarrow \frac{1} {\infty+1} =0

And there we have the complete result:

p11p_1\rightarrow1 and p20p_2\rightarrow0

The important trick is that we divide by the exponential term corresponding to the token we are currently calculating.

For p1p_1 , dividing by e2/Te^{2/T} gives us e1/Te^{-1/T} , which approaches 00 .

For p2p_2 , dividing by e1/Te^{1/T} gives us e1/Te^{1/T} , which approaches \infty .

That makes it explicit why the probability of the larger logit approaches 11 , while the probability of the smaller logit approaches 00 .

And there it is: While the absolute numbers themselves don't matter, their ratio is becoming decisive.

Softmax doesn't care how enormous the numbers are. It cares how enormous they are relative to one another.

How is it then possible to assign Temperature = 0?

This is where the mathematical model and the practical API diverge slightly.

At (T=0), the Softmax equation itself isn't defined. In practice, a system may interpret temperature = 0 as a request for greedy or otherwise non-sampling decoding rather than literally dividing by zero.

Conceptually:

argmax(z)\operatorname{argmax}(z)

rather than:

softmax(z/0)\operatorname{softmax}(z/0)

Temperature zero isn't "Softmax with zero."

At T=0T=0 , the Softmax formula itself is not defined because we would be dividing by zero.

In practice, temperature = 0 is usually used to tell the system: Just choose the token with the highest probability.

There is one small catch. What if two tokens have exactly the same highest logit?

Paris      2
London     2
Berlin     1
Enter fullscreen mode Exit fullscreen mode

Now Paris and London are tied.

If we look at what happens as TT gets closer and closer to zero, neither one becomes more likely than the other. They both keep the same probability.

There is no mathematical way for Softmax itself to choose between two exactly equal maximum logits. So the tie has to be resolved by the decoding algorithm, not by Softmax.

If there is no clear winner, the decoding system needs a rule for resolving the tie.

For example, a system could define a deterministic tie-breaking rule such as:

1. Find the highest logit.
2. If several tokens have the same highest logit,
   choose the token with the smallest token ID.
Enter fullscreen mode Exit fullscreen mode

Then:

Paris      2   ← token ID 471
London     2   ← token ID 892
Berlin     1
Enter fullscreen mode Exit fullscreen mode

would always select Paris.


Sampling selects a token from the distribution

At this point, we have a probability distribution.

Suppose:

Paris      70 %
London     20 %
Berlin     10 %
Enter fullscreen mode Exit fullscreen mode

Sampling means: Draw a token according to this distribution.

Run Result
One run → Paris
Another → Paris
Another → London
Another → Paris
Another → Berlin

The distribution itself has not changed. The concrete draw can.

A probabilistic model does not necessarily produce a probabilistic output.

So the variability comes from the sampling process itself: Temperature changes the distribution. Sampling is the random selection.

You can think about it like this:

Probability distribution
          ↓
     Temperature
          ↓
"How strongly should
 the options differ?"
          ↓
       Sampling
          🎲
          ↓
        Token
Enter fullscreen mode Exit fullscreen mode

Or, less formally: Temperature changes the weights. Sampling rolls the dice.

Sampling and Seeds

What exactly does the computer do when we sample?

As it normally doesn't have access to some magical source of true randomness, a concrete implementation can use a pseudo-random number generator, or PRNG. A pseudo-random generator produces a sequence of values from a mathematical algorithm and an initial state, commonly represented by a seed.

As a consequence, the sampling process can be probabilistic in its behavior while the concrete sequence of pseudo-random numbers is deterministic if the relevant state is fixed.

If the relevant conditions are identical — including the model parameters, input, generation settings, and random state or seed — the same pseudo-random sequence can be generated again.

So there are two different perspectives here.

From the perspective of the model or user:

Sampling behaves probabilistically.

From the perspective of a fixed implementation with a fixed pseudo-random state:

The concrete sequence can be deterministic and reproducible.

"Random" does not automatically mean "irreproducible." And "probabilistic" does not automatically mean "nondeterministic at every level of the system." The implementation matters.


Top-k and Top-p constrain the sampling space

Now the terminology starts to fall into place.

Top-k says: Only consider the (k) most likely tokens.

For example, with top-k = 3, only the three most likely tokens remain candidates for sampling: Paris, London, and Berlin.

Token Probability Top-k = 3
Paris 50%
London 25%
Berlin 15%
Madrid 7%
Rome 3%

Top-p works differently: It keeps the smallest set of tokens whose cumulative probability mass is at least (p), after sorting candidates by probability in descending order.

For example, with top-p = 0.8, we keep adding the most likely tokens until the cumulative probability reaches at least 80%.

Token Probability Cumulative probability Top-p = 0.8
Paris 50% 50%
London 25% 75%
Berlin 15% 90%
Madrid 7% 97%
Rome 3% 100%

Here, Paris + London gives us 75%, so we need Berlin to reach 90%.
Therefore, Paris, London, and Berlin remain candidates.

Top-k fixes the number of candidates. Top-p fixes the probability mass. Both can constrain the sampling space.


It's time to clean up the Terminology

I had started with a deceptively simple question:

Is an LLM deterministic or probabilistic?

By now, we understood several very different things:

  • the mathematical calculation, which can be deterministic
  • the probability distribution the model represents
  • the sampling process, which introduces variability
  • the pseudo-random generator that can itself be deterministic under a fixed state
  • the stochastic process that produced the model parameters during training
  • and numerical differences in the hardware and inference implementation

So "deterministic or probabilistic" is simply too broad a description.

Layer / aspect Character What is happening?
Mathematics / Forward Pass Deterministic Fixed parameters and input are processed through a fixed computational graph to produce logits and Softmax probabilities.
Language Modeling Probabilistic The model approximates a probability distribution over possible next tokens.
Temperature Deterministic transformation Temperature changes the logits before Softmax and therefore changes the resulting probability distribution.
Sampling Probabilistic behavior A token is selected according to the probability distribution.
Sampling implementation / PRNG Deterministic under fixed state A pseudo-random generator can produce a reproducible sequence when its state and relevant conditions are fixed.
Greedy decoding Deterministic The highest-probability token is selected instead of sampling.
Training Stochastic The model parameters are produced through a training process involving stochastic elements.
Inference Deterministic under fixed conditions Once the parameters are fixed, the forward pass can produce the same result under identical inference conditions.
Hardware / floating point Numerically variable Finite precision and parallel computation can introduce small numerical differences that may affect reproducibility.
Reproducibility Conditional Reproduction depends on model version, parameters, tokenizer, runtime, hardware, numerical conditions, decoding configuration, and random state where applicable.

Looking at it this way, the answer becomes much less binary: There isn't one single "probabilistic part" of an LLM but different properties at different levels. And that distinction is becoming much mor more useful than the original yes-or-no question.


And that's the whole path from logits to a token.

The model produces the scores. Softmax turns them into a probability distribution. Temperature reshapes it. Top-k and Top-p can constrain the candidates. And sampling selects what comes next. What initially sounded like one operation — the LLM generates the next token — is actually a chain of very different operations. Once you follow a single token through that chain, the process becomes much less mysterious. There is no single "generation" step. There is a sequence of mathematical transformations and a decoding decision at the end.

Top comments (0)