Seyed Masoud Hosseini · Overview · Study log · Ideas · Transcript · RSS feed
Neural Networks: Zero to Hero · Lecture 6 of 10 · 56:21
Lecture 6: Building a WaveNet
Study guide
What this lecture covers
The lecture answers how to make the makemore character-level model deeper without simply widening a single hidden layer. It replaces the flat multi-layer perceptron from earlier lectures with a hierarchical, tree-like architecture that progressively fuses pairs of characters, arriving at something structurally similar to DeepMind's WaveNet. It follows directly from the MLP and BatchNorm work in the earlier makemore lectures (the manual backprop lecture is treated as a self-contained aside).
Along the way, the existing custom layers (Linear, BatchNorm1d, Tanh) are reorganized into reusable, PyTorch-like building blocks, including new Embedding and FlattenConsecutive modules and a Sequential container. After watching, you can explain why context is fused gradually instead of all at once, restructure a flat network into a hierarchical one, and recognize a subtle BatchNorm bug that appears when moving from two-dimensional to three-dimensional tensors.
Key ideas
- Progressive fusion: instead of flattening all context characters into one big vector before the first layer, WaveNet-style architectures fuse two characters at a time, then pairs of pairs, and so on, preserving structure longer.
- PyTorch-like containers: a custom
Sequentialmodule holds a list of layers and applies them in order, mirroringtorch.nn.Sequential. - Matrix multiply over extra batch dimensions: PyTorch's matmul operates on the last dimension and treats all leading dimensions as batch dimensions, which is what makes grouped, parallel processing of character pairs possible.
FlattenConsecutive: a custom layer that reshapes(B, T, C)into(B, T/n, C*n), fusingnconsecutive elements into the last dimension instead of flattening everything at once.- BatchNorm dimensionality bug: applying the two-dimensional BatchNorm implementation to three-dimensional input silently computes per-position statistics instead of per-channel ones; fixing it means reducing over dimensions
(0, 1)instead of just0. - Model state and bugs: forgetting to set a layer's
trainingflag correctly, or leaving a module in a stale mode, can silently corrupt results, as shown when a single-example forward pass hits BatchNorm in training mode. - Convolutions as an efficiency trick: sliding this same tree structure over an input sequence using convolutions computes the same result as looping in Python, but faster, because it reuses shared intermediate nodes and moves the loop into fast kernels.
Walkthrough
Intro and motivation (0:00)
The lecture explains the goal: take more than three characters of context and process them through a deeper network that fuses information gradually, rather than squashing everything into one hidden layer at once. This is connected to the 2016 WaveNet paper's hierarchical, tree-like prediction structure.
Starter code walkthrough (0:01:40)
The code picks up from the earlier MLP lecture, reusing the custom Linear, BatchNorm1d, and Tanh layer classes built to mimic torch.nn's API. The dataset and initial training loop are unchanged, reaching a validation loss around 2.10.
Fixing the loss plot and simplifying the forward pass (0:06:56)
The noisy per-step loss plot is smoothed by reshaping the loss list into rows and averaging. The embedding lookup and the flattening/concatenation step are then extracted into dedicated Embedding and Flatten modules so the entire forward pass becomes a single call through a Sequential model.
Pytorchifying the code: layers, containers, and a bug (0:09:16)
After introducing the Sequential container, a bug appears: sampling from the model with a single example produces gibberish because BatchNorm was left in training mode and computed a variance over one example, which is undefined. The fix highlights how easy it is to introduce bugs by not tracking a layer's train/eval state.
Implementing the WaveNet-style architecture (0:21:36)
The context length is increased from 3 to 8 characters. A naive flat model with the larger context already improves validation loss to about 2.02. The lecture then builds the FlattenConsecutive layer, which reshapes (B, T, C) into (B, T/n, C*n) by fusing n consecutive character embeddings, and stacks multiple Linear, BatchNorm1d, Tanh blocks with this flattening between them to build the tree-like structure, matching parameter count to the earlier flat model for a fair comparison.
Fixing the BatchNorm1d bug for 3D inputs (0:38:50)
Once the network processes three-dimensional tensors, the existing BatchNorm1d computes per-position statistics (1 x 4 x 68) instead of per-channel statistics, because it only reduces over dimension 0. The fix reduces over dimensions (0, 1) when the input is three-dimensional, correctly treating both the batch and the group position as batch dimensions. Retraining with the fix gives a small improvement, from 2.029 to 2.022.
Scaling up and final results (0:46:07)
Increasing embedding size and hidden units within the same hierarchical architecture pushes validation loss down to about 1.993, though the lecture notes there is no systematic experimental harness yet, only manual guessing and checking.
Convolutions, torch.nn, and the development process (0:47:44)
The lecture connects the hand-built tree structure to convolutions: a convolution slides the same small network across an input sequence and reuses shared intermediate computations, which is what makes it efficient compared to independently forwarding every position. It closes with reflections on the deep learning development process (prototyping in notebooks, wrestling with tensor shapes, PyTorch's inconsistent documentation) and previews future topics: dilated causal convolutions, residual and skip connections, and recurrent architectures.
Before you watch
- Complete the earlier makemore MLP lecture that builds the
Linear,BatchNorm1d, andTanhlayers and the initial two-layer network, since this lecture's starter code builds directly on it. - Be comfortable with BatchNorm's training/eval mode distinction and running statistics from that lecture.
- Familiarity with
torch.viewand broadcasting rules will help with the shape reasoning throughout.
Check your understanding
- Why does fusing two characters at a time, rather than flattening all context characters immediately, allow the network to be made deeper productively?
- What change does
FlattenConsecutivemake to the tensor shape, and how does it differ from the originalFlattenlayer? - Why did the two-dimensional
BatchNorm1dimplementation silently produce incorrect per-position statistics once the input became three-dimensional, and what dimensions does the fix reduce over? - What caused the single-example sampling bug, and why does it relate to BatchNorm specifically?
- In what sense does a convolution over a sequence compute the same result as looping over each position with the tree-structured model, only faster?
Chapters
- 0:00 intro
- 1:40 starter code walkthrough
- 6:56 let’s fix the learning rate plot
- 9:16 pytorchifying our code: layers, containers, torch.nn, fun bugs
- 17:11 overview: WaveNet
- 19:33 dataset bump the context size to 8
- 19:55 re-running baseline code on block_size 8
- 21:36 implementing WaveNet
- 37:41 training the WaveNet: first pass
- 38:50 fixing batchnorm1d bug
- 45:21 re-training WaveNet with bug fix
- 46:07 scaling up our WaveNet
- 46:58 experimental harness
- 47:44 WaveNet but with “dilated causal convolutions”
- 51:34 torch.nn
- 52:28 the development process of building deep neural nets
- 54:17 going forward
- 55:26 improve on my loss! how far can we improve a WaveNet on this data?
From the YouTube description
We take the 2-layer MLP from previous video and make it deeper with a tree-like structure, arriving at a convolutional neural network architecture similar to the WaveNet (2016) from DeepMind. In the WaveNet paper, the same hierarchical architecture is implemented more efficiently using causal dilated convolutions (not yet covered). Along the way we get a better sense of torch.nn and what it is and how it works under the hood, and what a typical deep learning development process looks like (a lot of reading of documentation, keeping track of multidimensional tensor shapes, moving between jupyter notebooks and repository code, ...).
Links:
- makemore on github: https://github.com/karpathy/makemore
- jupyter notebook I built in this video: https://github.com/karpathy/nn-zero-to-hero/blob/master/lectures/makemore/makemore_part5_cnn1.ipynb
- collab notebook: https://colab.research.google.com/drive/1CXVEmCO_7r7WYZGb5qnjfyxTvQa13g5X?usp=sharing
- my website: https://karpathy.ai
- my twitter: https://twitter.com/karpathy
- our Discord channel: https://discord.gg/3zy8kqD9Cp
Supplementary links:
- WaveNet 2016 from DeepMind https://arxiv.org/abs/1609.03499
- Bengio et al. 2003 MLP LM https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf
Chapters:
intro
00:00:00 intro
00:01:40 starter code walkthrough
00:06:56 let’s fix the learning rate plot
00:09:16 pytorchifying our code: layers, containers, torch.nn, fun bugs
implementing wavenet
00:17:11 overview: WaveNet
00:19:33 dataset bump the context size to 8
00:19:55 re-running baseline code on block_size 8
00:21:36 implementing WaveNet
00:37:41 training the WaveNet: first pass
00:38:50 fixing batchnorm1d bug
00:45:21 re-training WaveNet with bug fix
00:46:07 scaling up our WaveNet
conclusions
00:46:58 experimental harness
00:47:44 WaveNet but with “dilated causal convolutions”
00:51:34 torch.nn
00:52:28 the development process of building deep neural nets
00:54:17 going forward
00:55:26 improve on my loss! how far can we improve a WaveNet on this data?
← Lecture 5: Becoming a Backprop Ninja · Lecture 7: Let's Build GPT From Scratch →
