Memory Augmentation › Memory Network Fundamentals
Language AI HandbookAll chapters Memory Augmentation › Memory Network FundamentalsOne section · Sections in this chapter3 / 12 ↓
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
Imagine you're reading a 500-page novel and someone asks you, "What color was the protagonist's childhood home?" You don't re-read the entire book. Instead, your mind somehow retrieves that specific detail from storage. Memory networks attempt to give neural networks this same capability: the ability to write information to an external store during processing and retrieve it on demand.
The Key Insight: Separate Storage from Computation
The fundamental innovation of memory networks is decoupling what the model remembers from how it processes information. In a standard neural network, "memory" exists implicitly in the hidden states, and information must survive through layer after layer of transformation. In a memory network, we provide an explicit storage mechanism that the model can query independently.
This separation immediately suggests two operations:
- Writing: The model encounters important information and stores it
- Reading: The model needs information and retrieves it from storage
But how should storage be organized? And how does the model know where to look when reading?
Content-Based Addressing: Finding Information by What It Contains
Consider how you might organize a library. You could assign each book a numbered shelf (positional addressing), or you could organize books by topic and keywords (content-based addressing). A positional system requires remembering exactly where you put each book. A content-based system lets you find books by describing what you're looking for.
Memory networks use content-based addressing. Each memory entry consists of two parts:
- A key : describes what the entry contains (like a book's title and keywords)
- A value : the actual stored content (like the book itself)
When the model wants to retrieve information, it provides a query describing what it's looking for. The memory system compares this query against all stored keys and returns the values whose keys match best.
The Mathematics of Matching: Similarity and Softmax
How do we measure whether a query matches a key? The most natural choice is the dot product. Given a query vector and a key vector , their similarity is:
where:
- is the query vector with dimension
- is the key for memory slot
- is the scalar similarity score
This score tells us how well the query matches each key. Larger positive values indicate better matches. But we need to convert these raw scores into a probability distribution, a set of weights that sum to 1, telling us how much to trust each memory entry.
The softmax function accomplishes this. For memory slots with similarity scores :
The exponential ensures all weights are positive (since for all ). The denominator normalizes so that . The weight can be interpreted as "the probability that memory slot contains what we're looking for."
Scaling for Stable Gradients
In practice, we add one refinement. When the dimension is large, dot products can become very large in magnitude. This pushes softmax outputs toward extreme values (nearly 0 or nearly 1), which causes gradient problems during training. The solution is scaled dot-product attention:
Why ? If each component of and has variance 1, then the dot product has variance (since we're summing independent products). Dividing by restores unit variance, keeping the scores in a well-behaved range regardless of dimension.
Retrieving the Answer: Weighted Combination
Once we have attention weights over all memory slots, we retrieve information by taking a weighted sum of the stored values:
where:
- is the retrieved vector (output)
- is the value stored in memory slot
- is the attention weight computed above
This weighted sum is fully differentiable, meaning gradients can flow back through the retrieval operation during training. The model learns which keys to associate with which values by receiving gradient signals about whether the retrieved information helped with the task.
Putting It Together: The Complete Memory Read
The full memory read operation combines all these steps:
where:
- is the matrix of all keys stacked as rows
- is the matrix of all values stacked as rows
- computes all similarity scores in parallel
This is identical to the attention mechanism used in transformers. The difference is conceptual: in standard self-attention, keys and values come from the input sequence; in a memory network, they come from an external storage that persists across inputs.
Implementation: A Basic Memory Network
Let's translate these mathematical concepts into working code. The implementation directly mirrors the formulas above:
import torch
import torch.nn as nn
import torch.nn.functional as F
class BasicMemoryNetwork(nn.Module):
"""
A basic memory network demonstrating core memory operations.
The memory stores key-value pairs. Reading uses content-based
attention over keys to retrieve values. Writing adds new entries.
"""
def __init__(self, memory_size, key_dim, value_dim):
super().__init__()
self.memory_size = memory_size
self.key_dim = key_dim
self.value_dim = value_dim
# Initialize memory as learnable parameters
# K ∈ R^(n × d): matrix of n keys, each with dimension d
self.memory_keys = nn.Parameter(torch.randn(memory_size, key_dim) * 0.1)
# V ∈ R^(n × d_v): matrix of n values, each with dimension d_v
self.memory_values = nn.Parameter(
torch.randn(memory_size, value_dim) * 0.1
)
# Query projection: transforms raw query into key space
self.query_proj = nn.Linear(key_dim, key_dim)
def read(self, query):
"""
Read from memory using content-based attention.
This implements: r = Σ_i softmax(q·K^T / √d)_i · v_i
Args:
query: (batch, key_dim) - what we're looking for
Returns:
retrieved: (batch, value_dim) - weighted sum of memory values
attention: (batch, memory_size) - attention weights α over memory
"""
# Project query into key space
query = self.query_proj(query)
# Step 1: Compute similarity scores s_i = q · k_i for all keys
# Matrix form: scores = q @ K^T gives all n scores at once
scores = torch.matmul(query, self.memory_keys.T) # (batch, n)
# Step 2: Scale by √d for numerical stability
scores = scores / np.sqrt(self.key_dim)
# Step 3: Convert to attention weights α_i via softmax
attention = F.softmax(scores, dim=-1) # (batch, n), sums to 1
# Step 4: Retrieve weighted sum r = Σ_i α_i · v_i
retrieved = torch.matmul(attention, self.memory_values) # (batch, d_v)
return retrieved, attention
def forward(self, query):
return self.read(query)# Demonstrate basic memory operations
memory_net = BasicMemoryNetwork(memory_size=64, key_dim=128, value_dim=256)
# Create a batch of queries
batch_size = 4
queries = torch.randn(batch_size, 128)
# Read from memory
retrieved_values, attention_weights = memory_net(queries)Basic Memory Network Demonstration: Memory size: 64 slots Key dimension: 128 Value dimension: 256 Query batch shape: torch.Size([4, 128]) Retrieved values shape: torch.Size([4, 256]) Attention weights shape: torch.Size([4, 64]) Attention statistics (first query): Top-5 slots: [57, 21, 46, 60, 7] Top-5 weights: ['0.019', '0.018', '0.017', '0.017', '0.017'] Sum of top-5: 0.089
The output reveals how attention weights distribute across memory slots. Even with random initialization (where keys and queries have no learned relationship), the softmax produces a sparse pattern. The top 5 slots capture a clear fraction of total attention mass. This shows the inherent focusing behavior of the exponential function. After training, these weights would reflect content similarity, because the model would learn to store related information under similar keys.
