3. MLP
Activity: Understanding Multi-Layer Perceptrons (MLPs)
This activity is designed to test your skills in Multi-Layer Perceptrons (MLPs).
The thread running through the activity is what a hidden layer buys you: one exercise by hand to see backpropagation move every parameter, then a dataset no straight line can touch, then more classes β and finally a fourth exercise that asks whether stacking a second hidden layer buys you anything at all.
Technical rules (they apply to the whole activity)
- Fix the random seed β
rng = np.random.default_rng(42)β and use the samerngthroughout the report. Results that cannot be reproduced score no points; - Every plot must have a title, axis labels, and a class legend;
- Allowed libraries:
numpy,pandas,matplotlib/seaborn, andscikit-learnonly for generating the datasets and splitting them (make_blobs,make_classification,train_test_split) and for the confusion matrix. The MLP itself β activations, loss, forward pass, gradients, and the update β MUST BE WRITTEN BY YOU. No TensorFlow, no PyTorch, noMLPClassifier. Using one voids the implementation criterion and the exercise that depends on it; - Whenever the statement asks for a number (a gradient, an accuracy, a parameter after the update), report the number in the text β not only in the code output;
- Organize the report with one heading per exercise and one subheading per item (
Exercise 1,A,B, β¦), in the same order as the statement, and number the figures as indicated. The last section of the report must be the Results summary described at the end of this page.
Exercise 1
One Step of Backpropagation, by Hand
Consider an MLP with 2 input features, 1 hidden layer of 2 neurons, and 1 output neuron. Use \(\tanh\) as the activation on both layers. The loss is the mean squared error over a single sample, so \(N = 1\):
Notation
This page writes \(\mathbf{W}^{(1)}, \mathbf{W}^{(2)}\) for the two weight matrices because Exercises 3 and 4 need an arbitrary number of layers. The lecture writes the same two-layer network as \(\mathbf{W}, \mathbf{V}\) with biases \(\mathbf{b}^h, b^y\) β so \(\mathbf{W}^{(1)} \equiv \mathbf{W}\), \(\mathbf{W}^{(2)} \equiv \mathbf{V}\), \(\mathbf{b}^{(1)} \equiv \mathbf{b}^h\) and \(b^{(2)} \equiv b^y\). The lecture's worked example uses the sigmoid; here you use \(\tanh\), whose derivative is \(\frac{d}{du}\tanh(u) = 1 - \tanh^2(u)\).
Use these values:
-
Input and target: \(\mathbf{x} = [0.5, -0.2]\), \(y = 1.0\)
-
Hidden weights: \(\mathbf{W}^{(1)} = \begin{bmatrix} 0.3 & -0.1 \\ 0.2 & 0.4 \end{bmatrix}\)
-
Hidden biases: \(\mathbf{b}^{(1)} = [0.1, -0.2]\)
-
Output weights: \(\mathbf{W}^{(2)} = [0.5, -0.3]\)
-
Output bias: \(b^{(2)} = 0.2\)
-
Learning rate: \(\eta = 0.3\)
Show every derivation and every intermediate number, to at least 4 decimal places. This exercise is done on paper (or in Markdown) β not by calling a library.
A β Forward pass
- Hidden pre-activations: \(\mathbf{z}^{(1)} = \mathbf{W}^{(1)} \mathbf{x} + \mathbf{b}^{(1)}\)
- Hidden activations: \(\mathbf{h}^{(1)} = \tanh(\mathbf{z}^{(1)})\)
- Output pre-activation: \(u^{(2)} = \mathbf{W}^{(2)} \mathbf{h}^{(1)} + b^{(2)}\)
- Output: \(\hat{y} = \tanh(u^{(2)})\), and then the loss \(L\).
B β Backward pass
Compute the gradient of the loss with respect to every weight and every bias. Start from \(\displaystyle \frac{\partial L}{\partial \hat{y}}\) and chain outward:
- \(\displaystyle \frac{\partial L}{\partial u^{(2)}}\), using the \(\tanh\) derivative;
- Output layer: \(\displaystyle \frac{\partial L}{\partial \mathbf{W}^{(2)}}\) and \(\displaystyle \frac{\partial L}{\partial b^{(2)}}\);
- Propagate back: \(\displaystyle \frac{\partial L}{\partial \mathbf{h}^{(1)}}\) and \(\displaystyle \frac{\partial L}{\partial \mathbf{z}^{(1)}}\);
- Hidden layer: \(\displaystyle \frac{\partial L}{\partial \mathbf{W}^{(1)}}\) and \(\displaystyle \frac{\partial L}{\partial \mathbf{b}^{(1)}}\).
C β Parameter update
With \(\eta = 0.3\), apply gradient descent to all eight parameters:
Report the numerical value of every updated parameter, and state whether the loss would go up or down on a second forward pass β and why.
Exercise 2
Binary Classification a Straight Line Cannot Do
A β Generate the data
Class 1 is split into two clusters that sit on opposite sides of Class 0, so no straight line can separate them β which is exactly the situation a hidden layer exists for. Use these parameters, unchanged:
from sklearn.datasets import make_blobs
centers = np.array([[0., 0.], [3., 3.], [-3., -3.]])
X, c = make_blobs(n_samples=[500, 250, 250], centers=centers,
cluster_std=1.2, random_state=42)
y = (c > 0).astype(int) # class 0: 1 cluster | class 1: 2 clusters
Split 80% train / 20% test with train_test_split(..., test_size=0.2, random_state=42, stratify=y).
Produce Figure 1: a scatter plot of the 1000 points colored by y.
B β Establish the baseline
Before building anything, fit a single linear boundary to the training set and report its test accuracy. You may use sklearn.linear_model.LogisticRegression here β this is the baseline, not your model. It should land near 48%: worse than guessing. Keep that number; item D asks about it.
C β Implement the MLP
Write an MLP from scratch. You choose the architecture, but it must have at least one hidden layer, and you must implement yourself:
- the forward pass;
- the loss (binary cross-entropy is the natural choice);
- the backward pass β every gradient derived, not autodiffed;
- the parameter update.
Write it as a function or class that takes the layer sizes as an argument. Exercises 3 and 4 reuse this same code, so a hard-coded two-layer network will cost you there.
Train for a reasonable number of epochs (a few hundred to a few thousand), recording the training loss each epoch. Then:
- Report the architecture, the learning rate, the number of epochs, and the test accuracy. A correct implementation lands around 90β93%.
- Produce Figure 2: training loss \(\times\) epoch.
- Produce Figure 3: the decision boundary over the test points β evaluate your network on a grid and shade the two regions.
D β Analysis
Why does the linear baseline score below 50% while your MLP clears 90%? Point at Figure 3: describe the shape of the region your network assigns to Class 1, and explain why no single straight line can produce it.
Exercise 3
Multi-Class, Same Network
A β Generate the data
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=1500, n_features=4, n_informative=4,
n_redundant=0, n_repeated=0, n_classes=3,
n_clusters_per_class=2, class_sep=1.0,
random_state=42)
That is 1500 samples, 4 informative features, 3 classes, 2 clusters each. Split 80/20 with the same seed and stratify=y.
B β Adapt the network
Extend your Exercise 2 implementation to three classes. The changes you need are in the output layer and the loss:
- the output layer now has 3 units;
- softmax turns those into probabilities;
- categorical cross-entropy replaces binary cross-entropy.
Derive \(\partial L / \partial u\) for the softmax + cross-entropy pair and show the derivation in the report β the result is famously simple, and knowing why is the point of this item.
Extra point
Worth +1 if the core of your Exercise 2 implementation is reused verbatim in Exercise 3: the forward pass, the backward pass and the update step must be the same code, untouched. The output size, the loss function and the hyperparameters may differ β those are arguments, not structure. The exercise grade is capped at 10/10, so this point insures you against losses elsewhere rather than adding to a full score.
C β Train and evaluate
- Report the architecture, hyperparameters, and test accuracy. Expect roughly 83β86%.
- Produce Figure 4: training loss \(\times\) epoch.
- Produce Figure 5: the confusion matrix on the test set.
D β Analysis
Which pair of classes does the network confuse most, according to Figure 5? The data has 4 informative dimensions and 6 clusters total β offer an explanation for the confusion that refers to that structure. Also report the logistic-regression baseline on this data (around 66%) and say what the hidden layer added.
Exercise 4
Does a Second Hidden Layer Help?
Same data as Exercise 3, same training budget, same seed. The only change is depth: at least 2 hidden layers.
This is a controlled comparison, not a hunt for a better score. Change one thing at a time and keep everything else fixed, or the comparison means nothing.
A β Train the deeper network
Report the architecture and the test accuracy, trained with the same number of epochs and the same learning rate as Exercise 3.
B β Compare
Produce Figure 6: the training-loss curves of the Exercise 3 network and the Exercise 4 network on the same axes. Then fill in this comparison in the text:
| hidden layers | parameters | final train loss | test accuracy | |
|---|---|---|---|---|
| Exercise 3 | 1 | |||
| Exercise 4 | β₯ 2 |
Run both with at least 3 different seeds and report the mean, so you are comparing models rather than luck.
C β Analysis
Report what you measure
The deeper network will most likely not beat the shallower one here, and may well do worse. That is the expected result and it is worth full marks when reported honestly and explained. Tuning until depth "wins" is not what is being graded.
Explain the result. Useful angles: how many clusters the data actually has and how complex a boundary they need; what a second layer adds in representational terms versus what it costs in optimization; whether the training loss tells the same story as the test accuracy. Say what kind of data would make the extra depth pay off.
Results summary
Close the report with this table, filled in:
| # | Quantity | Value |
|---|---|---|
| 1 | Ex. 1 β \(\mathbf{z}^{(1)}\) and \(\mathbf{h}^{(1)}\) | |
| 2 | Ex. 1 β \(u^{(2)}\), \(\hat{y}\) and \(L\) | |
| 3 | Ex. 1 β the eight updated parameters | |
| 4 | Ex. 2 β linear baseline test accuracy | |
| 5 | Ex. 2 β architecture and test accuracy | |
| 6 | Ex. 3 β architecture and test accuracy | |
| 7 | Ex. 3 β most-confused class pair | |
| 8 | Ex. 4 β architecture and test accuracy | |
| 9 | Ex. 4 β accuracy difference against Ex. 3 (mean over β₯ 3 seeds) |
Evaluation Criteria
The deliverable for this activity is a report that includes:
- Exercise 1 worked entirely by hand, every step shown to at least 4 decimals.
- The code for your MLP and for the data generation, commented.
- Figures 1 to 6, numbered as requested.
- Your answers to the analysis questions in items D (and 4C).
- The Results summary table.
Important Notes:
-
The deliverable is a GitHub Pages site backed by a public repository β see Submission Format for the required layout, front matter and checklist;
-
There is a strict no-plagiarism policy. Any form of plagiarism will result in a zero grade for the activity and may lead to further disciplinary action under the university's academic integrity policies;
-
The deadline for each activity is not extended β NO EXCEPTIONS will be made for late submissions.
-
AI collaboration is allowed, but each student MUST UNDERSTAND and be able to explain every part of the submitted code and analysis. Any use of AI tools must be properly cited. ORAL EXAMS may be conducted.
-
All deliverables for individual activities should be submitted through the course platform insper.blackboard.com.
Grading Criteria:
Each row is worth the points indicated, awarded in full, partially (half), or not at all: in full when the item is complete and correct; partially when it is implemented but missing the requested analysis, or when the analysis lacks the numerical result that supports it; zero when absent or incorrect.
The accuracy value itself is not graded. The datasets are fixed and seeded, so the numbers are what they are; what is graded is the method, the figures, and reporting honestly what you got β including in Exercise 4, where the expected finding is that depth does not help.
Exercise 1 β Backpropagation by hand (2 points):
| Points | Criterion | What earns full credit |
|---|---|---|
| 0.5 | Forward pass (A) | \(\mathbf{z}^{(1)}\), \(\mathbf{h}^{(1)}\), \(u^{(2)}\), \(\hat{y}\) and \(L\) with the given values, to at least 4 decimals. |
| 1.0 | Backward pass (B) | \(\partial L/\partial \hat{y}\), \(\partial L/\partial u^{(2)}\), both weight gradients and both bias gradients β each one derived, not just stated. |
| 0.5 | Parameter update (C) | All eight parameters updated with \(\eta = 0.3\), values reported, with the loss-direction answer. |
Exercise 2 β Binary classification (3 points):
| Points | Criterion | What earns full credit |
|---|---|---|
| 0.5 | Data and baseline (A, B) | The specified generator and seed, an 80/20 stratified split, Figure 1, and the linear baseline reported. |
| 1.5 | MLP from scratch (C) | Forward pass, loss, gradients and update all hand-written and correct; layer sizes are an argument, not hard-coded. |
| 0.5 | Training and figures (C) | Architecture and hyperparameters stated, test accuracy reported, Figures 2 and 3 correct. |
| 0.5 | Analysis (D) | Explains the sub-50% linear baseline from the geometry of Figure 3, not merely by asserting non-linearity. |
Exercise 3 β Multi-class (3 points):
| Points | Criterion | What earns full credit |
|---|---|---|
| 0.5 | Data (A) | The specified generator and seed, 80/20 stratified split. |
| 1.5 | Multi-class adaptation (B) | Softmax and categorical cross-entropy implemented, with \(\partial L/\partial u\) derived in the report. |
| 0.5 | Training and figures (C) | Architecture and hyperparameters stated, test accuracy reported, Figures 4 and 5 correct. |
| 0.5 | Analysis (D) | Identifies the most-confused pair from the confusion matrix and explains it from the cluster structure; reports the baseline comparison. |
| +1.0 | Extra β verbatim reuse (B) | The Exercise 2 forward/backward/update code reused untouched. Optional; the exercise grade is capped at 10/10. |
Exercise 4 β Depth comparison (2 points):
| Points | Criterion | What earns full credit |
|---|---|---|
| 0.5 | Deeper network (A) | At least 2 hidden layers, same data, seed and training budget as Exercise 3. |
| 1.0 | Controlled comparison (B) | The comparison table filled in, Figure 6 with both loss curves, and both models averaged over at least 3 seeds. |
| 0.5 | Explanation (C) | Explains the result β including when depth loses β from the structure of the data, and says what data would reward the extra depth. |