Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 43 additions & 6 deletions src/components/GuideBlocks.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,21 @@
blocks,
figure,
conceptFig,
widget,
demo,
onLesson,
onPreset
}: {
slug: string;
blocks: Block[];
/** Renders a full <figure class="fig"> for an id, given the caption HTML. */
/** Renders a full <figure> for an id, given the caption HTML. */
figure?: Snippet<[string, string]>;
/** Renders the small illustration inside a concept box. */
/** Renders the illustration inside a concept box (or an overlay's backdrop). */
conceptFig?: Snippet<[string]>;
/** Renders an interactive island (widget blocks). */
widget?: Snippet<[string]>;
/** Chapter-demo override when the CTA isn't the slug's chapterPreset. */
demo?: { label: string; run: () => void };
onLesson?: (slug: string) => void;
onPreset?: (slug: string) => void;
} = $props();
Expand All @@ -58,7 +64,38 @@
{:else if b.kind === 'look'}
<p class="look">{@html richToHtml(b.text, dark)}</p>
{:else if b.kind === 'display'}
<div class="formula-display">{@html texD(formulas[b.formula])}</div>
<div class="formula-display" class:center={b.center}>{@html texD(formulas[b.formula])}</div>
{:else if b.kind === 'recipe'}
<blockquote class="recipe">{@html richToHtml(b.text, dark)}</blockquote>
{:else if b.kind === 'list'}
<ul class="knob-bullets">
{#each b.items as item}<li>{@html richToHtml(item, dark)}</li>{/each}
</ul>
{:else if b.kind === 'conceptOverlay'}
<div class="concept concept-bg-overlay">
{#if conceptFig}{@render conceptFig(b.fig)}{/if}
<div class="concept-fade"></div>
<div class="concept-text concept-text-overlay">
<h4>{b.title}</h4>
{#each b.paras as t}<p>{@html richToHtml(t, dark)}</p>{/each}
</div>
</div>
{:else if b.kind === 'proof'}
{@const lastP = b.blocks.reduce((acc, x, i) => (x.kind === 'p' ? i : acc), -1)}
<div class="proof">
<div class="proof-title">{b.title}</div>
{#each b.blocks as pb, i}
{#if pb.kind === 'p'}
<p class="proof-p">{@html richToHtml(pb.text, dark)}{#if i === lastP}{' '}<span class="proof-qed">∎</span>{/if}</p>
{:else if pb.kind === 'display'}
<div class="formula-display center">{@html texD(formulas[pb.formula])}</div>
{:else if pb.kind === 'figure'}
{#if figure}{@render figure(pb.id, richToHtml(pb.caption, dark))}{/if}
{/if}
{/each}
</div>
{:else if b.kind === 'widget'}
{#if widget}{@render widget(b.id)}{/if}
{:else if b.kind === 'concept'}
<div class="concept">
<div class="concept-text">
Expand All @@ -77,12 +114,12 @@
{/if}
{/each}

{#if lessonId || preset}
{#if lessonId || preset || demo}
<ChapterCta
{lessonId}
onLesson={lessonId && onLesson ? () => onLesson(slug) : null}
demo={preset && onPreset ? () => onPreset(slug) : null}
demoLabel={preset?.title ?? 'Watch a quick demo'}
demo={demo ? demo.run : preset && onPreset ? () => onPreset(slug) : null}
demoLabel={demo?.label ?? preset?.title ?? 'Watch a quick demo'}
/>
{/if}
{#if chRefs[slug]}
Expand Down
947 changes: 204 additions & 743 deletions src/components/HelpModal.svelte

Large diffs are not rendered by default.

26 changes: 24 additions & 2 deletions src/content/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,32 @@ export type Block =
/** A "look at the app right now" pointer (p.look) — an app-only register. */
| { kind: 'look'; text: Rich }
/** A numbered display equation from the formula registry. */
| { kind: 'display'; formula: FormulaKey }
| { kind: 'display'; formula: FormulaKey; center?: boolean }
/** The imperative recipe blockquote — "Nudge. Measure. Divide." */
| { kind: 'recipe'; text: Rich }
/** A bulleted list (the ▸-marker knob-bullets style). */
| { kind: 'list'; items: Rich[] }
/** A titled concept box, optionally with a small illustration beside it. */
| { kind: 'concept'; title: string; text: Rich; fig?: string }
/**
* The full-bleed concept variant: a background visual (named by fig,
* supplied by the chapter shell) behind a fade, with the titled text
* overlaid on the right.
*/
| { kind: 'conceptOverlay'; title: string; paras: Rich[]; fig: string }
/**
* A boxed derivation. Inner blocks render in the proof register (p →
* .proof-p, displays centred, figures as .proof-fig); the renderer seals
* the last paragraph with the ∎.
*/
| { kind: 'proof'; title: string; blocks: Block[] }
/** An "In a billion dimensions" honesty note — print's margin channel. */
| { kind: 'hd'; text: Rich }
/** A figure: the id names an app-side (later: computed) visual; the caption lives here. */
| { kind: 'figure'; id: string; caption: Rich };
| { kind: 'figure'; id: string; caption: Rich }
/**
* An interactive island with no print analogue of its own (the schedule
* gallery, later the race) — the shell supplies it; print will use a
* fallback figure.
*/
| { kind: 'widget'; id: string };
72 changes: 72 additions & 0 deletions src/content/chapters/ch-curvature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { Block } from '../blocks';

/** Chapter 8 · The bend of the bowl — curvature, the Hessian, κ, and the 2. */
export const chCurvature: Block[] = [
{
kind: 'p',
text: 'The last chapter ended on a formula pulled out of a hat: stay under $2/\\lambda_{\\max}$. This chapter earns it. What we need is one more number at every point of the landscape — not how tilted the ground is, but how quickly the tilt itself changes as you walk. The slope of the slope: the **curvature**.'
},
{
kind: 'p',
text: 'Feel the difference first. A wine glass and a soup bowl can be equally steep where you stand — same slope — but descend a little and the glass *tightens* while the bowl *relaxes*. Curvature is the rate of that tightening, and you already own the tool that measures it: nudge $\\alpha$ and divide — only this time, watch how the *slope* answers, not the loss. The derivative of the derivative, written $\\partial^2 \\mathcal{L}/\\partial \\alpha^2$ and, for the rest of this chapter, called $\\lambda$: big $\\lambda$, sharp bend; small $\\lambda$, gentle one; zero, flat as a board.'
},
{
kind: 'figure',
id: 'curvature-bend',
caption: 'Same slope underfoot — the dashed tangent is shared — but two different futures: the red curve tightens, the green one relaxes. The first derivative can’t tell them apart at the marker; the second one, $\\lambda$, is exactly what does.'
},
{
kind: 'proof',
title: 'Where the 2 comes from — in four lines',
blocks: [
{
kind: 'p',
text: 'Take the cleanest bowl there is: $\\mathcal{L} = \\tfrac{1}{2}\\lambda\\alpha^2$, curvature $\\lambda$ everywhere, minimum at zero. Its slope at $\\alpha$ is $\\lambda\\alpha$, so one step of gradient descent is'
},
{ kind: 'display', formula: 'contraction', center: true },
{
kind: 'p',
text: 'Every step *multiplies the distance to the bottom* by the same factor $(1-\\gamma\\lambda)$ — and that one multiplier is the whole story. While $\\gamma\\lambda < 1$ the factor sits between 0 and 1: a smooth glide in. At $\\gamma\\lambda = 1$ the factor is 0 — you land at the bottom in *one hop* ($\\gamma = 1/\\lambda$ is this bowl’s own perfect learning rate). Between 1 and 2 the factor is negative but small: overshoot to the far wall, yet closer each bounce. At exactly 2, you bounce between two mirror points forever. And past 2 every bounce lands *higher* than the last — divergence. There is the edge, and there is the 2.'
}
]
},
{
kind: 'figure',
id: 'curvature-regimes',
caption: 'The multiplier, run for real: amber dots are actual gradient-descent iterates on $\\mathcal{L} = \\tfrac{1}{2}\\lambda\\alpha^2$, starting from the ringed point. Glide, one-hop, shrinking bounce, growing bounce — four values of $\\gamma\\lambda$, one factor $(1-\\gamma\\lambda)$.'
},
{
kind: 'p',
text: 'Now open the second knob. At any point of a real landscape the surface bends by a *different amount in different directions* — along a valley’s floor, barely; across it, sharply. The honest bookkeeping is a small table of bendings called the **Hessian**:'
},
{ kind: 'display', formula: 'hessianMatrix', center: true },
{
kind: 'p',
text: 'Don’t let the box intimidate you. The two diagonal entries are exactly the $\\partial^2$ curvatures you just built, one per knob; the corner entry (the same number twice) records the *twist* — how nudging one knob changes the *other* knob’s slope. Four numbers, and together they pin down the little bowl that best fits the surface right where you stand. Zoom in on any smooth landscape and that fitted bowl *is* the landscape — the same way the fitted line was, one derivative ago.'
},
{
kind: 'aside',
text: '**Meet the whole family.** Stack one output’s slopes into a column and you have the **gradient**. Give the machine *many* outputs — a network predicting a hundred things at once — and each output brings its own row of slopes; the full table is the **Jacobian**, the gradient’s big sibling. And the Hessian you just met? Differentiate each entry of the gradient and stack the results: *the Hessian is exactly the Jacobian of the gradient.* One move — nudge, divide, tabulate — wearing three sizes.'
},
{
kind: 'p',
text: 'A stretched or twisted bowl still has a **gentlest** direction and a **sharpest** one — turn it in your hands until you face them. Their two bendings are called $\\lambda_{\\min}$ and $\\lambda_{\\max}$, and the last chapter’s speed limit can now be read honestly: the *sharpest* bend polices $\\gamma$ — that is $\\gamma < 2/\\lambda_{\\max}$ — while your progress along the *gentlest* direction is paid at the rate $(1 - \\gamma\\lambda_{\\min})$ per step. One $\\gamma$, two masters.'
},
{
kind: 'p',
text: 'How badly can the two masters disagree? Take their ratio:'
},
{ kind: 'display', formula: 'kappa', center: true },
{
kind: 'p',
text: 'the **condition number**. $\\kappa = 1$ is a perfectly round bowl: any safe $\\gamma$ lands you in a few hops. $\\kappa = 10$ means the sharp direction forces a $\\gamma$ so timid that the gentle direction keeps about 80% of its remaining distance *every step*. Ravines, trenches, the long crawl — they are all this one number wearing different landscapes, and Part IV’s entire optimizer family tree is organised around outwitting it.'
},
{
kind: 'look',
text: 'The app will show you the Hessian live. In the Loss & Gradient panel’s header, switch on the **curvature lens**: the ellipse drawn at the marker *is* the fitted bowl seen from above — long axis the gentle bend, short axis the sharp one — with $\\kappa$ read out beside it. On a saddle, the direction that curves *down* turns red and dashed: the escape route.'
},
{
kind: 'hd',
text: 'Up there the ravine doesn’t just stretch — it multiplies. A real network’s loss has millions of curvature directions, and measured spectra show a vast, nearly flat bulk hugging zero plus a handful of steep outliers: less a valley than a canyon system with a few sheer walls and endless soft floor. Condition numbers in the wild reach 10⁵ and beyond, so the crawl this chapter proved isn’t a corner case — it is the default condition of deep learning. That is why every method in Part IV ships in every deep-learning library.'
}
];
45 changes: 45 additions & 0 deletions src/content/chapters/ch-derivative.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Block } from '../blocks';

/** Chapter 4 · How steep, exactly? — the derivative built from nudge-and-divide. */
export const chDerivative: Block[] = [
{
kind: 'p',
text: 'Part I kept saying *slope* and trusted your legs to know what it meant. Before the walking starts in earnest, let’s put a number on it — because the number is the whole trick, and you can build it yourself with nothing but a subtraction and a division.'
},
{
kind: 'p',
text: 'Here is the move. Stand somewhere on a 1-D loss curve — say $\\alpha = 2$ on **Fit a Slope**, whose loss happens to be $\\mathcal{L}(\\alpha)=\\alpha^2$, so $\\mathcal{L}(2)=4$. **Nudge** the knob by some small amount *h*, and divide the loss’s response by the nudge. Nudge by *h* = 0.1 and the loss climbs from 4 to 4.41 — a rise of 0.41 over a run of 0.1: ratio **4.1**. Try *h* = 0.01: the ratio comes out 4.01. Try 0.001: **4.001**. The nudges are vanishing, but the ratio isn’t wandering — it is *settling*, and the number it settles on is 4.'
},
{
kind: 'recipe',
text: 'Nudge. Measure the response. Divide. Then let the nudge shrink — the number the ratio settles on is the **derivative**: the slope of the loss *at a point*.'
},
{ kind: 'display', formula: 'derivativeLimit', center: true },
{
kind: 'p',
text: 'Read it slowly, once: the fraction is exactly the nudge-and-divide you just did, and $\\lim_{h \\to 0}$ (“the limit as *h* goes to zero”) is the settling you just watched. Nothing else is hiding in there. The settling also tells you something about the ground itself: zoom in far enough on any smooth curve and it straightens into a line — the derivative is that line’s slope. Two chapters from now, that “zoom until straight” picture carries a real proof on its back; and its fine print — *the line only speaks for the ground right under you* — grows up to become the learning rate’s whole story.'
},
{
kind: 'figure',
id: 'derivative-secant',
caption: 'The limit, drawn: each grey chord leans on the curve a nudge *h* away — slope $2\\alpha + h$ on this parabola — and as *h* shrinks, the chords tilt into the one blue line whose slope is exactly $2\\alpha$: the tangent. The derivative is where the chords were heading all along.'
},
{
kind: 'p',
text: 'Two knobs, same recipe, one new courtesy: with $\\alpha$ and $\\beta$ both live, nudge **one and freeze the other**. The ratio you get is a **partial derivative**, written with a curly $\\partial$ — say it “partial”, and yes, it is the symbol on this lab’s front door:'
},
{ kind: 'display', formula: 'partialDef', center: true },
{
kind: 'p',
text: '$\\partial \\mathcal{L}/\\partial \\alpha$ reads: *nudge $\\alpha$, hold $\\beta$ still, divide the response by the nudge.* Do it once per knob and you are holding two numbers. Stacking those two numbers into a single arrow is exactly where the next chapter begins.'
},
{
kind: 'figure',
id: 'derivative-slices-3d',
caption: '“Hold the other knob still,” made literal: each translucent plane freezes one knob, and the surface passes through it as an *ordinary curve*. The blue cut’s slope at the marker is $\\partial\\mathcal{L}/\\partial\\alpha$, the amber cut’s is $\\partial\\mathcal{L}/\\partial\\beta$ — two ordinary derivatives, at right angles, on one surface. Drag to spin it.'
},
{
kind: 'aside',
text: '**How the app really does it:** you could compute every slope by literal nudging (the finite-difference recipe above — it’s how the curvature lens works). But nudging carries a whisper of error, so each problem here ships a hand-derived exact formula for its gradient instead — and the test suite trusts nothing: every formula is re-checked against nudge-and-divide at many random points. *Differentiate by hand, verify by nudge* — a professional habit worth stealing.'
}
];
Loading
Loading