By the end of this lecture, you should be able to:
Note
Lecture 10 gave the big picture (PyTorch, CNNs). Today we open the black box and do the math by hand on a tiny network.
A drug works at a medium dose, but not at low or high doses.
| Layer | What it is | In our example |
|---|---|---|
| Input layer | the features we feed in | 1 node: Dose |
| Hidden layer(s) | nodes between input and output | 1 hidden layer with 2 nodes |
| Output layer | the prediction(s) | 1 node: Effectiveness |
Numbers we multiply by:
\(-34.4,\ -2.52,\ -1.30,\ 2.28\)
They stretch or flip a value.
Numbers we add:
\(+2.14,\ +1.29,\ -0.58\)
They shift a value up or down.
Familiar idea
A straight line \(y = 0.64x + 0.95\) also has a weight (slope 0.64) and a bias (intercept 0.95). A neural network is many of these, glued together with activation functions.
We’ll plug doses from 0 to 1 into the network and watch it build its output, one piece at a time.
Note
To keep the math simple, doses and effectiveness are scaled to go from 0 to 1. For now, assume the parameters have already been learned. We’ll see how in Part 3.
| Dose | \(x = \text{Dose} \times (-34.4) + 2.14\) | \(\text{SoftPlus}(x)\) | \(\times (-1.30)\) |
|---|---|---|---|
| 0.0 | 2.14 | 2.25 | −2.93 |
| 0.2 | −4.74 | 0.01 | −0.01 |
| 0.4 | −11.62 | ≈ 0 | ≈ 0 |
| 1.0 | −32.26 | ≈ 0 | ≈ 0 |
Same recipe, different numbers: multiply by −2.52, add 1.29, SoftPlus, then multiply by 2.28.
At dose 0: \(\;0 \times (-2.52) + 1.29 = 1.29 \;\to\; \text{SoftPlus}(1.29) = 1.53 \;\to\; 1.53 \times 2.28 = 3.50\)
At dose 0: \(\;-2.93 + 3.50 = 0.57\), then \(\;0.57 + (-0.58) \approx 0.0\) ✓ (the drug is not effective)
Compute the network’s prediction for a medium dose of 0.5.
Top node
\(0.5 \times (-34.4) + 2.14 = \;?\)
\(\text{SoftPlus}(\,?\,) \times (-1.30) = \;?\)
Bottom node
\(0.5 \times (-2.52) + 1.29 = \;?\)
\(\text{SoftPlus}(\,?\,) \times 2.28 = \;?\)
A neural network builds a complicated shape by taking simple activation functions and:
| Operation | Done by | Effect on the curve |
|---|---|---|
| multiply the input | weight before the node | stretch / squeeze sideways (and flip) |
| add to the input | bias before the node | slide left or right |
| multiply the output | weight after the node | stretch / flip up and down |
| add curves together | the sum node | combine shapes |
| add the final bias | final bias | shift up or down |
More nodes = more pieces to combine = more flexible squiggles.
Today’s simplified example
Pretend we already know every parameter except the final bias. We’ll use backpropagation to find the final bias, starting from 0. Training data: just 3 doses, 0, 0.5 and 1, with effectiveness 0, 1 and 0.
With final bias = 0, the predictions are 0.57, 1.61, 0.58.
Sum of Squared Residuals:
\[ \text{SSR} = \sum_{i=1}^{n} (\text{Observed}_i - \text{Predicted}_i)^2 \]
\[ \begin{aligned} \text{SSR} &= (0 - 0.57)^2 + (1 - 1.61)^2 + (0 - 0.58)^2 \\ &\approx 0.33 + 0.37 + 0.34 = 1.04 \end{aligned} \]
Lower SSR = better fit. We want the final bias that gives the lowest SSR.
The prediction depends on the final bias, and the SSR depends on the prediction:
\[ \frac{d\,\text{SSR}}{d\,\text{Final Bias}} = \frac{d\,\text{SSR}}{d\,\text{Predicted}} \times \frac{d\,\text{Predicted}}{d\,\text{Final Bias}} \]
Part 1: SSR with respect to Predicted
\[ \frac{d\,\text{SSR}}{d\,\text{Predicted}} = \sum_{i=1}^{n} -2\,(\text{Observed}_i - \text{Predicted}_i) \]
Part 2: Predicted with respect to the Final Bias
\(\text{Predicted} = \text{blue} + \text{orange} + \text{Final Bias}\)
The blue and orange curves don’t depend on the final bias, so
\[ \frac{d\,\text{Predicted}}{d\,\text{Final Bias}} = 0 + 0 + 1 = 1 \]
Putting the two parts together: \(\displaystyle \frac{d\,\text{SSR}}{d\,\text{Final Bias}} = \sum_{i=1}^{n} -2\,(\text{Observed}_i - \text{Predicted}_i) \times 1\)
Plug in the 3 training points (final bias = 0):
\[ \frac{d\,\text{SSR}}{d\,\text{Final Bias}} = -2(0 - 0.57) + -2(1 - 1.61) + -2(0 - 0.58) \approx 1.14 + 1.22 + 1.16 \approx 3.5 \]
| Gradient descent rule | With learning rate 0.1 |
|---|---|
| Step size = derivative × learning rate | \(3.5 \times 0.1 = 0.35\) |
| New bias = old bias − step size | \(0 - 0.35 = -0.35\) |
| Step | Final bias | SSR | Derivative |
|---|---|---|---|
| 0 | 0.00 | 1.04 | 3.54 |
| 1 | −0.35 | 0.17 | 1.41 |
| 2 | −0.50 | 0.03 | 0.57 |
| 3 | −0.55 | 0.005 | 0.23 |
| 4 | −0.57 | 0.002 | 0.09 |
| 5 | −0.58 | 0.001 | 0.04 |
| … | ≈ −0.59 | ≈ 0.001 | ≈ 0 |
The steps get smaller automatically as the slope flattens near the bottom. We land at ≈ −0.58, the value from the start of the lecture! 🎉
In a real network, every weight and bias is updated at the same time:
parameter = parameter − learning_rate × derivativeNote
In PyTorch (Lecture 10), step 3 is loss.backward() and step 4 is optimizer.step(). The library does the chain rule for you, but now you know what it’s computing!
| Neural network | Logistic regression | |
|---|---|---|
| Flexibility | Fits almost any shape | One S-shaped curve |
| Choices to make | # layers, # nodes, activation, learning rate … (an art) | Very few (closer to a science) |
| Data needed | Usually a lot | Works with less |
| Interpretability | Hard: a black box | Easy: each coefficient has a meaning |
| Best for | Complex patterns: images, text, audio | Simple, tabular problems; explaining results |
Tip
Start simple. Try logistic regression (or a tree) first, and use a neural network if the simple model isn’t good enough.
from sklearn.neural_network import MLPClassifier # multi-layer perceptron
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
nn = make_pipeline(
StandardScaler(), # networks train better on scaled data
MLPClassifier(hidden_layer_sizes=(10, 10), # 2 layers x 10 nodes
activation="relu",
learning_rate_init=0.01,
max_iter=2000,
random_state=0)
)
nn.fit(X_train, y_train)
print("Test accuracy:", nn.score(X_test, y_test))MLPClassifier Settings| Argument | Meaning |
|---|---|
hidden_layer_sizes |
nodes per hidden layer, e.g. (2,) = 1 layer with 2 nodes, like today’s example |
activation |
"relu", "logistic" (sigmoid), "tanh" |
learning_rate_init |
the gradient-descent learning rate |
max_iter |
maximum number of epochs |
Use MLPRegressor for numeric targets (like effectiveness).
Open the practice notebook and work through it with a partner:
You will:
MLPClassifier on curved data, and compare it with logistic regression.