Part VIII — Deep Learning

Chapter 26 · Artificial Neural Networks

Part VIII opens with the model that named an industry. A bank has 10,000 customer records and one anxious question: who is about to leave? The artificial neural network answers it — and this chapter builds it from first principles. The neuron is Chapter 11's logistic unit; the network is logistic units stacked in layers; the training is calculus run backwards. Every step is executed by hand on a tiny network — forward pass, chain rule, one learning step with exact arithmetic — before Keras trains the real 12-6-6-1 churn network and every number on the page comes from that actual run. And because the next question is always "can it predict a price?", the chapter closes the loop: three changes turn the yes/no network into a regression network, which then faces tomorrow's AAPL — and the honest baseline that beats it.

📖 Chapter 26 of 33 ⏱ ~100 min read Mostly Core Stable Volatile 📝 Quiz · 42 MCQs

🎯 Learning Objectives

1. The Neuron: From Biology to Arithmetic

1.1 Why copy the brain?

The human brain is the most powerful learning machine we know. Deep learning copies its structure in the hope that a machine built the same way can learn too. The copy starts small: with a single neuron.

Real neurons were first drawn in detail by the Spanish neuroscientist Santiago Ramón y Cajal in 1899. He stained brain tissue, looked at it under a microscope, and drew what he saw. Each neuron has three parts: a cell body, short branches called dendrites, and one long tail called the axon. One neuron alone can do very little — like one ant. Millions of neurons, connected, learn to see, hear and think — like an ant colony. That is the bet of deep learning: connect enough simple units and something powerful emerges.

Dendrites — receive signals Cell body Axon — sends the signal out Synapse — the gap; the signal crosses, there is no physical contact Next neuron
Figure 26.1 — A biological neuron, in the spirit of Ramón y Cajal's 1899 drawings. Dendrites receive signals; the axon sends the neuron's own signal out. Between one neuron's axon and the next neuron's dendrites sits the synapse — the gap the signal crosses. Artificial neurons copy only the flow of signals, not the biology.

Two roles carry over. Dendrites receive incoming signals. The axon transmits the neuron's own signal out. A signal travels down one neuron's axon and arrives at the next neuron's dendrites — and the two never touch. The connection where the signal crosses is the synapse.

Artificial neurons borrow the word. Every connection between two artificial neurons is called a synapse. This avoids an awkward question: is a given connection an axon or a dendrite — and whose? A synapse is simply where a signal passes. That is the only meaning the term carries in this book.

1.2 The artificial neuron

An artificial neuron (also called a node) receives input signals through its synapses and produces one output signal. The inputs come either from the input layer or from other hidden neurons — the mechanism is the same. In the brain, the input layer plays the role of the senses: everything you see, hear and touch arrives as electrical signals. In a network, the input layer is simply the row of feature values.

x₁ x₂ ⋮ xₙ w₁ w₂ wₙ synapses — each carries a weight Σ φ 1. weighted sum  2. activation 3. pass the signal on ŷ output value for that same row input layer one row of your data
Figure 26.2 — The artificial neuron. Input values x₁ … xₙ (the features of one observation) arrive over synapses; every synapse carries a weight. Inside the neuron: the weighted sum, then the activation function φ. The output feeds the next layer — or is the network's answer.

Two facts about the inputs decide whether the network works at all:

standardization:  xᵢ² = (xᵢ − μ) / σ   →   mean 0, variance 1
normalization:  xᵢ² = (xᵢ − min) / (max − min)   →   all values in [0, 1]
Either fix works; the scenario decides. This chapter standardizes (Section 4's StandardScaler). Scaling is not optional polish for a network — it decides whether training succeeds.

1.3 What the output can be

The neuron's output value can take three forms:

Output typeExampleOutput layer
Continuous (a number)a priceone output neuron
Binary (yes/no)customer exits or staysone output neuron — sigmoid or threshold
Categorical (k classes)France / Germany / Spaink output neurons — dummy variables, one per class

A categorical output needs several output values, not one: the categories become dummy variables. This chapter's task is binary (churn or not), so the output layer will hold a single sigmoid neuron.

1.4 Weights: where learning lives

Every synapse carries a number called a weight. The weight decides how much of each input signal passes through: important signals get large weights, unimportant ones get weights near zero. Training a network means adjusting every weight in every synapse until the predictions fit the data. That process — gradient descent driven by backpropagation — is Section 3's subject. Whenever you hear "the network learned", read: its weights changed.

1.5 Inside the neuron: three steps

Signals arrive at the neuron. Then, in order:

  1. Compute the weighted sum of the inputs — each input times its weight, plus a bias.
  2. Apply the activation function to that sum.
  3. Pass the result on as the neuron's output signal.
z = w₁x₁ + w₂x₂ + … + wₙxₙ + b      a = g(z)
x = inputs · w = weights · b = bias · g = activation function · a = the neuron's output, which becomes the next layer's input

Look closely: with g = sigmoid, this is exactly the logistic regression of Chapter 11 — a weighted sum squashed to (0, 1). One neuron is one logistic unit. Stack such units in layers, each feeding the next, and the network can bend and recombine its inputs into representations no single linear boundary could reach. Why the non-linear g is essential is Section 2's warning; the calculus of adjusting the weights is Section 3's work.

1.6 The activation menu: four classic functions

The activation function decides whether — and how strongly — a neuron passes its signal on. Four classics cover most of the field.

Threshold (step) 1 0 z = 0 weighted sum z → φ(z) = 1 if z ≥ 0, else 0 Sigmoid 1 0.5 0 z = 0 weighted sum z → φ(z) = 1 / (1 + e−z) Rectifier (ReLU) 1 0 z = 0 weighted sum z → φ(z) = max(0, z) Hyperbolic tangent (tanh) +1 0 −1 z = 0 weighted sum z → φ(z) = tanh(z)
Figure 26.3 — The four classic activation functions. Horizontal axis: the weighted sum z. Vertical axis: what the neuron passes on. Threshold: a hard 0/1 switch. Sigmoid: a smooth 0-to-1 curve — logistic regression's squashing function. Rectifier: zero below 0, the identity above. Tanh: sigmoid-shaped, but centred at zero, running from −1 to +1.
ActivationFormulaRangeUsed for
Threshold (step)1 if z ≥ 0, else 0{0, 1}Yes/no output — the original perceptron
ReLU (rectifier)max(0, z)[0, ∞)Hidden layers — the modern default
Sigmoid1 / (1 + e−z)(0, 1)Binary output — a probability
Tanh(eᶿ − eḝ) / (eᶿ + eḝ)(−1, 1)Hidden layers (older nets, recurrent nets)
Softmaxeᶿᵣ / Σ eᶿᵢ(0, 1), sums to 1Multi-class output (Ch 13's multiclass cousin)

In one sentence each. Threshold is a rigid switch: below zero it passes 0, at zero or above it passes 1 — yes or no, nothing in between, and (Section 3) no useful gradient to learn from. Sigmoid is its smooth cousin: the same 0-to-1 range with soft slopes, which is why logistic regression uses it and why gradient descent can train it. Rectifier passes 0 for negative sums and the sum itself above zero — cheap to compute and the hidden-layer default. Tanh is sigmoid-shaped but centred at zero, from −1 to +1, which keeps hidden signals balanced around 0.

Suppose the dependent variable is binary — exit or stay. Which functions fit? Two. The threshold function answers directly with 0 or 1. The sigmoid answers with the probability of getting 1 — round it for a hard answer, or keep it and apply Chapter 18's threshold logic. The standard recipe for binary classification combines two of the four: ReLU in the hidden layers, sigmoid at the output — exactly the network Section 2 builds.

📝
Note — why ReLU won the hidden layers: it is dirt-cheap to compute (a max), it does not saturate (sigmoid's gradient collapses toward 0 for large |z|, which used to stall deep networks — the "vanishing gradient" problem), and it still delivers the nonlinearity that makes stacking worthwhile.
💡
Pro Tip — further reading: two classic papers reward a second pass once the practical chapters are done. Yann LeCun et al., Efficient BackProp (1998) — the standard reference on scaling inputs and training tricks. Xavier Glorot et al., Deep Sparse Rectifier Neural Networks (2011) — why the rectifier works so well in deep networks.

2. The Network and Its Algebra

2.1 What do hidden neurons actually learn?

A property-price example makes the hidden layer concrete. Four inputs: area (sq ft), bedrooms, distance to the city and age. One output: the price. The network is already trained — the weights are fixed. What did its hidden neurons each decide to look for?

Each neuron keeps weights only on the inputs it cares about; the rest get weights of zero and are simply ignored. One neuron watches area + distance: it fires for properties close to the city that still have unusually large space — the "unfair square footage" deals. Another watches area + bedrooms + age: large, many-bedroom, new builds — exactly what young families buy. A third watches age alone: under 100 years it stays silent, but past 100 the home becomes historic and the neuron's contribution rises — the rectifier function in action: zero until a threshold, then a growing signal.

That is the trick hidden layers play: each neuron combines raw inputs into a new attribute nobody hand-designed — "family-home-ness", "historic-ness" — and fires only when its pattern is present. One neuron cannot predict a price. Many specialists, combined in the output layer, can — the ant colony again.

Area (sq ft) Bedrooms Distance to city Age ① ② ③ ④ ⑤ Price output layer ① area + short distance — unusually big homes near the city ② area + many bedrooms + new build — family homes ③ age > 100 — historic value: zero until 100, then rising (a rectifier) ④ ⑤ two more detectors, trained on combinations nobody narrated
Figure 26.4 — What hidden neurons learn, on a property-price network (4 → 5 → 1). Each neuron keeps weights only on the inputs it uses — inactive (zero-weight) synapses are not drawn. Every detector combines raw inputs into a new attribute; the output layer blends the detectors into a price. Fully connected, this little network carries 4×5 + 5 = 25 weights — a number Section 3 will make famous.

2.2 The churn network

The churn network, exactly as the source builds it: 12 inputs → 6 ReLU neurons → 6 ReLU neurons → 1 sigmoid output. Twelve inputs because the raw 10 features become 12 after encoding (Section 4). The output is a single number — the probability the customer leaves. Its 12 hidden detectors are trained, not narrated: some may watch Germany + inactive, others balance + products — the same specialisation, discovered by gradient descent rather than told.

x₁ x₂ x₃ … 12 inputs (scaled) hidden 1 — 6 ReLU hidden 2 — 6 ReLU sigmoid P(churn)
Figure 26.5 — The churn network: 12 → 6 → 6 → 1 (127 trainable parameters). Every edge is a weight; every circle sums its incoming edges and applies its activation.

2.3 Counting the parameters, by hand

A layer with n inputs and m neurons owns a weight per edge plus a bias per neuron: n × m + m parameters. The real network:

LayerWeightsBiasSubtotal
input → hidden 112 × 6 = 72678
hidden 1 → hidden 26 × 6 = 36642
hidden 2 → output6 × 1 = 617
Total127 — verified: ann.count_params() = 127

In matrix form, one layer is one line: a = g(Wᵗx + b), where Wᵗ is that layer's weight matrix. The whole forward pass is three such lines chained — a¹ = ReLU(W₁ᵗx + b₁), then a² = ReLU(W₂ᵗa¹ + b₂), then ŷ = σ(W₃ᵗa² + b₃). GPUs earn their keep by doing these matrix products at enormous speed.

⚠
Warning — the linear-collapse trap: remove the activations (g = identity) and the three layers multiply into a single matrix: a composition of linear maps is a linear map. The network would collapse to Chapter 11's logistic regression with extra steps. The nonlinearity is not decoration; it is the entire point of depth.

3. Learning: Gradient Descent & Backprop, by Hand

There are two ways to get a program to do what you want. The first: write every rule yourself — pointy ears means cat, floppy ears means dog — and chase every exception by hand. The second: build a network, show it inputs and the correct answers, and let it find the rules on its own. Deep learning is the second road. The name of the game from here is learning: how do the weights get set without anyone telling them?

The simplest network — input layer straight into one output neuron — is the perceptron, built by Frank Rosenblatt in 1957 as the first model meant to learn and adjust itself. Everything in this section happens in the perceptron first, then scales up. One convention before starting: y is the actual value from the data; ŷ ("y-hat") is the network's prediction. Learning means making ŷ agree with y.

3.1 The learning loop and the cost function

Feed one row in. The weights scale it, the neuron activates, out comes ŷ. Compare ŷ to the actual y. The difference is the error, and the cost function is the number that measures it. The classic choice — half the squared error:

C = ½ (y − ŷ)²     over a whole dataset:   J = ½ Σᵢ (yᵢ − ŷᵢ)²
Many cost functions exist; this squared error is the classic one for a continuous output. The churn network uses binary cross-entropy, −[y·ln ŷ + (1−y)·ln(1−ŷ)] — same job, a shape suited to probabilities. Training always means one thing: minimise the cost. The lower the cost, the closer ŷ sits to y.
one row of data x₁ … xₙ the network weights w₁ … wₙ weighted sum → activation ŷ compare with actual y cost C = ½(y − ŷ)² update the weights — and feed the same row in again ŷ = predicted  ·  y = actual  ·  repeat until the cost stops falling
Figure 26.6 — The learning loop on one row: predict ŷ, compare with the actual y, compute the cost, feed the error back, update the weights. With a single training row you repeat this loop until ŷ meets y. With a whole dataset the loop becomes an epoch — and the bookkeeping question of when to update the weights (per row? per batch?) is Section 3.4's subject.

3.2 Why not just try every weight? (brute force fails)

One honest idea first: why not try thousands of candidate weights, compute the cost for each, and keep the best? For one weight, it works. For a real network it dies, and the property network of Section 2.1 shows why. Fully connected it has 25 weights (4×5 + 5×1). Test 1,000 candidate values per weight:

1,000²⁵ = 10⁷ⁱ combinations
10⁷ⁱ ÷ (93 × 10¹⁵ tests/second) ≈ 10⁵⁸ seconds ≈ 10⁵⁰ years
93 × 10¹⁵ per second = 93 petaflops, the speed of Sunway TaihuLight, the world's fastest supercomputer as of June 2016 — and the estimate generously assumes one full weight-test per floating-point operation, which is far better than reality. 10⁵⁰ years is vastly longer than the universe has existed. This is the curse of dimensionality: combinations grow explosively with every added weight — and the churn network has 127 weights, real networks millions. Brute force is a dead end.

3.3 Gradient descent: follow the slope

The working idea is cheaper: stand on the cost curve and look which way is downhill. Pick a starting weight, work out the slope of the cost at that point (the gradient — one derivative), and step the weight the downhill way. At the new point, check the slope again. Repeat. Like a ball rolling down a valley: not a straight ride, a tightening zigzag that settles at the bottom. The bottom is the minimum of the cost — the best weights we can reach from where we started.

Convex cost — one valley, one minimum cost weight w → minimum — best weights start Non-convex cost — two valleys cost weight w → local minimum batch descent can stop here global minimum SGD's noise rattles out
Figure 26.7 — Gradient descent follows the slope. Left: on a convex cost, the zigzag of steps (dashed) tightens into the single minimum. Right: on a non-convex cost — easy to get in several dimensions even with a squared-error cost — plain descent can settle in a local minimum while the true best weights sit deeper. Noisy, row-by-row steps can jump out. Same idea in many dimensions: keep walking the downhill direction of the cost surface.

In symbols, one step of the walk is the update rule — and it is the nudge rule this chapter's hand example will execute:

wᵢᵧ ̸ = wᵢ − η · ∂L/∂wᵢ
η = learning rate (step size). The gradient points uphill in loss, so we subtract it — step downhill. An epoch is one full pass over the training set.

3.4 Batch, stochastic, mini-batch — when do the weights update?

Plain gradient descent quietly assumes the cost curve is convex: one bowl, one minimum. Real cost surfaces are often non-convex (Figure 26.7, right) — a different cost function, or a squared error stretched over many dimensions, can grow extra valleys. Descent that updates carefully can then settle in a local minimum and miss the best weights. The fix is when you update:

MethodWeights updated…Character
Batch gradient descentonce per full pass — all rows first, then one updatedeterministic (same start → same result); needs all data in memory; can settle in a local minimum
Stochastic gradient descent (SGD)after every single rowfaster and lighter (one row at a time); noisy steps jump out of local minima; results vary run to run — it is random by design
Mini-batch gradient descentafter each small batch of rowsthe practical middle — steadier than SGD, cheaper than batch. The churn network trains this way: batch_size = 32

So the full learning story over a dataset: feed every row (all through the same network — the weights are shared, not per-row), accumulate the cost, update the weights by following the slope, and start the next pass. The whole cycle — forward pass, cost, backward pass, weight update — goes by the name backpropagation.

3.5 Backpropagation by hand: the 2-2-1 network

The network starts with random weights and must learn the 127 numbers of Section 2 — but the hard part is knowing ∂L/∂w for weights buried deep in the stack. That is backpropagation proper: the chain rule applied systematically from the output backwards. Watch it work on a network small enough to hold in your head: 2 inputs → 2 ReLU neurons → 1 sigmoid output, exact numbers throughout.

SymbolValueMeaning
x = (x₁, x₂)(1, 2)one training example
y1true label (this customer churned)
w₁₁, w₂₁; b₁0.5, 0.2; 0.1into hidden neuron 1
w₁₂, w₂₂; b₂−0.3, 0.8; −0.1into hidden neuron 2
W₀ = (0.7, −0.4); b₀hidden → output

3.6 Forward pass

z₁ = 0.5(1) + 0.2(2) + 0.1 = 1.0  →  a₁ = ReLU(1.0) = 1.0
z₂ = −0.3(1) + 0.8(2) − 0.1 = 1.2  →  a₂ = ReLU(1.2) = 1.2
z₀ = 0.7(1.0) − 0.4(1.2) + 0.05 = 0.27  →  ŷ = σ(0.27) = 0.5671
L = −ln(0.5671) = 0.5672
Convention: wᵢᵢ reads "weight from input i into neuron j". The network predicts 0.57 churn probability; the truth is 1 — under-predicted, and every arrow that pushed ŷ up will be strengthened.

3.7 Backward pass — the chain rule, one arrow at a time

Backpropagation's engine is the error signal δ at each neuron: how much the loss would change if that neuron's pre-activation z moved. Signals flow backwards, each layer's δ built from the next layer's:

Output: δ₀ = ∂L/∂z₀ = ŷ − y = 0.5671 − 1 = −0.4329
The sigmoid × cross-entropy pair collapses to this beautifully clean form — the same gradient LogisticRegression optimises in Chapter 11. Negative means: raising z₀ lowers the loss.

With δ₀ in hand, the output layer's gradients are one multiplication each (∂L/∂w = δ × the input arriving on that edge):

GradientComputationValueMeaning
∂L/∂w₀₁δ₀ × a₁ = −0.4329 × 1.0−0.4329increase this weight
∂L/∂w₀₂δ₀ × a₂ = −0.4329 × 1.2−0.5195increase (more: busier path)
∂L/∂b₀δ₀−0.4329raise the bias

Now push δ₀ back into the hidden layer. Each hidden neuron's error is the δ₀ flowing in through its output weight, multiplied by its own activation's slope — ReLU's derivative is 1 where z > 0 (both neurons here, z₁ = 1.0, z₂ = 1.2) and 0 where z < 0:

δ₁ = (w₀₁ · δ₀) · ReLU′(z₁) = 0.7 × (−0.4329) × 1 = −0.3030
δ₂ = (w₀₂ · δ₀) · ReLU′(z₂) = (−0.4) × (−0.4329) × 1 = +0.1732
Note δ₂ is positive: its output weight is negative, so pushing z₂ down would raise ŷ. The sign flip is the chain rule doing its job. And if a neuron's z had been < 0, ReLU′ = 0 would zero its δ entirely — a "dead" neuron that this step cannot revive.

Hidden-layer gradients, same one-multiplication rule:

GradientComputationValue
∂L/∂w₁₁x₁ × δ₁ = 1 × (−0.3030)−0.3030
∂L/∂w₂₁x₂ × δ₁ = 2 × (−0.3030)−0.6061
∂L/∂w₁₂x₁ × δ₂ = 1 × 0.1732+0.1732
∂L/∂w₂₂x₂ × δ₂ = 2 × 0.1732+0.3463
∂L/∂b₁, ∂L/∂b₂δ₁, δ₂−0.3030, +0.1732

3.8 One learning step

Apply the update with learning rate η = 0.1 — subtract 0.1 × each gradient. For example w₀₁: 0.7 − 0.1(−0.4329) = 0.7433 (up, as promised), and w₁₂: −0.3 − 0.1(0.1732) = −0.3173 (down). Re-running the forward pass with all nine updated numbers gives the verdict:

ŷ: 0.5671 → 0.6434   ·   L: 0.5672 → 0.4410
One step, all 9 parameters nudged, prediction moved 0.08 toward the truth and the loss fell 22%. Multiply by millions of such steps (batches × epochs) and you have training. Backpropagation is nothing more than this table — automated, layered, and fast.
💡
Pro Tip: every gradient in the tables above is a product of quantities the forward pass already computed (activations) times δ's flowing backwards. That is why frameworks store the forward pass — backprop reuses it. Nothing here required anything beyond multiplication and the chain rule.

3.9 The full training recipe — seven steps

Everything from this section, as one checklist. These are the steps the Keras fit() call of Section 4 runs for you — worth knowing what it is actually doing:

  1. Initialize the weights to small random numbers near zero — never exactly zero (Keras does this automatically).
  2. Feed in one row: each feature goes to its own input node.
  3. Forward-propagate, left to right: weights scale how strongly each neuron activates, layer after layer, until ŷ comes out.
  4. Compare ŷ with the actual y and measure the error — the cost.
  5. Back-propagate the error, right to left, and update every weight in the same pass, each in proportion to its share of the error. The learning rate η sets the size of the nudge.
  6. Repeat steps 2–5 after each row (stochastic) or after each batch (batch / mini-batch).
  7. Epoch and repeat: one full pass over the training set is one epoch; run epoch after epoch until the cost stops falling.
📝
Note — the 1980s breakthrough: backpropagation adjusts all weights simultaneously. It "knows" which part of the error each weight is responsible for, so no weight is ever tuned in isolation. That single property — simultaneous, responsibility-weighted updates — is why backpropagation spread so fast in the 1980s and still drives every modern network, this chapter's 127-parameter churn net included.
💡
Pro Tip — further reading: Andrew Trask, A Neural Network in 13 Lines of Python (Part 2: Gradient Descent) (2015) — a short, honest walkthrough with useful tricks. Michael Nielsen, Neural Networks and Deep Learning (free online book, 2015) — the full mathematics of gradient descent and backpropagation, gently introduced. For a catalogue of cost functions and when each fits, see the Cross Validated list "Cost functions used in neural networks, alongside applications".

4. Training the Churn Network

4.1 The data, prepared

Churn_Modelling.csv: 10,000 customers, 14 columns, label Exited (1 = left the bank). Chapter 3's full toolkit applies before a single neuron fires:

📝
Note — class imbalance, spotted early: churners are 20.4% of the data (2,037/10,000). A model that predicts "stays" for everyone scores 79.75% on the test set — the majority baseline. Keep it in view; Section 6 needs it.

4.2 The compile step — three choices

ChoiceHereWhy
OptimizerAdamSGD with per-parameter adaptive learning rates and momentum — the sensible default; converges fast without hand-tuning η
Lossbinary_crossentropySigmoid output's natural partner (Ch 11/25) — and the source of the clean δ₀ = ŷ − y
Batch size / epochs32 / 100mini-batch gradient descent (Section 3.4): 8,000/32 = 250 weight updates per epoch; 100 epochs = 25,000 updates total

4.3 The real learning curve

Training loss and accuracy at eight checkpoints of the actual run (seed 0, the numbers this chapter's model produced):

0.60 0.50 0.40 0.30 epoch 1 50 100 loss 0.589 — epoch 1 loss 0.332 — epoch 100
Figure 26.8 — The real training-loss curve: the crash happens in the first 20 epochs; epochs 20–100 buy 0.015 more.
Epoch151020305075100
Loss0.5890.4260.3800.3470.3430.3380.3350.332
Train acc0.7120.8030.8310.8590.8600.8620.8640.865

Two honest readings. First, the plateau is real: by epoch 20 the network has captured nearly everything 127 parameters can hold; the remaining 80 epochs shave a sliver — diminishing returns, exactly like Chapter 6's bias-variance story told in epochs instead of degrees. Second, no memorisation: final training accuracy 0.8645 versus test accuracy 0.8620 (next section) — a gap of 0.3 points. With only 127 parameters against 8,000 examples, this network is far too small to memorise; deep learning's overfitting problem arrives with millions of parameters (Section 7).

📋 Stable content — Reviewed: October 2026

5. Implementation in Python (Keras)

The complete pipeline first — then the build step goes under the microscope. Every line of it has options, and knowing those options is the difference between using Keras and understanding it.

import numpy as np
import pandas as pd
import tensorflow as tf

# ----- Part 1: data preparation (Chapter 3's full toolkit)
dataset = pd.read_csv('Churn_Modelling.csv')
X = dataset.iloc[:, 3:-1].values          # drop RowNumber, CustomerId, Surname
y = dataset.iloc[:, -1].values            # Exited

from sklearn.preprocessing import LabelEncoder, OneHotEncoder, StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import train_test_split

le = LabelEncoder()
X[:, 2] = le.fit_transform(X[:, 2])       # Gender: Female=0, Male=1
ct = ColumnTransformer(
    transformers=[('encoder', OneHotEncoder(), [1])],   # Geography
    remainder='passthrough')
X = np.array(ct.fit_transform(X))         # France/Germany/Spain first 3 cols → 12 features

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=0)
sc = StandardScaler()
X_train = sc.fit_transform(X_train)       # fit on train ONLY (Ch 3 rule)
X_test = sc.transform(X_test)

# ----- Part 2: build the ANN (12 → 6 → 6 → 1, 127 parameters)
ann = tf.keras.models.Sequential([
    tf.keras.layers.Input(shape=(X_train.shape[1],)),
    tf.keras.layers.Dense(units=6, activation='relu'),
    tf.keras.layers.Dense(units=6, activation='relu'),
    tf.keras.layers.Dense(units=1, activation='sigmoid'),
])

# ----- Part 3: compile and train
ann.compile(optimizer='adam',
            loss='binary_crossentropy',
            metrics=['accuracy'])
ann.fit(X_train, y_train, batch_size=32, epochs=100, verbose=0)

# ----- Part 4: predict and evaluate
y_pred = (ann.predict(X_test, verbose=0) > 0.5).astype(int)

from sklearn.metrics import confusion_matrix, accuracy_score
print(confusion_matrix(y_test, y_pred))   # [[1511  84]
                                          #  [ 192 213]]
print(accuracy_score(y_test, y_pred))     # 0.862

# The homework customer: France, credit 600, Male, 40, tenure 3,
# balance 60000, 2 products, card, active, salary 50000
p = ann.predict(sc.transform(
    [[1, 0, 0, 600, 1, 40, 3, 60000, 2, 1, 1, 50000]]), verbose=0)
print(p)                                  # [[0.0287]] → stays (2.9% churn risk)

Listing 26.1 — The complete churn ANN (source: artificial_neural_network.py, modernised: Input layer, verbosity).

5.1 Building the ANN, line by line — and the options at each

ann = tf.keras.models.Sequential([
    tf.keras.layers.Input(shape=(X_train.shape[1],)),
    tf.keras.layers.Dense(units=6, activation='relu'),
    tf.keras.layers.Dense(units=6, activation='relu'),
    tf.keras.layers.Dense(units=1, activation='sigmoid'),
])
ann.summary()   # sanity check: must print "Total params: 127"

Listing 26.2 — Part 2 alone. Five lines, five decisions — model type, input shape, layer type, width, activation.

Sequential or Functional? Sequential builds the network as a sequence of layers — input layer, then the fully connected layers one after another, then the output layer — which is exactly the shape every network in this book has. It is not the only shape networks come in: the other family is the computational graph, where neurons connect outside any layer order (restricted Boltzmann machines are the classic example — advanced territory, not in this book). And within layer-shaped networks, the Functional API covers graphs that branch: two inputs (image + metadata), two outputs (price + class), shared layers, or the skip connections of Chapter 29. Same building blocks — only the wiring differs:

# style A — the source course's step-by-step style: .add() one layer at a time
ann = tf.keras.models.Sequential()
ann.add(tf.keras.layers.Input(shape=(12,)))
ann.add(tf.keras.layers.Dense(units=6, activation='relu'))
ann.add(tf.keras.layers.Dense(units=6, activation='relu'))
ann.add(tf.keras.layers.Dense(units=1, activation='sigmoid'))

# style B — the same network, Functional API: layers called like functions
inputs = tf.keras.Input(shape=(12,))
x = tf.keras.layers.Dense(6, activation='relu')(inputs)
x = tf.keras.layers.Dense(6, activation='relu')(x)
outputs = tf.keras.layers.Dense(1, activation='sigmoid')(x)
ann = tf.keras.Model(inputs=inputs, outputs=outputs)

Listing 26.3 — Two more spellings of the same 12 → 6 → 6 → 1 network. Listing 26.2's list-of-layers form and style A's .add() form are interchangeable; the Functional form (B) is for branching graphs.

Worth reading the import path once, slowly: tf.keras — the Keras library, part of TensorFlow since version 2.0 (before that they were separate installs); tf.keras.models — the module holding model-level classes like Sequential; tf.keras.layers — the module holding every layer you can add: Dense, Dropout, Conv2D, and the rest. The same layer zoo exists in PyTorch under other names — the concepts transfer one-to-one.

The Input layer declares what one row looks like: 12 numbers, no batch dimension. In the .add() style you can even omit it — the first Dense creates the input layer automatically from its own input specification. The older style passed input_dim=12 to the first Dense layer for the same reason; the explicit Input object is the modern recommended form — cleaner summary(), safer model rebuilds.

Dense means fully connected — every input reaches every neuron in the layer, which is why hidden layer 1 owns 12×6 + 6 = 78 parameters (Section 2.3's count). The .add() method accepts any layer from the layers module: a Dense for the fully connected stacks of this chapter, a Dropout layer to fight overfitting, a Conv2D for images (Ch 27), recurrent layers for sequences (Ch 29) — all created the same way, all trained by the same Section 3 machinery. For tabular data, Dense is the tool.

units=6 — how many neurons per hidden layer? This is the most asked question in deep learning, and the honest answer is: no rule of thumb — experimentation. Units is a hyperparameter: a number you choose before training, which training never adjusts — unlike the weights, which training exists to adjust. Common starting points:

Start small, grow only when validation results say so.

activation — chosen per layer, from Section 1.6's menu. Hidden layers take 'relu' (the default; 'tanh' the older alternative). The output layer's activation is decided by the problem, and it drags the loss function along with it:

ProblemOutput layerLoss
Binary (this chapter)Dense(1, activation='sigmoid')binary_crossentropy
Multi-class, one-hot labelsDense(k, activation='softmax')categorical_crossentropy
Multi-class, integer labelsDense(k, activation='softmax')sparse_categorical_crossentropy
Regression (a price)Dense(1) — linear, no activationmse (or mae)

How many hidden layers? Also no formula — a ladder of ambition:

Where do the weights start? kernel_initializer. Step 1 of the seven-step recipe (Section 3.9): small random numbers near zero, never zero. Keras does this automatically — glorot_uniform (Xavier) is the default all-rounder; 'he_normal' is the recommended partner for ReLU. Initializing with 'zeros' would not be an option but a bug: identical neurons receiving identical gradients would stay identical forever — symmetry never breaks, nothing learns:

# the explicit form of what Keras already does for us
tf.keras.layers.Dense(6, activation='relu', kernel_initializer='he_normal')

Listing 26.4 — Initializers: Glorot (default) or He (ReLU's partner); randomness is load-bearing.

5.2 Compile: the three dials, and their alternatives

Section 4.2 explains why this run picks adam + binary_crossentropy. The menu behind each dial:

# a legitimate alternative compile — RMSprop would train this net fine
ann.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])

Listing 26.5 — Swapping optimizers is a one-word change; the loss must match the output layer, the optimizer may be anything sensible.

5.3 fit() beyond the defaults

The training method is fit() — the very same method that has trained every model in this book since Chapter 3; a Keras network is just another estimator. It takes the same X_train, y_train, plus two arguments networks additionally care about. The canonical run trains plain — 32-row batches, 100 epochs, nothing else. Real projects add guards. The same fit call with the production touches:

from tensorflow.keras.callbacks import EarlyStopping

h = ann.fit(
    X_train, y_train,
    batch_size=32, epochs=100,
    validation_split=0.1,          # hold out 10% — watch overfitting live
    callbacks=[EarlyStopping(
        patience=10,               # tolerate 10 flat epochs, then stop
        restore_best_weights=True)],   # keep the BEST epoch, not the last
    class_weight={0: 1., 1: 3.9},  # churners are 20.4% — weigh their errors
    verbose=0)

Listing 26.6 — fit() with guards: validation, early stopping, class weighting. Options, not obligations.

💡
Pro Tip — single-customer predictions: the double brackets are not ceremony: predict wants a 2D array — one row per customer. And the one-hot columns come first (France = 1, 0, 0), matching where the ColumnTransformer placed them. Scale the row with the trained scaler — never raw values, never a refit scaler.
# TensorFlow is not part of scikit-learn — install it first
pip install tensorflow          # full (GPU-capable)
pip install tensorflow-cpu      # smaller, CPU-only — fine for this chapter's networks

# reproducibility (the numbers in this chapter used both seeds)
import numpy as np, tensorflow as tf
np.random.seed(0); tf.random.set_seed(0)

Listing 26.7 — Environment and seeding. Exact numbers still vary slightly across machines/BLAS builds; the story does not.

6. Reading the Results

The test set: 2,000 customers, 405 of them churners. At the default threshold 0.5, the real confusion matrix:

Predicted
ActualStaysChurns
Stays (1,595)1,511 TN84 FP — retention spend wasted
Churns (405)192 FN — customers lost silently213 TP — caught

Chapter 18's metrics, computed: accuracy = 1,724/2,000 = 0.862; precision = 213/297 = 0.717; recall = 213/405 = 0.526; F1 = 0.607. Context makes these numbers meaningful:

Model (same split, same scaling)AccuracyRecallF1Verdict
Predict "stays" always (majority)0.7980.000—the floor to beat
Logistic regression (Ch 11)0.8110.2370.337+1.3 acc, blind to 3 of 4 churners
ANN 12-6-6-10.8620.5260.607+5.1 acc over logistic; recall more than doubled

The network's advantage is not just accuracy — it found interactions the linear model could not: logistic regression catches only 96 of 405 churners; the ANN catches 213. The predicted probabilities are genuinely graded: min 0.002, mean 0.222, max 1.000 on the test set.

6.1 The threshold is a business decision

A retention campaign costs money per contact; a lost customer costs far more. Chapter 18's threshold logic, run on the real probabilities:

ThresholdAccuracyPrecisionRecallFPFNReading
0.30.8230.5500.682226129catch 2 of 3 churners; chase 226 false leads
0.50.8620.7170.52684192the default balance
0.70.8660.8700.39524245aim precisely, miss 6 of 10 churners

Note the trap: threshold 0.7 has the highest accuracy (0.866) and is the worst decision for a retention team — accuracy is dominated by the 80% majority class (Ch 18's lesson, live). If a retention offer costs little and a churner lost costs much, 0.3 is the operating point.

6.2 The homework customer, answered

The source's own test: France, credit score 600, Male, 40, tenure 3, balance $60,000, 2 products, has card, active, salary $50,000 — the real model's answer:

P(churn) = 0.0287 → this customer stays. No goodbye.
Now change three things — Germany instead of France, age 65 instead of 40, inactive instead of active — and the same network says 0.9360: leaves. The network is not mystified by combinations; it has learned how the factors compound.

6.3 What did it learn? Ask the data

A neural network will not hand you a coefficient table — but the dataset's own churn rates show exactly which signals those 127 parameters absorbed:

SignalChurn rateContrast
GeographyFrance 16.2% · Spain 16.7% · Germany 32.4%Germany doubles the risk
Activityinactive 26.9% · active 14.3%nearly 2×
Age (mean)churners 44.8 · stayers 37.47 years apart
Products1: 27.7% · 2: 7.6% · 3: 82.7% · 4: 100%nonlinear — a tree-shaped pattern
Balance (mean)churners $91,109 · stayers $72,745richer customers leave more
🌎
Real World: customer-churn prediction is one of the highest-ROI applications of tabular machine learning in industry — acquiring a customer typically costs several times retaining one. Production systems are exactly this shape: encoded demographics and activity signals in, a churn probability out, feeding a retention queue ranked by risk.

7. ANN vs the Models So Far

After 25 chapters, the honest ledger. The ANN beat logistic regression by five accuracy points and doubled recall — but Chapter 16's decision trees and Chapter 17's forests were not run on this dataset here, and on tabular data of this size they are formidably hard to beat. What each family is actually for:

ModelShines atCostInterpretability
Logistic regression (Ch 11)baselines, odds ratios, small dataminutesexcellent — signed coefficients
Decision tree (Ch 16)rules, nonlinear patterns like products 3–4secondsexcellent — readable rules
Random forest / boosting (Ch 17, 32)tabular data of this size — usually the winnerminutesmoderate — importances
ANN (this chapter)scale, interactions, unstructured data (text Ch 25, images Ch 27)hours + tuningpoor — 127 numbers, no story

Three honest caveats for the road. Scale hunger: ANNs want thousands of examples per feature — at 10,000 rows this network is near the sensible minimum. Tuning burden: layer counts, learning rates, batch sizes, epochs — the source's 6-6 architecture is a sensible hand choice, not a derived one. Opacity: when a regulator asks "why was this customer flagged?", the 127 weights offer no sentence-level answer. The modern playbook: start with the simple model, reach for the network when the simple model demonstrably leaves value behind — or when the data is images, audio or text, where nothing else comes close.

💡
Pro Tip — the no-Keras ANN: scikit-learn ships its own network, MLPClassifier(hidden_layer_sizes=(6, 6), activation='relu', max_iter=100) — same idea, no TensorFlow install, integrates with pipelines and cross-validation. For chapter-scale problems it is often all you need.
💡
Take it to finance: the Loan Default mini-project points this chapter's network at credit risk — 1,500 loan applications, a race against logistic regression (raw and feature-engineered), and an approval threshold priced in dollars. Downloadable data, full solution.

8. From Yes/No to How Much: Regression with the ANN

The churn network answers a yes/no question: will this customer leave? Chapter 25 met three-answer questions. There is a third shape, and finance asks it constantly: how much? What will the price be tomorrow? What will demand be next week? The same network answers it — with a new head and a new loss. Nothing else changes.

8.1 Three changes

This chapter so far (yes/no)How much (regression)
Label y0 or 1a number — the return
Output layerDense(1, activation='sigmoid')Dense(1) — no activation, linear
Lossbinary_crossentropymean_squared_error
MetricaccuracyMAE / RMSE (Chapter 10)

Why remove the activation? The sigmoid squeezes its output into 0–1 — a probability. A price can be any number, so the last neuron must be free to output any number: just weights, sum, done. And one practical habit: scale y too (subtract the train mean, divide by the train standard deviation), train on the scaled target, then un-scale the predictions.

8.2 The finance rules — more important than the code

8.3 The real run: can the ANN read tomorrow's AAPL?

We ran it, for real, on the course's own equity snapshot (AAPL, January 2020 – September 2026). Features: the last five daily returns. Target: tomorrow's return. Time split: train on the first 80% of days (through 2025-05-27), test on the last 338 days. Same 8-4-1 network as the mini-project — with the linear head and MSE loss:

# ---- Listing 26.8: the regression head, end to end ----
r = np.diff(np.log(close))                  # daily log returns
X = np.array([r[t-5:t] for t in range(5, len(r))])   # 5 lagged returns
y = r[5:]                                   # tomorrow's return
cut = int(0.8 * len(y))                     # TIME split — no shuffle
sc = StandardScaler()
X_train, X_test = sc.fit_transform(X[:cut]), sc.transform(X[cut:])
y_train, y_test = y[:cut], y[cut:]
ym, ys = y_train.mean(), y_train.std()      # scale y on train statistics

model = tf.keras.Sequential([
    tf.keras.layers.Dense(8, activation='relu', input_shape=(5,)),
    tf.keras.layers.Dense(4, activation='relu'),
    tf.keras.layers.Dense(1)])              # linear head — any number
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(X_train, (y_train - ym) / ys, epochs=100, batch_size=32, verbose=0)
p = model.predict(X_test, verbose=0).ravel() * ys + ym   # un-scale

print(np.abs(p - y_test).mean())            # 0.01112   MAE
print(np.abs(y_test).mean())                # 0.01088   naive baseline's MAE
print(((p > 0) == (y_test > 0)).mean())     # 0.518     direction accuracy

Listing 26.8 — The regression ANN on AAPL's next-day return. Deterministic run (seed 0, op-determinism on): every number reproduces on the same TensorFlow build.

MAEDirection
ANN (8-4-1 linear)0.0111251.8%
Naive: "price unchanged"0.01088coin = 50.0%

Read it honestly. The correlation between predicted and actual returns is −0.01; R² is −0.04. The network works — it outputs a number, and the last close of $333.02 got a tomorrow prediction of $334.05 — and it is beaten by doing nothing. Five lagged returns carry almost no signal. Markets price obvious information in seconds; Mini-Project 1 found the same wall with headlines, and Applied Project 1 with technical indicators. This is that lesson arriving from the ANN's direction.

What would make it a real experiment rather than a parable? Features with genuine information (order flow, fundamentals, cross-asset signals), walk-forward retraining instead of one split, and transaction costs in every number. Applied Project 3 (SVR) will meet this exact task again with a different learner — and should expect the same honest wall.

⚠ Volatile content — Reviewed: October 2026 · Next review: January 2027

9. The Deep Learning Ecosystem & the Road Ahead

The framework landscape this chapter's code lives in — a fast-moving neighbourhood, hence this section's quarterly review cycle:

Next stop, Chapter 27: Convolutional Neural Networks — the architecture that taught networks to see. Instead of feeding 12 tidy columns, we feed pixels; instead of Dense layers connecting everything to everything, convolutional layers slide small weight patches across the image — and the same gradient-descent machinery of this chapter does the learning.

🛠 Hands-On: The Churn Early-Warning SystemIntermediate

The retention team has budget for 400 outreach calls per quarter — roughly the number of churners in the test set. Build the network, verify the hand arithmetic, then choose the operating point that spends those calls best, and defend it.

Steps

  1. Hand-verify Section 2's parameter count: 12-6-6-1 → 127. Then confirm with ann.count_params().
  2. Hand-run the 2-2-1 network of Section 3 for the second training example x = (2, 1), y = 0 (same weights): forward pass, δ₀, all nine gradients, one η = 0.1 step, and the new loss.
  3. Train the real churn network (Listing 26.1); verify the epoch-100 loss lands near 0.33 and the confusion matrix near [[1511, 84], [192, 213]].
  4. Compute accuracy/precision/recall/F1 by hand from the matrix; verify against sklearn.
  5. Threshold study 0.1–0.9: for each, how many of the 400-call budget is spent on true churners? Recommend an operating point.
  6. Score the homework customer and the Germany/65/inactive variant; verify the stay/leave flip.
  7. Bonus: rerun with sklearn's MLPClassifier(hidden_layer_sizes=(6, 6)) and compare.
View Solution / Walkthrough

Step 2 (hand, x = (2, 1), y = 0): forward: z₁ = 0.5(2) + 0.2(1) + 0.1 = 1.3, a₁ = 1.3; z₂ = −0.3(2) + 0.8(1) − 0.1 = 0.1, a₂ = 0.1; z₀ = 0.7(1.3) − 0.4(0.1) + 0.05 = 0.92, ŷ = σ(0.92) = 0.7150. Loss = −ln(1 − 0.7150) = 1.2554 — the network is confidently wrong. Backward: δ₀ = ŷ − y = +0.7150; ∂L/∂w₀₁ = 0.7150 × 1.3 = 0.9296, ∂L/∂w₀₂ = 0.7150 × 0.1 = 0.0715; δ₁ = 0.7(0.7150)(1) = 0.5005, δ₂ = (−0.4)(0.7150)(1) = −0.2860; ∂L/∂w₁₁ = 2(0.5005) = 1.0011, ∂L/∂w₂₁ = 1(0.5005) = 0.5005, ∂L/∂w₁₂ = 2(−0.2860) = −0.5720, ∂L/∂w₂₂ = 1(−0.2860) = −0.2860. One η = 0.1 step drops ŷ 0.7150 → 0.6165 and the loss 1.2554 → 0.9584. Positive δ₀ now means: push ŷ down — the sign of learning depends entirely on the label.

# Steps 3-7
import numpy as np, pandas as pd, tensorflow as tf
np.random.seed(0); tf.random.set_seed(0)

ds = pd.read_csv('Churn_Modelling.csv')
X = ds.iloc[:, 3:-1].values; y = ds.iloc[:, -1].values
from sklearn.preprocessing import LabelEncoder, OneHotEncoder, StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import train_test_split
le = LabelEncoder(); X[:, 2] = le.fit_transform(X[:, 2])
X = np.array(ColumnTransformer([('e', OneHotEncoder(), [1])],
                               remainder='passthrough').fit_transform(X))
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=.2, random_state=0)
sc = StandardScaler(); Xtr = sc.fit_transform(Xtr); Xte = sc.transform(Xte)

ann = tf.keras.models.Sequential([
    tf.keras.layers.Input(shape=(12,)),
    tf.keras.layers.Dense(6, activation='relu'),
    tf.keras.layers.Dense(6, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')])
ann.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
h = ann.fit(Xtr, ytr, batch_size=32, epochs=100, verbose=0)
print('final loss %.4f, params %d' % (h.history['loss'][-1], ann.count_params()))

probs = ann.predict(Xte, verbose=0).ravel()
from sklearn.metrics import confusion_matrix, accuracy_score
cm = confusion_matrix(yte, (probs >= .5).astype(int))
print(cm, accuracy_score(yte, (probs >= .5).astype(int)))
# [[1511 84] [192 213]] 0.862

for t in [.1, .2, .3, .4, .5, .6, .7, .8, .9]:
    yp = (probs >= t).astype(int)
    tp, fp = int(((yp==1)&(yte==1)).sum()), int(((yp==1)&(yte==0)).sum())
    print('t=%.1f  calls spent %3d  on true churners %3d  hit-rate %.2f'
          % (t, tp+fp, tp, tp/max(tp+fp, 1)))

# homework + risk variant
for row, name in [([[1,0,0,600,1,40,3,60000,2,1,1,50000]], 'homework'),
                  ([[0,1,0,600,1,65,3,60000,2,1,0,50000]], 'risk   ')]:
    print(name, '%.4f' % ann.predict(sc.transform(row), verbose=0)[0][0])

Expected results (real): final loss ≈ 0.33; cm [[1511, 84], [192, 213]], acc 0.862, P 0.717, R 0.526, F1 0.607; homework 0.0287 (stays), risk variant 0.9360 (leaves). Threshold anchors from the canonical run: t = 0.3 spends 502 calls for 276 true churners (hit rate 0.550); t = 0.5 spends 297 calls for 213 (hit rate 0.718); t = 0.7 spends 184 calls for 160 (hit rate 0.870). Between the 502-call and 297-call anchors, call volume falls monotonically as t rises — so a threshold in the 0.35–0.45 range is where your sweep will land the 400-call budget, with a hit rate between 0.55 and 0.72. Recommendation: with a hard 400-call cap, take the threshold whose call count just fits the budget, not the accuracy-maximising 0.7 — Chapter 18's cost logic deciding a Chapter 26 model.

🎯 Key Takeaways

1

The artificial neuron copies the biological one's flow: signals in through synapses, weighted sum, activation, signal out. Dendrites receive, axons transmit — synapses carry the weights, and training is nothing but adjusting them.

2

A neuron takes the features of one single observation in and returns one value out. Inputs must be scaled — standardized or normalized — and the output layer's shape follows the output type: one neuron for continuous/binary, one dummy neuron per class for categorical.

3

Four classic activations: threshold (rigid yes/no), sigmoid (smooth probability), rectifier (the hidden-layer default), tanh (zero-centred S). Binary classification's standard recipe: ReLU in the hidden layers, sigmoid at the output.

4

A neuron with sigmoid is Chapter 11's logistic unit; a network is layers of them, and the nonlinearity between layers is the entire point of depth.

5

Brute force cannot set the weights — 25 weights with 1,000 candidates each is already 10⁷⁵ combinations. Gradient descent follows the slope of the cost instead; the churn net trains mini-batch style (batch_size = 32: one update per batch, 250 per epoch).

6

The churn network is 12 → 6 → 6 → 1 with ReLU hidden layers, sigmoid output: 127 parameters, counted by hand as (12·6+6) + (6·6+6) + (6·1+1).

7

No formula picks the architecture: start at units ≈ (inputs + outputs)/2 (here 6), grow on validation evidence. Match output layer to loss — sigmoid+BCE binary, softmax+categorical multi-class, linear+MSE regression — and always initialize small-random (Glorot/He), never zeros.

8

Backpropagation is the chain rule with a to-do list: δ flows backwards (δ₀ = ŷ − y for sigmoid + cross-entropy), every gradient is one multiplication, and every weight is updated at once. One hand step moved ŷ from 0.5671 to 0.6434 and cut the loss 22%.

9

Real results: accuracy 0.862 vs majority 0.798 and logistic 0.811 — recall doubled to 0.526. The homework customer stays (P = 0.029); the Germany/65/inactive variant leaves (0.936). Networks learn factor combinations, not isolated coefficients.

Test Your Understanding

42 questions across the chapter — the neuron and its biological roots, the activation menu, what hidden neurons learn, the learning loop and gradient descent, the algebra, backpropagation by hand, the Keras build choices, the real training run, honest model choice, and the regression extension. Choose one answer per question, then check your answers.

Q1.The dataset and task:

Q2.An artificial neuron computes:

Q3.A single neuron with sigmoid activation is precisely:

Q4.Why ReLU in hidden layers rather than sigmoid?

Q5.Why sigmoid at the output of this network?

Q6.In a biological neuron, signals are received by the dendrites and sent out along the axon. The synapse is:

Q7.The threshold (step) activation function:

Q8.If the dependent variable is categorical with k classes, the output layer needs:

Q9.In the property-price example, a hidden neuron that fires only for homes over 100 years old shows that:

Q10.Why can't we find the best weights by brute force — trying every combination?

Q11.The churn network trains with batch_size = 32. That makes it:

Q12.(Hand) The parameter count of 12 → 6 → 6 → 1:

Q13.How do you choose units — the number of neurons in a hidden layer?

Q14.Why must the weights be initialized to small random values rather than all zeros?

Q15.Adding the second hidden layer is literally what makes this a deep-learning model, because:

Q16.units and batch_size are called hyperparameters because:

Q17.Why must RowNumber, CustomerId and Surname be dropped?

Q18.Stacking layers without activation functions would:

Q19.(Hand, Section 3) The forward pass with x = (1, 2) gives z₁ = 1.0 because:

Q20.For a sigmoid output with binary cross-entropy, the output error signal is:

Q21.ReLU's derivative in the backward pass is:

Q22.(Hand) With δ₀ = −0.4329 and a₂ = 1.2, the gradient ∂L/∂w₀₂ is:

Q23.One SGD step at η = 0.1 on the hand network achieved:

Q24.The sign of the gradient on w₀₁ was −0.4329, so the update rule does what?

Q25.An "epoch" is:

Q26.Why batch_size = 32 rather than all 8,000 at once?

Q27.Adam, the chosen optimizer, is best described as:

Q28.(Real) The training curve showed:

Q29.Train accuracy 0.8645 vs test accuracy 0.8620 means:

Q30.(Real) The confusion matrix [[1511, 84], [192, 213]] gives recall:

Q31.The majority baseline (predict "stays" always) scores:

Q32.(Real) ANN vs logistic regression on the same split:

Q33.(Real) Threshold 0.7 maximises accuracy (0.866) but:

Q34.(Real) The homework customer (France, 40, active) and the variant (Germany, 65, inactive) got:

Q35.Why must features be scaled for an ANN (but a decision tree shrugs)?

Q36.The gender and geography encodings used here:

Q37.(Real data insight) Which pattern did Section 6.3 surface?

Q38.Honest model choice for THIS dataset's size and shape:

Q39.The scikit-learn route to the same model, no TensorFlow install:

Q40.Chapter 27 (CNNs) extends this chapter by:

Q41.To turn this chapter's yes/no network into one that predicts a number (a return, a price), the essential change is:

Q42.(Real) In the AAPL next-day experiment of Section 8, the honest headline result was: