Skip to content

Introduction to Neural Networks (The Perceptron)

Every deep network is built from one unit: multiply each input by a weight, add them up, add a bias, and pass the total through an activation. This page takes that unit as far as it goes — which is exactly one straight line — proves where it stops, and fixes it with nine parameters.

  • The perceptron’s forward pass and its 1958 learning rule, both written out and run.
  • Why it converges on AND in 9 epochs and never converges on XOR, measured.
  • A four-line proof by contradiction that no straight line can compute XOR.
  • Exactly 14 of the 16 two-input boolean functions a single perceptron can learn — enumerated, not asserted.
  • The two-layer XOR construction, with weights you can check by hand.
  • Why the step function makes gradient descent impossible, and what replaced it.

For inputs xRn\mathbf{x} \in \mathbb{R}^{n}, weights wRn\mathbf{w} \in \mathbb{R}^{n} and a bias bRb \in \mathbb{R}:

z=wx+b=i=1nwixi+b,y^=step(z)={1z>00z0z = \mathbf{w}^\top \mathbf{x} + b = \sum_{i=1}^{n} w_i x_i + b, \qquad \hat{y} = \mathrm{step}(z) = \begin{cases} 1 & z > 0 \\ 0 & z \le 0 \end{cases}

The geometry is the whole story. wx+b=0\mathbf{w}^\top\mathbf{x} + b = 0 is a hyperplane — a line in 2D, a plane in 3D. w\mathbf{w} is the direction perpendicular to it, and bb decides how far from the origin it sits: the distance is b/w\lvert b \rvert / \lVert \mathbf{w} \rVert. Everything on one side outputs 1; everything on the other outputs 0. A perceptron is a line, and a decision about which side you are on.

diagram Diagram mermaid

Rosenblatt’s 1958 rule visits one row at a time and only acts when it is wrong:

wiwi+η(yy^)xi,bb+η(yy^)w_i \leftarrow w_i + \eta\,(y - \hat{y})\,x_i, \qquad b \leftarrow b + \eta\,(y - \hat{y})

There are only three cases. Prediction correct, yy^=0y - \hat{y} = 0: nothing moves. Predicted 0 when the answer was 1, yy^=+1y - \hat{y} = +1: add ηxi\eta x_i, pushing zz up for this input. Predicted 1 when the answer was 0: subtract, pushing zz down. That is the entire algorithm — no derivatives, no loss function.

perceptron.py
import numpy as np
 
def perceptron(X, y, lr=0.1, max_epochs=100, seed=0):
    """Rosenblatt's rule. Returns whether it separated the data, and in how many passes."""
    rng = np.random.default_rng(seed)
    w, b = rng.normal(0, 0.01, X.shape[1]), 0.0
    for epoch in range(1, max_epochs + 1):
        errors = 0
        for xi, target in zip(X, y):
            prediction = 1 if xi @ w + b > 0 else 0
            step = lr * (target - prediction)          # +lr, -lr, or exactly 0
            if step != 0:
                w, b, errors = w + step * xi, b + step, errors + 1
        if errors == 0:                                 # a full clean pass
            return {"converged": True, "epochs": epoch, "w": w, "b": b}
    return {"converged": False, "epochs": max_epochs, "w": w, "b": b}

The Perceptron Convergence Theorem guarantees this halts if the data is linearly separable. Note what it does not promise: nothing about the quality of the line, and nothing at all when the data is not separable.

figure Three gates solved, one impossible matplotlib
Four panels showing the two-input truth tables as points. AND, OR and NAND each have a green separating line found by the perceptron. The XOR panel has no line: instead a dashed segment joins the two positive points, another joins the two negative points, and they cross at the centre. Four panels showing the two-input truth tables as points. AND, OR and NAND each have a green separating line found by the perceptron. The XOR panel has no line: instead a dashed segment joins the two positive points, another joins the two negative points, and they cross at the centre.
The XOR panel is the proof rather than an illustration: joining the two positives and joining the two negatives produces segments that cross, so the classes' convex hulls overlap and no straight line can have one class strictly on each side.
GateOutcomeFinal weightsBias
ANDconverged in 9 epochs[+0.201, +0.199]−0.30
ORconverged in 3 epochs[+0.001, +0.099]+0.00
NANDconverged in 4 epochs[−0.199, −0.001]+0.20
XORnever converged (stopped at 100)[−0.199, −0.001]+0.10
figure Misclassified rows per epoch, out of 4 matplotlib
Line plot of misclassified rows per epoch for four gates. OR drops to zero by epoch 3, NAND by epoch 4, AND by epoch 9. XOR rises to 4 errors and stays there for all 40 epochs. Line plot of misclassified rows per epoch for four gates. OR drops to zero by epoch 3, NAND by epoch 4, AND by epoch 9. XOR rises to 4 errors and stays there for all 40 epochs.
AND takes longer than OR because its separating line has less room. XOR climbs to 4 errors out of 4 and stays — the rule keeps applying corrections that undo each other, forever. There is no learning-rate or epoch-count setting that changes this.

The convergence plot shows one run failing. A stronger statement is available cheaply: search every single neuron and check that none of them works.

figure 40,401 weight pairs per gate, best bias chosen for each pair matplotlib
Left: a heat map of how many of XOR's four rows each weight pair classifies correctly, over a 201 by 201 grid of w1 and w2. The map is banded and its brightest regions reach 3, never 4. Right: bars of the share of weight pairs that fully solve each gate — 24.75% for AND, OR and NAND, and 0.0% for XOR. Left: a heat map of how many of XOR's four rows each weight pair classifies correctly, over a 201 by 201 grid of w1 and w2. The map is banded and its brightest regions reach 3, never 4. Right: bars of the share of weight pairs that fully solve each gate — 24.75% for AND, OR and NAND, and 0.0% for XOR.
This is the impossibility proof restated as a measurement. For every (w1, w2) on a 201x201 grid the best available bias is used, so the search is not being defeated by a bad intercept — and XOR still tops out at 3 of 4 rows in every one of the 40,401 cells. The other three gates are solved by 10,000 of them apiece. Nothing about the learning rule failed on XOR; there was nothing to find.
GateBest of 4 rowsWeight pairs that solve itShare
AND410,00024.75%
OR410,00024.75%
NAND410,00024.75%
XOR300.00%

Look at OR’s weights: w1=+0.001w_1 = +0.001, b=0b = 0. That line passes within 0.001 of two of its four data points. It is a valid separator, so the algorithm stopped — the perceptron takes the first line that works, not the best one. That is precisely the gap support vector machines close by maximising the margin instead.

Suppose weights w1,w2w_1, w_2 and bias bb exist with w1x1+w2x2+b>0w_1x_1 + w_2x_2 + b > 0 exactly when XOR(x1,x2)=1\mathrm{XOR}(x_1, x_2) = 1. Write down what each row demands:

RowTargetConstraint
(0,0)(0,0)0b0b \le 0
(0,1)(0,1)1w2+b>0w_2 + b > 0
(1,0)(1,0)1w1+b>0w_1 + b > 0
(1,1)(1,1)0w1+w2+b0w_1 + w_2 + b \le 0

Add rows 2 and 3: w1+w2+2b>0w_1 + w_2 + 2b > 0, so w1+w2>2bw_1 + w_2 > -2b. Row 4 says w1+w2bw_1 + w_2 \le -b. Together:

2b<w1+w2b    2b<b    b>0-2b < w_1 + w_2 \le -b \;\Longrightarrow\; -2b < -b \;\Longrightarrow\; b > 0

which contradicts row 1. No such (w1,w2,b)(w_1, w_2, b) exists — for a perceptron or for any linear classifier, at any learning rate, with any amount of data.

So how many boolean functions can one neuron learn?

Section titled “So how many boolean functions can one neuron learn?”

Two inputs give 24=162^4 = 16 possible truth tables. Running the rule on every one of them:

14 of 16 are learnable. The two that are not are exactly [0,1,1,0][0,1,1,0] (XOR) and [1,0,0,1][1,0,0,1] (XNOR).

That reframes the 1969 objection usefully. A single neuron is not weak in general — it handles 87.5% of two-input logic. It fails on precisely the functions whose positive examples sit on one diagonal, and that is a statement about geometry, not capacity.

XOR is “OR, but not AND”, which is expressible as AND(OR,NAND)\mathrm{AND}(\mathrm{OR}, \mathrm{NAND}) — and each of those three is linearly separable. Wire them in two layers with a hidden layer of two units:

W1=[1111],b1=[0.51.5],W2=[11],b2=1.5\mathbf{W}_1 = \begin{bmatrix} 1 & -1 \\ 1 & -1 \end{bmatrix}, \quad \mathbf{b}_1 = \begin{bmatrix} -0.5 \\ 1.5 \end{bmatrix}, \quad \mathbf{W}_2 = \begin{bmatrix} 1 \\ 1 \end{bmatrix}, \quad b_2 = -1.5

Hidden unit 1 computes x1+x20.5>0x_1 + x_2 - 0.5 > 0, which is OR. Hidden unit 2 computes x1x2+1.5>0-x_1 - x_2 + 1.5 > 0, which is NAND. The output computes h1+h21.5>0h_1 + h_2 - 1.5 > 0, which is AND. Every row:

x1x_1x2x_2h1h_1 (OR)h2h_2 (NAND)outputXOR
000100
011111
101111
111000

Exact match, using 9 parameters and no training at all. The hidden layer’s job is to re-represent the input so that the output layer’s straight line becomes sufficient — which is what every hidden layer in every deep network is doing.

The step function cannot be trained by gradient descent

Section titled “The step function cannot be trained by gradient descent”

Gradient descent needs L/w\partial L / \partial w, and the chain rule routes that through step(z)\mathrm{step}'(z):

step(z)=0for all z0,undefined at z=0\mathrm{step}'(z) = 0 \quad \text{for all } z \ne 0, \qquad \text{undefined at } z = 0
zzstep(z)\mathrm{step}(z)numeric derivative
−2.000.0
−0.100.0
+0.110.0
+2.010.0

Every update becomes wwη0w \leftarrow w - \eta \cdot 0. The weights never move. This is why Rosenblatt needed a bespoke rule, and why the smooth sigmoid — whose derivative reaches 0.25, as measured on the activations page — was the change that made backpropagation possible.

Swapping the step for a sigmoid turns the same unit into something trainable:

one_trainable_neuron.py
from tensorflow import keras
 
# One neuron, two inputs. The only change from a TLU is the activation.
model = keras.Sequential([
    keras.layers.Input((2,)),
    keras.layers.Dense(1, activation="sigmoid"),
])
model.compile(loss="binary_crossentropy", optimizer="sgd", metrics=["accuracy"])

Two MNIST digit pairs, 2,000 training rows each, the perceptron rule against a single sigmoid unit trained for 20 epochs:

TaskPerceptronTest accuracySigmoid unit
0 vs 1separated the training set0.99761.0000
3 vs 5never separated it0.96630.9508
4 vs 9never separated it0.95130.9635

Two things worth noticing. Handwritten 0s and 1s are close to linearly separable in raw pixel space, which is why the rule terminates and both models are near-perfect. And on 3 vs 5 the perceptron beat the sigmoid unit (0.9663 against 0.9508) despite never converging — non-convergence means “no line separates the training set”, not “the line is useless”. Failing to converge is information about your data.

sketch A perceptron: weighted sum, then activate p5.js
A signal travels in along each weighted input, fires the neuron, then travels out to the output — one full forward pass, looping. Thicker edges are larger weights.

The second sketch is the learning rule itself. Each click applies one epoch of updates; the line moves only on rows it gets wrong. Switch to XOR and watch it never settle.

sketch The perceptron rule, one epoch per click p5.js
Four points from a logic gate and the current decision line. Each click runs one epoch of Rosenblatt's rule, showing which rows were misclassified and how the line moved. AND, OR and NAND settle; XOR oscillates forever.

Expecting non-convergence to be a bug. If the data is not linearly separable the rule cannot terminate, by construction. The fix is a different model, not more epochs.

Reading the perceptron’s line as a good line. It stops at the first separator it finds. The OR run above ended with a boundary 0.001 away from two of its four points.

Using the step function with gradient descent. Its derivative is 0 everywhere, so the update is exactly zero. Any modern “perceptron” you train with an optimiser has a sigmoid or ReLU in it, not a step.

Assuming a bias-free neuron is only slightly weaker. Without bb, the boundary must pass through the origin. AND is unlearnable in that setting: every row of the truth table would need w1x1+w2x2>0w_1x_1 + w_2x_2 > 0 to be false at (0,0)(0,0), which is already forced.

Believing the 1969 objection killed the idea because a neuron is weak. One neuron learns 14 of the 16 two-input functions. Two layers and 9 parameters cover the other two. What was missing for another 17 years was a way to train the second layer.

  • A perceptron is y^=step(wx+b)\hat{y} = \mathrm{step}(\mathbf{w}^\top\mathbf{x} + b): a hyperplane, plus a decision about which side you are on.
  • The rule wiwi+η(yy^)xiw_i \leftarrow w_i + \eta(y - \hat{y})x_i only fires on mistakes, and converged in 9, 3 and 4 epochs on AND, OR and NAND.
  • XOR is impossible for any linear classifier — the four constraints force b>0b > 0 and b0b \le 0 at once.
  • 14 of 16 two-input boolean functions are learnable by one neuron; the exceptions are XOR and XNOR.
  • Two layers, 9 parameters, no training: OR and NAND in the hidden layer, AND on top, exactly reproducing XOR.
  • step(z)=0\mathrm{step}'(z) = 0 everywhere, which is why sigmoid replaced it and why backpropagation had to wait.
  • On 3 vs 5 MNIST digits the non-converged perceptron scored 0.9663 against a sigmoid unit’s 0.9508 — non-convergence describes the data, not the quality of the answer.
pch.quizTag pch.quizDefaultTitle
  1. The perceptron rule runs for 100 epochs on XOR and never converges. What should you conclude?

    pch.quizShowAnswer

    B — XOR is not linearly separable, so no setting of any hyperparameter will make it converge — the four truth-table constraints force b > 0 and b <= 0 simultaneously — The Perceptron Convergence Theorem only guarantees termination for linearly separable data. The contradiction proof on this page rules out every possible line, so tuning cannot help. Two layers and 9 parameters can.

  2. How many of the 16 two-input boolean functions can a single perceptron learn?

    pch.quizShowAnswer

    B — 14 — every function except XOR [0,1,1,0] and XNOR [1,0,0,1], which was verified by running the rule on all 16 truth tables — A single neuron is not weak in general; it fails on precisely the two functions whose positive examples sit on opposite diagonals, which is a geometric property rather than a capacity limit.

  3. Why can gradient descent not train a step-function neuron?

    pch.quizShowAnswer

    B — Its derivative is 0 for every z except 0 (where it is undefined), so w -= lr * grad leaves every weight exactly where it was — The chain rule multiplies by step'(z) = 0, so every update is exactly zero. This is why Rosenblatt needed a bespoke rule and why the smooth sigmoid — derivative up to 0.25 — was the change that unlocked backpropagation.

  4. The measured OR run ended with w = [+0.001, +0.099] and b = 0.00, a boundary passing within 0.001 of two data points. What does that tell you about the algorithm?

    pch.quizShowAnswer

    B — It stops at the first line that separates the data, not the best one — maximising the margin instead is exactly what an SVM adds — Zero errors is the stopping condition, so any separating line ends training. A boundary that grazes the data generalises worse than a centred one, which is the gap the max-margin objective closes.

  5. In the two-layer XOR construction, what is the hidden layer doing?

    pch.quizShowAnswer

    B — Re-representing the input as (OR, NAND), a space in which a single straight line — AND — is enough — Both hidden units compute linearly separable functions of the raw input. In their output space the two XOR-positive rows both become (1,1), so the output neuron's straight line suffices. Every hidden layer in every deep network is doing this.

You have taken one neuron as far as it goes, and seen the two-layer fix built by hand. Continue to Multi-Layer Perceptron (MLP) to stack these units properly and see how the hidden layer’s re-representation is learned rather than hand-designed.

Exercise 2 – Implement the learning rule

Section titled “Exercise 2 – Implement the learning rule”

Exercise 3 – Enumerate what one neuron can learn

Section titled “Exercise 3 – Enumerate what one neuron can learn”

Exercise 5 – Show the step function has no gradient

Section titled “Exercise 5 – Show the step function has no gradient”

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading