diff --git a/vision-models/beginner/README.md b/vision-models/beginner/README.md new file mode 100644 index 0000000..2ac3d3e --- /dev/null +++ b/vision-models/beginner/README.md @@ -0,0 +1,24 @@ +# Beginner vision deck — "How machines learn to see" + +Materials for presenting the IOAI Section 3 (computer vision) syllabus to a +zero-background 14–18 audience. + +``` +beginner/ +├── presentation/ +│ ├── how_machines_learn_to_see.pdf ← present this (fullscreen in any PDF viewer) +│ └── how_machines_learn_to_see.pptx ← same deck, Google Slides–ready (upload to Drive) +├── teacher/ +│ ├── teacher_guide.pdf ← READ THIS FIRST — every slide explained in simple English +│ └── PRESENTER_SCRIPT.md ← compact per-slide talking points + Q&A cards +├── build/ ← scripts that generate everything +│ ├── build_beginner_deck.py ← deck PDF (uv run --with weasyprint python build_beginner_deck.py) +│ ├── build_pptx.py ← PPTX (uv run --with pymupdf --with python-pptx python build_pptx.py) +│ ├── build_teacher_guide.py ← teacher guide (uv run --with weasyprint --with pymupdf python build_teacher_guide.py) +│ ├── guide_content.py ← the teacher guide text lives here +│ └── CONTENT_DRAFT.md ← original content outline +└── assets/ ← real outputs from the repo notebooks used in the deck +``` + +Suggested prep order: teacher_guide.pdf once (≈45 min) → skim PRESENTER_SCRIPT.md → +practice slide 6 (the hand convolution) → present from presentation/. diff --git a/vision-models/beginner/assets/astronaut.png b/vision-models/beginner/assets/astronaut.png new file mode 100644 index 0000000..834cda0 Binary files /dev/null and b/vision-models/beginner/assets/astronaut.png differ diff --git a/vision-models/beginner/assets/aug_crop.png b/vision-models/beginner/assets/aug_crop.png new file mode 100644 index 0000000..48db3e0 Binary files /dev/null and b/vision-models/beginner/assets/aug_crop.png differ diff --git a/vision-models/beginner/assets/aug_flip.png b/vision-models/beginner/assets/aug_flip.png new file mode 100644 index 0000000..80d9c39 Binary files /dev/null and b/vision-models/beginner/assets/aug_flip.png differ diff --git a/vision-models/beginner/assets/aug_jitter.png b/vision-models/beginner/assets/aug_jitter.png new file mode 100644 index 0000000..ae61b84 Binary files /dev/null and b/vision-models/beginner/assets/aug_jitter.png differ diff --git a/vision-models/beginner/assets/aug_noise.png b/vision-models/beginner/assets/aug_noise.png new file mode 100644 index 0000000..ecae5e7 Binary files /dev/null and b/vision-models/beginner/assets/aug_noise.png differ diff --git a/vision-models/beginner/assets/aug_original.png b/vision-models/beginner/assets/aug_original.png new file mode 100644 index 0000000..4255eb3 Binary files /dev/null and b/vision-models/beginner/assets/aug_original.png differ diff --git a/vision-models/beginner/assets/chelsea.png b/vision-models/beginner/assets/chelsea.png new file mode 100644 index 0000000..fae212d Binary files /dev/null and b/vision-models/beginner/assets/chelsea.png differ diff --git a/vision-models/beginner/assets/chelsea_400.png b/vision-models/beginner/assets/chelsea_400.png new file mode 100644 index 0000000..166fbfd Binary files /dev/null and b/vision-models/beginner/assets/chelsea_400.png differ diff --git a/vision-models/beginner/assets/chelsea_gray.png b/vision-models/beginner/assets/chelsea_gray.png new file mode 100644 index 0000000..bedb84b Binary files /dev/null and b/vision-models/beginner/assets/chelsea_gray.png differ diff --git a/vision-models/beginner/assets/chelsea_marked.png b/vision-models/beginner/assets/chelsea_marked.png new file mode 100644 index 0000000..2392c9e Binary files /dev/null and b/vision-models/beginner/assets/chelsea_marked.png differ diff --git a/vision-models/beginner/assets/chelsea_zoom.png b/vision-models/beginner/assets/chelsea_zoom.png new file mode 100644 index 0000000..a5e8b08 Binary files /dev/null and b/vision-models/beginner/assets/chelsea_zoom.png differ diff --git a/vision-models/beginner/assets/clip_demo_raw.png b/vision-models/beginner/assets/clip_demo_raw.png new file mode 100644 index 0000000..c8a1e74 Binary files /dev/null and b/vision-models/beginner/assets/clip_demo_raw.png differ diff --git a/vision-models/beginner/assets/clip_results.json b/vision-models/beginner/assets/clip_results.json new file mode 100644 index 0000000..086f19e --- /dev/null +++ b/vision-models/beginner/assets/clip_results.json @@ -0,0 +1 @@ +{"prompts": ["a photo of a cat", "a photo of a dog", "a photo of a tiger", "a photo of a pizza"], "probs": [0.9793983697891235, 0.0047552092000842094, 0.015624566935002804, 0.00022184147383086383]} \ No newline at end of file diff --git a/vision-models/beginner/assets/det_astronaut.png b/vision-models/beginner/assets/det_astronaut.png new file mode 100644 index 0000000..c2b841e Binary files /dev/null and b/vision-models/beginner/assets/det_astronaut.png differ diff --git a/vision-models/beginner/assets/det_chelsea.png b/vision-models/beginner/assets/det_chelsea.png new file mode 100644 index 0000000..e39dac7 Binary files /dev/null and b/vision-models/beginner/assets/det_chelsea.png differ diff --git a/vision-models/beginner/assets/det_results.json b/vision-models/beginner/assets/det_results.json new file mode 100644 index 0000000..646e838 --- /dev/null +++ b/vision-models/beginner/assets/det_results.json @@ -0,0 +1 @@ +{"chelsea": [["cat", 97]], "astronaut": [["person", 91]]} \ No newline at end of file diff --git a/vision-models/beginner/assets/diff_0.png b/vision-models/beginner/assets/diff_0.png new file mode 100644 index 0000000..1580529 Binary files /dev/null and b/vision-models/beginner/assets/diff_0.png differ diff --git a/vision-models/beginner/assets/diff_1.png b/vision-models/beginner/assets/diff_1.png new file mode 100644 index 0000000..4a34035 Binary files /dev/null and b/vision-models/beginner/assets/diff_1.png differ diff --git a/vision-models/beginner/assets/diff_2.png b/vision-models/beginner/assets/diff_2.png new file mode 100644 index 0000000..95ca984 Binary files /dev/null and b/vision-models/beginner/assets/diff_2.png differ diff --git a/vision-models/beginner/assets/diff_3.png b/vision-models/beginner/assets/diff_3.png new file mode 100644 index 0000000..8c6160f Binary files /dev/null and b/vision-models/beginner/assets/diff_3.png differ diff --git a/vision-models/beginner/assets/diff_4.png b/vision-models/beginner/assets/diff_4.png new file mode 100644 index 0000000..466767f Binary files /dev/null and b/vision-models/beginner/assets/diff_4.png differ diff --git a/vision-models/beginner/assets/fmap_horizontal.png b/vision-models/beginner/assets/fmap_horizontal.png new file mode 100644 index 0000000..a6a6a67 Binary files /dev/null and b/vision-models/beginner/assets/fmap_horizontal.png differ diff --git a/vision-models/beginner/assets/fmap_vertical.png b/vision-models/beginner/assets/fmap_vertical.png new file mode 100644 index 0000000..e495ead Binary files /dev/null and b/vision-models/beginner/assets/fmap_vertical.png differ diff --git a/vision-models/beginner/assets/learned_filters_raw.png b/vision-models/beginner/assets/learned_filters_raw.png new file mode 100644 index 0000000..663c83f Binary files /dev/null and b/vision-models/beginner/assets/learned_filters_raw.png differ diff --git a/vision-models/beginner/assets/pixel_grid.json b/vision-models/beginner/assets/pixel_grid.json new file mode 100644 index 0000000..2ccc299 --- /dev/null +++ b/vision-models/beginner/assets/pixel_grid.json @@ -0,0 +1 @@ +{"x": 167, "y": 93, "values": [[67, 59, 53, 51, 48, 38, 21, 7, 10, 14], [64, 68, 61, 58, 49, 28, 16, 9, 9, 6], [73, 70, 70, 63, 46, 20, 12, 9, 10, 7], [86, 91, 84, 67, 48, 25, 17, 16, 11, 8], [95, 91, 89, 65, 53, 50, 42, 36, 25, 18], [72, 64, 58, 58, 62, 75, 80, 72, 61, 50], [52, 52, 87, 44, 52, 63, 72, 79, 84, 77], [45, 95, 153, 50, 30, 30, 39, 49, 58, 62], [44, 114, 172, 68, 40, 32, 29, 26, 30, 29], [46, 130, 185, 79, 50, 46, 43, 36, 29, 19]]} \ No newline at end of file diff --git a/vision-models/beginner/assets/resnet_filters.png b/vision-models/beginner/assets/resnet_filters.png new file mode 100644 index 0000000..727f3eb Binary files /dev/null and b/vision-models/beginner/assets/resnet_filters.png differ diff --git a/vision-models/beginner/assets/seg_cutout.png b/vision-models/beginner/assets/seg_cutout.png new file mode 100644 index 0000000..01764f6 Binary files /dev/null and b/vision-models/beginner/assets/seg_cutout.png differ diff --git a/vision-models/beginner/assets/seg_mask.png b/vision-models/beginner/assets/seg_mask.png new file mode 100644 index 0000000..d48db00 Binary files /dev/null and b/vision-models/beginner/assets/seg_mask.png differ diff --git a/vision-models/beginner/assets/seg_photo.png b/vision-models/beginner/assets/seg_photo.png new file mode 100644 index 0000000..166fbfd Binary files /dev/null and b/vision-models/beginner/assets/seg_photo.png differ diff --git a/vision-models/beginner/build/.gitignore b/vision-models/beginner/build/.gitignore new file mode 100644 index 0000000..85b8071 --- /dev/null +++ b/vision-models/beginner/build/.gitignore @@ -0,0 +1 @@ +_thumbs/ diff --git a/vision-models/beginner/build/CONTENT_DRAFT.md b/vision-models/beginner/build/CONTENT_DRAFT.md new file mode 100644 index 0000000..da54a24 --- /dev/null +++ b/vision-models/beginner/build/CONTENT_DRAFT.md @@ -0,0 +1,257 @@ +# Vision Models From Zero — beginner deck content draft (v1, pre-research-merge) + +Audience: 14–18, zero coding/AI. Presenter: Rone (~20, no background). +Per-topic ladder: plain words → analogy → picture → proper name → code → knobs. +Rule: ONE new idea per slide. A term is never used before it is earned. + +## PART 0 — SETUP + +### S1 Cover +Eyebrow: IOAI 2026 SYLLABUS · SECTION 3 · COMPUTER VISION +Title: How machines learn to see +(whitespace) + +### S2 The promise +Title: One day, one story +- Part 1 — The trick: how a computer turns numbers into "cat". We compute it by hand. +- Part 2 — The learning: nobody programs it; it practices and improves. +- Part 3 — The shortcut: borrow a model someone already trained. +- Part 4 — The superpowers: find objects, cut them out, talk to images, create images. +- Part 5 — The cheat sheets: every term and every knob on two slides you can keep. +Dek: If you can multiply small numbers, you can follow every step today. + +### S3 Five words we'll use all day (base vocabulary, earned up front) +Plain table, 5 rows: +- MODEL — a machine made of numbers that turns an input into a guess. (vending machine for answers) +- DATASET — the pile of examples we teach with (photos + correct answers). +- LABEL — the correct answer attached to an example ("this photo is a cat"). +- TRAINING — showing the model examples until its guesses get good. +- ACCURACY — out of 100 tries, how many guesses were right. +Note: only these five. Everything else gets introduced when earned. + +### S4 Statement (dark): A computer has never seen a cat. + +### S5 A photo is just a grid of numbers +- Zoom into any photo far enough → squares. One square = one PIXEL. +- Each pixel is one number: 0 = black, 255 = white, in-between = gray. +- Color photo = three grids stacked: red, green, blue. +Visual: chelsea_marked.png + real 10×10 pixel grid (values from pixel_grid.json) +NAME box: pixel; the grid is called a TENSOR (just "a grid of numbers, possibly stacked"). +Knob: image size 32×32 = 3,072 numbers → 224×224 = 150,528 numbers. More numbers = more detail = more work. +Interaction: which corner has the big numbers — bright fur or dark pupil? + +## PART 1 — THE TRICK + +### S6 Statement-ish setup → hand convolution (centerpiece) +Title: The trick, by hand (it's just times tables) +Visual: three grids — 5×5 input (columns of 2s then 0s), 3×3 stencil (1 0 -1 ×3 rows), multiply-and-add = 6; flat region = 0. +- Take a tiny 3×3 grid of numbers — a stencil. +- Lay it on the photo. Multiply matching cells. Add the nine answers. +- Big total = "my pattern is HERE". Zero = "nothing here". +Interaction: compute the nine products together. +NAME box: this operation = CONVOLUTION. The stencil = FILTER (or KERNEL). + +### S7 Run the filter everywhere → feature map +- Slide the stencil across the whole photo, write down the answer at every stop. +- You get a new "image" that glows where the pattern lives. +Visual: chelsea_gray → fmap_vertical (whiskers glow) → fmap_horizontal. +NAME box: the output map = FEATURE MAP. +Knob: rotate the filter 90° → finds horizontal edges instead. Different numbers in the stencil = different pattern found. + +### S8 Stride & padding (the two sliding rules) +Plain: stride = how big a step the stencil takes. padding = invisible zeros glued around the border so the edges get a turn. +Visual: SVG — same 6×6 grid, stride 1 vs stride 2 positions; padding ring shown as dashed cells. +Knobs: +- stride 1 → output almost same size; stride 2 → output half size (cheaper, blurrier view). +- no padding: 3×3 filter shrinks 7×7 → 5×5; padding 1 keeps 7×7 → 7×7. +- kernel 3×3 → 7×7: sees bigger patterns at once, but 27 → 147 weights per filter (slower, needs more data). + +### S9 Pooling — shrink and forgive +Plain: look at each little 2×2 window, keep only the biggest number ("was the pattern here at all?"). +- Photo shrinks to half → deeper layers are cheaper. +- Pattern moved by one pixel? The max usually doesn't change → small shifts stop mattering. +- AVERAGE pooling = take the mean instead. GLOBAL average pooling at the very end = average each whole map to ONE number → a short list summarizing the photo. +Visual: SVG 4×4 grid → 2×2 max pooled, colored quadrants. +NAME box: MAX POOLING / AVERAGE POOLING / GLOBAL AVERAGE POOLING. +Knob: pool 2×2 → image halves; pool too much → you throw away where things were. + +### S10 ReLU — the "keep the good news" switch +Plain: after each conv, replace every negative number with 0; keep positives. +Why: without a bend between layers, stacking layers = one big layer (a stack of rulers is still a ruler). The bend lets layers build on each other. +Visual: SVG graph of max(0,x). +NAME box: ACTIVATION FUNCTION; this one is ReLU. (Others exist: sigmoid squashes to 0–1, tanh to −1–1.) + +### S11 The full machine: a CNN classifier +Pipeline: photo → [filter → ReLU → pool] → [again] → global average → short list of numbers → score per class → highest score wins. +- The short list of numbers that summarizes the photo = EMBEDDING. +- Scores turned into percentages that add to 100 = SOFTMAX. +- One label for the whole photo = IMAGE CLASSIFICATION. +Visual: horizontal pipeline SVG ending in "cat 92%". +NAME box: CONVOLUTIONAL NEURAL NETWORK (CNN). EMBEDDING. SOFTMAX. IMAGE CLASSIFICATION. + +### S12 Code card 1 — the whole eye in a few lines +```python +import torch.nn as nn +model = nn.Sequential( + nn.Conv2d(3, 32, kernel_size=3, padding=1), # 32 stencils, 3x3 + nn.ReLU(), # keep the good news + nn.MaxPool2d(2), # shrink, forgive shifts + nn.Conv2d(32, 64, kernel_size=3, padding=1), + nn.ReLU(), + nn.AdaptiveAvgPool2d(1), nn.Flatten(), # summarize -> embedding + nn.Linear(64, 10), # 10 class scores +) +``` +Knobs: out_channels 32→64: more patterns per layer, slower; kernel_size 3→7: bigger view, many more weights; remove ReLU → whole stack collapses to one layer (accuracy tanks). + +## PART 2 — THE LEARNING + +### S13 Statement (dark): Nobody programs the filters. The network finds them. + +### S14 Learning = the hot-and-cold game +Loop: photo → model guesses ("70% dog") → compare with label ("cat") → one number says how wrong = LOSS → nudge every filter number a tiny step that makes it less wrong → repeat. +Visual: loop SVG. +NAME box: LOSS (wrongness score). The nudging recipe = BACKPROPAGATION + GRADIENT DESCENT. One full pass through the dataset = EPOCH. + +### S15 Gradient descent = ball rolling downhill +Plain: imagine wrongness as a hilly landscape; training rolls the ball downhill step by step. Step size = LEARNING RATE. +Visual: SVG hill with ball + arrows; second panel: too-big steps bouncing across the valley. +Knobs: +- learning rate too small → crawls, training takes forever. +- learning rate 10× too big → loss jumps around or explodes; never settles. +- typical: start ~0.001 (Adam) and let it shrink. ADAM = gradient descent with momentum & per-knob step sizes — the sensible default. + +### S16 Overfitting — memorizing vs understanding +Plain: a student who memorizes past papers aces practice, fails the real exam. Networks do that too. +- Split data: TRAIN (study material) vs TEST (the real exam, never studied). +- Train accuracy keeps climbing; test accuracy rises then FALLS → it started memorizing. +Visual: SVG two curves (train up, test up-then-down) with "stop here" marker. +NAME box: OVERFITTING, TRAIN/TEST SPLIT, EARLY STOPPING. +Knobs: more epochs → train acc up forever, test acc up then down; more/varied data → overfitting later; dropout/weight decay = built-in "forget a little on purpose" brakes. + +### S17 Augmentation — free extra photos +Plain: a flipped cat is still a cat. Flip, crop, recolor, add noise → the model never sees the exact same photo twice → it can't memorize. +Visual: aug_original/flip/crop/jitter/noise row (REAL images). +Code card 2: +```python +train_tf = transforms.Compose([ + transforms.RandomCrop(32, padding=4), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), +]) +``` +Knobs: only on TRAINING photos, never the test; too strong (crop 90% away) → label destroyed, accuracy drops; classic trap: flipping digits turns 6 into 9 — augmentation must keep the label true. + +### S18 What the network actually learned +Visual: resnet_filters.png — all 64 first-layer filters of ResNet18 (trained on 1.28M photos). +- These are real learned stencils. Nobody drew them. +- They look like edge & color detectors — the network DISCOVERED that edges are the right first step. +- Layer 2 looks at edge maps → finds textures (fur, stripes). Higher layers: parts (ear, eye) → whole objects. +Ladder graphic: edges → textures → parts → objects. + +### S19 Receipts — real numbers from this repo's notebooks +Bar chart: 50.0% small CNN from scratch (4,000 photos, 3 epochs) → 74.2% finetuned ResNet18 (1,000 photos!) → 88.5% CLIP (0 training photos). +Random guessing on 10 classes = 10%. +Interaction: guess the from-scratch score before reveal. +Dek: the rest of the deck explains the two bigger bars. + +## PART 3 — THE SHORTCUT + +### S20 Statement (dark): Don't start from scratch. Borrow trained eyes. + +### S21 Pre-trained encoders +Plain: someone already trained a big CNN on 1.28 million photos (ImageNet, 1000 classes). Its filters already know edges, fur, wheels, faces. Download it, reuse it. +NAME box: PRE-TRAINED ENCODER / BACKBONE. ImageNet. +- The early layers are general-purpose: useful for ANY photo task — X-rays, satellites, your dataset. +Visual: stack diagram "1.28M photos → trained filters → reusable". + +### S22 ResNet — the skip connection +Why famous: deeper SHOULD be better, but stacks past ~30 layers trained WORSE — the learning signal faded passing through so many layers. +Fix: each block outputs input + small correction (x + F(x)); a shortcut wire carries the input around the block. +- If a block has nothing to add, it can pass the input through untouched → extra depth can't hurt. +- The learning signal flows back through the shortcuts undiminished → 50/101/152 layers train fine. +Visual: SVG residual block (two paths joining at +). +NAME box: RESNET, SKIP (RESIDUAL) CONNECTION. + +### S23 Transfer learning & finetuning +Plain: take the pretrained backbone, rip off its last layer (it answers the wrong question: 1000 ImageNet classes), bolt on a fresh small layer for YOUR classes, train gently. +Three gears: +- FREEZE backbone, train only new head → tiny data (hundreds of photos), fastest, safest. +- FINETUNE everything with a small learning rate → moderate data, best accuracy, can "wreck" good filters if LR too big. +- PARAMETER-EFFICIENT (LoRA/adapters): add tiny trainable side-pieces, keep backbone frozen → cheap, low memory. +Code card 3: +```python +from torchvision.models import resnet18, ResNet18_Weights +model = resnet18(weights=ResNet18_Weights.IMAGENET1K_V1) +model.fc = nn.Linear(512, 10) # new head: my 10 classes +``` +Knobs: data tiny → freeze more; LR for pretrained layers ~10× smaller than for the new head; forget to normalize inputs the way the backbone expects → accuracy silently drops. +This is the 74.2%-with-1,000-photos recipe. + +## PART 4 — THE SUPERPOWERS + +### S24 Statement (dark): Same trick. Four superpowers. + +### S25 Object detection — what AND where +Plain: classification says "there's a cat somewhere"; detection draws a BOX around every object + label + confidence %. +Visual: det_chelsea (CAT 97%), det_astronaut (PERSON 91%) — real outputs. +Plain-words extras: +- IoU = how much the predicted box overlaps the true box (0 = miss, 1 = perfect). +- NMS = the cleanup step that deletes duplicate boxes for the same object. +Knob: confidence threshold 0.9 → fewer, surer boxes (misses shy objects); 0.3 → catches more, more false alarms. + +### S26 The three detector families (table) +- YOLO — "You Only Look Once": one single pass over a grid → fastest, real-time video. +- SSD — single pass but predicts from several map sizes → catches small AND large objects. +- DETR — a transformer (S29 vocabulary...?) emits a set of boxes directly; no anchors, no NMS cleanup; cleaner but slower to train. +Choosing: speed → YOLO; classic multi-scale → SSD; end-to-end clean → DETR. +(In practice: download pretrained, finetune on your boxes.) + +### S27 Segmentation — cut out the exact shape +Plain: boxes are rough. Segmentation decides for EVERY pixel: cat or not-cat → a mask. +Visual: seg_photo → seg_mask → seg_cutout (real outputs). +U-Net plain: an hourglass. Left side shrinks (WHAT is here), right side grows back (WHERE exactly), and skip wires hand the sharp details across, otherwise masks come out blobby. +NAME box: SEGMENTATION, MASK, U-NET, ENCODER–DECODER. +Where you've seen it: background blur in video calls, portrait mode, outlining organs in scans. + +### S28 Self-supervised learning — learning without labels +Plain: labels are expensive (doctors, experts). But photos are free. Trick: make two different crops/recolors of the SAME photo and tell the network "these two must land at the same spot in your embedding space; different photos must land apart." +- The augmentations from S17 are the engine here. +- Result: a pretrained backbone with ZERO human labels. +NAME box: SELF-SUPERVISED LEARNING, CONTRASTIVE LEARNING. +Visual: SVG — one photo → two augmented views → arrows to nearby points; different photo → far point. + +### S29 CLIP — a model that speaks image AND text +Plain: CLIP read 400 million internet photos WITH their captions. Two towers: one embeds images, one embeds sentences, trained so matching pairs land close. +Superpower: classify with SENTENCES — no training. "a photo of a cat" vs "a photo of a dog" → whichever sentence lands closest to the image wins. +Visual: clip bar row — cat 97.9%, tiger 1.6%, dog 0.5%, pizza 0.0% (real outputs). +Code card 4 (3 lines). +NAME box: CLIP, ZERO-SHOT. +Knob: change the wording → results change ("a photo of a cat" beats just "cat"; add "a blurry photo…" prompts for blurry datasets). +(One-line honesty: inside CLIP's image tower is a TRANSFORMER — it chops the image into 16×16 patches and lets every patch look at every other patch. That's all we need today.) + +### S30 GANs — the forger and the detective +Plain: two networks play a game. The FORGER (generator) makes fake images from random noise; the DETECTIVE (discriminator) guesses real or fake. Each round both get better. Eventually the fakes fool everyone. +NAME box: GAN — GENERATOR vs DISCRIMINATOR (adversarial = "playing against"). +Knob/note: famously unstable to train (if one player gets too good, the game collapses). + +### S31 Diffusion — images out of static +Plain: train by RUINING photos — add a little noise, again and again until pure static. The network learns ONE humble skill: undo a little bit of noise. +Generate: start from fresh static, apply the cleanup over and over → an image crystallizes. +Visual: diff_0..diff_4 chain with arrow pointing BACKWARD labeled "generation runs this way". +NAME box: DIFFUSION MODEL. This is the engine of the famous image generators. + +## PART 5 — CHEAT SHEETS + +### S32 The map — syllabus checklist +Table: every Section 3 syllabus row → where it lives in this deck → six-word summary. + +### S33 Glossary — every term in plain words (2 columns, ~24 terms) + +### S34 Knob cheat sheet +Table: knob → turn it up → turn it down (kernel size, stride, padding, pooling, learning rate, epochs, augmentation strength, freeze vs finetune, confidence threshold, prompt wording). + +### S35 Close (mirror cover) +- Everything here is the official IOAI 2026 syllabus, Section 3. +- Two notebooks in this repo rerun every number you saw (50.0 → 74.2 → 88.5). +- You computed a convolution by hand. The rest is that, repeated. Questions. diff --git a/vision-models/beginner/build/build_beginner_deck.py b/vision-models/beginner/build/build_beginner_deck.py new file mode 100644 index 0000000..2b1c8fe --- /dev/null +++ b/vision-models/beginner/build/build_beginner_deck.py @@ -0,0 +1,1290 @@ +#!/usr/bin/env python3 +"""Build 'How Machines Learn to See' — beginner deck for IOAI 2026 Section 3. + +Audience: 14-18yo with zero coding/AI background, taught by a fellow student. +1920x1080, Viktor profile, WeasyPrint. Run: uv run python build_beginner_deck.py +""" +import json +import html as html_mod +from pathlib import Path + +HERE = Path(__file__).parent +A = HERE.parent / "assets" + +pix = json.load(open(A / "pixel_grid.json"))["values"] +clip = json.load(open(A / "clip_results.json")) + +# ---------------------------------------------------------------- css +CSS = """ +@import url("https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500&display=swap"); + +@page { size: 1920px 1080px; margin: 0; } +:root { + --c-ink: #000000; --c-surface: #FFFFFF; + --c-dim: rgba(0,0,0,0.55); --c-faint: rgba(0,0,0,0.35); + --c-line: rgba(0,0,0,0.12); --c-fill-subtle: rgba(0,0,0,0.04); + --font-display: "Satoshi", sans-serif; --font-mono: "Roboto Mono", monospace; +} +html, body { margin:0; padding:0; } +body { font-family: var(--font-display); font-weight:400; color:var(--c-ink); + background:#fff; font-feature-settings:"tnum" 1,"lnum" 1; } +.slide { position:relative; width:1920px; height:1080px; + box-sizing:border-box; background:#fff; color:#000; + page-break-after:always; } +.inner { position:absolute; top:0; left:0; right:0; bottom:0; padding:72px 120px; + box-sizing:border-box; overflow:hidden; } +.slide--dark { background:#000; color:#fff; } +.slide--dark .eyebrow { color:rgba(255,255,255,0.55); } +.slide--dark .pagenum { color:rgba(255,255,255,0.35); } +.slide--dark .dek { color:rgba(255,255,255,0.55); } +p, .dek, figcaption, .capn { text-wrap:pretty; } h1,h2,.title { text-wrap:balance; } +.eyebrow { font-family:var(--font-mono); font-size:13px; letter-spacing:0.16em; + text-transform:uppercase; color:var(--c-dim); } +.title { font-size:72px; line-height:1.05; font-weight:500; letter-spacing:-0.025em; + margin:20px 0 0 0; } +.statement { font-size:124px; line-height:1.0; font-weight:500; letter-spacing:-0.03em; margin:0; } +.dek { font-size:28px; line-height:1.3; color:var(--c-dim); margin:0; } +.pagenum { position:absolute; right:120px; top:76px; font-family:var(--font-mono); + font-size:13px; letter-spacing:0.16em; color:var(--c-faint); } +.bullets { margin:0; padding:0; list-style:none; } +.bullets li { font-size:26px; line-height:1.36; margin-bottom:18px; padding-left:44px; + position:relative; } +.bullets li:before { content:"\\2192"; position:absolute; left:0; color:var(--c-faint); } +.bullets li b { font-weight:500; } +.cap { font-family:var(--font-mono); font-size:13px; letter-spacing:0.16em; + text-transform:uppercase; color:var(--c-dim); } +.capn { font-size:19px; color:var(--c-dim); line-height:1.35; } +img { image-rendering:auto; } +img.px { image-rendering:pixelated; } +.code { font-family:var(--font-mono); font-size:21px; line-height:1.55; + background:var(--c-fill-subtle); padding:30px 38px; white-space:pre; } +.code .cm { color:var(--c-dim); } + +/* boxes: the recurring teaching furniture */ +.tbox { background:var(--c-fill-subtle); padding:24px 32px; box-sizing:border-box; } +.tbox .cap { display:block; margin-bottom:12px; } +.tbox--ink { background:#000; color:#fff; } +.tbox--ink .cap { color:rgba(255,255,255,0.55); } +.tbox p { margin:0; font-size:23px; line-height:1.38; } +.tbox p b { font-weight:500; } +.knob-row { display:grid; grid-template-columns: 1fr 1fr 1fr; } +.knob-row > div { padding:0 28px; } +.knob-row > div:first-child { padding-left:0; } +.knob-row > div:not(:last-child) { border-right:none; } + +/* simple data tables */ +table.tt { border-collapse:collapse; width:100%; } +table.tt th { font-family:var(--font-mono); font-size:13px; letter-spacing:0.16em; + text-transform:uppercase; color:var(--c-dim); font-weight:500; text-align:left; + padding:14px 24px 14px 0; border-bottom:1px solid var(--c-ink); } +table.tt td { font-size:23px; line-height:1.32; padding:13px 24px 13px 0; + border-bottom:1px solid var(--c-line); vertical-align:top; } +table.tt tr:last-child td { border-bottom:1px solid var(--c-ink); } +table.tt td.term { font-weight:500; white-space:nowrap; } +table.tt td.mono { font-family:var(--font-mono); font-size:20px; } + +.interact { position:absolute; left:120px; bottom:56px; font-family:var(--font-mono); + font-size:13px; letter-spacing:0.16em; text-transform:uppercase; } +.interact b { font-weight:500; background:#000; color:#fff; padding:6px 12px; margin-right:14px; } +.slide--dark .interact b { background:#fff; color:#000; } +""" + +# ---------------------------------------------------------------- helpers +SLIDES = [] + +def slide(body, dark=False, num=True): + """num=True auto-numbers from position (cover passes num=False).""" + cls = "slide slide--dark" if dark else "slide" + n = f'
{len(SLIDES)+1:02d}
' if num else "" + SLIDES.append(f'
{n}{body}
') + +def head(eyebrow, title, dek=None, title_size=None): + ts = f"font-size:{title_size}px;" if title_size else "" + d = f'

{dek}

' if dek else "" + return (f'
{eyebrow}
' + f'

{title}

{d}') + +def namebox(terms, width=None, ink=True, label="The proper name"): + """terms: list of (TERM, plain gloss).""" + rows = "".join( + f'

{t}' + f' — {g}

' if g else + f'

{t}

' + for t, g in terms) + w = f"width:{width}px;" if width else "" + cls = "tbox tbox--ink" if ink else "tbox" + return f'
{label}{rows}
' + +def knobbox(knobs, width=None, label="Turn the knob"): + """knobs: list of (knob, effect).""" + rows = "".join( + f'

{k}' + f' → {e}

' + for k, e in knobs) + w = f"width:{width}px;" if width else "" + return f'
{label}{rows}
' + +def bullets(items, size=None, gap=None): + st = "" + if size: st += f"font-size:{size}px;" + if gap: st += f"margin-bottom:{gap}px;" + lis = "".join(f'
  • {i}
  • ' for i in items) + return f'' + +def code(src, caption=None, size=21): + esc = html_mod.escape(src).replace(""", '"') + # gray comments + out_lines = [] + for ln in esc.split("\n"): + if "#" in ln: + i = ln.index("#") + ln = ln[:i] + f'{ln[i:]}' + out_lines.append(ln) + cap = f'
    {caption}
    ' if caption else "" + return f'
    {chr(10).join(out_lines)}
    {cap}' + +def grid_svg(values, cell=72, fs=26, highlight=None, gray_zero=False): + """Draw a numeric grid as SVG. highlight: (r0,c0,r1,c1) inclusive box.""" + rows, cols = len(values), len(values[0]) + w, h = cols * cell, rows * cell + parts = [f''] + for r in range(rows): + for c in range(cols): + v = values[r][c] + fill = "#FFFFFF" + if gray_zero and v == 0: + fill = "#E8E8E8" + parts.append(f'') + parts.append(f'{v}') + if highlight: + r0, c0, r1, c1 = highlight + parts.append(f'') + parts.append("") + return "".join(parts) + +# ================================================================ PART 0 — SETUP + +# ---- S1 cover +slide(num=False, body=f""" +
    IOAI 2026 SYLLABUS · SECTION 3 · COMPUTER VISION · TEAM PREP
    +

    How machines
    learn to see

    +""") + +# ---- S2 the promise +slide(head("THE PLAN", "One day, one story", + "If you can multiply small numbers, you can follow every step today. " + "New words are only used AFTER we earn them.") + f""" +
    +{bullets([ + "Part 1 — The trick. How a pile of numbers becomes the word “cat”. We compute it by hand.", + "Part 2 — The learning. Nobody programs it. It practices, like the hot-and-cold game.", + "Part 3 — The shortcut. Borrow a model someone already trained on a million photos.", + "Part 4 — The superpowers. Find objects, cut them out, talk to images, create images.", + "Part 5 — The cheat sheets. Every term and every knob, on slides you keep.", +], size=24, gap=30)} +
    +""") + +# ---- S3 base vocabulary +slide(head("BEFORE WE START", "Five words we will use all day", + "Only these five up front. Every other term gets introduced when you have already understood the idea behind it.") + f""" +
    + + + + + + + +
    WordWhat it means, in plain words
    MODELa machine made of numbers: input goes in, a guess comes out
    DATASETthe pile of examples we teach with — photos plus their correct answers
    LABELthe correct answer attached to one example: “this photo is a cat”
    TRAININGshowing the model examples, over and over, until its guesses get good
    ACCURACYout of 100 tries, how many guesses were right
    +
    +""") + +# ---- S4 statement +slide(f""" +
    PART 1 · THE TRICK
    +

    A computer has never
    seen a cat.

    +""", dark=True) + +# ---- S5 photo = grid of numbers +pix5 = [row[:7] for row in pix[:5]] +slide(head("PART 1 · THE TRICK", "A photo is just a grid of numbers") + f""" +
    +
    + {bullets([ + "Zoom far enough into any photo and it falls apart into little squares.", + "Each square holds one number: 0 means black, 255 means white, in-between means gray.", + "A color photo is three grids stacked — one for red, one for green, one for blue. That is ALL the computer gets.", + ], size=26)} +
    + {namebox([("PIXEL", "one little square of the photo"), + ("TENSOR", "a grid of numbers (possibly several stacked) — nothing scarier than that")])} +
    +
    +
    +
    + +
    Chelsea — a real photo, with a tiny square marked on her eye. Below: the REAL numbers inside that square.
    +
    +
    {grid_svg(pix5, cell=54, fs=19)}
    +
    +
    +
    ASK THE ROOM Big numbers — in the bright fur or the dark pupil? (255 = white)
    +""") + +# ================================================================ PART 1 — THE TRICK + +# ---- S6 hand convolution (centerpiece) +inp = [[2,2,0,0,0],[2,2,0,0,0],[2,2,0,0,0],[2,2,0,0,0],[2,2,0,0,0]] +filt = [[1,0,-1],[1,0,-1],[1,0,-1]] +slide(head("PART 1 · THE TRICK", "The trick, by hand — it is just times tables", + "The image has a bright stripe (2s) next to a dark area (0s). Where they meet is an edge. Can math FIND that edge?") + f""" +
    +
    +
    1 · A tiny image
    + {grid_svg(inp, cell=80, fs=28, highlight=(0,0,2,2), gray_zero=True)} +
    Bright stripe of 2s, dark area of 0s. The bold box is where we lay the stencil first.
    +
    +
    +
    2 · A 3×3 stencil
    + {grid_svg(filt, cell=80, fs=28)} +
    Nine chosen numbers. Plus on the left, minus on the right.
    +
    +
    +
    3 · Multiply matching cells, add all nine
    +
    + 2×1 + 2×0 + 0×(−1)  =  2
    + 2×1 + 2×0 + 0×(−1)  =  2
    + 2×1 + 2×0 + 0×(−1)  =  2
    + total  =  6  → “my pattern is HERE” +
    +

    + Now slide the stencil onto the flat dark area: every cell is 0, every product is 0, + total = 0 — “nothing here.” The stencil only speaks on its own pattern.

    +
    +
    +
    +{namebox([("CONVOLUTION", "this multiply-and-add operation — the heart of all computer vision"), + ("FILTER (or KERNEL)", "the little stencil of numbers")])} +
    +
    DO IT TOGETHER Everyone computes the nine products out loud before the reveal
    +""") + +# ---- S7 feature map +slide(head("PART 1 · THE TRICK", "Slide it everywhere — the photo answers back") + f""" +
    +
    + {bullets([ + "Slide the SAME stencil across the whole photo — thousands of little multiply-and-adds.", + "Write the answer at every stop. You get a new image that glows where the pattern lives.", + "Look: the whiskers and eye outlines light up — they are vertical-ish edges.", + "This is real output — this exact stencil ran on this exact photo.", + ], size=26)} +
    + {namebox([("FEATURE MAP", "the glow-map an image produces for one filter: bright = pattern found here")])} +
    +
    +
    +
    +
    +
    The photo, in grays
    +
    +
    Our vertical-edge stencil: whiskers glow
    +
    +
    Same stencil rotated 90°: horizontal edges glow
    +
    + Turn the knob +

    Different numbers in the stencil → a different pattern found. Rotate it → horizontal edges. The numbers ARE the pattern.

    +
    +
    +
    +
    +""") + +# ---- S8 stride & padding +def stride_svg(): + # 6x6 grid, show 3x3 window positions for stride 1 vs stride 2 + cell = 52 + def panel(stride, label): + w = 6*cell + parts = [f''] + for r in range(6): + for c in range(6): + parts.append(f'') + # first window + parts.append(f'') + # second window at stride + parts.append(f'') + parts.append(f'{label}') + parts.append("") + return "".join(parts) + return panel(1, "STRIDE 1 — step one cell"), panel(2, "STRIDE 2 — step two cells") + +def padding_svg(): + cell = 52 + n = 8 # 6 + padding ring + w = n*cell + parts = [f''] + for r in range(n): + for c in range(n): + edge = r in (0, n-1) or c in (0, n-1) + if edge: + parts.append(f'') + parts.append(f'0') + else: + parts.append(f'') + parts.append("") + return "".join(parts) + +sv1, sv2 = stride_svg() +slide(head("PART 1 · THE TRICK", "Two sliding rules: stride and padding", + "Both are just rules about HOW the stencil walks across the photo.") + f""" +
    +
    +
    Stride — the step size
    +
    {sv1}{sv2}
    +

    Stride 1: the stencil visits every position. Stride 2: it skips every other one — the answer map comes out half the size.

    +
    +
    +
    Padding — zeros glued around the border
    + {padding_svg()} +

    Without padding the border pixels never sit under the stencil’s center, and the map shrinks each layer. The dashed ring of invisible zeros fixes both.

    +
    +
    + {knobbox([ + ("stride 1 → 2", "output map half the size; cheaper, but a blurrier view"), + ("no padding", "a 3×3 stencil shrinks a 7×7 photo to 5×5 every layer"), + ("padding 1", "the map stays the same size as the photo"), + ("kernel 3×3 → 7×7", "sees bigger patterns at once, but 27 → 147 numbers to learn per filter — slower, needs more data"), + ])} +
    +
    +""") + +# ---- S9 pooling +def pool_svg(): + cell = 88 + vals = [[1,3,2,1],[4,8,1,0],[2,1,9,5],[0,3,2,6]] + quads = [(0,0,8),(0,2,2),(2,0,4),(2,2,9)] + w = 4*cell + parts = [f''] + for r in range(4): + for c in range(4): + v = vals[r][c] + mx = max(vals[(r//2)*2][(c//2)*2], vals[(r//2)*2][(c//2)*2+1], vals[(r//2)*2+1][(c//2)*2], vals[(r//2)*2+1][(c//2)*2+1]) + fill = "rgba(0,0,0,0.10)" if v == mx else "#fff" + parts.append(f'') + parts.append(f'{v}') + # quad borders + parts.append(f'') + parts.append(f'') + parts.append(f'') + parts.append(f'') + # arrow + ax = 4*cell + 40 + parts.append(f'') + parts.append(f'') + # output 2x2 + ox = ax + 120 + out = [[8,2],[3,9]] + for r in range(2): + for c in range(2): + parts.append(f'') + parts.append(f'{out[r][c]}') + parts.append("") + return "".join(parts) + +slide(head("PART 1 · THE TRICK", "Pooling — shrink the map, forgive small shifts") + f""" +
    +
    + {bullets([ + "Look at each little 2×2 window and keep only the biggest number: “was the pattern here at all?”", + "The map shrinks to half size → everything after it is 4× cheaper.", + "If the pattern moves by one pixel, the biggest number in the window usually does not change → small shifts stop mattering.", + "Cousin: average pooling takes the mean instead. At the very end, averaging each whole map down to ONE number gives a short summary list of the photo.", + ], size=25)} +
    +
    +
    Max pooling, 2×2 — keep each quadrant’s winner
    + {pool_svg()} +
    + {namebox([("MAX / AVERAGE POOLING", "keep the max (or the mean) of each small window"), + ("GLOBAL AVERAGE POOLING", "average each whole map to one number — the final summary step")])} +
    +
    +
    +""") + +# ---- S10 ReLU +def relu_svg(): + w, h = 560, 360 + parts = [f''] + cx, cy = w/2, h/2 + parts.append(f'') + parts.append(f'') + parts.append(f'') + parts.append(f'') + parts.append(f'negative → 0') + parts.append(f'positive → kept') + parts.append("") + return "".join(parts) + +slide(head("PART 1 · THE TRICK", "ReLU — the “keep the good news” switch") + f""" +
    +
    + {bullets([ + "After each convolution, walk over the map and replace every negative number with 0. Positives pass through untouched. That is the entire rule.", + "Why bother? Stacking plain multiply-and-add layers is like stacking rulers — a stack of rulers is still a ruler. One bend between layers lets each layer build something NEW on top of the last.", + "Without it, a 50-layer network computes nothing more than a 1-layer one.", + ], size=25)} +
    + {namebox([("ACTIVATION FUNCTION", "the bend between layers; ReLU is the standard one"), + ("RELU", "max(0, x) — negatives become 0, positives stay"), + ("SIGMOID / TANH", "older bends that squash numbers into 0–1 or −1–1 — you will see the names")])} +
    +
    +
    {relu_svg()}
    +
    +""") + +# ---- S11 the full CNN +def pipeline_svg(): + w, h = 1640, 250 + parts = [f''] + stages = [("PHOTO", "224×224×3"), ("FILTERS + RELU", "many glow-maps"), ("POOL", "half size"), + ("FILTERS + RELU", "deeper patterns"), ("POOL", "half again"), + ("GLOBAL AVERAGE", "short list = embedding"), ("SCORES", "cat 92%")] + n = len(stages); bw = 200; gap = (w - n*bw) / (n-1) + for i, (t, s) in enumerate(stages): + x = i*(bw+gap) + fill = "#000" if i == n-1 else "#fff" + tcol = "#fff" if i == n-1 else "#000" + scol = "rgba(255,255,255,0.55)" if i == n-1 else "rgba(0,0,0,0.55)" + parts.append(f'') + parts.append(f'{t}') + parts.append(f'{s}') + if i < n-1: + ax = x + bw + parts.append(f'') + parts.append(f'') + parts.append("") + return "".join(parts) + +slide(head("PART 1 · THE TRICK", "Stack it all — the complete seeing machine", + "Filters find edges. The next layer looks at the GLOW-MAPS and finds combinations: textures, then parts, then objects.") + f""" +
    {pipeline_svg()}
    +
    + {namebox([("CONVOLUTIONAL NEURAL NETWORK — CNN", "the whole stack of filter layers"), + ("EMBEDDING", "the short list of numbers that summarizes the photo"), + ("IMAGE CLASSIFICATION", "one label for the whole photo — what this machine does")])} + {namebox([("SOFTMAX", "turns final scores into percentages that add to 100"), + ("EDGES → TEXTURES → PARTS → OBJECTS", "what layers 1, 2, 3… learn to find, in that order")], label="And the ladder it climbs")} +
    +""") + +# ---- S12 code card 1 +slide(head("PART 1 · CODE", "The whole “eye” is a few honest lines", + "Every line below is something you already understand. Read the comments, not the syntax.") + f""" +
    +
    +{code('''import torch.nn as nn + +model = nn.Sequential( + nn.Conv2d(3, 32, kernel_size=3, padding=1), # 32 stencils, 3x3, on R,G,B + nn.ReLU(), # keep the good news + nn.MaxPool2d(2), # shrink, forgive shifts + nn.Conv2d(32, 64, kernel_size=3, padding=1), # 64 deeper stencils + nn.ReLU(), + nn.AdaptiveAvgPool2d(1), # global average -> summary + nn.Flatten(), + nn.Linear(64, 10), # 10 class scores +)''', caption="A real, complete image classifier — this shape scores ~50% on a 10-class photo task (chance = 10%)")} +
    +
    + {knobbox([ + ("out_channels 32 → 64", "more patterns spotted per layer; slower, more to learn"), + ("kernel_size 3 → 7", "each stencil sees a bigger patch; weights jump 27 → 147 per filter"), + ("delete the ReLU lines", "the stack collapses into one big ruler — accuracy tanks"), + ("add more conv blocks", "deeper ladder: textures → parts → objects; needs more data and time"), + ])} +
    +
    +""") + +# ---- quick check 1 +def quiz(part, qa, myth=None): + rows = "".join( + f'

    {q}

    ' + f'

    {a}

    ' + for q, a in qa) + m = "" + if myth: + m = (f'
    Myth vs reality' + f'

    Myth: {myth[0]}

    Reality: {myth[1]}

    ') + slide(head(f"{part} · QUICK CHECK", "Close the laptop lids — quick check", + "Answers out loud, no grades. If the room can answer these, we have earned the next part.") + f""" +
    +
    {rows}
    +
    {m}
    +
    +""") + +quiz("PART 1", [ + ("A friend says “the computer looks at the photo.” What does it ACTUALLY get?", + "A grid of numbers, 0–255 — three grids for a color photo. Nothing else."), + ("What does a filter do, in one sentence?", + "It slides over the photo, multiplies and adds, and writes a big number wherever its pattern appears."), + ("Why does a CNN need ReLU between layers?", + "Without the bend, the whole stack collapses into one layer — stacked rulers are still a ruler."), + ("What does pooling buy us?", + "Smaller maps (cheaper) and forgiveness for small shifts of the pattern."), +], myth=("AI sees the way we do — it just “recognizes” the cat.", + "It has no eyes and no idea what a cat is. It does millions of multiply-and-adds on a number grid, and the numbers it multiplies by were learned, not programmed.")) + +# ================================================================ PART 2 — THE LEARNING + +# ---- S13 statement +slide(f""" +
    PART 2 · THE LEARNING
    +

    Nobody programs the filters.
    The network finds them.

    +

    On slide 6, WE chose the nine stencil numbers. In a real network, all the stencils — millions of numbers — start as random noise. Then they learn.

    +""", dark=True) + +# ---- S14 learning loop +def loop_svg(): + w, h = 700, 560 + parts = [f''] + boxes = [("PHOTO", 60, 40), ("MODEL GUESSES", 60, 170), ("COMPARE WITH LABEL", 60, 300), ("LOSS = HOW WRONG", 60, 430)] + for t, x, y in boxes: + parts.append(f'') + parts.append(f'{t}') + if y < 430: + parts.append(f'') + parts.append(f'') + # feedback arrow + parts.append(f'') + parts.append(f'') + parts.append(f'NUDGE EVERY NUMBER') + parts.append("") + return "".join(parts) + +slide(head("PART 2 · THE LEARNING", "Learning is the hot-and-cold game") + f""" +
    +
    + {bullets([ + "Show the network a photo. It guesses: “70% dog, 20% cat…” — badly, because its stencils are random.", + "We know the label is “cat”, so we hand it one number — how wrong it was. Big number = ice cold.", + "A recipe then tells every stencil number which direction to nudge to be slightly less wrong.", + "One photo, one tiny nudge. A million photos later, the random stencils have become the edge detectors you saw earlier. Nobody put them there.", + ], size=25)} +
    + {namebox([("LOSS", "the wrongness score — that is the entire meaning of the word"), + ("BACKPROPAGATION", "the recipe that works out each number’s nudge direction"), + ("EPOCH", "one full pass through the whole dataset")])} +
    +
    +
    {loop_svg()}
    +
    +""") + +# ---- S15 gradient descent +def hill_svg(): + w, h = 640, 420 + parts = [f''] + # valley curve + parts.append(f'') + # ball positions stepping down + pts = [(110, 88), (180, 130), (240, 210), (290, 285), (335, 340)] + for i, (x, y) in enumerate(pts): + r = 13 if i == len(pts)-1 else 10 + fill = "#000" if i == len(pts)-1 else "rgba(0,0,0,0.3)" + parts.append(f'') + parts.append(f'downhill, step by step') + return "".join(parts) + "" + +def bounce_svg(): + w, h = 640, 420 + parts = [f''] + parts.append(f'') + pts = [(120, 95), (520, 75), (170, 122), (470, 95)] + for i, (x, y) in enumerate(pts): + parts.append(f'') + if i < len(pts)-1: + nx, ny = pts[i+1] + parts.append(f'') + parts.append(f'steps too big — bouncing across the valley') + return "".join(parts) + "" + +slide(head("PART 2 · THE LEARNING", "Wrongness is a landscape. Training rolls downhill.", + "Imagine every possible setting of the stencil numbers as a point in a hilly landscape — height = loss. Training = rolling the ball to a low spot.") + f""" +
    +
    +
    Good step size
    + {hill_svg()} +
    +
    +
    Step size 10× too big
    + {bounce_svg()} +
    +
    +
    + {namebox([("GRADIENT DESCENT", "the rolling-downhill procedure"), + ("LEARNING RATE", "the step size — the single most important knob in training"), + ("ADAM", "gradient descent with momentum and self-adjusting steps; the sensible default")])} + {knobbox([ + ("learning rate too small", "the ball crawls; training takes forever"), + ("learning rate 10× too big", "loss bounces or explodes; it never settles"), + ("a good default", "Adam with learning rate 0.001, then let it shrink during training"), + ])} +
    +""") + +# ---- S16 overfitting +def overfit_svg(): + w, h = 760, 460 + parts = [f''] + # axes + parts.append(f'') + parts.append(f'') + # train curve: rises steadily + parts.append(f'') + # test curve: rises then falls + parts.append(f'') + # stop marker + parts.append(f'') + parts.append(f'STOP HERE') + parts.append(f'train ↑') + parts.append(f'test ↓ = memorizing') + parts.append(f'TIME (EPOCHS) →') + parts.append(f'ACCURACY →') + return "".join(parts) + "" + +slide(head("PART 2 · THE LEARNING", "Overfitting — memorizing is not understanding") + f""" +
    +
    + {bullets([ + "A student who memorizes past papers aces every practice test — and fails the real exam. Networks do exactly this.", + "So we split the dataset: TRAIN photos (study material) and TEST photos (the real exam — the network NEVER studies these).", + "Watch both scores. Train accuracy climbs forever. Test accuracy rises… then falls. The fall is the moment memorizing started.", + "Fixes: stop at the peak, get more varied data, or use the “forget a little on purpose” brakes below.", + ], size=25)} +
    + {namebox([("OVERFITTING", "scoring high on study material, low on the real exam"), + ("TRAIN / TEST SPLIT", "study material vs the never-seen exam"), + ("EARLY STOPPING · DROPOUT · WEIGHT DECAY", "the standard anti-memorizing brakes")])} +
    +
    +
    + {overfit_svg()} +
    + {knobbox([("more epochs", "train accuracy up forever; test accuracy up, then down"), + ("more varied data", "memorizing starts later or never")])} +
    +
    +
    +""") + +# ---- S17 augmentation +slide(head("PART 2 · THE LEARNING", "Augmentation — free extra photos", + "A flipped cat is still a cat. So every time a photo is used, change it a little — the network can never just memorize it.") + f""" +
    +
    ORIGINAL
    +
    FLIPPED
    +
    CROPPED
    +
    RECOLORED
    +
    NOISY
    +
    +
    +
    +{code('''train_tf = transforms.Compose([ + transforms.RandomCrop(32, padding=4), # random framing + transforms.RandomHorizontalFlip(), # mirror half the time + transforms.ToTensor(), +])''', caption="Real lines from the training code that produced the numbers on slide 19")} +
    + {knobbox([ + ("apply to TEST photos", "never — the exam must stay fixed"), + ("too strong (crop 90% away)", "the label itself is destroyed; accuracy drops"), + ("the classic trap", "flipping digits turns 6 into 9 — every change must keep the label true"), + ])} +
    +""") + +# ---- S18 learned filters +slide(head("PART 2 · THE LEARNING", "What the network actually taught itself") + f""" +
    +
    + {bullets([ + "These are the REAL first-layer stencils of ResNet18, a famous network trained on 1.28 million photos.", + "Nobody drew these. They started as random noise.", + "They became edge and color detectors — the network discovered that edges are the right place to start seeing.", + "Layer 2 looks at edge-maps and finds textures: fur, stripes, mesh. Higher layers: ear, eye, wheel — then “cat”.", + ], size=25)} +
    +
    + +

    All 64 first-layer filters of ResNet18, enlarged. Compare with the stencil you computed by hand on slide 6.

    +
    + EDGES  →  TEXTURES  →  PARTS  →  OBJECTS +
    +

    the ladder every trained CNN climbs, layer by layer

    +
    +
    +""") + +# ---- S19 receipts +def bars_svg(): + w, h = 1500, 520 + data = [("50.0%", "SMALL CNN, FROM SCRATCH", "4,000 training photos · 3 epochs", 50.0), + ("74.2%", "PRETRAINED RESNET18, FINETUNED", "only 1,000 training photos", 74.2), + ("88.5%", "CLIP, ZERO-SHOT", "0 training photos", 88.5)] + parts = [f''] + bw = 330; gap = (w - 3*bw) / 2 + maxh = 360 + for i, (v, t, s, pct) in enumerate(data): + x = i*(bw+gap); bh = maxh * pct/100; y = 430 - bh + parts.append(f'') + parts.append(f'{v}') + parts.append(f'{t}') + parts.append(f'{s}') + # chance line + cy = 430 - maxh*0.10 + parts.append(f'') + parts.append(f'CHANCE 10%') + return "".join(parts) + "" + +slide(head("PART 2 · RECEIPTS", "Real numbers, run on an ordinary laptop CPU", + "Task: 10 kinds of small photos (CIFAR-10) — cat, ship, truck… Random guessing scores 10%. Every number below is reproducible from the notebooks in this repo.") + f""" +
    {bars_svg()}
    +

    The two bigger bars are Part 3 and Part 4 of this talk: borrowing trained eyes, and models that learned from the whole internet.

    +
    ASK THE ROOM Before revealing: guess what the from-scratch CNN scored. Closest wins.
    +""") + +# ================================================================ PART 3 — THE SHORTCUT + +# ---- statement +slide(dark=True, body=f""" +
    PART 3 · THE SHORTCUT
    +

    Never start
    from scratch.

    +

    Someone already spent weeks of computer time teaching a network to see, on 1.28 million photos. Those trained eyes are free to download.

    +""") + +# ---- pretrained + transfer learning +def transfer_svg(): + w, h = 700, 480 + parts = [f''] + # backbone + parts.append(f'') + parts.append(f'TRAINED EYES') + parts.append(f'all the filter layers') + parts.append(f'edges → textures → parts') + parts.append(f'🔒 FROZEN — keep as-is') + # old head crossed out + parts.append(f'') + parts.append(f'OLD DECISION') + parts.append(f'1000 classes') + parts.append(f'') + parts.append(f'') + # new head + parts.append(f'') + parts.append(f'NEW DECISION') + parts.append(f'YOUR 10 classes') + parts.append(f'') + parts.append(f'') + parts.append(f'swap only the last piece, retrain that') + return "".join(parts) + "" + +slide(head("PART 3 · THE SHORTCUT", "Borrow trained eyes, swap the final decision", + "A chef who switches from Italian to Japanese food does not relearn chopping and seasoning — only the new recipes. Networks transfer the same way.") + f""" +
    +
    + {bullets([ + "Networks trained on ImageNet (1.28M photos, 1000 classes) already know edges, textures, fur, wheels — skills useful for ANY photo task.", + "Chop off only the final decision layer, bolt on a fresh one for YOUR classes, train mostly that.", + "That is how 1,000 photos beat 4,000 on slide 20: 74.2% vs 50.0% — the eyes came pre-trained.", + "Rule of thumb: with a pretrained start, 100–1,000 photos per class is often enough.", + ], size=25)} +
    + {namebox([("PRETRAINED MODEL / BACKBONE", "a network someone already trained; the part you keep"), + ("TRANSFER LEARNING", "reusing trained eyes on a new task"), + ("FINETUNING", "the gentle retraining on your own photos"), + ("FREEZING", "locking layers so training cannot change them")])} +
    +
    +
    {transfer_svg()}
    +
    +""") + +# ---- ResNet skip connections +def skip_svg(): + w, h = 660, 430 + parts = [f''] + # main path + parts.append(f'') + parts.append(f'INPUT x') + parts.append(f'') + parts.append(f'FILTER LAYERS') + parts.append(f'learn the CHANGE') + parts.append(f'') + parts.append(f'+') + parts.append(f'') + parts.append(f'') + parts.append(f'') + parts.append(f'') + # skip arrow + parts.append(f'') + parts.append(f'') + parts.append(f'SKIP CONNECTION') + parts.append(f'the express lane:') + parts.append(f'x rides through untouched') + parts.append(f'') + parts.append(f'') + parts.append(f'OUTPUT = change + x') + return "".join(parts) + "" + +slide(head("PART 3 · THE SHORTCUT", "ResNet — why “deeper” suddenly worked", + "Surprise from 2015: a 56-layer network scored WORSE than a 20-layer one. Deeper should never be worse — a deep net could just copy the shallow one. The fix was one humble arrow.") + f""" +
    +
    + {bullets([ + "In a deep stack, the learning signal must travel back through every layer — like a message whispered down a 50-person line, it fades to nothing.", + "ResNet adds an express lane around every block: the input skips ahead and is added back at the end.", + "Each block now only learns the small CHANGE it wants to make. Doing nothing is easy: change = 0.", + "The learning signal rides the express lanes backwards too — 50, 100, 152 layers suddenly train fine.", + ], size=25)} +
    + {namebox([("SKIP / RESIDUAL CONNECTION", "the express lane: output = input + learned change"), + ("RESNET-18 / -50", "the standard pretrained backbones; the number counts the layers")])} +
    +
    +
    {skip_svg()}
    +
    +""") + +# ---- code card 2: transfer learning +slide(head("PART 3 · CODE", "Transfer learning is four honest lines", + "This is the exact recipe behind the 74.2% bar — borrow, freeze, swap, train.") + f""" +
    +
    +{code('''from torchvision import models + +model = models.resnet18(weights="IMAGENET1K_V1") # 1. borrow trained eyes + +for p in model.parameters(): + p.requires_grad = False # 2. freeze them + +model.fc = nn.Linear(512, 10) # 3. swap the decision layer + +# 4. train as usual -- only the new layer learns''', caption="Result on our laptop: 74.2% with 1,000 photos — vs 50.0% from scratch with 4,000")} +
    +
    + {knobbox([ + ("unfreeze everything", "more flexible, but with few photos it overfits — and a high learning rate DESTROYS the pretrained eyes"), + ("tiny dataset (<500 photos)", "keep frozen — frozen usually WINS here"), + ("plenty of photos", "unfreeze with a small learning rate (0.0001) for the backbone"), + ("weights=None", "random start — you are back to the 50% bar"), + ])} +
    +
    +""") + +# ================================================================ PART 4 — SUPERPOWERS + +# ---- statement +slide(dark=True, body=f""" +
    PART 4 · THE SUPERPOWERS
    +

    One label per photo
    was just the beginning.

    +

    Same trained eyes, different heads bolted on: find every object, cut them out pixel by pixel, talk to images in plain English, create images from nothing.

    +""") + +# ---- detection +slide(head("PART 4 · SUPERPOWERS", "Object detection — WHERE, not just what") + f""" +
    +
    +
    +
    Real output of a detector (SSDLite) on our cat: cat 97%
    +
    +
    Same model, busier photo: person 91% — many objects, one pass
    +
    +
    + {bullets([ + "The model outputs a list: box + label + confidence for every object it can find — all in one pass over the photo.", + "Messy detail: the raw model proposes MANY overlapping boxes per object. A cleanup step keeps each cluster’s most confident box and deletes the rest.", + "How do we grade a box? Overlap with the true box: area of overlap ÷ area of union. 1.0 = perfect, ≥0.5 traditionally counts as a hit.", + ], size=24)} +
    + {namebox([("BOUNDING BOX", "the rectangle around an object"), + ("CONFIDENCE", "how sure the model is about that box"), + ("IoU — INTERSECTION OVER UNION", "the overlap score for grading boxes"), + ("NMS — NON-MAXIMUM SUPPRESSION", "the duplicate-box cleanup step")])} +
    +
    + {knobbox([("confidence cutoff 0.5 → 0.8", "fewer boxes, fewer false alarms — but real objects get missed"), + ("NMS overlap limit down", "stricter cleanup; two cats sitting together may merge into one box")])} +
    +
    +
    +""") + +# ---- detector families +slide(head("PART 4 · SUPERPOWERS", "Three detector families you should recognize", + "All reuse a pretrained backbone as their eyes — the difference is the head and the speed/accuracy trade.") + f""" +
    + + + + + + + + + + + +
    FamilyThe idea, in plain wordsCharacterPick it when
    YOLO
    “You Only Look Once”
    cut the photo into a grid; every cell predicts boxes for objects centered in it — one single passthe speed king — 150+ photos/second on a GPUlive video, robots, drones
    SSDsame one-pass idea, but predicts from several map sizes at once — big maps catch small objectsfast and light — it produced the cat slidephones and small devices
    DETRa transformer head asks ~100 learned questions: “is there an object like X anywhere?” — no grid, no NMS cleanup neededelegant, accurate, slower to trainaccuracy over speed, modern stacks
    +
    +

    For the exam: know the names, the one-pass idea, and that YOLO = real-time. You do not need their internals.

    +""") + +# ---- segmentation +slide(head("PART 4 · SUPERPOWERS", "Segmentation — a label for every pixel") + f""" +
    +
    PHOTO
    +
    MASK — EVERY PIXEL: CAT OR NOT
    +
    CUTOUT = PHOTO × MASK
    +
    +
    +
    + {bullets([ + "Boxes are rectangles; cats are not. Segmentation answers per pixel: which class does this pixel belong to?", + "The classic shape is a letter U: shrink the photo to understand WHAT is there, then grow it back to full size to say WHERE, pixel-perfect.", + "Shrinking loses fine detail — so express lanes copy crisp edges from the shrinking side straight to the growing side. Skip connections again!", + ], size=24)} +
    +
    + {namebox([("SEGMENTATION MASK", "the per-pixel answer sheet"), + ("SEMANTIC vs INSTANCE", "“all cat pixels” vs “cat #1 vs cat #2”"), + ("U-NET / ENCODER-DECODER", "the shrink-then-grow architecture with skip connections"), + ("WHERE YOU MET IT", "background blur on video calls, medical scans, self-driving")])} +
    +
    +""") + +# ---- quick check 2 +quiz("PARTS 3–4A", [ + ("Why did 1,000 photos beat 4,000 photos in our experiment?", + "The 1,000-photo model started with pretrained eyes (transfer learning); the 4,000-photo model started from random noise."), + ("What does a skip connection let a block do?", + "Learn only the small CHANGE to its input — and let the learning signal travel deep without fading."), + ("Detection vs segmentation, one sentence each?", + "Detection: a box + label + confidence per object. Segmentation: a class for every single pixel."), + ("What does NMS throw away?", + "Duplicate overlapping boxes around the same object — keeps the most confident one."), +], myth=("To use deep learning you need millions of photos and a giant computer.", + "With a pretrained backbone, 100–1,000 photos per class and a laptop is often enough. Our 74.2% ran on a CPU.")) + +# ---- self-supervised +def contrastive_svg(): + w, h = 700, 460 + parts = [f''] + # photo to two views + parts.append(f'') + parts.append(f'ONE PHOTO') + for dy, lbl in [(0, "VIEW A: crop+recolor"), (140, "VIEW B: flip+blur")]: + parts.append(f'') + parts.append(f'{lbl}') + parts.append(f'') + # embedding space + parts.append(f'') + parts.append(f'EMBEDDING SPACE') + parts.append(f'') + parts.append(f'') + parts.append(f'pull together') + parts.append(f'') + parts.append(f'') + parts.append(f'other photos: push apart') + return "".join(parts) + "" + +slide(head("PART 4 · SUPERPOWERS", "Self-supervised — learning without any labels", + "Labels are the expensive part: humans must tag every photo. The internet has billions of photos with no tags. Trick: make the photo its own label.") + f""" +
    +
    + {bullets([ + "Take a photo. Make two different augmented views of it (crop one, flip and blur the other) — slide 18’s tricks, reused.", + "Rule for the network: views of the SAME photo must land close together in embedding space; different photos far apart.", + "To pull this off it must learn what MATTERS in an image — real seeing skills, no human labels anywhere.", + "Then add a small decision layer with just a few labeled photos on top. Nearly free trained eyes.", + ], size=24)} +
    + {namebox([("SELF-SUPERVISED LEARNING", "the data provides its own training signal — no human labels"), + ("CONTRASTIVE LEARNING", "the pull-together / push-apart recipe")])} +
    +
    +
    {contrastive_svg()}
    +
    +""") + +# ---- CLIP +clip_rows = "".join( + f'
    ' + f'{lbl}' + f'
    ' + f'{pct:.1f}%
    ' + for lbl, pct in sorted(zip(clip["prompts"], [p * 100 for p in clip["probs"]]), + key=lambda x: -x[1]) +) + +slide(head("PART 4 · SUPERPOWERS", "CLIP — the model you can talk to", + "Don’t teach it classes at all. Describe them in English and let the model pick the closest description.") + f""" +
    +
    + {bullets([ + "CLIP is TWO encoders: one turns photos into embeddings, one turns sentences into embeddings — in the SAME space.", + "Trained on 400 million internet (photo, caption) pairs with the contrastive recipe from the last slide: photo and its caption pull together.", + "To classify: embed the photo, embed each candidate sentence, pick the closest. New classes = new sentences. Zero retraining.", + "That is the 88.5% bar from slide 20 — zero training photos.", + ], size=24)} +
    + {namebox([("CLIP", "contrastive language–image pretraining — a shared space for photos and text"), + ("ZERO-SHOT", "solving a task with no task-specific training examples"), + ("PROMPT", "the sentence you classify with — wording matters")])} +
    +
    +
    +
    Real CLIP output on our cat photo
    + {clip_rows} +
    + {knobbox([("rephrase the prompt", "“a photo of a {{class}}” beats the bare word — a few % for free"), + ("add classes", "just add sentences — no retraining"), + ("bigger CLIP (ViT-L/14)", "~63% → ~75% zero-shot on the 1000-class benchmark, ~3× slower")])} +
    +
    +
    +""") + +# ---- code card 3: CLIP +slide(head("PART 4 · CODE", "Zero-shot classification, complete and honest") + f""" +
    +
    +{code('''import clip, torch + +model, preprocess = clip.load("ViT-B/32") # trained eyes + text eyes +labels = ["a photo of a cat", "a photo of a dog", + "a photo of a tiger", "a photo of a pizza"] + +image = preprocess(my_photo).unsqueeze(0) # photo -> tensor +text = clip.tokenize(labels) # sentences -> tokens + +img_emb = model.encode_image(image) # both into the SAME space +txt_emb = model.encode_text(text) + +sims = (img_emb @ txt_emb.T).softmax(dim=-1) # closeness -> percentages''', caption="Output on our cat: cat 97.9% · tiger 1.6% · dog 0.5% · pizza 0.0%")} +
    +
    + {knobbox([ + ("swap the labels list", "an entirely new classifier, instantly"), + ("“cat” → “a photo of a cat”", "matches how captions looked in training — accuracy up"), + ("ask something weird", "“a photo of a grumpy cat” works too — it is just a sentence"), + ])} +
    +
    +""") + +# ---- GANs +def gan_svg(): + w, h = 720, 420 + parts = [f''] + parts.append(f'') + parts.append(f'GENERATOR') + parts.append(f'noise → fake photo') + parts.append(f'') + parts.append(f'DISCRIMINATOR') + parts.append(f'real or fake?') + parts.append(f'') + parts.append(f'') + parts.append(f'fakes') + parts.append(f'') + parts.append(f'') + parts.append(f'real photos') + parts.append(f'') + parts.append(f'') + parts.append(f'“caught you” — feedback the generator learns from') + parts.append(f'both get better every round — until fakes pass inspection') + return "".join(parts) + "" + +slide(head("PART 4 · SUPERPOWERS", "GANs — a counterfeiter and a detective", + "Now the other direction: not understanding images — CREATING them.") + f""" +
    +
    + {bullets([ + "Two networks train against each other. The counterfeiter turns random noise into fake photos. The detective sees real photos and fakes, and calls real-or-fake.", + "Every round the detective gets sharper, so the counterfeiter must get better — an arms race that ends in photorealistic fakes.", + "Honest subtlety: the counterfeiter NEVER sees a real photo. It learns only from the detective’s feedback.", + "Famous, fast, but moody to train — sometimes the counterfeiter finds ONE face that always works and prints it forever.", + ], size=24)} +
    + {namebox([("GAN — GENERATIVE ADVERSARIAL NETWORK", "the two-player forgery game"), + ("GENERATOR / DISCRIMINATOR", "the counterfeiter / the detective"), + ("MODE COLLAPSE", "the one-trick-forger failure mode")])} +
    +
    +
    {gan_svg()}
    +
    +""") + +# ---- diffusion +slide(head("PART 4 · SUPERPOWERS", "Diffusion — the idea behind modern image generators", + "DALL·E, Stable Diffusion, Midjourney — under the hood it is this. And you already know every ingredient.") + f""" +
    +
    Generation runs RIGHT to LEFT: start from pure noise, remove a little at each step
    +
    +
    CLEAN
    +
    STEP 250
    +
    STEP 500
    +
    STEP 750
    +
    PURE NOISE
    +
    +
    +
    +
    + {bullets([ + "Training: take a photo, add a random amount of noise, ask a network to predict the noise. Easy, stable homework — no arms race.", + "Generating: start from pure noise, remove a little ~50 times. A text prompt (via CLIP-style text understanding) steers every step.", + ], size=24)} +
    +
    + {namebox([("DIFFUSION MODEL", "learns to remove noise; generates by denoising step by step"), + ("GUIDANCE SCALE", "how hard the prompt steers — ~7.5 is typical; too high looks burnt"), + ("STEPS", "more = better and slower; 20–50 is common")])} +
    +
    +""") + +# ================================================================ PART 5 — CHEAT SHEETS + +# ---- quick check 3 +quiz("PART 4B", [ + ("How does CLIP classify a photo of a thing it was never explicitly taught?", + "It embeds the photo and your candidate sentences into the same space and picks the closest sentence."), + ("In a GAN, which network never sees a real photo?", + "The generator — it learns only from the discriminator’s feedback."), + ("What is a diffusion model’s only skill?", + "Removing a little noise. Generation = applying that skill ~50 times starting from pure noise."), + ("Self-supervised learning has no labels. What replaces them?", + "The photo itself: two augmented views must land close together in embedding space."), +]) + +# ---- syllabus map +slide(head("PART 5 · CHEAT SHEET", "The IOAI syllabus map — where we covered what", + "Every Section 3 topic, the slide where it lives, and the one line to remember.") + f""" +
    + + + + + + + + + + + + + + +
    Syllabus topicSlidesOne line to remember
    Images as tensors, pixels05a photo is 3 stacked grids of numbers 0–255
    Convolution, filters, stride, padding06–08slide a stencil, multiply & add; stride = step size, padding = zero border
    Pooling (max/avg/global)09keep each window’s winner — smaller map, shift-forgiving
    Activations (ReLU)10zero the negatives — without the bend, depth is fake
    CNNs & classification11–12stacked filters climb edges → textures → parts → objects
    Loss, gradient descent, overfitting, augmentation14–18roll downhill on the wrongness landscape; never grade on the study set
    Pretrained encoders, ResNet, transfer learning21–23borrow trained eyes, swap the decision layer; skips let depth train
    Detection (YOLO/SSD/DETR), IoU, NMS25–26box + label + confidence in one pass; IoU grades, NMS dedupes
    Segmentation, U-Net27a class per pixel; shrink to understand, grow back to localize
    Self-supervised / contrastive29two views of one photo pull together — the data labels itself
    CLIP & zero-shot30–31photos and sentences share one space; new classes = new sentences
    Generative: GANs, diffusion32–33forgery arms race vs learn-to-denoise — diffusion won the quality war
    +
    +""") + +# ---- glossary +gloss = [ + ("pixel", "one square of a photo, holding a number 0–255"), + ("tensor", "a grid (or stack of grids) of numbers"), + ("model", "a machine made of numbers: input → guess"), + ("label", "the correct answer attached to an example"), + ("convolution", "slide a stencil, multiply matching cells, add"), + ("filter / kernel", "the stencil of numbers"), + ("feature map", "the glow-map: bright = pattern found here"), + ("stride", "how far the stencil steps each move"), + ("padding", "zeros glued around the border"), + ("pooling", "keep each window's max (or mean) — shrink the map"), + ("ReLU", "negatives → 0; the bend that makes depth real"), + ("CNN", "the full stack of filter layers"), + ("embedding", "the short number-list summary of an input"), + ("softmax", "scores → percentages that sum to 100"), + ("loss", "the wrongness score"), + ("gradient descent", "nudge every number downhill on the loss"), + ("learning rate", "the step size of those nudges"), + ("epoch", "one full pass through the dataset"), + ("overfitting", "memorizing the study set, failing the exam"), + ("augmentation", "flip/crop/recolor — free extra photos"), + ("pretrained / backbone", "a network someone already trained"), + ("transfer learning", "reuse trained eyes on a new task"), + ("finetuning", "gentle retraining on your own data"), + ("skip connection", "express lane: output = input + change"), + ("bounding box / IoU / NMS", "detection's rectangle, grade, and dedupe"), + ("segmentation mask", "a class for every pixel"), + ("self-supervised", "the data provides its own training signal"), + ("CLIP / zero-shot", "shared photo-text space; classify by sentence"), + ("GAN", "counterfeiter vs detective"), + ("diffusion", "generate by removing noise, step by step"), +] +half = (len(gloss) + 1) // 2 +def gcol(items): + return "".join( + f'

    {t}' + f' — {d}

    ' for t, d in items) +slide(head("PART 5 · CHEAT SHEET", "Every word we earned today — all 30") + f""" +
    +
    {gcol(gloss[:half])}
    +
    {gcol(gloss[half:])}
    +
    +""") + +# ---- knob cheat sheet +slide(head("PART 5 · CHEAT SHEET", "The knob table — change this, get that", + "The practical round usually asks exactly this: predict what a change does before you run it.") + f""" +
    + + + + + + + + + + + + + + +
    Turn this knobWhat happens
    more filters per layer (32 → 128)richer patterns spotted; ~4× the numbers to learn and compute
    bigger kernel (3×3 → 7×7)sees wider patterns; 9 → 49 multiplies per stop
    stride 1 → 2output map halves; faster, blurrier view
    remove ReLUdepth collapses to one layer; accuracy tanks
    learning rate ×100loss bounces or explodes — never settles
    learning rate ÷100crawls; may never arrive in time
    train far past the test-accuracy peakoverfitting — train score up, exam score down
    add flip/crop augmentationtypically +3–8% test accuracy on small datasets
    pretrained start vs random startour receipts: 74.2% with 1,000 photos vs 50.0% with 4,000
    detector confidence cutoff upfewer false alarms, more missed objects
    better CLIP prompt wordinga few % accuracy for free — no retraining
    diffusion guidance scale up (7.5 → 20)obeys the prompt harder; colors go burnt, variety drops
    +
    +""") + +# ---- close +slide(dark=True, body=f""" +
    THE END · GO WIN
    +

    It was never magic.
    It was multiply, add,
    and practice.

    +

    Everything today ran on an ordinary laptop, and every number on these slides is real. The notebooks to re-run them are in our repo.

    +""") + +# ================================================================ render +def main(): + html = ("" + f"" + "".join(SLIDES) + "") + out_html = HERE / "beginner_deck.html" + out_html.write_text(html) + print(f"slides: {len(SLIDES)}") + from weasyprint import HTML + HTML(str(out_html), base_url=str(HERE.parent)).write_pdf(str(HERE.parent / "presentation" / "how_machines_learn_to_see.pdf")) + print("PDF written") + +if __name__ == "__main__": + main() diff --git a/vision-models/beginner/build/build_pptx.py b/vision-models/beginner/build/build_pptx.py new file mode 100644 index 0000000..f5e012d --- /dev/null +++ b/vision-models/beginner/build/build_pptx.py @@ -0,0 +1,28 @@ +# Builds the Google Slides-ready PPTX from the rendered deck PDF. +# Each slide is a full-bleed image, so the design survives any import. +# Run: uv run --with pymupdf --with python-pptx python build_pptx.py +import os, tempfile +import fitz +from pptx import Presentation +from pptx.util import Inches + +HERE = os.path.dirname(os.path.abspath(__file__)) +DECK = os.path.join(HERE, "..", "presentation", "how_machines_learn_to_see.pdf") +OUT = os.path.join(HERE, "..", "presentation", "how_machines_learn_to_see.pptx") + +def main(): + doc = fitz.open(DECK) + prs = Presentation() + prs.slide_width, prs.slide_height = Inches(13.333), Inches(7.5) + blank = prs.slide_layouts[6] + with tempfile.TemporaryDirectory() as td: + for i, page in enumerate(doc): + png = os.path.join(td, f"{i}.png") + page.get_pixmap(dpi=96).save(png) # 1920x1080 + s = prs.slides.add_slide(blank) + s.shapes.add_picture(png, 0, 0, width=prs.slide_width, height=prs.slide_height) + prs.save(OUT) + print("wrote", OUT, f"({len(doc)} slides)") + +if __name__ == "__main__": + main() diff --git a/vision-models/beginner/build/build_teacher_guide.py b/vision-models/beginner/build/build_teacher_guide.py new file mode 100644 index 0000000..d94fc54 --- /dev/null +++ b/vision-models/beginner/build/build_teacher_guide.py @@ -0,0 +1,104 @@ +# Builds teacher_guide.pdf — a slide-by-slide companion in simple English. +# Run from this folder: uv run --with weasyprint --with pymupdf python build_teacher_guide.py +import html as H +import os +import fitz +from weasyprint import HTML +from guide_content import GUIDE + +HERE = os.path.dirname(os.path.abspath(__file__)) +DECK_PDF = os.path.join(HERE, "..", "presentation", "how_machines_learn_to_see.pdf") +THUMB_DIR = os.path.join(HERE, "_thumbs") +OUT = os.path.join(HERE, "..", "teacher", "teacher_guide.pdf") + +CSS = """ +@page { size: 210mm 297mm; margin: 16mm 16mm 18mm 16mm; + @bottom-right { content: counter(page); font-family: 'Roboto Mono', monospace; + font-size: 8pt; color: #999; } } +* { box-sizing: border-box; } +body { font-family: 'Satoshi', 'Helvetica Neue', Arial, sans-serif; color: #000; + font-size: 10.5pt; line-height: 1.5; margin: 0; } +.cover { page-break-after: always; padding-top: 60mm; } +.cover .kicker { font-family: 'Roboto Mono', monospace; font-size: 9pt; letter-spacing: 0.2em; + text-transform: uppercase; color: #666; } +.cover h1 { font-size: 34pt; font-weight: 500; letter-spacing: -0.02em; line-height: 1.1; + margin: 8mm 0 6mm 0; } +.cover p { font-size: 12pt; color: #333; max-width: 150mm; } +.howto { background: #f4f4f4; padding: 6mm 7mm; margin-top: 10mm; max-width: 160mm; } +.howto p { font-size: 10.5pt; margin: 0 0 2.5mm 0; } +.entry { page-break-inside: avoid; margin-bottom: 9mm; border-top: 1.2pt solid #000; + padding-top: 4mm; } +.entry .num { font-family: 'Roboto Mono', monospace; font-size: 8.5pt; letter-spacing: 0.18em; + color: #666; text-transform: uppercase; } +.entry h2 { font-size: 15pt; font-weight: 600; letter-spacing: -0.01em; margin: 1.5mm 0 3.5mm 0; } +.thumb { width: 74mm; float: right; margin: 0 0 3mm 5mm; border: 0.4pt solid #ccc; } +.lbl { font-family: 'Roboto Mono', monospace; font-size: 7.5pt; letter-spacing: 0.18em; + text-transform: uppercase; color: #888; margin: 3mm 0 1mm 0; } +.mean p { margin: 0 0 2.5mm 0; } +.say { background: #000; color: #fff; padding: 3.5mm 4.5mm; margin-top: 2mm; } +.say p { margin: 0; font-size: 10pt; } +.say .lbl { color: #aaa; margin: 0 0 1mm 0; } +.ask { background: #f4f4f4; padding: 3mm 4.5mm; margin-top: 2.5mm; } +.ask p { margin: 0 0 1.5mm 0; font-size: 9.5pt; } +.ask .lbl { margin: 0 0 1mm 0; } +.clear { clear: both; } +b { font-weight: 600; } +""" + + +def render_thumbs(): + os.makedirs(THUMB_DIR, exist_ok=True) + doc = fitz.open(DECK_PDF) + for i, page in enumerate(doc): + out = os.path.join(THUMB_DIR, f"s{i+1:02d}.png") + if not os.path.exists(out): + page.get_pixmap(dpi=60).save(out) + return len(doc) + + +def entry_html(e): + paras = "".join(f"

    {p}

    " for p in e["mean"]) + ask = "" + if e.get("ask"): + qa = "".join(f'

    “{H.escape(q)}” — {H.escape(a)}

    ' for q, a in e["ask"]) + ask = f'
    If they ask
    {qa}
    ' + return f""" +
    +
    Slide {e['n']:02d} of 39
    +

    {e['title']}

    + +
    What this actually means
    +
    {paras}
    +
    Say this

    {e['say']}

    + {ask} +
    +
    """ + + +def main(): + n = render_thumbs() + assert n == len(GUIDE) == 39, (n, len(GUIDE)) + cover = """ +
    +
    Teacher guide · read me the night before
    +

    How machines learn to see
    — explained simply, slide by slide

    +

    This guide walks through all 39 slides of the presentation in the simplest English possible. + For each slide: a small picture of it, what it actually means, the key sentence to say out loud, + and answers to questions students might ask.

    +
    +

    How to use this guide

    +

    1. Read it once, start to finish. It takes about 45 minutes.

    +

    2. Any part that makes you pause — read that section twice. The cheat-sheet slides (36–38) double as your own revision check.

    +

    3. While presenting, you do not need this guide open — the “say this” lines are the backbone, and the slides carry the rest.

    +

    4. Slide 6 is the heart of the whole talk. Practice the nine multiplications once so you can lead them confidently.

    +
    +
    """ + body = cover + "".join(entry_html(e) for e in GUIDE) + html = f"{body}" + os.makedirs(os.path.dirname(OUT), exist_ok=True) + HTML(string=html, base_url=HERE).write_pdf(OUT) + print("wrote", OUT) + + +if __name__ == "__main__": + main() diff --git a/vision-models/beginner/build/guide_content.py b/vision-models/beginner/build/guide_content.py new file mode 100644 index 0000000..b6090a6 --- /dev/null +++ b/vision-models/beginner/build/guide_content.py @@ -0,0 +1,260 @@ +# Teacher guide content — one entry per slide, written in the simplest English possible. +# mean = "what this actually means" paragraphs; say = what to say out loud; ask = likely questions. + +GUIDE = [ +dict(n=1, title="Cover — How machines learn to see", +mean=["This is just the title page. The talk is about computer vision: how a computer can look at a photo and tell you what is in it.", +"One promise to make at the start: every number in this deck is real. We really ran these experiments on a normal laptop. Nothing is made up."], +say="Today you will learn how a computer recognizes a cat, finds it in a photo, and even draws one. The only math you need is multiplying small numbers."), + +dict(n=2, title="The plan", +mean=["The day has five parts, and they build on each other like a story:", +"Part 1: the one basic trick a computer uses to 'see' (we will do it by hand with real numbers). Part 2: how the computer learns that trick by itself, by practicing. Part 3: a shortcut — instead of teaching a computer from zero, you borrow one that someone already taught. Part 4: the cool extras — finding objects in a photo, cutting them out, talking to a model in English, and creating new images. Part 5: cheat sheets to keep.", +"The rule for the whole day: we never use a fancy word before explaining the idea behind it in plain words first."], +say="One day, one story. If you can multiply small numbers, you can follow every single step."), + +dict(n=3, title="Five starter words", +mean=["Before starting, we agree on five words. These are the only words used without warning later, so make sure YOU are comfortable with all five:", +"MODEL = the 'machine' itself. It is really just a huge collection of numbers. You put something in (a photo), it gives a guess back ('cat').", +"DATASET = the pile of examples we teach with. For us: lots of photos, each with its correct answer attached.", +"LABEL = the correct answer for one example. The photo of a cat has the label 'cat'.", +"TRAINING = showing the model examples again and again until its guesses become good. Like practice.", +"ACCURACY = a score. Out of 100 tries, how many did it get right? 90% accuracy = 90 right out of 100."], +say="Read all five out loud, slowly. Tell them: every other word today gets explained before we use it.", +ask=[("Is a model a robot?", "No — it is just a very long list of numbers stored in a file, plus a recipe for using them. No body, no eyes, no brain.")]), + +dict(n=4, title="(Dark slide) A computer has never seen a cat", +mean=["This is a dramatic pause slide. The point: a computer has no eyes. It has never 'seen' anything. So when we say it recognizes a cat, something else must be going on — and that something is just math on numbers.", +"This question ('so what DOES it get?') sets up the whole next slide."], +say="Pause. Ask the room: if it has no eyes, what does the computer actually get when we give it a photo? Let them guess before advancing."), + +dict(n=5, title="A photo is just a grid of numbers", +mean=["This is the first big idea. If you zoom very far into any photo, it breaks into tiny squares. Each square is one PIXEL.", +"Each pixel holds ONE number between 0 and 255. 0 means black, 255 means white, numbers in between are shades of gray. So a black-and-white photo is literally a big grid (table) of numbers, like a spreadsheet.", +"A color photo is THREE of these grids stacked on top of each other: one grid says how red each pixel is, one how green, one how blue. Mixing red, green and blue makes every color.", +"The grid of numbers on the slide is REAL — it is the actual numbers inside the small square marked on the cat's eye.", +"The scary word TENSOR just means 'a grid of numbers, possibly several stacked'. That is all. A photo is a tensor."], +say="Ask: where are the BIG numbers — in the bright fur or the dark pupil? (Bright fur, because 255 = white.) Then the key sentence: three grids of numbers — that is ALL the computer ever gets.", +ask=[("Why 0 to 255?", "Computers store numbers in bytes. One byte can hold exactly 256 different values (0–255). It is just a convention.")]), + +dict(n=6, title="Convolution by hand — THE most important slide", +mean=["Take your time here. If the students understand this slide, the rest of the day is easy.", +"The tiny image: a column of 2s (bright stripe) next to 0s (dark area). Where bright meets dark, there is an EDGE — a border. Question: can math FIND that edge?", +"The stencil: nine numbers we chose — left column is 1s (plus), right column is −1s (minus), middle is 0s.", +"The move: lay the stencil on the image. Multiply each image number by the stencil number sitting on top of it. Add all nine answers. That single total is the result for that position.", +"On the edge: the 2s sit on the +1 side, the 0s on the −1 side → total = 6, a big number. The math is shouting 'my pattern is HERE!'", +"On a flat dark area: everything is 0 → total = 0. The math says 'nothing here.'", +"So a stencil of nine numbers can DETECT an edge. This multiply-and-add move is called CONVOLUTION, and the stencil is called a FILTER (or KERNEL). Those two words sound scary but you just did the whole thing with times tables."], +say="Do the nine multiplications OUT LOUD with the room before showing the total. Then the reveal: congratulations, you just did a convolution.", +ask=[("Who chose the nine stencil numbers?", "We did, for teaching. In a real network nobody chooses them — they are learned. That is Part 2.")]), + +dict(n=7, title="Slide it everywhere — feature maps", +mean=["One position gives one number. Now slide the same stencil across the WHOLE photo, stopping at every position, doing the multiply-and-add each time, and writing down each answer.", +"All those answers together form a new image. It is bright wherever the stencil's pattern was found, dark where it was not. This 'glow map' is called a FEATURE MAP.", +"On the slide you can see it really works: with our vertical-edge stencil, the cat's whiskers and eye outlines glow, because those are vertical-ish edges. Rotate the stencil 90 degrees and horizontal edges glow instead.", +"Key idea: the NUMBERS in the stencil decide WHAT pattern is found. Different numbers = different pattern."], +say="Point at the glowing whiskers: this is the real output of the exact stencil we just used, run on this exact photo."), + +dict(n=8, title="Stride and padding", +mean=["Two small, boring-but-needed rules about HOW the stencil walks across the photo. Don't overthink these.", +"STRIDE = the step size. Stride 1: the stencil stops at every position. Stride 2: it jumps two cells each move, so it makes half as many stops, and the answer map comes out half the size. Faster, but a blurrier view.", +"PADDING = a ring of zeros glued around the photo's border. Why? Two problems: (1) without it, the border pixels never get to sit under the CENTER of the stencil, so the edges of the photo get ignored; (2) the answer map comes out a bit smaller than the photo every time, so after many layers it shrinks away. The ring of zeros fixes both. The zeros are 'invisible' — they add nothing to the multiply-and-add."], +say="Both are just walking rules: stride = how big the steps are, padding = zeros around the border so the edges are not ignored. Don't linger here."), + +dict(n=9, title="Pooling — shrink the map, forgive small shifts", +mean=["Take the glow map and cut it into little 2×2 windows. From each window, keep ONLY the biggest number and throw the other three away. This is MAX POOLING.", +"Why keep the max? The big number means 'the pattern was found here'. We only care IF it was found in this small area — not its exact pixel. So we keep the strongest signal per window.", +"Two wins: (1) the map becomes half the size, so everything after is 4× cheaper to compute; (2) if the pattern shifts by one pixel, the biggest number in the window is usually still the same — so tiny shifts of the object stop mattering. A cat slightly to the left is still a cat.", +"Check the example with them: top-left window holds 1, 3, 4, 8 → keep 8.", +"Cousins: AVERAGE POOLING keeps the mean instead of the max. And at the very end of a network, GLOBAL AVERAGE POOLING averages each whole map down to ONE number — making a short summary list of the photo."], +say="Keep each window's winner. Verify one quadrant together out loud."), + +dict(n=10, title="ReLU — the 'keep the good news' switch", +mean=["The rule itself is tiny: go through the map and replace every NEGATIVE number with 0. Positive numbers pass through unchanged. That is literally all ReLU does.", +"The hard part is WHY. Here is the simplest version: convolution is just multiplying and adding — 'straight-line math'. If you stack many straight-line steps, the result is still one straight-line step. Like stacking rulers: a stack of rulers is still a ruler. So 50 layers would compute nothing more than 1 layer could. Depth would be fake.", +"Putting one little 'bend' (the ReLU) between layers breaks the straight line. Now each layer can build something genuinely NEW on top of the previous one. The bend is what makes deep networks actually deep.", +"Words: any 'bend' function between layers is called an ACTIVATION FUNCTION. ReLU is the standard modern one. SIGMOID and TANH are older bends — students only need to recognize the names."], +say="Anchor sentence: negatives become zero — and without that little bend, a 50-layer network is exactly as smart as a 1-layer one.", +ask=[("Why would the map have negative numbers at all?", "Stencils contain negative numbers (like our −1 column), so totals can come out negative. Negative roughly means 'the opposite of my pattern is here' — ReLU throws that away and keeps only positive evidence.")]), + +dict(n=11, title="The complete CNN stack", +mean=["Now plug everything together into one pipeline. Photo goes in → a layer of many stencils + ReLU → pooling → another layer of stencils + ReLU → pooling → global average → final scores ('cat 92%').", +"The magic of stacking: layer 1's stencils look at the photo and find edges. Layer 2's stencils look at the GLOW MAPS of layer 1 — so they find combinations of edges: corners, textures (fur, stripes). Layer 3 finds combinations of textures: an ear, an eye, a wheel. The ladder goes EDGES → TEXTURES → PARTS → OBJECTS. Nobody tells the network to do this — it appears by itself.", +"Words on this slide: the whole stack is a CNN (convolutional neural network). The short summary list of numbers near the end is the EMBEDDING. SOFTMAX is the small last step that turns scores into percentages that add to 100. The whole job (one label per photo) is called IMAGE CLASSIFICATION."], +say="Stencils on stencils. Layer 1 finds edges, layer 2 finds textures, layer 3 finds parts. Nobody told it to — that ladder appears on its own."), + +dict(n=12, title="Code card 1 — the CNN in PyTorch", +mean=["Don't fear this slide — every line is a thing the students already understand. Read the COMMENTS, not the syntax.", +"Conv2d(3, 32, kernel_size=3) = 'make 32 stencils, each 3×3, that look at the 3 color grids'. ReLU() = the bend. MaxPool2d(2) = pooling. AdaptiveAvgPool2d(1) = global average (the summary list). Linear(64, 10) = the final scoring step that turns the summary into 10 class scores.", +"This is a real, complete classifier — this exact shape scored about 50% on a 10-class photo task, where blind guessing scores 10%.", +"The knob box is exam material: more stencils = more patterns spotted but slower; bigger stencils = sees bigger patches but many more numbers to learn; DELETE the ReLU lines and accuracy collapses (the ruler-stack problem); more layers = deeper ladder but needs more data and time."], +say="Read the comments out loud, mapping each line to the idea they already know. Then quiz them gently: what happens if I delete the ReLU lines?"), + +dict(n=13, title="Quick check 1", +mean=["First checkpoint. Run it relaxed and out loud — popcorn style, no grades. The answers are on the slide.", +"The four questions test: (1) the computer only gets grids of numbers; (2) a filter slides, multiplies and adds, and lights up where its pattern is; (3) ReLU stops the stack collapsing into one layer; (4) pooling makes maps smaller and forgives small shifts.", +"The myth box matters: AI does NOT see like us. It has no eyes and no idea what a cat 'is'. It does millions of multiply-and-adds on a number grid."], +say="If the room struggles on the ReLU question, re-anchor with: 'the bend that makes depth real — stacked rulers are still a ruler.'"), + +dict(n=14, title="(Dark slide) Nobody programs the filters", +mean=["Transition into Part 2. On slide 6, WE chose the nine stencil numbers by hand. A real network has MILLIONS of stencil numbers. Nobody could choose them all by hand — and nobody does.", +"They start as random noise, and the network finds good values by practicing. How that practice works is the whole of Part 2."], +say="One breath: 'we picked nine numbers; a real network has millions, and nobody picks them. They are learned. How?' Advance."), + +dict(n=15, title="The learning loop (loss, backpropagation, epoch)", +mean=["Learning is the hot-and-cold game. The loop: (1) show the network a photo. Its stencils are random, so it guesses badly: '70% dog'. (2) We know the label is 'cat', so we calculate ONE number that says how wrong the guess was. That number is the LOSS. Big loss = ice cold. Small loss = warm. That's the entire meaning of the word 'loss': the wrongness score.", +"(3) A mathematical recipe then works out, for EVERY stencil number in the network, which direction to nudge it (up or down a tiny bit) to make the same mistake slightly smaller. That recipe is called BACKPROPAGATION — you don't need to know its inside, just its job: 'it tells every number which way to nudge'.", +"(4) Repeat with the next photo. One photo = one tiny nudge. After a million photos, the random stencils have turned into the edge detectors from slide 7. Nobody put them there.", +"An EPOCH = one full pass through the whole dataset (every photo seen once)."], +say="Guess → compare with the answer → nudge every number a tiny bit → repeat. That is all training is."), + +dict(n=16, title="Gradient descent and learning rate", +mean=["A picture for the nudging. Imagine every possible setting of all the stencil numbers as one point in a landscape with hills and valleys. The HEIGHT of each point = the loss (the wrongness) you'd get with that setting. Good settings live in the valleys.", +"Training = placing a ball somewhere random and rolling it DOWNHILL, step by step. That procedure is GRADIENT DESCENT ('gradient' just means slope — follow the slope down).", +"The LEARNING RATE is the SIZE of each step. It is the single most important setting in training. Too small: the ball crawls — training takes forever. Too big (right picture): the ball leaps right over the valley, bounces back and forth across it forever, and never settles. The loss 'explodes'.", +"A safe default the students can quote: the Adam optimizer with learning rate 0.001, shrinking the rate as training goes."], +say="Wrongness is a landscape, training rolls a ball downhill, and the learning rate is how big each step is.", +ask=[("How does the ball know which way is downhill?", "Calculus gives the slope at the current point for every number at once. In code it is one line: loss.backward(). You never compute it by hand.")]), + +dict(n=17, title="Overfitting — memorizing is not understanding", +mean=["Picture a student who memorizes last year's exam papers. Perfect on the practice papers — fails the real exam, because the real exam has new questions. Networks do exactly this: with enough practice on the same photos, they start memorizing those exact photos instead of learning what a cat looks like. That is OVERFITTING.", +"The protection: split the dataset in two. TRAIN photos = study material. TEST photos = the real exam — the network NEVER trains on these, we only use them to measure honestly.", +"The graph: train accuracy keeps climbing forever (memorizing always helps the study set). Test accuracy rises, peaks, then FALLS. The moment it falls is the moment memorizing started. So: stop at the peak.", +"Names to recognize (not to deeply learn): EARLY STOPPING (stop at the peak), DROPOUT and WEIGHT DECAY (built-in 'forget a little on purpose' brakes), and getting more varied data — the best fix of all."], +say="Never grade a model on its study material. Watch the test score, and stop when it peaks."), + +dict(n=18, title="Augmentation — free extra photos", +mean=["A flipped photo of a cat is still a cat. A slightly cropped one too. So: every time a training photo is used, change it a little at random — flip it, shift the framing, tweak the colors. The network can never memorize an exact photo because it never sees the same photo twice. It is forced to learn the IDEA of the cat instead. This is AUGMENTATION — basically free extra training data.", +"The code lines on the slide are the real ones from our own training run.", +"Two traps to mention: (1) NEVER augment the test photos — the exam must stay fixed. (2) Every change must keep the label true: flipping a digit 6 turns it into a 9 — that augmentation would destroy the label."], +say="A flipped cat is still a cat — so flip, crop and recolor on the fly, and memorizing becomes impossible."), + +dict(n=19, title="What the network taught itself", +mean=["This slide is proof that the whole story is true. You are looking at the REAL first-layer stencils of ResNet18, a famous network trained on 1.28 million photos.", +"Every one of these little patches started as pure random noise. After training, they became edge detectors and color detectors — strikingly similar to the stencil we built BY HAND on slide 6.", +"Nobody drew them. The network discovered, by rolling downhill on the loss, that edges are the right place to start seeing. Layer 2 (not shown) finds textures like fur and stripes; higher layers find ears, eyes, wheels."], +say="Nobody drew these. They started as random noise — and training turned them into the same kind of stencil we built by hand."), + +dict(n=20, title="RECEIPTS — the three numbers to remember", +mean=["The most quotable slide of the deck. The task: classify small photos into 10 classes (cat, ship, truck...). Blind guessing scores 10%. Everything ran on a normal laptop CPU, and is reproducible from our repo.", +"Bar 1: a small CNN trained from scratch on 4,000 photos → 50.0%. Bar 2: a borrowed (pretrained) ResNet18, gently retrained on only 1,000 photos → 74.2%. Bar 3: CLIP, with ZERO training photos → 88.5%.", +"Sit with that: fewer photos, better score — and then NO photos, best score. The rest of the talk explains those two bigger bars: Part 3 = borrowing trained eyes, Part 4 = models that learned from the whole internet."], +say="Before revealing, ask the room to guess the from-scratch score — closest wins. Then: remember these three numbers — 50, 74, 88.5 — with 4,000, 1,000, and ZERO photos."), + +dict(n=21, title="(Dark slide) Never start from scratch", +mean=["Transition into Part 3. Someone already spent weeks of computer time training a network to see, on 1.28 million photos. Those 'trained eyes' are free to download. So in practice, you almost never train from zero."], +say="One breath, advance."), + +dict(n=22, title="Transfer learning — borrow trained eyes", +mean=["The chef story: a chef who switches from Italian to Japanese cooking does not relearn how to chop and season — only the new recipes. Networks transfer the same way.", +"A network trained on ImageNet (1.28 million photos, 1000 classes) already knows edges, textures, fur, wheels — skills useful for ANY photo task. So: keep all the filter layers ('the trained eyes'), chop off only the final decision layer, bolt on a fresh one for YOUR classes, and train mostly just that new piece.", +"That is exactly how 1,000 photos beat 4,000 on the receipts slide: 74.2% vs 50.0%. The eyes came pre-trained.", +"Words: the downloaded network = PRETRAINED MODEL or BACKBONE. Reusing it = TRANSFER LEARNING. The gentle retraining = FINETUNING. Locking layers so training can't change them = FREEZING.", +"Rule of thumb worth quoting: with a pretrained start, 100–1,000 photos per class is often enough."], +say="Keep the trained eyes, swap the final decision layer. That is how 1,000 photos beat 4,000."), + +dict(n=23, title="ResNet — why deeper suddenly worked", +mean=["A real story from 2015: researchers found a 56-layer network scored WORSE than a 20-layer one. That should be impossible — a deeper net could in principle just copy the shallow one and ignore the extra layers. Something was broken.", +"What was broken: during training, the 'nudge' signal must travel backwards through every layer. Like a message whispered down a line of 50 people, it fades to almost nothing by the time it reaches the early layers. Deep nets couldn't train.", +"The fix is one humble arrow: a SKIP CONNECTION (or residual connection) — an express lane around every block of layers. The input skips ahead and is simply ADDED back to the block's output. So output = input + the block's learned CHANGE.", +"Two consequences: each block only needs to learn the small change it wants to make (and 'do nothing' becomes easy: change = 0); and the learning signal can ride the express lanes backwards without fading. Suddenly 50, 100, even 152 layers train fine.", +"RESNET-18 and RESNET-50 are the standard pretrained backbones; the number counts the layers."], +say="Tell the 2015 surprise first, then the fix: one express lane that adds the input back. Each block learns only the small change."), + +dict(n=24, title="Code card 2 — transfer learning in four lines", +mean=["The exact recipe behind the 74.2% bar. Line 1: download ResNet18 with its ImageNet-trained weights ('borrow trained eyes'). Lines 2–3: freeze every parameter so training can't damage the eyes. Line 4: replace the final decision layer (fc) with a fresh one for our 10 classes. Then train normally — only the new layer actually learns.", +"Knob box (exam material): unfreeze everything = more flexible but easily overfits with few photos, and a high learning rate destroys the pretrained eyes. Under ~500 photos: keep frozen — frozen usually wins. Plenty of photos: unfreeze, but with a tiny learning rate (0.0001) for the backbone. weights=None = random start = you're back to the 50% bar."], +say="Four honest lines: borrow, freeze, swap, train."), + +dict(n=25, title="(Dark slide) One label per photo was just the beginning", +mean=["Transition into Part 4. So far the machine answers one question: 'what is in this photo?' — one label per photo. The same trained eyes, with different 'heads' bolted on top, can do much more: find every object, cut them out pixel by pixel, understand English descriptions, and create brand-new images."], +say="One breath, advance."), + +dict(n=26, title="Object detection — WHERE, not just what", +mean=["Detection answers 'what is WHERE'. The model outputs a LIST: for every object it finds, a rectangle around it (BOUNDING BOX), a label, and a CONFIDENCE (how sure it is). All found in one pass over the photo. The slide shows real output on our cat photo: 'cat 97%'.", +"Messy detail worth knowing: the raw model actually proposes MANY overlapping boxes around the same object. A cleanup step called NMS (non-maximum suppression) keeps the most confident box of each cluster and deletes the duplicates.", +"How do we grade a predicted box? IoU (intersection over union): area where the predicted and true boxes OVERLAP, divided by the area they COVER TOGETHER. 1.0 = perfect match; 0.5 or more traditionally counts as a hit.", +"Knobs: raise the confidence cutoff → fewer false alarms but real objects get missed. Make NMS stricter → two cats sitting close together may merge into one box."], +say="Box + label + confidence, for every object, in one pass. IoU grades a box, NMS deletes the duplicates."), + +dict(n=27, title="Three detector families — YOLO, SSD, DETR", +mean=["Students only need the names, the one-line idea, and the 'character' of each. All three reuse a pretrained backbone as their eyes.", +"YOLO ('You Only Look Once'): cut the photo into a grid; every grid cell predicts boxes for objects centered in it — one single pass. The speed king: 150+ photos per second on a GPU. Pick for live video, robots, drones.", +"SSD: same one-pass idea, but predicts from several map sizes at once, so big maps catch small objects. Fast and light — this is the model that produced our cat slide. Pick for phones and small devices.", +"DETR: a transformer head asks about 100 learned 'questions' ('is there an object like X anywhere?'). No grid, and no NMS cleanup needed. Elegant and accurate, slower to train. Pick when accuracy beats speed.", +"For the exam: know the names, the one-pass idea, and that YOLO = real-time. Internals are NOT needed."], +say="Know the names and characters: YOLO = speed king, SSD = light for phones, DETR = the elegant transformer one."), + +dict(n=28, title="Segmentation — a label for every pixel", +mean=["Boxes are rectangles, but cats are not. SEGMENTATION answers, for every single pixel: which class does this pixel belong to? The answer sheet — a grid where each pixel is marked 'cat' or 'not cat' — is the SEGMENTATION MASK. Multiply photo × mask and you get a perfect cutout.", +"Two flavors: SEMANTIC = 'all cat pixels' as one group; INSTANCE = 'cat #1's pixels vs cat #2's pixels' separately.", +"The classic architecture is shaped like the letter U (U-NET): the left side SHRINKS the photo (convolutions + pooling — exactly Part 1) to understand WHAT is there; the right side GROWS it back to full size to say WHERE, pixel-perfect.", +"Problem: shrinking loses fine detail. Fix: express lanes copy the crisp edges from the shrinking side straight across to the growing side. Skip connections again — same trick as ResNet!", +"Where they've met it: background blur in video calls, medical scans, self-driving cars."], +say="A class for EVERY pixel. Shrink to understand what, grow back to say where — and skip connections carry the sharp edges across."), + +dict(n=29, title="Quick check 2", +mean=["Checkpoint for Parts 3 and 4a. Answers on the slide: (1) 1,000 beat 4,000 because of pretrained eyes; (2) a skip connection lets a block learn only the small CHANGE, and lets the learning signal travel deep without fading; (3) detection = box + label + confidence per object, segmentation = a class per pixel; (4) NMS throws away duplicate overlapping boxes.", +"The myth box is the most important: 'you need millions of photos and a giant computer' — FALSE. With a pretrained backbone, 100–1,000 photos per class and a laptop is often enough. Our 74.2% ran on a CPU."], +say="Run it popcorn-style. Spend the most time on the myth — it is the most empowering fact of the day."), + +dict(n=30, title="Self-supervised learning — no labels at all", +mean=["The expensive part of a dataset is not the photos — it's the LABELS, because humans must tag every photo by hand. The internet has billions of photos with no tags. Can a network learn to see from unlabeled photos? Yes — trick: make the photo its own label.", +"The recipe: take one photo. Make TWO different augmented views of it (crop one; flip and blur the other — slide 18's tricks reused). Rule for the network: embeddings of views of the SAME photo must land CLOSE together; embeddings of DIFFERENT photos must land far apart. Pull together, push apart — that's why it's called CONTRASTIVE LEARNING.", +"To satisfy this rule, the network is forced to learn what actually MATTERS in an image (the cat-ness survives a crop and a blur; the exact pixels don't). Real seeing skills, zero human labels. This whole family is SELF-SUPERVISED LEARNING — the data supplies its own training signal.", +"Afterwards, add a small decision layer trained with just a few labeled photos — nearly-free trained eyes."], +say="Two augmented views of the same photo must land close together. To manage that, the network must understand images — and no human ever labeled anything."), + +dict(n=31, title="CLIP — the model you can talk to", +mean=["CLIP takes the contrastive trick one step further: don't teach it classes at all. Describe the classes in English.", +"CLIP is TWO encoders: one turns PHOTOS into embeddings, the other turns SENTENCES into embeddings — into the SAME space. Trained on 400 million (photo, caption) pairs from the internet, with the pull-together/push-apart rule: each photo and its own caption pull close.", +"To classify any photo: embed the photo, embed each candidate sentence ('a photo of a cat', 'a photo of a dog', ...), pick the CLOSEST sentence. Want new classes? Just write new sentences. ZERO retraining. That is ZERO-SHOT classification — solving a task with no task-specific training examples — and it is the 88.5% bar from the receipts slide.", +"The sentence you classify with is called the PROMPT, and its wording matters: 'a photo of a cat' scores a few % better than just 'cat', because that's what internet captions looked like.", +"Real output on our cat photo: cat 97.9%, tiger 1.6%, dog 0.5%, pizza 0.0%."], +say="Two encoders, one shared space for photos and sentences. To classify: pick the closest sentence. New classes = new sentences."), + +dict(n=32, title="Code card 3 — zero-shot with CLIP", +mean=["Walk this one slowly — it is the complete zero-shot recipe, honest, nothing hidden. Load CLIP (photo eyes + text eyes). Write the labels as sentences. Turn the photo into a tensor, the sentences into tokens. Encode BOTH into the same space. Measure closeness, turn into percentages with softmax (slide 11's word!).", +"Knobs: swap the labels list → an entirely new classifier, instantly. Phrase labels as 'a photo of a ...' → accuracy up for free. Ask something weird → 'a photo of a grumpy cat' works too; it is just a sentence."], +say="Nine lines, no training loop anywhere — and it scored 88.5%. Output on our cat: 97.9%."), + +dict(n=33, title="GANs — a counterfeiter and a detective", +mean=["Now the OTHER direction: not understanding images, but CREATING them. A GAN is two networks training against each other.", +"The GENERATOR (counterfeiter) turns random noise into fake photos. The DISCRIMINATOR (detective) is shown real photos and fakes, and must call real-or-fake. Every round the detective gets sharper, so the counterfeiter must get better — an arms race that ends in photorealistic fakes.", +"The honest subtlety students love: the counterfeiter NEVER sees a real photo. It learns only from the detective's feedback ('caught you'). It learns to paint faces without ever seeing one — only from being caught.", +"GANs are famous and fast, but moody to train. Classic failure: MODE COLLAPSE — the counterfeiter finds ONE face that always fools the detective and prints it forever (the one-trick forger)."], +say="A counterfeiter and a detective, raising each other's game — and the counterfeiter never sees a real photo."), + +dict(n=34, title="Diffusion — how modern image generators work", +mean=["DALL·E, Stable Diffusion, Midjourney — under the hood, this. And the students already know every ingredient.", +"Training: take a real photo, add a random amount of noise (static), and ask a network to predict WHAT noise was added. That's it. Easy, stable homework — no arms race, no moody training.", +"Generating: start from PURE noise and apply the network's only skill — 'remove a little noise' — about 50 times in a row. Step by step an image emerges from the static. Read the slide's photo strip RIGHT to LEFT: pure noise → cat.", +"A text prompt steers every denoising step (using CLIP-style text understanding — last slide's idea reused!). That's how 'a cat astronaut on the moon' comes out.", +"Knobs: GUIDANCE SCALE = how hard the prompt steers (~7.5 typical; too high = burnt, oversaturated images). STEPS = more is better but slower; 20–50 is common.", +"GANs vs diffusion: diffusion won the image-quality war (stable training, more variety); GANs are still faster at generating."], +say="Its only skill is removing a bit of noise. Generation = that skill 50 times, starting from pure static, with the prompt steering each step."), + +dict(n=35, title="Quick check 3", +mean=["Final checkpoint, Part 4b. Answers on the slide: (1) CLIP classifies unseen things by embedding photo and sentences into the same space and picking the closest sentence; (2) in a GAN, the GENERATOR never sees a real photo; (3) a diffusion model's only skill is removing a little noise — applied ~50 times from pure noise; (4) in self-supervised learning, the labels are replaced by the photo itself: two augmented views must land close together."], +say="Popcorn-style again. If these land, the syllabus is covered."), + +dict(n=36, title="Cheat sheet 1 — the syllabus map", +mean=["Every IOAI Section 3 topic, the slide where it was covered, and a one-line summary to remember. This is the revision index for the whole deck.", +"Tell the students to screenshot this slide — before the exam they can check each row and jump back to the right slide if anything feels shaky."], +say="This is your revision index. Screenshot it."), + +dict(n=37, title="Cheat sheet 2 — all 30 terms", +mean=["Every word 'earned' during the day, each with a one-line plain-words meaning. All 30 of them.", +"Useful for you too: if you can read every line of this slide and nod, you understand the deck. Any line that makes you pause — go back to that slide in this guide and re-read it."], +say="Thirty words, all earned, all one-liners. Screenshot."), + +dict(n=38, title="Cheat sheet 3 — the knob table", +mean=["The practical-round drill table: 'change this → that happens'. The competition's practical questions usually ask exactly this — predict the effect of a change BEFORE running the code.", +"Practice idea for a team session: cover the right column, read a knob out loud, let everyone predict, then reveal."], +say="Cover the right column and quiz each other — this is exactly the practical-round skill."), + +dict(n=39, title="Closing — it was never magic", +mean=["The closing message: everything in the deck was multiply, add, and practice. Everything ran on an ordinary laptop, every number was real, and the notebooks to re-run them are in the team repo.", +"End on the empowering note: nothing today was beyond them — they computed the core operation by hand within the first ten minutes."], +say="It was never magic. It was multiply, add, and practice. Go win."), +] diff --git a/vision-models/beginner/presentation/how_machines_learn_to_see.pdf b/vision-models/beginner/presentation/how_machines_learn_to_see.pdf new file mode 100644 index 0000000..8bc019f Binary files /dev/null and b/vision-models/beginner/presentation/how_machines_learn_to_see.pdf differ diff --git a/vision-models/beginner/presentation/how_machines_learn_to_see.pptx b/vision-models/beginner/presentation/how_machines_learn_to_see.pptx new file mode 100644 index 0000000..6670677 Binary files /dev/null and b/vision-models/beginner/presentation/how_machines_learn_to_see.pptx differ diff --git a/vision-models/beginner/teacher/PRESENTER_SCRIPT.md b/vision-models/beginner/teacher/PRESENTER_SCRIPT.md new file mode 100644 index 0000000..b3fb52f --- /dev/null +++ b/vision-models/beginner/teacher/PRESENTER_SCRIPT.md @@ -0,0 +1,143 @@ +# Presenter script — "How machines learn to see" + +For the presenter. Audience: 14–18, zero coding/AI background. ~60–75 min with quizzes. +Rules that make this work: **never say a term before its Name Box appears**, do the +slide-6 multiplication WITH the room (it's the heart of the whole deck), and run the +three quick-checks out loud — no grades, just answers. + +--- + +## 01 — Cover +"Today, in about an hour, you'll understand how a computer recognizes a cat, finds it in a photo, and even draws one from scratch. The only math you need is multiplying small numbers. Everything on these slides actually ran on a normal laptop — every number is real." + +## 02 — The plan +Walk the five parts in one breath each. Promise: "Maximum ONE new word per slide, and every word lands in a cheat sheet at the end." + +## 03 — Five starter words +Read all five out loud. "These are the only words I'll use without warning. Everything else gets introduced properly when we reach it." Don't rush — these five carry the day. + +## 04 — (dark) A computer has never seen a cat +Pause here. "It has no eyes. So what DOES it get?" Let someone guess, then advance. + +## 05 — A photo is a grid of numbers +Point at the marked square on Chelsea's eye, then the real numbers below. Ask the room: where are the big numbers — bright fur or dark pupil? (Bright fur; 255 = white.) Key sentence: "Three stacked grids of numbers. That is ALL the computer ever gets." + +## 06 — Convolution by hand ★ THE CENTERPIECE +Slow down. Do the nine multiplications out loud with the room before showing the total. "Bright stripe next to dark area = an edge. The stencil multiplied and added... and got a BIG number exactly where the edge is, and 0 where nothing happens." Then the reveal: "Congratulations — you just did a convolution. That word scares people. It's times tables." + +## 07 — Slide it everywhere +"Same stencil, every position, write down every answer — you get a map of where the pattern lives." Point at the whisker glow in the real feature map: "This is real output of that exact stencil on Chelsea." + +## 08 — Stride & padding +Quick slide. "Two boring rules about HOW the stencil walks: step size, and zeros glued on the border so edges aren't ignored." Don't linger. + +## 09 — Pooling +"Keep each window's winner. Map shrinks, and a pattern that shifts one pixel still wins its window — small shifts stop mattering." Verify one quadrant with the room (top-left: 1,3,4,8 → 8). + +## 10 — ReLU +The one abstract slide in Part 1. Anchor sentence: "Negatives become zero. Without that little bend, stacking 50 layers collapses into the power of ONE layer — depth would be fake." + +## 11 — The full CNN stack +The payoff: "Stencils on stencils. Layer 1 finds edges; layer 2 looks at edge-maps and finds corners and textures; layer 3 finds eyes and wheels. Nobody told it to — that ladder appears on its own." + +## 12 — Code card 1 +"Every line is a thing you already know." Read the lines mapping words to code. Then the knob box: this is exam material — what happens if I remove ReLU? Make the kernel 7×7? + +## 13 — QUICK CHECK 1 +Out loud, popcorn style. If the room struggles on ReLU, re-anchor: "the bend that makes depth real." + +## 14 — (dark) Nobody programs the filters +"On slide 6, WE chose the nine numbers. In a real network there are millions. Nobody picks them. They're learned. How?" + +## 15 — The learning loop +"Guess → compare to the label → nudge every number so the same mistake shrinks → repeat." That's training. The wrongness score has a name: loss. + +## 16 — Gradient descent +"Imagine every possible setting of all the stencil numbers as a hilly landscape; height = wrongness. Training = rolling a ball downhill." Right panel: step too big = bouncing across the valley forever. Learning rate = THE most important knob. + +## 17 — Overfitting +The student-who-memorized-past-papers analogy. Point at the two curves: train score keeps climbing, exam score peaks then falls. "Never grade on the study set." + +## 18 — Augmentation +"A flipped cat is still a cat — free extra photos." The code lines shown are the real ones from our training run. + +## 19 — What the network learned by itself +"Nobody drew these filters. They started as random noise and became edge detectors — the same kind we hand-built on slide 6." + +## 20 — RECEIPTS ★ remember these three numbers +50.0% from scratch with 4,000 photos. 74.2% with only 1,000 photos. 88.5% with ZERO photos. "Chance is 10%. The two bigger bars are the rest of this talk." + +## 21 — (dark) Never start from scratch +One breath, advance. + +## 22 — Transfer learning +Chef analogy: switching from Italian to Japanese, you don't relearn chopping. "Keep the trained eyes, swap the final decision layer. That's how 1,000 beat 4,000." + +## 23 — ResNet skips +Tell the 2015 surprise: 56 layers scored WORSE than 20. "The fix is one arrow: an express lane that adds the input back. Each block only learns the small CHANGE. Signals stop fading; 152 layers train fine." + +## 24 — Code card 2 +"Four honest lines. Borrow, freeze, swap, train." Knob box again = exam material. + +## 25 — Detection +Point at the real outputs: cat 97%, person 91%. "Box + label + confidence, all in one pass." Then IoU (overlap ÷ union grades a box) and NMS (delete duplicate boxes). + +## 26 — YOLO / SSD / DETR +"Know the names and characters: YOLO = speed king for live video, SSD = light for phones (it made the cat slide), DETR = transformer asking ~100 questions, no cleanup needed." Internals NOT needed. + +## 27 — Segmentation +"Cats aren't rectangles. A class for EVERY pixel." U shape: shrink to understand what, grow back to say where — and skip connections carry the crisp edges across. "Skip connections again!" + +## 28 — QUICK CHECK 2 +The myth matters most: "you need millions of photos" — no, our 74.2% ran on a CPU with 1,000 photos. + +## 29 — Self-supervised +"Labels are the expensive part. Trick: two augmented views of the same photo must land close together in embedding space. To manage that, the network must learn what MATTERS in images. No human labels anywhere." + +## 30 — CLIP +"Two encoders — photos and SENTENCES — into the same space, trained on 400M internet photo-caption pairs. To classify: pick the closest sentence. New classes = new sentences, zero retraining. That's the 88.5% bar." + +## 31 — Code card 3 +Walk it slowly; this is the full zero-shot recipe. Real output: cat 97.9%, tiger 1.6%. + +## 32 — GANs +Counterfeiter vs detective arms race. The honest subtlety students love: "the counterfeiter NEVER sees a real photo — only the detective's feedback." Mode collapse = the one-trick forger. + +## 33 — Diffusion +Read the strip RIGHT to LEFT: pure noise → cat. "Its only skill is removing a little noise. Generation = that skill ~50 times, with a text prompt steering each step. This is DALL·E / Stable Diffusion / Midjourney." + +## 34 — QUICK CHECK 3 → 35 syllabus map → 36 glossary → 37 knob table +"These three slides ARE your revision. Screenshot them." The knob table is the practical-round drill: predict what a change does BEFORE running it. + +## 38–39 — Close +"It was never magic. Multiply, add, and practice. The notebooks to re-run every number are in our repo." + +--- + +## Q&A cards — likely questions + +**"How does the network know what to nudge?"** +The loss landscape has a slope at every point; calculus gives the downhill direction for every number at once. You don't compute it by hand — `loss.backward()` does. (Name if pressed: backpropagation.) + +**"Who decided the filter numbers in slide 6?"** +We did, for teaching. In real networks they start random and training nudges them — slide 19 shows the result. + +**"Is this how my brain works?"** +Loosely inspired, not a copy. Neurons don't do backpropagation as far as we know. + +**"Why 0–255?"** +One byte holds 256 values. Pure convention. + +**"What's a tensor really?"** +A grid of numbers, possibly stacked. A photo is a 3×height×width tensor. Nothing scarier. + +**"Can CLIP be wrong?"** +Yes — it inherits internet biases and can be fooled by text in the image (a pear with "iPod" written on it). Good honest answer to volunteer. + +**"GAN vs diffusion — who wins?"** +Diffusion won the quality war (stable training, better variety); GANs are still faster at generation time. + +**"Could we run this?"** +Yes — every number in the deck came from notebooks in this repo, CPU only. + +**If asked anything you don't know:** "Good question — not on our syllabus, but let's look it up after." Never bluff. diff --git a/vision-models/beginner/teacher/teacher_guide.pdf b/vision-models/beginner/teacher/teacher_guide.pdf new file mode 100644 index 0000000..d29ac19 Binary files /dev/null and b/vision-models/beginner/teacher/teacher_guide.pdf differ