Repetition Penalties › Combining Penalties with Sampling Strategies

Michael BrenndoerferUpdated August 1, 20254 min read
Language AI HandbookAll chapters
Part 28 of 67GPT Architecture
Chapter 205Repetition Penalties › Combining Penalties with Sampling Strategies
Reading section 7 of 13 · Combining Penalties with Sampling Strategies · 4 minSection 7 of 13 · 4 min

Book contents

Section 7 of 13 · 4 min read

Repetition Penalties › Combining Penalties with Sampling StrategiesOne section · Sections in this chapter7 / 13 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

In practice, repetition penalties work alongside temperature, top-k, and nucleus sampling. The interactions between these components matter for understanding and tuning generation quality. Each technique operates at a different stage of the logit-to-token pipeline, and applying them in the correct order is essential for the intended behavior.

The typical processing order is well-defined and follows a natural logic:

  1. Get raw logits from the model's final layer
  2. Apply repetition/frequency/presence penalties to adjust logit values for previously used tokens
  3. Apply temperature scaling to control distribution sharpness (dividing logits by temperature)
  4. Apply top-k or nucleus truncation to limit candidates to the most promising tokens
  5. Sample from the resulting distribution

This order is not arbitrary. Repetition penalties should be applied before temperature because the penalty magnitudes are calibrated for logit-scale values. If you apply temperature first (which rescales all logits), the same penalty value would have a different effective strength depending on the temperature used. Applying penalties first ensures consistent behavior regardless of temperature.

Similarly, sampling truncation (top-k or nucleus) should come after penalties because the penalties change which tokens are most competitive. If you truncated first, then applied penalties, you might inadvertently keep penalized tokens in the candidate set while excluding better alternatives. Applying penalties before truncation ensures the truncation operates on the post-penalty distribution, correctly identifying the best candidates after repetition is accounted for.

In[26]:
Code
def generate_with_combined_strategies(
    model,
    tokenizer,
    prompt,
    repetition_penalty=1.0,
    frequency_penalty=0.0,
    presence_penalty=0.0,
    temperature=1.0,
    top_p=0.9,
    max_tokens=50,
):
    """
    Generate text combining multiple penalty types with sampling strategies.
    """
    input_ids = tokenizer.encode(prompt, return_tensors="pt")
    generated_ids = input_ids[0].tolist()
    prompt_length = len(generated_ids)
    token_counts = {}

    for _ in range(max_tokens):
        with torch.no_grad():
            outputs = model(torch.tensor([generated_ids]))
            logits = outputs.logits[0, -1, :].clone()

        # Track tokens generated so far (excluding prompt)
        generated_so_far = generated_ids[prompt_length:]
        generated_set = set(generated_so_far)

        # Count occurrences
        for token_id in generated_so_far:
            token_counts[token_id] = token_counts.get(token_id, 0) + 1

        # Apply repetition penalty
        if repetition_penalty != 1.0:
            for token_id in generated_set:
                if logits[token_id] > 0:
                    logits[token_id] = logits[token_id] / repetition_penalty
                else:
                    logits[token_id] = logits[token_id] * repetition_penalty

        # Apply frequency penalty
        if frequency_penalty > 0:
            for token_id, count in token_counts.items():
                logits[token_id] = logits[token_id] - frequency_penalty * count

        # Apply presence penalty
        if presence_penalty > 0:
            for token_id in generated_set:
                logits[token_id] = logits[token_id] - presence_penalty

        # Apply temperature
        logits = logits / temperature

        # Apply nucleus sampling
        sorted_logits, sorted_indices = torch.sort(logits, descending=True)
        cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=0), dim=0)
        sorted_indices_to_remove = cumulative_probs > top_p
        sorted_indices_to_remove[1:] = sorted_indices_to_remove[:-1].clone()
        sorted_indices_to_remove[0] = False
        indices_to_remove = sorted_indices_to_remove.scatter(
            0, sorted_indices, sorted_indices_to_remove
        )
        logits[indices_to_remove] = float("-inf")

        # Sample
        probs = F.softmax(logits, dim=0)
        next_token = torch.multinomial(probs, num_samples=1).item()
        generated_ids.append(next_token)

        if next_token == tokenizer.eos_token_id:
            break

    return tokenizer.decode(generated_ids, skip_special_tokens=True)
Out[27]:
Console
Prompt: 'Here are some ideas for improving productivity:'

======================================================================

No penalties:
Here are some ideas for improving productivity:

Show Me The Details

First off, let's talk about how much time I spend on Twitter. I know you're not the only one who's tired of being told that you shouldn't spend more time on Twitter than you should. I know that I want to see my tweets read more and more frequently.

This is something
----------------------------------------------------------------------

Repetition only (θ=1.2):
Here are some ideas for improving productivity:
"Don't make a bunch of extra widgets just to get stuff done. Put them on top and do it well." – Gary Dewey, YouTube Engineer (not long ago). Stop making some arbitrary split-screen widget with no taskbar or multitasking effects that makes your life feel like shit for hours at the least! New features: V
----------------------------------------------------------------------

Frequency only (α=0.5):
Here are some ideas for improving productivity:

Show Me The Details: Show me what you need to get done at work. Make it very clear where the project is being held, and how productive that piece of information will be for a change in your life or career! Focus on one thing per task group; focus more often than not (with less distraction) On each separate activity specific
----------------------------------------------------------------------

Presence only (β=0.8):
Here are some ideas for improving productivity:

Show Me The Details: Show me what you need to get done at work. Make it very clear where the project is being done, where you're being done and where you're going. Show people the value that you have created. Show people the benefits of your work. Show them how to spend their money.

This all comes
----------------------------------------------------------------------

Combined (θ=1.1, α=0.3):
Here are some ideas for improving productivity:
"Don't make a bunch of extra widgets just to get stuff done. Put them on top and do it well." – Gary Dewey, YouTube Engineer (not long ago). Stop making some arbitrary split-screen widget with no taskbar or multitasking effects that makes your life feel like shit for hours at the least! New features: V
----------------------------------------------------------------------

Comparing the outputs reveals how each penalty type shapes generation. Without penalties, the model may fall into repetitive patterns. Repetition penalty alone provides uniform discouragement of repeated tokens. Frequency penalty creates graduated pressure that builds as tokens accumulate, while presence penalty encourages the model to continuously introduce new vocabulary. The combined approach balances these effects, often producing the most natural-sounding output by gently discouraging repetition without forcing unnatural word choices.

You can also combine penalties with n-gram blocking for a hybrid approach: the probabilistic penalties soften the probability distribution to make repetition less attractive, while n-gram blocking provides a hard guarantee against the most egregious exact repetitions. This combination is particularly effective for tasks like abstractive summarization, where you want rich vocabulary diversity but also need to ensure the summary does not copy entire sentences from the source document.