EVERYTHING AIAI engineering, made visual
0/13 complete
LESSON 01 · DEEP LEARNING × AI · BUILD

Weights, a bias,
and one hard decision.

The perceptron is the atom of neural networks: multiply inputs by weights, add a bias, then step. It learns by fixing its own mistakes — until the data cannot be split by one straight line. That failure is where depth begins.

45 MIN · 7 CHAPTERSPREREQ · PHASE 1 · LINEAR ALGEBRA
FIG. 01 / MISTAKE-DRIVEN LINE · THEN DEPTH
class 0 class 1 misclassified solved by depth
LESSON 01TYPE · BUILD~45 MINPREREQ · PHASE 1 · LINEAR ALGEBRA INTUITIONORIGINAL LESSON ↗
THE 60-SECOND VERSIONThen split one open ↓
01 / WEIGHTED SUM, THEN STEP

One neuron does exactly one thing.

Multiply each input by its weight, add a bias, and step the total: 1 if it is at or above zero, 0 otherwise. With w = (1, 1) and b = −1.5 the neuron computes AND; keep the weights and raise b to −0.5 and it computes OR. Three numbers, one decision.

z = w·x + b → step(z)
02 / MISTAKES MOVE THE LINE

Right answers change nothing. Wrong ones drag the line.

wᵢ ← wᵢ + lr · error · xᵢ and b ← b + lr · error. Predicted 0 but should be 1? Add the inputs, scaled. Predicted 1 but should be 0? Subtract. The weights set the line's direction; the bias slides it sideways — and a full pass with zero mistakes ends training.

no error, no change
03 / ONE LINE IS A CEILING

XOR is where the single neuron hits a wall.

XOR asks for 1 when exactly one input is 1 — its two class-1 rows sit on one diagonal, its class-0 rows on the other. No straight line splits them; the best any line does is 3 of 4. Stack an OR neuron and a NAND neuron, feed their outputs to an AND neuron, and XOR is solved.

separability is a property of the data
MENTAL MODEL IN ONE SENTENCE

A perceptron is a weighted vote with a threshold: each input votes, its weight says how loudly, the bias says how many votes it takes to win — and learning is the tally being rewritten after every wrong answer.

By the end you will be able to compute a perceptron’s output and boundary by hand; run the learning rule step by step on AND, OR, NOT and NAND; explain why XOR is impossible for a single line at any learning rate; build the two-layer OR × NAND → AND fix; and say exactly what the step function does to gradients — the bridge to backpropagation.

ONE NEURON, ONE DECISION

Inputs in.
One hard call out.

You know vectors and dot products. The perceptron answers the next question — how does a machine learn which transformation to use? — with the smallest possible machine: weights, a bias, and a step.

Take some inputs. Multiply each by a weight. Add a bias. If the total is at least zero, output 1; otherwise output 0. That is the entire perceptron — the source lesson’s words, not a simplification. Every neural network ever built is layers of this idea stacked together.

z = w₁x₁ + w₂x₂ + … + wₙxₙ + b = w·x + b step(z) = 1 if z ≥ 0 0 if z < 0 "z is the weighted sum: how strongly the inputs argue for 1, with the bias as the head start; step throws away everything except the final verdict."

Each weight does two jobs at once: its sign says which side of the decision that input argues for, and its size says how loudly. The bias is the one number that does not scale any input — it shifts the whole decision up or down, which is why it is described as the threshold. And the step function is the hard part: it makes the output binary, and (as the last chapter shows) it also kills gradients.

one neuron · the source lesson’s diagramx₁w₁x₂w₂x₃w₃bias bΣ(wᵢxᵢ) + b= zstep(z)0 or 1out
The whole architecture. Three inputs, one weighted sum, one step. The source lesson builds exactly this as a Python class; the lab below is the same arithmetic with dials.
Worked check: two sums, two verdicts

Take x = (2, 3), w = (0.5, −0.4). The weights disagree: x₁ argues for firing, x₂ argues against, and w₂ is losing.

b = 0.1: z = 0.5·2 + (−0.4)·3 + 0.1 = 1.0 − 1.2 + 0.1 = −0.1 → z < 0 → output 0 b = 0.5: z = 1.0 − 1.2 + 0.5 = 0.3 → z ≥ 0 → output 1 the inputs and weights did not move. The bias alone flipped the answer — that is what "threshold" means.

In the other direction, w = (0.5, 0.1), b = 0.1 gives z = 1.0 + 0.3 + 0.1 = 1.4 → 1: when both weights agree with the inputs, the same three numbers produce a much more confident 1. Confidence is invisible to the step, though — z = 1.4 and z = 0.01 both print “1”.

Anatomy of one neuron: weights, bias, step

Move the five dials and watch the same arithmetic the source code runs: multiply each input by its weight, add the bias, then step the total.

w₁ = 1w₂ = 1b = -1.5x₁1x₂1biasz = Σ wᵢxᵢ + b0.5step(z)1outone weighted sum, one decision
z = w₁·x₁ + w₂·x₂ + b = 1·1 + 1·1 + (-1.5) = 1 + 1 + (-1.5) = 0.5 z ≥ 0 → step(z) = 1 set x = (1, 1), w = (1, 1), b = −1.5: z = 0.5 → out 1 (this is the AND neuron firing) keep those weights, switch to (0, 1): z = −0.5 → out 0 (AND says no) keep x = (1, 1), raise b to 0: z = 2.0 → out 1 (the bias only moves the line — it does not change what an input contributes)

Sign and size both matter: flipping a weight inverts what that input argues for, while |w| sets how loud the argument is. The bias is the one dial that fires with no input at all.

Quick check

Keeping the weights fixed, you raise the bias b. What happens to the perceptron?

THE DECISION BOUNDARY

Weights draw a line.
Bias slides it.

For two inputs, the equation w₁x₁ + w₂x₂ + b = 0 is a straight line, and the perceptron’s answer is simply which side of it you are on. Training does not change the equation’s form — it moves the line until the colors split.

Set z to exactly zero and you get the decision boundary: w₁x₁ + w₂x₂ + b = 0. Every point with z ≥ 0 outputs 1; every point below outputs 0. In two dimensions that is a line; with three inputs a plane; in general a hyperplane — the same object Phase 1 used to define half-spaces.

Solving for x₂ turns the boundary into a familiar slope-intercept line, which makes the roles of the three numbers obvious:

w₁x₁ + w₂x₂ + b = 0 x₂ = −(w₁ / w₂)·x₁ − b / w₂ "the direction of the line comes from the ratio w₁/w₂; the bias moves the line up and down without rotating it."

One more piece of geometry worth memorizing: the vector w is always perpendicular to the boundary, pointing toward the class-1 side. And the quantity z measures how far a point is from switching sides, once you divide out the weights’ length: distance = |z| / ‖w‖.

x₁x₂w·x + b = 0x₁ + x₂ = 1.5w = (1, 1)0001outputs 1 →outputs 0
The AND gate with the hand-picked weights w = (1, 1), b = −1.5. The boundary x₁ + x₂ = 1.5 is perpendicular to w: the weight vector points into the class-1 region, and its length scales how far the line sits from the origin for a given bias.
Worked check: the AND line, then a distance

Take the hand-picked AND neuron — w = (1, 1), b = −1.5. Set the boundary to zero and rearrange:

1·x₁ + 1·x₂ − 1.5 = 0 x₂ = 1.5 − x₁ (slope −1, crossing the axes at 1.5) scoring all four AND rows: (0, 0) z = −1.5 → 0 ✓ (0, 1) z = −0.5 → 0 ✓ (1, 0) z = −0.5 → 0 ✓ (1, 1) z = +0.5 → 1 ✓ distance check for the positive point (1, 1): z = 0.5, ‖w‖ = √(1² + 1²) = 1.414 distance = |z| / ‖w‖ = 0.5 / 1.414 ≈ 0.354 → (1, 1) sits 0.354 units from the boundary, on the class-1 side; the nearest class-0 row is (1, 0), the same distance on the other side.

This is why the same weights with b = −0.5 turn AND into OR: only the offset changed, so the line slid down-left and swallowed (0, 1) and (1, 0). Slopes come from weights; thresholds come from bias.

Move the line until the colors split

Three weights, one straight line. Drag the sliders and try to separate AND and OR — then switch to XOR and watch the best you can do stall at three of four.

AND · 4/4 correct w = (1, 1), b = -1.5 every row, scored: (0, 0) z = -1.50 → 0 ✓ (0, 1) z = -0.50 → 0 ✓ (1, 0) z = -0.50 → 0 ✓ (1, 1) z = 0.50 → 1 ✓ boundary: x₂ = -1·x₁ + 1.5 try this: w = (1, 1), b = −1.5 is 4/4 on the AND dataset; switch to OR and the same weights with b = −0.5 are 4/4 there. On XOR, no setting of these three dials ever reaches 4/4 — the best any straight line manages is 3/4 = 1 mistake.

Only the ratio of w and b matters to the decision: multiplying all three numbers by the same positive constant draws the exact same line.

Quick check

You multiply every weight and the bias by 2 — w = (2, 2), b = −3 for the same AND neuron. What changes?

THE LEARNING RULE

Wrong answers drag the line.
Right answers do nothing.

This is the whole learning algorithm in six lines. Read it once as arithmetic and once as a sentence: whenever the perceptron is wrong, add the inputs to the weights (scaled) if it should have fired, and subtract them if it should not have.

Here is the source lesson’s learning rule, verbatim in spirit:

for each training example (x, y_true): y_pred = predict(x) error = y_true − y_pred if error ≠ 0: for each weight: wᵢ ← wᵢ + lr · error · xᵢ b ← b + lr · error "error is +1 when we should have fired but didn't, −1 when we fired and shouldn't have, and 0 when we already agree."

Four details do most of the work. First, error is one of three numbers — there is no gradient and no calculus here, just a sign. Second, the update adds the input vector itself, scaled: that is why the boundary rotates toward (or away from) the example that was wrong. Third, the bias update has no xᵢ — it fires on every mistake, which is exactly the sliding-the-line motion from Chapter 02. Fourth, a correct prediction contributes nothing: training ends the first time a full pass has zero mistakes.

Worked check: one update, then two full training runs

First, the single update that a beginner most often gets wrong. It is epoch 2 of AND training, with w = (0.1, 0.1), b = −0.1, looking at the row (0, 1) whose target is 0:

z = 0.1·0 + 0.1·1 + (−0.1) = 0.0 step(0.0) = 1 ← the tie rule: z = 0 counts as 1 error = 0 − 1 = −1 ← we fired when we shouldn't have w₂ ← 0.1 + 0.1·(−1)·1 = 0.0 b ← −0.1 + 0.1·(−1) = −0.2 "the input x₂ was 1, so the weight attached to it moves; x₁ was 0, so w₁ has nothing to drag."

Now run the full loop, starting from zeros with lr = 0.1. The tables below are the source algorithm’s actual end-of-epoch states, reproduced from the same update rule:

AND · converging line: it ends as x₂ = 2 − 2x₁, i.e. 2x₁ + x₂ = 2.
epochweights at endbias at endmistakes in the pass
1(0.1, 0.1)0.02
2(0.2, 0.1)−0.13
3(0.2, 0.1)−0.23
4(0.2, 0.1)−0.20 — done
OR · converging line: x₂ = 1 − x₁, i.e. x₁ + x₂ = 1. Watch the zero-target rows: whenever z lands exactly on 0, step(0) = 1 turns the visit into a mistake.
epochweights at endbias at endmistakes in the pass
1(0.0, 0.1)0.02
2(0.1, 0.1)0.02
3(0.1, 0.1)−0.11
4(0.1, 0.1)−0.10 — done

Both runs converge at epoch 4: a full pass with zero mistakes. The two traces fail in different ways, though. The OR run’s weights move only twice (the (0, 1) and (1, 0) mistakes of the first two epochs set w to (0.1, 0.1), which draws the same line as the hand-picked (1, 1)); after that, every correction is pure bias — sliding the line from x₁ + x₂ = 0 down to x₁ + x₂ = 1. The AND run keeps swapping which weight is wrong for three epochs before the direction settles on w = (0.2, 0.1) and the bias slides to −0.2. For linearly separable data this rule is guaranteed to settle in a finite number of updates — Rosenblatt’s convergence theorem. For anything else it never does, and Chapter 05 shows the smallest counterexample.

The learning loop, one visit at a time

The source’s training loop, paused between examples. Step through and watch the line move only on mistakes — and stop moving once a full pass is clean.

visit 1 / 16 · epoch 1 · example (0, 0) target 0 z = 0.00·0 + 0.00·0 + (0.00) = 0.000 (exactly zero: the step rule counts a tie as 1) step(0.000) = 1 → error = 0 − 1 = -1 wrong, so update with lr = 0.10 and error = −1: w1 ← 0.000 − 0.10·1·0 = 0.000 w2 ← 0.000 − 0.10·1·0 = 0.000 b ← 0.000 − 0.10·1 = -0.100 weights now: w = (0.000, 0.000), b = -0.100 rows correct: 3/4 mistakes per epoch: 2, 3, 3, 0 converged at epoch 4 — a full pass with zero mistakes. next: (0, 1) is already correct — no update for AND from zeros, lr only scales the weights: every update is proportional to lr, so the same boundary is reached — lr = 0.1 gives w = (0.2, 0.1), lr = 0.5 would give w = (1.0, 0.5), and both draw 2x₁ + x₂ = 2.

Weights start at zero, exactly like the source class. Because step(0) = 1, the very first visit — (0, 0) with target 0 — is already a mistake, and its update lands entirely on the bias: the input is (0, 0), so there is nothing for the weights to grab.

Quick check

Training starts at w = (0, 0), b = 0 and the first example is (0, 0) with target 0. What happens?

AND, OR, NOT — DECISIONS YOU ALREADY KNOW

The gates a single neuron
can learn by heart.

AND, OR, NOT and NAND all have something in common: plot their rows in the unit square and a straight line separates the 0s from the 1s. That is precisely the class of problems one perceptron can solve — and the perceptron learning rule finds the line by itself.

The source lesson trains three gates and prints the weights each run converges to. Three facts fall out of that exercise:

AND w = (1, 1), b = −1.5 → fires only on (1, 1) OR w = (1, 1), b = −0.5 → fires on anything but (0, 0) NOT w = (−1), b = 0 → fires on 0, silent on 1 "one weight per input, one bias, and the decision falls out."

The first interesting fact is that the same weights can express different gates — only the bias changes between AND and OR. The second is that the loop discovers these numbers: starting from zeros it walks to a separating line in four epochs for both gates. The third is a warning wrapped in an edge case: the boundary line belongs to class 1. Because the step rule is z ≥ 0 → 1, a row that lands exactly on the line fires 1. That is harmless for OR (its boundary passes through (0, 1) and (1, 0), which are both targets of 1) and dangerous for AND, as the worked check below shows.

The source lesson’s logic gates, with XOR and NAND added beside them. These four rows are the entire training set for the chapter — each gate is its own four-example problem.
inputsANDORXORNAND
0 00001
0 10111
1 00111
1 11100
Read a gate as a map from two bits to one bit. Three of the four numeric columns are linearly separable; XOR is the odd one out, and Chapter 05 is about exactly why.

Gate trainer: the source loop, run to convergence

Pick a gate, press train, and read the weights the perceptron settles on — all from the same fifteen lines of update rule. XOR is not in the list because it never settles; that is Chapter 05.

AND · rows visited in this order · press train to fill the table
x1x2targetzpredverdict
000
010
100
111
Each row is one training example. z is measured with the final weights; the table is recomputed from the displayed arithmetic every time you change a control.
ready: AND, 4 training rows, w = (0, 0), b = 0 to start. press train and the loop stops at the first epoch with no mistakes.

1 only when both inputs are 1 — the strict gate. Change the learning rate and retrain: the final weights scale with lr, but the boundary they draw is identical — only the sign of z decides.

Worked check: the trained AND weights, and the tie underneath

The source loop’s AND run ends at w = (0.2, 0.1), b = −0.2, so the boundary is 0.2x₁ + 0.1x₂ = 0.2, i.e. 2x₁ + x₂ = 2. Score every row with it:

(0, 0) z = −0.2 → 0 ✓ (0, 1) z = −0.1 → 0 ✓ (1, 0) z = 0.2 − 0.2 = 0.0 → 1 on paper (✗, target is 0) 0 in the source's floats (✓) (1, 1) z = 0.2 + 0.1 − 0.2 = 0.1 → 1 ✓ hand-picked AND weights, for contrast: w = (1, 1), b = −1.5 → z = −1.5, −0.5, −0.5, +0.5 every row has a comfortable margin.

The third row is the knife edge. In exact arithmetic (1, 0) has z = 0, and step(0) = 1, so the run would not have converged — it would still be making a mistake on a row whose target is 0. The source code passes only because the accumulated floating-point weights are a hair off: 0.2x₁ + 0.1x₂ − 0.2 evaluates to −2.8 × 10⁻¹⁷ for (1, 0), which is below zero. Nothing is wrong with the algorithm; this is what “the boundary belongs to class 1” means when a training run is allowed to stop the moment the weights happen to land on the line.

Two honest ways to handle it in your own implementation: pick the tie convention deliberately (and know that OR’s clean convergence depends on it), or nudge the converged bias by a small margin so the boundary sits strictly between the classes, as the hand-picked weights do. The lab flags any row whose |z| is below 10⁻⁹ so you can see the seam.

THE XOR PROBLEM

Some data
has no straight answer.

XOR outputs 1 when exactly one input is 1 — the exclusive “either, but not both”. Plot its four rows and they form a checkerboard: 1s on one diagonal, 0s on the other. No straight line splits a checkerboard, and that single sentence is the most famous limitation in the history of neural networks.

Chapter 04’s gates are all linearly separable. XOR is not. Here is the source table:

XOR gate: x₁ x₂ out 0 0 0 0 1 1 1 0 1 1 1 0 "the output is 1 when the inputs disagree, 0 when they agree."

This is not a hard problem to describe — it is one of the simplest functions on two bits — but a perceptron cannot represent it. In 1969 Marvin Minsky and Seymour Papert proved exactly this, and the critique of single-layer learning that followed is conventionally blamed for helping cool neural-network funding for a decade. The math is uncontroversial: a perceptron can only draw one hyperplane, and XOR needs two decisions. The history is messier — funding politics played a role too — but the theorem stands.

ANDone line works: x₁ + x₂ = 1.5XORevery line leaves one row wrong
The source lesson’s picture, drawn to scale. AND’s colors fall on opposite sides of one line; XOR’s do not — the two orange rows sit on one diagonal and the two blue rows on the other. The dashed line on the right is x₁ + x₂ = 0.5, one of the best available; it still misses the ringed (1, 1).
Worked check: why no line works, with the numbers

Try the most natural candidate, the line x₁ + x₂ = 0.5 (weights (1, 1), bias −0.5):

(0, 0) z = −0.5 → 0 ✓ (0, 1) z = +0.5 → 1 ✓ (1, 0) z = +0.5 → 1 ✓ (1, 1) z = +1.5 → 1 ✗ (target is 0) 3 of 4. Brute force over 720 line directions, choosing the best offset for each, agrees: the minimum is 1 error. the convex-hull argument: draw the segment between the two 1-rows, (0,1)–(1,0); draw the segment between the two 0-rows, (0,0)–(1,1); the segments cross at (0.5, 0.5). If a line put the 1-rows on one side and the 0-rows on the other, each segment would lie wholly in a half-plane — two segments that cross cannot.

So the perceptron’s limit is not about epochs or learning rates. The best a single line can do is 3 of 4 — 75% — and the source’s 1000-epoch run confirms it: every epoch has at least two mistakes at visit time, and from epoch 3 onward all four visits are corrected in turn while the weights orbit the same four values. The loop ends exactly where it started, forever.

XOR: a wall no single line gets past

Put the two orange truth-table rows on one side and the two blue rows on the other with one straight line. Then switch to “train anyway” and watch the perceptron loop forever without settling.

manual · 2/4 correct, 2 errors w = (0.40, 1), b = -0.70 (0, 0) z = -0.70 → 0 ✓ (target 0) (0, 1) z = 0.30 → 1 ✓ (target 1) (1, 0) z = -0.30 → 0 ✗ (target 1) (1, 1) z = 0.70 → 1 ✗ (target 0) best found so far by brute force: 1 error (3/4), with the boundary almost vertical at x₁ = 0. The classic choice x₁ + x₂ = 0.5 also scores 3/4 — it is wrong only on (1,1). Why 1 is the floor: the class-1 points (0,1) and (1,0) have a convex hull that crosses the hull of (0,0) and (1,1), so every line leaves at least one row on the wrong side.

This is not a bug and not a tuning problem: the update rule is doing its job on data whose two classes cannot be cut by one straight line. No learning rate or epoch count fixes a geometry problem.

Quick check

Which of these is NOT linearly separable in the (x₁, x₂) plane?

DEPTH FIXES IT: OR × NAND → AND

Stack the lines.
Bend the boundary.

The fix for XOR is not a better perceptron — it is more of them. One layer computes two new numbers, and the output neuron classifies those. The result is a decision boundary no single line can draw.

The trick is a piece of Boolean algebra that looks obvious in hindsight: XOR = (x₁ OR x₂) AND NOT(x₁ AND x₂). “At least one of them, and not both.” Each clause is a single perceptron, and the clauses combine with one more perceptron. The source lesson hand-wires exactly that, with three neurons and these weights:

hidden layer OR w = (1, 1), b = −0.5 → h₁ = "at least one is 1" NAND w = (−1, −1), b = 1.5 → h₂ = "not both are 1" output layer AND w = (1, 1), b = −1.5 → fires only if h₁ and h₂ agree "The hidden layer is not a hidden mystery: it is two new features, each computed by a perceptron you already know."

Notice what the hidden layer did. The input space had four corners and two impossible classes. The hidden space also has four points — except two of them collide: (0, 1) and (1, 0), the rows that made XOR non-separable, both become the hidden pair (1, 1). Once they coincide, a single line separates that dot from the others. A hidden layer is a change of coordinates chosen so the next layer’s problem is linearly separable. That sentence is the whole idea of deep learning in one line.

The hand-wired network on all four XOR rows. OR and NAND are the hidden layer; AND is the output. Every number is computed in the labs below.
inputOR neuron (h₁)NAND neuron (h₂)AND neuron (out)XOR target
(0, 0)z = −0.5 → 0z = +1.5 → 1z = −0.5 → 00
(0, 1)z = +0.5 → 1z = +0.5 → 1z = +0.5 → 11
(1, 0)z = +0.5 → 1z = +0.5 → 1z = +0.5 → 11
(1, 1)z = +1.5 → 1z = −0.5 → 0z = −0.5 → 00
Worked check: the band, the boundaries, and the output line

Each hidden neuron is a line, and the two lines carve the unit square into three regions:

OR fires when x₁ + x₂ ≥ 0.5 (above the line x₂ = 0.5 − x₁) NAND fires when x₁ + x₂ ≤ 1.5 (below the line x₂ = 1.5 − x₁) both fire ⇔ 0.5 ≤ x₁ + x₂ ≤ 1.5 ← the middle band output AND is 1 exactly on that band. row check, exactly: (0, 0) sum 0.0 → outside the band → h = (0, 1) → out 0 ✓ (0, 1) sum 1.0 → inside → h = (1, 1) → out 1 ✓ (1, 0) sum 1.0 → inside → h = (1, 1) → out 1 ✓ (1, 1) sum 2.0 → above the band → h = (1, 0) → out 0 ✓

The output boundary in hidden space is the line h₁ + h₂ = 1.5, and it is a straight line — the nonlinearity came from stacking, not from bending any single neuron. The same construction scales: layer k’s outputs are layer k+1’s inputs, and each layer can carve the previous layer’s features into finer pieces.

Two layers, three neurons, XOR solved

Flip the two inputs and follow the numbers: an OR neuron and a NAND neuron each draw a line, and a final AND neuron combines their answers. Weights are hand-picked from the source lesson — a teaching construction, not something this network learned.

w = +1w = +1w = −1w = −1+1+1x₁0x₂1ORz = 0.5h = 1NANDz = 0.5h = 1ANDz = 0.5out = 1outhidden layer · two straight linesoutput · one more line
XOR = (x₁ OR x₂) AND NOT (x₁ AND x₂). The hidden layer turns two inputs into two new numbers, and the output neuron classifies those. Weights: OR (1, 1, b = −0.5), NAND (−1, −1, b = 1.5), AND (1, 1, b = −1.5).
x = (0, 1) · target XOR = 1 OR z = 1·0 + 1·1 + (−0.5) = 0.5 → h₁ = 1 NAND z = (−1)·0 + (−1)·1 + 1.5 = 0.5 → h₂ = 1 AND z = 1·1 + 1·1 + (−1.5) = 0.5 → out = 1 ✓ the two-layer network gets this row right.
all four rows, current one highlighted
x₁x₂h₁h₂outXOR
000100
011111
101111
111000

Notice (0,1) and (1,0) both become the hidden pair (1,1): the two points that were on opposite sides in the input space collapse onto the same dot. Chapter 06 draws that collapse.

Where XOR becomes separable: the hidden space

Same four rows, two views. In input space two lines carve a band; in hidden space those same rows land on three dots — and one line splits them.

input → hidden → output (0, 0) → (0, 1) → 0 (0, 1) → (1, 1) → 1 (1, 0) → (1, 1) → 1 (1, 1) → (1, 0) → 0 read the mapping: (0,1) and (1,0) — the two rows that made XOR impossible — fold onto the SAME hidden point (1,1). Once they coincide, the output neuron only has to separate (1,1) from (0,1) and (1,0), which one line does ( h₁ + h₂ = 1.5 ). input space: 4 points, no single line works. hidden space: 3 dots, one line works. same network, same step function — only the coordinates changed.

A hidden layer is a change of coordinates chosen to make the problem easy for the next layer. Real networks search for those coordinates by gradient descent; here they were chosen by hand so the geometry is visible.

BEYOND THE STEP — SMOOTH LEARNING

Hard calls don’t train.
Smooth ones do.

Everything you built so far is the real thing: the source lesson’s thirty-line class is a working learning machine. To scale it into modern networks, exactly one design decision changes — and it changes everything: the step becomes a curve.

The source lesson, in full: a weighted sum, a step, and the update rule — here is the class, unchanged, so you can see there is no magic left out.

perceptron.py — the whole learning machinepython
class Perceptron:
    def __init__(self, n_inputs, learning_rate=0.1):
        self.weights = [0.0] * n_inputs
        self.bias = 0.0
        self.lr = learning_rate

    def predict(self, inputs):
        total = sum(w * x for w, x in zip(self.weights, inputs))
        total += self.bias
        return 1 if total >= 0 else 0

    def train(self, training_data, epochs=100):
        for epoch in range(epochs):
            errors = 0
            for inputs, target in training_data:
                prediction = self.predict(inputs)
                error = target - prediction
                if error != 0:
                    errors += 1
                    for i in range(len(self.weights)):
                        self.weights[i] += self.lr * error * inputs[i]
                    self.bias += self.lr * error
            if errors == 0:
                print(f"Converged at epoch {epoch + 1}")
                return
        print(f"Did not converge after {epochs} epochs")
Run it on AND / OR / NOT and it converges; run it on XOR for 1000 epochs and it never does. Both outcomes are in the source lesson's output.

In production the same core loop returns in one import — and the real differences appear at scale:

the step function becomes sigmoid, ReLU or another smooth curve weights are learned by backpropagation, not a per-mistake nudge layers get deeper: 3, 10, 100+ each layer builds new features from the previous layer's outputs "one perceptron can draw straight lines; stacked and smoothed, it can approximate any shape."

The reason the step must go is in its slope. A gradient-following optimizer asks “which way is downhill?” — and the step function’s answer is “flat” everywhere except z = 0, where it is a vertical cliff. The sigmoid answers honestly, with a slope you can multiply through layer after layer. That is the door backpropagation walks through, and it is Lesson 03.

Step vs sigmoid: the gradient lives in the curve

Same weighted sum on the x-axis, two different last-mile functions. Move the marker and watch the slope: the step is flat everywhere (and undefined at zero), the sigmoid always has a little grip.

z = 1.00, k = 4 step(z) = 1 (0 below 0, 1 at and above) σ(k·z) = σ(4.00) = 0.9820 local slope = k · σ · (1 − σ) = 0.0707 step's slope: 0 on both sides, undefined at z = 0 — which means an update rule that follows the slope has nothing to follow. σ's slope is never exactly zero, so even a badly wrong row still sends a small signal to the weights. That single fact is why modern networks replace the step function instead of trying to train through it. push k up to 10 and watch σ bend like a step — but look at the slope readout: at z = ±2 it is still small, never zero. Smooth everywhere, even when it looks sharp.

The sigmoid is exactly the function behind logistic regression, Phase 2’s classifier: same weighted sum plus bias, then a smooth squashing instead of a hard call.

The five-line production version (source lesson's “Use It”)python
from sklearn.linear_model import Perceptron as SkPerceptron
import numpy as np

X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([0, 0, 0, 1])          # AND

clf = SkPerceptron(max_iter=100, tol=1e-3)
clf.fit(X, y)
print([clf.predict([x])[0] for x in X])   # [0, 0, 0, 1]
Same loop — weighted sum, step, update on error — with convergence checks, multiple losses and sparse input support bolted on.
Quick check

Which sentence best captures the difference between the perceptron and logistic regression?

CHECK YOURSELF

Six questions.
Then the terms worth keeping.

Answer before you look. The XOR question and the tie-convention question are the two that separate a memorized definition from a working instinct — the first is the lesson’s punchline, the second is the edge case that hides inside it.

0 / 6 answered · 0 correct

01What mathematical operation does a perceptron perform on its inputs before applying the activation function?

02What does “linearly separable” mean for a classification problem?

03Why does a single perceptron fail to learn the XOR function?

04In the perceptron learning rule, what happens when the prediction matches the target?

05How is XOR solved using multiple perceptrons?

06The step function outputs 1 when z ≥ 0. What does that mean for a point that lies exactly on the decision boundary?

Key terms, demystified

Click a card to swap the lazy description for what it actually means.

Exercises from the lesson

Four problems with exact numbers — train NAND, watch the boundary move through AND training, build a 2-of-3 majority vote, and evaluate the hand-wired XOR network row by row. Try first; a worked answer is one click away.

  1. Train the source's perceptron on NAND — the universal gate. Report the weights it converges to from zero weights, write its decision boundary as an equation, and verify all four rows by hand.
    Show one worked answer

    With lr = 0.1 starting from w = (0, 0), b = 0, the loop converges at epoch 6 with w = (−0.2, −0.1), b = 0.2; the epoch mistake counts are 1, 3, 3, 2, 1, 0. Boundary: −0.2x₁ − 0.1x₂ + 0.2 = 0, which multiplies to 2x₁ + x₂ = 2, i.e. x₂ = 2 − 2x₁. Row check: (0,0) → z = 0.2 → 1 ✓; (0,1) → z = 0.1 → 1 ✓; (1,0) → z = 0.0 → 1 ✓ (the tie lands on the correct side here — target 1); (1,1) → z = −0.1 → 0 ✓. The hand-wired alternative from the source's XOR network is just NOT-AND: w = (−1, −1), b = 1.5, boundary x₁ + x₂ = 1.5. Both lines are valid; the trained one is flatter and lower because the loop walked there from zeros.

  2. Instrument the training loop to print the decision boundary at the end of every epoch of AND training. Describe how the line shifts, and why the two kinds of weight change do different things.
    Show one worked answer

    From zeros with lr = 0.1 the end-of-epoch states are: epoch 1, w = (0.1, 0.1), b = 0 → x₂ = −x₁ (2 mistakes); epoch 2, w = (0.2, 0.1), b = −0.1 → x₂ = 2 − 2x₁ (3 mistakes); epoch 3, same weights, b = −0.2 → x₂ = 2 − 2x₁ (3 mistakes — only the offset moved); epoch 4, the same line makes 0 mistakes and training stops. The weight change rotates the line (from slope −1 to slope −2); the bias change translates it without changing direction. Early mistakes fix the rotation, later ones slide the line. A footnote you can only see by tracking the boundary: in exact arithmetic the final line passes through (1,0), whose target is 0 — the run is saved by floating-point roundoff (z = −2.8 × 10⁻¹⁷). Tracking the line shows the geometry and the tie convention at once.

  3. Build a 3-input perceptron that outputs 1 only when at least two of the three inputs are 1 (a majority vote). Is this function linearly separable? Prove it with explicit weights and a boundary, and say what the source's zero-initialized loop converges to.
    Show one worked answer

    Yes, it is separable: w = (1, 1, 1), b = −1.5 gives z = (number of ones) − 1.5, so z ≥ 0 exactly when at least two inputs are 1: z = 0.5 for two ones, z = −0.5 for one, z = 1.5 for three. The boundary is the plane x₁ + x₂ + x₃ = 1.5, slicing the cube between the one-1 corners and the two-1 corners. Trained from zeros at lr = 0.1, the loop converges at epoch 4 with w = (0.1, 0.1, 0.1), b = −0.2 — the same direction scaled by 0.1 (multiply by 10: x₁ + x₂ + x₃ = 2, and the two-one rows give z = 0, counted as 1). Mistake counts read 4, 3, 1, 0. Read it as a vote: each input casts +1, and the bias sets how many votes are needed to win.

  4. Using the source's hand-wired weights, evaluate the two-layer XOR network on all four rows. Write down the hidden pair each row becomes, then explain in one sentence why the hidden layer makes the problem separable.
    Show one worked answer

    (0,0) → OR z = −0.5 → h₁ = 0, NAND z = 1.5 → h₂ = 1; output AND z = 0 + 1 − 1.5 = −0.5 → 0 ✓. (0,1) → OR z = 0.5 → 1, NAND z = 0.5 → 1; AND z = 1 + 1 − 1.5 = 0.5 → 1 ✓. (1,0) → the identical hidden pair (1,1) → 0.5 → 1 ✓. (1,1) → OR z = 1.5 → 1, NAND z = −0.5 → 0; AND z = 1 + 0 − 1.5 = −0.5 → 0 ✓. The hidden layer is a change of coordinates: the two rows that made XOR non-separable, (0,1) and (1,0), land on exactly the same hidden point (1,1), so the output neuron only has to separate (1,1) from (0,1) and (1,0) — and one line, h₁ + h₂ = 1.5, does it. The weights are a teaching construction; Lesson 03 trains them by backpropagation.

Terms this lesson borrows from later lessons (or outside)

You do not need to master these here. Each one gets a proper treatment in its own lesson; the one-line meaning is enough to keep reading. Orange dotted underlines in the prose point back to this list.

  • dot productMultiplying two vectors element by element and summing: w·x = w₁x₁ + w₂x₂. A perceptron is one dot product, a bias, and a step. (Phase 1, Lesson 02)
  • hyperplaneThe higher-dimensional generalization of a line: the set of points where w·x + b = 0. One hyperplane splits space into two half-spaces — exactly one perceptron's worth of decision. (Phase 1, Lesson 02)
  • learning rateThe multiplier on each weight update. In this lesson's zero-initialized perceptron it only scales the weights; in gradient-based training it controls how far each step travels and can make training diverge. (Phase 1, Lesson 08)
  • derivative / gradientThe slope that tells an optimizer which way to move. The step function's slope is zero almost everywhere, which is exactly why smooth activations replaced it. (Phase 1, Lesson 04)
  • logistic regression / sigmoidThe same weighted sum plus bias, but with a smooth sigmoid output read as a probability and trained by minimizing log loss. The perceptron is its hard-call ancestor. (Phase 2, Lesson 03)
KEEP GOING

A picture is a start.
Practice is the rest.

This lesson is a port of an open course. Everything here traces back to it — and the next step is running the code yourself.

Lesson text adapted from AI Engineering from Scratch (Phase 03, Lesson 01) and the Math Foundations Notebook reference build. The eight labs (neuron anatomy, boundary dial, learning-loop stepper, gate trainer, XOR failure search, two-layer network, hidden-space view and step-vs-sigmoid), the hand-worked update arithmetic, the AND / OR / NOT / NAND convergence traces, the z = 0 tie note and its floating-point footnote, and the hidden-space mapping of all four XOR rows are original to this page. Every weight, z value, epoch count and accuracy shown is computed live from the source lesson's algorithm (the same zero initialization, step(z) = 1 if z >= 0, and per-mistake update rule), so the numbers on the page match running the source code.