Code Architecture — Neural Turing Machines Notebook
Overview
The notebook implements a simplified Neural Turing Machine (NTM) with a differentiable memory bank and content-based addressing, trains it on the copy task, and visualises the read/write attention weightings over memory slots across timesteps.
Section-by-Section Breakdown
1. Setup & Imports
- Installs and imports
torch,torch.nn,numpy,matplotlib. - Sets random seed for reproducibility.
- Detects CUDA (Kaggle provides Tesla P100/T4 GPUs).
2. NTM Addressing Module (NTMAddressing)
The core differentiable addressing mechanism, implemented as a standalone module so it can be reused by any head.
Key functions:
cosine_similarity(k, M): Computes batched cosine similarity between key vector k and each row of memory M. Returns similarity scores of shape (batch, N).content_addressing(k, beta, M): Softmax over similarity scores scaled by key strength β. Returns weightings w_c of shape (batch, N).location_addressing(w_prev, w_c, g, s, gamma): Combines content and location-based addressing:- Interpolation: w_g = g · w_c + (1−g) · w_prev
- Shift: w̃ = circular_convolution(w_g, s) — convolution with shift weighting s
- Sharpen: w = w̃^γ / Σ w̃^γ
circular_convolution(w, s): Implements the shift as a circular convolution using a flattened approach. For shift range {-1, 0, +1}, s has 3 elements; the convolution rolls w by the appropriate amounts.
Data flow:
key k (batch, M) ──→ cosine_similarity ──→ scores (batch, N)
↓ × β, softmax
w_c (batch, N)
w_prev (batch, N) ──→ interpolation(g) ──→ w_g (batch, N)
↓ circular_conv(s)
w̃ (batch, N)
↓ sharpen(γ)
w (batch, N) [final weighting]
3. NTM Head (NTMHeadBase, NTMReadHead, NTMWriteHead)
NTMHeadBase: Abstract base class. Holds an LSTM controller (or linear layer for feedforward mode). Produces all addressing parameters via linear projections:- key k_t: (batch, M) — via
tanhactivation - key strength β_t: scalar — via
softplus+ 1 (ensures β ≥ 1) - interpolation gate g_t: scalar — via
sigmoid - shift weighting s_t: (batch, 3) — via
softmax(for shifts {-1, 0, +1}) - sharpening γ_t: scalar — via
softplus+ 1 (ensures γ ≥ 1)
- key k_t: (batch, M) — via
NTMReadHead: Adds a linear layer to produce the read vector r_t = Σ w_t(i) · M_t(i).NTMWriteHead: Adds linear layers for erase vector e_t (batch, M, viasigmoid) and add vector a_t (batch, M, viatanh).
Output shapes from controller:
- Controller hidden: (batch, controller_size)
- All head parameters: derived via separate Linear(controller_size, param_dim) layers
4. NTM Model (NeuralTuringMachine)
- Constructor: Takes
input_size,output_size,controller_size,memory_n(N),memory_m(M),num_heads,num_shifts. - Internal state:
memory(batch, N, M),read_vectors(batch, num_heads, M),prev_weightings(batch, num_heads, N). init_state(batch_size): Initialises memory to small random values, read vectors to zeros, weightings to uniform.forward(input):- Concatenate input with previous read vectors → controller input
- Run LSTM controller → hidden state
- For each head: compute addressing parameters → get weightings → read/write
- Update memory: erase then add (Equations 3, 4)
- Concatenate read vectors → output layer → output
- Return output and new state
Memory update (per write head):
M̃(i) = M(i) ⊙ (1 − w(i) · e) # erase
M(i) = M̃(i) + w(i) · a # add
where ⊙ is element-wise, w is (N,), e and a are (M,).
5. Copy Task Data Generator (copy_task_batch)
- Generates random binary sequences of shape (batch, seq_len, input_size).
- Input format: [seq_len vectors of 8 bits] + [delimiter flag (extra dim)] + [T_pad zeros]
- Target format: [T_pad zeros] + [seq_len copies of input vectors]
input_size = 8(8-bit vectors), plus 1 dimension for delimiter = 9 total input dims- Sequence lengths randomised between 1 and 20 during training.
Shapes:
- Input tensor: (batch, seq_len + 1 + seq_len, input_size + 1) — sequence + delimiter + output period
- Target tensor: same shape, with targets in the output period
6. Training Loop
- Loss: Binary cross-entropy (BCE) per sequence — the paper reports bits-per-sequence.
- Optimizer: RMSProp (or Adam as a modern alternative), learning rate 1e-4.
- Gradient clipping: Elementwise clip to (-10, 10) per the paper.
- Episode reset: Memory, controller hidden state, and read vectors are reset at the start of each sequence.
- Training loop: 10,000 episodes (reduced from paper’s ~100K for Kaggle runtime), batch size 1–4.
- Reporting: Loss printed every 1000 episodes; loss curve plotted at the end.
7. Evaluation & Generalisation Test
- After training, test on sequences of length 10 (in training range) and length 50 (out of training range).
- Compare NTM vs. a plain LSTM baseline (3-layer LSTM, 256 hidden units).
- Report bits-per-sequence for both.
8. Memory Visualisation
- Extract the read/write weightings (w_t) at each timestep during a copy task episode.
- Read head attention heatmap: Timesteps (y-axis) vs. memory locations (x-axis), colour = weighting intensity.
- Write head attention heatmap: Same layout for write weightings.
- Memory snapshot: Show the memory matrix M at key timesteps (start, mid-sequence, end).
- These visualisations reveal the learned algorithm: the write head sequentially fills memory slots, and the read head sequentially reads them back during the output phase.
Key Classes Summary
| Class | Responsibility |
|---|---|
NTMAddressing | Static methods for content + location addressing math |
NTMHeadBase | Base class producing addressing parameters from controller output |
NTMReadHead | Read head: computes read vector from weightings + memory |
NTMWriteHead | Write head: produces erase + add vectors for memory update |
NeuralTuringMachine | Full NTM: controller + memory + heads, forward step, state init |
NTMController | LSTM controller wrapper (or feedforward MLP) |
Deliberate Simplifications vs. Full Paper
- Single read/write head: The paper uses 1 head for copy, but we use 1 combined head for simplicity. The architecture supports multiple heads.
- Memory size: 128×20 in the paper; we use 32×10 or 64×10 to reduce training time on Kaggle.
- Controller size: 100 units in the paper (feedforward) or LSTM; we use 100 LSTM units.
- Training episodes: ~10K instead of ~100K+ for Kaggle’s 30-minute limit. The model still converges on shorter sequences.
- Location-based addressing: Included but simplified — shift range is {-1, 0, +1} (3 shifts) as in the paper.
- No repeat-copy or priority-sort tasks: Only the copy task is implemented as it’s the canonical NTM demo.
- LSTM baseline: A simple 1-layer LSTM rather than the paper’s 3-layer stacked LSTM.
- Batch processing: Sequences processed one at a time (batch_size=1) for clarity; the architecture supports batching.
Data Flow Summary
Input x_t (batch, input_size)
↓
[concat with prev read vectors] → (batch, input_size + num_heads*M)
↓
LSTM Controller → hidden h_t (batch, controller_size)
↓
├─→ Read Head: k,β,g,s,γ → addressing → w_read → r_t = Σ w(i)·M(i)
├─→ Write Head: k,β,g,s,γ,e,a → addressing → w_write → M = erase(M,w,e) + add(M,w,a)
└─→ Output Layer: [r_1, r_2, ...] → y_t (batch, output_size)