Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›Creating Own LLM›Positional Encoding and Its Importance

Core Concepts of Large Language Models

Positional Encoding and Its Importance

Positional encoding is a technique used to inject information about the order of tokens into a transformer-based language model, since the model's self-attention mechanism is inherently order-agnostic. It matters because natural language relies heavily on word order to convey meaning, and without positional encoding, the model would treat input sequences as unordered sets. You reach for positional encoding whenever you build or modify a transformer architecture, as it is a fundamental component for enabling the model to understand sequence structure.

Why Order Matters in Language Models

Language is fundamentally sequential. The meaning of a sentence like 'The cat chased the dog' is entirely different from 'The dog chased the cat,' even though both contain the same words. Traditional neural networks like RNNs or LSTMs process sequences step-by-step, inherently preserving order. However, transformers, which power modern large language models, use self-attention to process all tokens in parallel. This parallelization is a strength for efficiency but introduces a critical weakness: the model has no built-in way to distinguish the position of tokens. Without positional information, the transformer would treat the input as a bag of words, losing all sequential context. Positional encoding solves this by explicitly adding position-dependent signals to each token's embedding, allowing the model to retain and utilize order information while still benefiting from parallel processing.

# Example: Demonstrating the need for positional encoding
# Without positional encoding, two identical tokens in different positions are indistinguishable
import numpy as np

# Token embeddings (simplified for demonstration)
token_embedding = np.array([0.5, 0.3])  # Represents a single token
sequence = np.array([token_embedding, token_embedding])  # Two identical tokens

print("Token embeddings without positional encoding:")
print(sequence)
print("Both tokens are identical, so the model cannot distinguish their positions.")

How Positional Encoding Works: The Intuition

Positional encoding works by adding a unique, position-dependent vector to each token's embedding. The key idea is that these vectors should be deterministic and consistent, so the model can learn to associate specific patterns with specific positions. The most common approach uses sine and cosine functions of different frequencies. This choice is not arbitrary: sine and cosine functions are periodic, which allows the model to generalize to sequence lengths it hasn't seen during training. Additionally, the use of multiple frequencies enables the model to capture both short-range and long-range dependencies. For example, high-frequency components can encode fine-grained positional differences between nearby tokens, while low-frequency components can encode broader positional relationships across the entire sequence. The resulting positional encodings are added to the token embeddings, creating a combined representation that carries both semantic and positional information.

# Generating positional encodings using sine and cosine functions
import numpy as np
import matplotlib.pyplot as plt

def positional_encoding(position, d_model):
    # position: current position in the sequence
    # d_model: dimension of the model's embeddings
    angle_rates = 1 / np.power(10000, (2 * (np.arange(d_model) // 2)) / np.float32(d_model))
    angle_rads = position * angle_rates
    # Apply sine to even indices and cosine to odd indices
    pos_encoding = np.zeros(d_model)
    pos_encoding[0::2] = np.sin(angle_rads[0::2])
    pos_encoding[1::2] = np.cos(angle_rads[1::2])
    return pos_encoding

# Visualize positional encodings for the first 50 positions
d_model = 64  # Embedding dimension
pos_encodings = np.array([positional_encoding(pos, d_model) for pos in range(50)])

plt.figure(figsize=(10, 6))
plt.pcolormesh(pos_encodings.T, cmap='viridis')
plt.xlabel('Position in Sequence')
plt.ylabel('Embedding Dimension')
plt.colorbar(label='Encoding Value')
plt.title('Positional Encodings for First 50 Positions')
plt.show()
print("The plot shows how positional encodings vary across positions and embedding dimensions.")

Adding Positional Encodings to Token Embeddings

Once positional encodings are generated, they are added directly to the token embeddings before the input is fed into the transformer. This addition is element-wise, meaning each dimension of the positional encoding vector is added to the corresponding dimension of the token embedding vector. The result is a new vector that represents both the token's meaning and its position in the sequence. This combined representation is what the transformer's self-attention mechanism processes. The addition operation is crucial because it preserves the original semantic information while injecting positional context. During training, the model learns to interpret these combined vectors, effectively learning how to use positional information to improve its predictions. The addition is simple but powerful, as it allows the model to disentangle semantic and positional information during attention computation.

# Adding positional encodings to token embeddings
import numpy as np

def generate_token_embeddings(vocab_size, d_model):
    # Randomly initialize token embeddings for demonstration
    return np.random.randn(vocab_size, d_model)

def add_positional_encodings(token_embeddings, max_seq_len, d_model):
    # Generate positional encodings for all positions up to max_seq_len
    pos_encodings = np.array([positional_encoding(pos, d_model) for pos in range(max_seq_len)])
    # Add positional encodings to token embeddings (broadcasting)
    return token_embeddings + pos_encodings[:token_embeddings.shape[0]]

# Example usage
vocab_size = 100  # Size of the vocabulary
d_model = 64  # Embedding dimension
max_seq_len = 50  # Maximum sequence length

token_embeddings = generate_token_embeddings(vocab_size, d_model)
input_with_pos_encoding = add_positional_encodings(token_embeddings, max_seq_len, d_model)

print("Shape of token embeddings:", token_embeddings.shape)
print("Shape of input with positional encodings:", input_with_pos_encoding.shape)
print("Positional encodings successfully added to token embeddings.")

Why Sine and Cosine Functions Are Effective

The choice of sine and cosine functions for positional encoding is rooted in their mathematical properties. First, they are periodic, which means the model can extrapolate to sequence lengths longer than those seen during training. For example, if the model is trained on sequences up to length 512, it can still generate meaningful positional encodings for sequences of length 1024 by leveraging the periodicity of the functions. Second, sine and cosine functions of different frequencies allow the model to capture both local and global positional relationships. High-frequency components (short wavelengths) encode fine-grained positional differences, while low-frequency components (long wavelengths) encode broader positional trends. Third, the use of both sine and cosine for alternating dimensions ensures that the positional encodings are unique for each position, even for very long sequences. This uniqueness is critical for the model to distinguish between positions accurately. Finally, the functions are deterministic and consistent, which means the same position always receives the same encoding, providing stability during training.

# Demonstrating the uniqueness and periodicity of sine/cosine positional encodings
import numpy as np

def positional_encoding_uniqueness(d_model, max_pos):
    # Generate positional encodings for a range of positions
    pos_encodings = np.array([positional_encoding(pos, d_model) for pos in range(max_pos)])
    # Check for uniqueness by computing pairwise distances
    distances = np.zeros((max_pos, max_pos))
    for i in range(max_pos):
        for j in range(max_pos):
            distances[i, j] = np.linalg.norm(pos_encodings[i] - pos_encodings[j])
    return distances

# Check uniqueness for the first 100 positions
d_model = 64
max_pos = 100
distances = positional_encoding_uniqueness(d_model, max_pos)

# Verify that all positions have unique encodings (distance > 0 for i != j)
unique = np.all(distances[np.triu_indices(max_pos, k=1)] > 0)
print("All positional encodings are unique:", unique)
print("Example distances between positions 0 and others:", distances[0, :10])

Alternatives and Trade-offs in Positional Encoding

While sine and cosine positional encodings are the most common, other approaches exist, each with trade-offs. Learned positional embeddings are an alternative where the model learns position-specific vectors during training. This approach can adapt to the specific data distribution but may struggle to generalize to sequence lengths not seen during training. Relative positional encodings, such as those used in Transformer-XL, encode the distance between tokens rather than their absolute positions. This can improve performance on tasks requiring long-range dependencies but adds complexity to the attention mechanism. Another approach is to use rotary positional embeddings (RoPE), which apply a rotation to the token embeddings based on their position. RoPE preserves the relative positional information while being more efficient for long sequences. The choice of positional encoding depends on the specific requirements of the task. For example, learned embeddings may work well for fixed-length sequences, while sine/cosine encodings are better for variable-length sequences. Understanding these trade-offs allows you to select the most appropriate method for your model's needs.

# Comparing learned positional embeddings vs. sine/cosine encodings
import numpy as np

# Learned positional embeddings (simplified)
class LearnedPositionalEmbeddings:
    def __init__(self, max_seq_len, d_model):
        self.max_seq_len = max_seq_len
        self.d_model = d_model
        self.embeddings = np.random.randn(max_seq_len, d_model)  # Random initialization
    
    def __call__(self, positions):
        return self.embeddings[positions]

# Sine/cosine positional encodings (reused from earlier)
def positional_encoding(position, d_model):
    angle_rates = 1 / np.power(10000, (2 * (np.arange(d_model) // 2)) / np.float32(d_model))
    angle_rads = position * angle_rates
    pos_encoding = np.zeros(d_model)
    pos_encoding[0::2] = np.sin(angle_rads[0::2])
    pos_encoding[1::2] = np.cos(angle_rads[1::2])
    return pos_encoding

# Compare the two methods
max_seq_len = 50
d_model = 64
positions = np.arange(max_seq_len)

learned_pe = LearnedPositionalEmbeddings(max_seq_len, d_model)
learned_embeddings = learned_pe(positions)
sine_cosine_embeddings = np.array([positional_encoding(pos, d_model) for pos in positions])

print("Shape of learned positional embeddings:", learned_embeddings.shape)
print("Shape of sine/cosine positional encodings:", sine_cosine_embeddings.shape)
print("Learned embeddings are random and must be trained, while sine/cosine are deterministic.")

Key points

  • Positional encoding is necessary because transformers process all tokens in parallel, losing the inherent order of the input sequence.
  • The most common positional encoding method uses sine and cosine functions of varying frequencies to create unique, position-dependent vectors.
  • Positional encodings are added element-wise to token embeddings, combining semantic and positional information into a single representation.
  • Sine and cosine functions are effective because they are periodic, allowing the model to generalize to unseen sequence lengths.
  • High-frequency components in positional encodings capture fine-grained positional differences, while low-frequency components capture broader trends.
  • Learned positional embeddings are an alternative but may not generalize as well to sequence lengths not seen during training.
  • Relative positional encodings and rotary positional embeddings (RoPE) are advanced methods that encode positional relationships more flexibly.
  • The choice of positional encoding method depends on the task requirements, such as sequence length variability and the need for long-range dependencies.

Common mistakes

  • Mistake: Assuming positional encoding is just adding a simple counter to embeddings. Why it's wrong: This fails to capture relative positions and lacks the high-frequency variations needed for the model to distinguish nearby tokens. Fix: Use sinusoidal functions or learned embeddings to encode position with rich, interpretable patterns.
  • Mistake: Using the same positional encoding for all sequence lengths. Why it's wrong: Fixed-length encodings can't generalize to longer sequences during inference, causing misalignment. Fix: Use a positional encoding scheme that scales with sequence length, like sinusoidal functions or dynamic learned embeddings.
  • Mistake: Ignoring the importance of positional encoding in decoder-only models. Why it's wrong: Without positional information, the model can't distinguish the order of tokens, leading to poor performance on tasks requiring sequential understanding. Fix: Always include positional encoding, even in decoder-only architectures.
  • Mistake: Treating positional encoding as a hyperparameter to tune aggressively. Why it's wrong: Over-optimizing positional encoding can lead to overfitting or instability during training. Fix: Stick to proven methods like sinusoidal encoding or learned embeddings, and focus tuning efforts on other parts of the model.
  • Mistake: Adding positional encoding directly to token embeddings without scaling. Why it's wrong: Token embeddings and positional encodings may have different magnitudes, causing one to dominate the other. Fix: Normalize or scale positional encodings to match the magnitude of token embeddings before addition.

Interview questions

What is positional encoding in the context of building your own large language model?

Positional encoding is a technique used to inject information about the position of tokens in a sequence into the model's input embeddings. In transformers, which are the backbone of most large language models, the self-attention mechanism is permutation-invariant, meaning it doesn't inherently understand the order of tokens. Without positional encoding, the model would treat the sequence 'I love coding' the same as 'coding love I'. Positional encoding solves this by adding a unique vector to each token's embedding based on its position, allowing the model to learn and utilize the sequential nature of the data. This is crucial for tasks like language modeling where word order directly impacts meaning.

Why can't we just use simple integer positions like 1, 2, 3 as positional encodings?

Using simple integer positions like 1, 2, 3 directly as positional encodings is problematic for several reasons. First, integers don't naturally fit into the continuous vector space of embeddings, making it hard for the model to learn meaningful relationships. Second, the scale of these integers can grow arbitrarily large for long sequences, which can cause numerical instability during training. Most importantly, simple integers don't capture the relative positional relationships between tokens effectively. The transformer architecture benefits from positional encodings that can represent both absolute and relative positions in a way that's learnable and generalizable. This is why we use more sophisticated methods like sinusoidal functions or learned embeddings that create unique, bounded vectors for each position.

Explain how sinusoidal positional encoding works with a code example.

Sinusoidal positional encoding, introduced in the original transformer paper, uses sine and cosine functions of different frequencies to create unique positional vectors. The key idea is that each position gets a vector where each dimension alternates between sine and cosine functions with wavelengths forming a geometric progression. Here's a simplified implementation in code: ```python def positional_encoding(position, d_model): pe = np.zeros((position, d_model)) for pos in range(position): for i in range(0, d_model, 2): pe[pos, i] = np.sin(pos / (10000 ** (2 * i / d_model))) pe[pos, i + 1] = np.cos(pos / (10000 ** (2 * i / d_model))) return pe ``` This creates a pattern where nearby positions have similar encodings, allowing the model to learn attention based on relative positions. The alternating sine and cosine functions ensure that each position gets a unique encoding while maintaining consistent patterns across different sequence lengths.

What are the advantages of learned positional embeddings compared to sinusoidal positional encoding?

Learned positional embeddings offer several advantages over fixed sinusoidal encodings. First, they can adapt to the specific patterns in your training data through backpropagation, potentially capturing more nuanced positional relationships. Second, they might learn more efficient representations for your particular task, as the model isn't constrained by the fixed sinusoidal pattern. Third, learned embeddings can potentially handle longer sequences better by focusing on the most relevant positional information. However, they come with trade-offs: they require more parameters, might overfit to your training data, and don't generalize as well to sequence lengths not seen during training. Sinusoidal encodings, in contrast, can handle arbitrary sequence lengths without additional training and have no learnable parameters, making them more memory efficient and better for transfer learning scenarios.

How does positional encoding interact with the attention mechanism in transformers?

Positional encoding interacts with the attention mechanism in transformers by providing the necessary sequential information that the attention mechanism lacks inherently. The attention mechanism computes relationships between tokens using their embeddings, but without positional information, it can't distinguish between different orderings of the same tokens. When we add positional encodings to the input embeddings, each token's representation becomes position-aware. The attention scores are then computed based on these enhanced representations, allowing the model to learn how much attention to pay to other tokens based on both their content and their relative positions. This interaction is crucial because it enables the model to learn patterns like 'the word that comes after X is often Y' or 'verbs typically follow subjects'. The positional information gets incorporated into the attention weights through the query-key dot products, where the model learns to associate certain positional patterns with specific attention distributions.

Implement a custom positional encoding that combines both sinusoidal and learned approaches. Explain your design choices.

Here's a custom positional encoding implementation that combines both approaches: ```python class HybridPositionalEncoding(nn.Module): def __init__(self, d_model, max_len=5000): super().__init__() self.d_model = d_model self.sinusoidal_pe = self._sinusoidal_pe(max_len, d_model) self.learned_pe = nn.Embedding(max_len, d_model) def _sinusoidal_pe(self, max_len, d_model): pe = torch.zeros(max_len, d_model) position = torch.arange(0, max_len).unsqueeze(1) div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model)) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) return pe.unsqueeze(0) def forward(self, x): # x shape: (batch_size, seq_len, d_model) seq_len = x.size(1) sin_pe = self.sinusoidal_pe[:, :seq_len].to(x.device) learn_pe = self.learned_pe(torch.arange(seq_len, device=x.device)) return x + sin_pe + learn_pe ``` My design choices were: 1) Using sinusoidal encoding as a base provides good generalization to unseen sequence lengths and helps with relative position understanding. 2) Adding learned embeddings allows the model to adapt to specific patterns in the training data. 3) The combination is additive because we want both signals to contribute to the final representation. 4) The learned embeddings are initialized randomly and trained with the rest of the model. 5) The sinusoidal component is pre-computed for efficiency. This hybrid approach aims to get the best of both worlds: the generalization of sinusoidal encoding and the adaptability of learned embeddings, which is particularly valuable when building your own LLM where you might encounter diverse sequence patterns.

All Creating Own LLM interview questions →

Check yourself

1. Why is positional encoding necessary in transformer-based LLMs?

  • A.It helps the model memorize token frequencies for better recall.
  • B.It provides the model with information about the order of tokens in the sequence, which is otherwise lost in self-attention.
  • C.It reduces the computational cost of self-attention by limiting the sequence length.
  • D.It replaces the need for token embeddings by encoding all information in the position.
Show answer

B. It provides the model with information about the order of tokens in the sequence, which is otherwise lost in self-attention.
The correct answer is that positional encoding provides order information. Self-attention mechanisms are permutation-invariant, meaning they don't inherently understand token order. Positional encoding injects this missing information. The other options are incorrect: positional encoding doesn't help with memorization, doesn't reduce computational cost, and doesn't replace token embeddings.

2. What is a key advantage of using sinusoidal functions for positional encoding in LLMs?

  • A.They allow the model to memorize exact positions for perfect recall.
  • B.They enable the model to generalize to sequence lengths not seen during training by leveraging relative position patterns.
  • C.They reduce the number of parameters in the model by eliminating the need for learned embeddings.
  • D.They ensure the model can only process fixed-length sequences, improving stability.
Show answer

B. They enable the model to generalize to sequence lengths not seen during training by leveraging relative position patterns.
The correct answer is that sinusoidal functions enable generalization to unseen sequence lengths. The periodic nature of sine and cosine functions allows the model to infer relative positions even for sequences longer than those in the training data. The other options are incorrect: sinusoidal functions don't enable memorization, don't eliminate learned embeddings, and don't restrict the model to fixed-length sequences.

3. How does positional encoding interact with token embeddings in a transformer model?

  • A.Positional encodings are concatenated with token embeddings before being fed into the self-attention layers.
  • B.Positional encodings are added to token embeddings element-wise, combining order and semantic information.
  • C.Positional encodings replace token embeddings entirely, as they contain all necessary information.
  • D.Positional encodings are multiplied with token embeddings to scale their importance dynamically.
Show answer

B. Positional encodings are added to token embeddings element-wise, combining order and semantic information.
The correct answer is that positional encodings are added to token embeddings element-wise. This combines the semantic information from token embeddings with the positional information. The other options are incorrect: positional encodings are not concatenated, they don't replace token embeddings, and they are not multiplied with them.

4. What happens if you remove positional encoding from a transformer-based LLM?

  • A.The model will still perform well because self-attention inherently understands token order.
  • B.The model will fail to distinguish the order of tokens, leading to poor performance on tasks requiring sequential understanding.
  • C.The model will become more efficient because it no longer needs to process positional information.
  • D.The model will automatically learn positional information from the data without explicit encoding.
Show answer

B. The model will fail to distinguish the order of tokens, leading to poor performance on tasks requiring sequential understanding.
The correct answer is that the model will fail to distinguish token order. Self-attention is permutation-invariant, so without positional encoding, the model cannot understand the sequence of tokens. The other options are incorrect: self-attention does not inherently understand order, removing positional encoding doesn't improve efficiency, and the model cannot learn positional information without explicit encoding.

5. Why might a learned positional embedding be preferred over sinusoidal positional encoding in some LLMs?

  • A.Learned embeddings are simpler to implement and require no additional parameters.
  • B.Learned embeddings can adapt to the specific patterns in the training data, potentially improving performance.
  • C.Learned embeddings guarantee generalization to sequences longer than those seen during training.
  • D.Learned embeddings eliminate the need for token embeddings, reducing the model's complexity.
Show answer

B. Learned embeddings can adapt to the specific patterns in the training data, potentially improving performance.
The correct answer is that learned embeddings can adapt to the training data's specific patterns. Unlike sinusoidal functions, learned embeddings are optimized during training and can capture dataset-specific positional patterns. The other options are incorrect: learned embeddings add parameters, don't guarantee generalization to longer sequences, and don't replace token embeddings.

Take the full Creating Own LLM quiz →

← PreviousSelf-Attention Mechanism and Multi-Head AttentionNext →Training Objectives: Language Modeling, Masked Language Modeling, and Next Sentence Prediction

Creating Own LLM

36 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app