Code Architecture — Deep Speech
Overview
The notebook implements a from-scratch Deep Speech model: a 5-layer RNN trained with CTC loss on audio spectrograms, producing character-level transcriptions. The implementation uses PyTorch and a synthetic toy dataset (generated programmatically) so it runs entirely self-contained on Kaggle/Colab without downloading large speech corpora.
Section-by-Section Breakdown
1. Imports and Setup
torch,torch.nn,torchaudiofor model and audio processinglibrosafor spectrogram computation (log filter banks)numpy,matplotlibfor data handling and visualization- Sets device (CUDA if available), random seeds for reproducibility
2. Character Vocabulary
- Alphabet: 29 characters — a–z (26), space, apostrophe, blank (CTC blank token)
char_to_idxandidx_to_charmappings- Blank token is index 0 (standard for PyTorch CTC)
3. Synthetic Speech Data Generation
- Deliberate simplification: Instead of using real speech corpora (Switchboard/Fisher require LDC access), we synthesize simple “audio” signals that encode character sequences as tone patterns
- Each “utterance” is a sequence of tones where each character maps to a distinct frequency pattern over a fixed number of spectrogram frames
- This gives us labeled (audio, text) pairs for training without external data downloads
generate_utterance(text): converts text → spectrogram framesSyntheticSpeechDataset: PyTorch Dataset wrapping generated utterances- Each character gets N frames of a unique frequency signature; this is learnable by the RNN but deliberately simple
4. Spectrogram Features
- Feature extraction: Compute log filter bank features from the synthetic audio
- For the toy implementation, features are generated directly as spectrogram-like vectors
- Feature dimension: 80 (matching paper’s 80 filter banks for Switchboard)
- Normalization: per-utterance mean subtraction and variance normalization
5. Data Loading and Batching
- Collate function handles variable-length sequences: sorts by length, pads with zeros
- Returns
(padded_spectrograms, input_lengths, target_labels, target_lengths) - Batch size: 16–32 (small for toy dataset)
6. Deep Speech Model (DeepSpeechModel)
- Layer 1 (Feed-forward with context): Conv1d that processes ±9 frames of context. Implemented as a 1D convolution with kernel_size=19 over the feature dimension, or equivalently a linear layer on concatenated context frames. Output: 256 hidden units.
- Layers 2–3 (Feed-forward): Linear layers with clipped ReLU, 256 units each
- Layer 4 (Bidirectional RNN):
nn.LSTMornn.RNNwith bidirectional=True, hidden_size=256. Forward and backward hidden states are summed (not concatenated) per the paper. - Layer 5 (Feed-forward): Linear layer, 256 units
- Output layer: Linear → softmax over 29 characters
- Clipped ReLU: Custom activation
g(z) = min(max(0, z), 20)applied to all hidden layers - Dropout: 5–10% on feed-forward layers
7. CTC Loss
- Uses
torch.nn.CTCLoss(blank=0, reduction='mean', zero_infinity=True) - Inputs: log-probabilities from the model (T, B, C), target labels, input lengths, target lengths
- CTC automatically handles the alignment between variable-length audio frames and character sequences
8. Training Loop
- Optimizer: Nesterov Accelerated Gradient (SGD with momentum=0.99, nesterov=True)
- Learning rate schedule: Anneal by constant factor after each epoch
- Epochs: 30 (small dataset converges quickly)
- Gradient clipping: Max norm 5.0 to prevent exploding gradients
- Tracks and plots training loss per epoch
9. Greedy CTC Decoder
greedy_ctc_decode(log_probs): Takes argmax at each timestep, collapses repeated characters, removes blank tokens- Returns decoded character string
10. Beam Search Decoder (Simplified)
beam_search_decode(log_probs, beam_width=100): Implements a basic beam search over CTC output probabilities- Without language model (simplified vs paper’s full N-gram beam search with α/β tuning)
- Returns top beam decoded string
11. Evaluation
- Computes Character Error Rate (CER) and Word Error Rate (WER) on test set
- CER = Levenshtein distance at character level / reference length
- WER = Levenshtein distance at word level / reference length
- Shows sample predictions vs ground truth
12. Visualization
- Plots training loss curve
- Displays sample spectrograms
- Shows prediction examples with CER/WER
Key Functions/Classes
| Component | Purpose |
|---|---|
DeepSpeechModel | 5-layer RNN: 3 FF + 1 BiRNN + 1 FF + softmax |
clipped_relu(z, max_val=20) | Clipped ReLU activation g(z) = min(max(0,z), 20) |
SyntheticSpeechDataset | Generates toy (spectrogram, text) pairs |
collate_fn(batch) | Pads variable-length sequences, returns batched tensors |
greedy_ctc_decode(log_probs) | Argmax + collapse repeats + remove blanks |
beam_search_decode(log_probs, beam_width) | Simplified beam search CTC decoding |
levenshtein(a, b) | Edit distance for CER/WER computation |
cer_rate(pred, ref) | Character Error Rate |
wer_rate(pred, ref) | Word Error Rate |
Data Flow / Shapes
Audio text → generate_utterance → spectrogram [T, 80]
↓
Batch collation → padded batch [B, T_max, 80] + lengths
↓
Context conv (Layer 1) → [B, T, 256]
↓
FF Layer 2 → [B, T, 256]
↓
FF Layer 3 → [B, T, 256]
↓
BiRNN Layer 4 → [B, T, 512] → sum directions → [B, T, 256]
↓
FF Layer 5 → [B, T, 256]
↓
Output Linear → [B, T, 29] → log_softmax → [T, B, 29]
↓
CTC Loss(targets, input_lengths, target_lengths) → scalar
↓
Greedy/Beam decode → predicted text string
Deliberate Simplifications vs Full Paper
- Synthetic data: The paper uses 5000+ hours of real speech (Switchboard, Fisher, Baidu internal). We generate toy audio programmatically — no external downloads needed.
- Model size: Paper uses 2048–2560 hidden units per layer; we use 256 for fast training on GPU within the 30-minute Kaggle limit.
- No language model: The paper integrates a 4–5 gram KenLM language model via beam search. We use plain CTC greedy/beam decoding without external LM.
- No multi-GPU: The paper uses data + model parallelism across GPUs. We train on a single GPU.
- No data synthesis (noise): The paper creates noisy training data by superimposing noise tracks. Our toy dataset is clean.
- LSTM instead of vanilla RNN: Paper uses simple ReLU recurrent units; we use LSTM for training stability in the toy setting (note: paper deliberately avoids LSTM for GPU efficiency, but our toy model benefits from LSTM’s gating).
- No striding: Paper uses stride-2 in the first layer to reduce sequence length. We skip this optimization since our sequences are already short.