M30: Feedforward Neural Networks

CSCI 3151 โ€” Foundations of Machine Learning

Frank Rudzicz

Learning Outcomes

By the end of this module, you should be able to:

  1. Explain what a feedforward neural network is as a composition of linear maps and nonlinear activations.
  2. Write down the mathematical form of a simple multi-layer perceptron (MLP) and track the shapes of its parameters.
  3. Compare the capacity of logistic regression vs. shallow/deep networks using parameter counts and decision boundaries.
  4. Implement a small FFNN classifier in Python (scikit-learn or PyTorch) on toy data and a real dataset (digits).
  5. Diagnose basic under/overfitting patterns in FFNNs using train/validation curves and decision boundary plots.

Linear model recap

For input \(x \in \mathbb{R}^{d}\):

\[ f_{\text{linear}}(x) = w^\top x + b \]

  • Classification: logistic regression uses

\[ p(y=1 \mid x) = \sigma(w^\top x + b),\quad \sigma(z) = \frac{1}{1 + e^{-z}}. \]

  • Decision boundary is a hyperplane:
    • Great when classes are (roughly) linearly separable.
    • Painful when you need curved or complicated boundaries.

We want flexible functions

  • Many real-world decision boundaries are nonlinear.
  • Kernel methods (M19โ€“M21) gave us one way to get nonlinear boundaries.
  • Another idea:

Build a function by stacking simple transforms layer by layer.

  • Each layer:
    • Applies a linear map
    • Passes through a nonlinear activation
  • Stack enough layers & units โ†’ very rich family of functions.

Biological neurons

A biological neuron (very roughly):

  • Receives signals on dendrites.
  • Integrates them in the cell body (soma).
  • If total input passes a threshold, fires an action potential down the axon to other neurons.
  • Learning happens in the adjustment of neurotransmitters that connect our neurons

Artificial neurons

Artificial neuron (McCulloch and Pitts 1943):

  • Inputs: a feature vector \[ \mathbf{a} = (a_1, \dots, a_M)^\top \]
  • Parameters: weights \(\mathbf{w} = (w_1, \dots, w_M)^\top\) and (optional) bias \(w_0\).
  • Pre-activation (weighted sum): \[ x \;=\; \sum_{i=1}^M w_i a_i + w_0. \]
  • Activation: \[ s \;=\; g(x), \] where \(g\) is a nonlinearity .
  • This can implement arbitrary Boolean logic

As a classifier (binary case)

  • Interpret \(x\) as a score for class \(1\) vs class \(0\).
  • With a step activation, we predict \[ \hat{y} = \begin{cases} 1 & \text{if } x \ge 0\\[4pt] 0 & \text{otherwise.} \end{cases} \]

  • This is not differentiable
  • Geometrically in \(\mathbb{R}^M\):
    • A perceptron is a linear classifier.
    • The decision boundary is the hyperplane \(\{\mathbf{a} : \mathbf{w}^\top \mathbf{a} + w_0 = 0\}\).

Activation functions

For differentiable training (gradient-based learning), smooth alternatives became popular:

  • Sigmoid (logistic): \[ g(x) = \sigma(x) = \frac{1}{1 + e^{-x}} \]
  • Hyperbolic tangent: \[ g(x) = \tanh(x) \] which squashes to \((-1, 1)\) instead of \((0, 1)\).
Figure 1

๐Ÿค” Key ideas:

  • Nonlinearity is essential: without it, stacked layers would collapse to a single linear map.
  • Smooth \(g\) enables gradient-based methods (e.g., stochastic gradient descent with backpropagation in deeper networks).

Rectified linear units (ReLUs) and softplus

Later, rectified linear units (ReLUs) (Glorot, Bordes, and Bengio 2011) became the default for many deep nets:

  • ReLU: \[ g(x) = \max (0, x) \]

  • Its smooth cousin, softplus: \[ g(x) = \log\left(1+e^x\right) \]

๐Ÿค” Why ReLUs?

  • They are piecewise linear and simple to compute.
  • They avoid saturation for large positive \(x\) (so gradients donโ€™t vanish there).
  • They encourage sparse activations (many units output exactly \(0\)), which can help with efficiency and representation.

Perceptron learning

Learning in a single perceptron (Rosenblatt 1958):

  • Training data: \((\mathbf{a}^{(i)}, y^{(i)})\), with \(y^{(i)} \in \{0, 1\}\).
  • Prediction: \(\hat{y}^{(i)} = \mathbf{1}\{ \mathbf{w}^\top \mathbf{a}^{(i)} + w_0 \ge 0 \}\).
  • A classic perceptron learning rule: \[ \mathbf{w} \leftarrow \mathbf{w} + \eta \,\big(y^{(i)} - \hat{y}^{(i)}\big)\,\mathbf{a}^{(i)}, \] where \(\eta > 0\) is a learning rate.
  • ๐Ÿ‘€ Weโ€™ll see the backpropagation learning rule in the next module

Note

  • If the perceptron misclassifies a point, we nudge \(\mathbf{w}\) so that the point moves toward the correct side of the decision boundary.
  • For linearly separable data, one can prove that this algorithm converges to a separating hyperplane (in finitely many steps).
  • There are alternatives; e.g., Hebb (1949) described โ€œCells that fire together wire together.โ€ and suggested that synaptic weights strengthen when pre- and post-synaptic neurons co-activate.

โ€ฆ and the XOR limitation

But there is a fundamental limitation (Minsky and Papert 1969):

  • A single perceptron is a linear classifier.
  • It cannot represent decision boundaries that are not linearly separable.

Classic counterexample: XOR

  • Inputs: \((a_1, a_2) \in \{0, 1\}^2\).
  • Labels: \(y = 1\) iff exactly one of \(a_1, a_2\) is \(1\).
  • No straight line in the \((a_1, a_2)\) plane can perfectly separate the \(y=1\) and \(y=0\) points.

This sets up the next step:

  • To solve XOR (and many real-world problems), we need hidden layers and compositions of nonlinearities.
  • That is the jump from single-layer perceptrons to multilayer neural networks.

Feedforward Neural Networks: Big Picture

๐Ÿง  Mental picture

  • Inputs: feature vector \(x\)
  • ๐Ÿ”‘ Hidden layers:
    • compute intermediate representations
  • Output layer:
    • returns logits / probabilities / predictions

Feedforward = information flows one way; no loops, no recurrence

L-layer Feedforward Net

For an input \(x \in \mathbb{R}^{d_0}\),

  • Layer sizes: \(d_0, d_1, \dots, d_L\)
    • \(d_0\): input dimension
    • \(d_L\): output dimension (e.g., number of classes)
  • Parameters:
    • For layer \(\ell = 1, \dots, L\)
      • Weights \(W_\ell \in \mathbb{R}^{d_\ell \times d_{\ell-1}}\)
      • Biases \(b_\ell \in \mathbb{R}^{d_\ell}\)

Define:

\[ \begin{aligned} h_0 &= x \\ z_\ell &= W_\ell h_{\ell-1} + b_\ell \quad (\text{pre-activation}) \\ h_\ell &= g_\ell(z_\ell) \quad (\text{post-activation}) \end{aligned} \]

  • The network computes \(f(x) = h_L\).
  • \(g_\ell\) is usually nonlinear (e.g. ReLU, tanh, sigmoid), except maybe the last layer.

๐Ÿ”ฌ Composition of Linear Layers is Linear

Proposition (non-trivial but short)
Consider a network with layers:

\[ h_1 = W_1 x + b_1,\quad h_2 = W_2 h_1 + b_2,\quad \ldots,\quad h_L = W_L h_{L-1} + b_L \]

and no nonlinearities (i.e., ๐Ÿ‘‰ all \(g_\ell\) are identity๐Ÿ‘ˆ). Then:

\[ h_L = W_{\text{eff}} x + b_{\text{eff}} \]

for some effective weight matrix \(W_{\text{eff}}\) and bias \(b_{\text{eff}}\); i.e., the whole network is just a single linear model.

Sketch of reasoning

  • For 2 layers:

\[ h_2 = W_2(W_1 x + b_1) + b_2 = (W_2 W_1)x + (W_2 b_1 + b_2) \]

  • Thatโ€™s linear in \(x\).
  • Repeat this argument layer by layer (induction) to collapse everything into one linear transform + bias.
  • Therefore: Without nonlinear activations, depth buys you nothing; the network is still just a linear model.

Capacity & Parameter Counts

Focus on a simple fully-connected network:

  • Input dimension: \(d_{\text{in}}\)
  • Hidden layer sizes: \(h_1, \dots, h_{L-1}\)
  • Output dimension: \(d_{\text{out}}\)

Total number of parameters:

\[ \sum_{\ell=1}^{L} \left( d_\ell \cdot d_{\ell-1} + d_\ell \right) \]

(since each layer has a weight matrix \(W_\ell\) and a bias \(b_\ell\))

Examples

Example: Logistic vs 1-hidden-layer MLP

Assume:

  • Input: \(d_{\text{in}} = 10\)
  • Binary classification: \(d_{\text{out}} = 1\)
  • Hidden layer: \(h = 20\)

Logistic regression:

  • Parameters: \(10\) weights + \(1\) bias = 11

1-hidden-layer network:

  • Input โ†’ hidden: \((10 + 1) \times 20 = 220\)
  • Hidden โ†’ output: \((20 + 1) \times 1 = 21\)
  • Total: 241 parameters

Order-of-magnitude = more capacity to capture structure

  • โ€ฆbut also more risk of overfitting.

Architecture Design: Width vs Depth

Practical takeaway (for this course):

  • Start small (e.g., 1โ€“2 hidden layers with modest width).
  • Let data size & validation performance guide complexity.

๐ŸŒ™ Example 1: Moons โ€” Linear vs MLP

Weโ€™ll revisit the classic two-moons dataset:

  • 2D features, binary labels
  • Nonlinear boundary needed for good performance
Code
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split

X, y = make_moons(n_samples=500, noise=0.25, random_state=3151)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=3151, stratify=y
)

fig, ax = plt.subplots()
scatter = ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, alpha=0.7)
ax.set_xlabel("x1")
ax.set_ylabel("x2")
ax.set_title("Two-moons training data")
plt.tight_layout()

๐ŸŒ™ Moons: Logistic Regression Baseline

Code
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

logreg = LogisticRegression()
logreg.fit(X_train, y_train)

y_train_pred = logreg.predict(X_train)
y_test_pred = logreg.predict(X_test)

print("Logistic regression accuracy:")
print(f"  train: {accuracy_score(y_train, y_train_pred):.3f}")
print(f"  test:  {accuracy_score(y_test, y_test_pred):.3f}")

# Plot decision boundary
xx, yy = np.meshgrid(
    np.linspace(X[:, 0].min() - 0.5, X[:, 0].max() + 0.5, 200),
    np.linspace(X[:, 1].min() - 0.5, X[:, 1].max() + 0.5, 200),
)
grid = np.c_[xx.ravel(), yy.ravel()]
probs = logreg.predict_proba(grid)[:, 1].reshape(xx.shape)

fig, ax = plt.subplots()
cs = ax.contourf(xx, yy, probs, levels=20, alpha=0.6)
scatter = ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, edgecolor="k", s=20)
ax.set_title("Logistic regression decision boundary")
ax.set_xlabel("x1")
ax.set_ylabel("x2")
plt.tight_layout()
Logistic regression accuracy:
  train: 0.860
  test:  0.847

  • Expect a roughly straight-ish separating boundary.
  • Likely underfits the curved moons.

๐ŸŒ™ Moons: 1-hidden-layer MLP

Code
from sklearn.neural_network import MLPClassifier

mlp = MLPClassifier(
    hidden_layer_sizes=(20,),
    activation="relu",
    solver="adam",
    max_iter=500,
    random_state=3151
)

mlp.fit(X_train, y_train)

y_train_pred_mlp = mlp.predict(X_train)
y_test_pred_mlp = mlp.predict(X_test)

print("MLP (20 hidden units, ReLU) accuracy:")
print(f"  train: {accuracy_score(y_train, y_train_pred_mlp):.3f}")
print(f"  test:  {accuracy_score(y_test, y_test_pred_mlp):.3f}")

# Decision boundary
probs_mlp = mlp.predict_proba(grid)[:, 1].reshape(xx.shape)

fig, ax = plt.subplots()
cs = ax.contourf(xx, yy, probs_mlp, levels=20, alpha=0.6)
scatter = ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, edgecolor="k", s=20)
ax.set_title("MLP decision boundary (1 hidden layer, ReLU)")
ax.set_xlabel("x1")
ax.set_ylabel("x2")
plt.tight_layout()
MLP (20 hidden units, ReLU) accuracy:
  train: 0.874
  test:  0.873

Moons: decision boundary from 1-hidden-layer MLP.

๐ŸŒ™ Moons: 2-hidden-layer MLP

Code
from sklearn.neural_network import MLPClassifier

mlp = MLPClassifier(
    hidden_layer_sizes=(20,20,),    # <- the only change is here! 
    activation="relu",
    solver="adam",
    max_iter=500,
    random_state=3151
)

mlp.fit(X_train, y_train)

y_train_pred_mlp = mlp.predict(X_train)
y_test_pred_mlp = mlp.predict(X_test)

print("MLP (20 + 20 hidden units, ReLU) accuracy:")
print(f"  train: {accuracy_score(y_train, y_train_pred_mlp):.3f}")
print(f"  test:  {accuracy_score(y_test, y_test_pred_mlp):.3f}")

# Decision boundary
probs_mlp = mlp.predict_proba(grid)[:, 1].reshape(xx.shape)

fig, ax = plt.subplots()
cs = ax.contourf(xx, yy, probs_mlp, levels=20, alpha=0.6)
scatter = ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, edgecolor="k", s=20)
ax.set_title("MLP decision boundary (2 hidden layers, ReLU)")
ax.set_xlabel("x1")
ax.set_ylabel("x2")
plt.tight_layout()
MLP (20 + 20 hidden units, ReLU) accuracy:
  train: 0.966
  test:  0.920

Moons: decision boundary from 2-hidden-layer MLP.

๐ŸŒ™ Moons: Interpreting the Result

โœ๏ธ Questions to ask:

  • Capacity:
    • When does the MLP boundary curve around the moons?
    • When are we using too few / enough / too many hidden units?
  • Generalization:
    • Is test accuracy similar to train accuracy?
    • If train โ‰ซ test, we may be overfitting.
  • Hyperparameters:
    • What happens if we:
      • Increase hidden units (width)?
      • Add another hidden layer (depth)?
      • Change activation (e.g., tanh)?

๐Ÿ’ฏ Example 2: Digits โ€” Logistic vs MLP

Now back to the slightly more realistic handwritten digits dataset.

  • Each example: \(8 \times 8 = 64\) pixel intensities
  • Task: classify digit (0โ€“9)
  • Weโ€™ve already seen digits in earlier modules (features, SVMs, PCA, t-SNE/UMAP).

Goal:

  • Compare logistic regression vs MLP on digits.
  • See how capacity affects performance.

๐Ÿ’ฏ Digits: Data & Baseline

Code
from sklearn.datasets import load_digits

digits = load_digits()
X = digits.data      # shape (n_samples, 64)
y = digits.target    # labels 0โ€“9

print("Digits shape:", X.shape)

# Quick visual of a few digits
fig, axes = plt.subplots(2, 5, figsize=(6, 3))
for i, ax in enumerate(axes.ravel()):
    ax.imshow(digits.images[i], cmap="gray")
    ax.set_title(int(y[i]))
    ax.axis("off")
plt.tight_layout()
Digits shape: (1797, 64)

๐Ÿ’ฏ Digits: Train/Validation/Test Split

Code
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

X_train, X_temp, y_train, y_temp = train_test_split(
    X, y, test_size=0.4, random_state=3151, stratify=y
)
X_val, X_test, y_val, y_test = train_test_split(
    X_temp, y_temp, test_size=0.5, random_state=3151, stratify=y_temp
)

print("Train size:", X_train.shape[0])
print("Val size:  ", X_val.shape[0])
print("Test size: ", X_test.shape[0])
Train size: 1078
Val size:   359
Test size:  360

๐Ÿ’ฏ Digits: Logistic Regression Pipeline

Code
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

logreg_pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("clf", LogisticRegression(
        max_iter=1000,
        multi_class="multinomial"
    ))
])

logreg_pipe.fit(X_train, y_train)

y_train_pred = logreg_pipe.predict(X_train)
y_val_pred = logreg_pipe.predict(X_val)
y_test_pred = logreg_pipe.predict(X_test)

logreg_results = {
    "model": "Logistic regression",
    "train_acc": accuracy_score(y_train, y_train_pred),
    "val_acc": accuracy_score(y_val, y_val_pred),
    "test_acc": accuracy_score(y_test, y_test_pred),
}

logreg_results
{'model': 'Logistic regression',
 'train_acc': 0.9981447124304267,
 'val_acc': 0.9721448467966574,
 'test_acc': 0.9666666666666667}

๐Ÿ’ฏ Digits: 1-hidden-layer MLP Pipeline

Code
mlp_pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("clf", MLPClassifier(
        hidden_layer_sizes=(64,),
        activation="relu",
        alpha=1e-4,        # L2 regularization
        max_iter=500,
        random_state=3151
    ))
])

mlp_pipe.fit(X_train, y_train)

y_train_pred_mlp = mlp_pipe.predict(X_train)
y_val_pred_mlp = mlp_pipe.predict(X_val)
y_test_pred_mlp = mlp_pipe.predict(X_test)

mlp_results = {
    "model": "MLP (1 hidden layer, 64 units)",
    "train_acc": accuracy_score(y_train, y_train_pred_mlp),
    "val_acc": accuracy_score(y_val, y_val_pred_mlp),
    "test_acc": accuracy_score(y_test, y_test_pred_mlp),
}

mlp_results
{'model': 'MLP (1 hidden layer, 64 units)',
 'train_acc': 1.0,
 'val_acc': 0.9665738161559888,
 'test_acc': 0.9666666666666667}

๐Ÿ’ฏ Digits: 2-hidden-layer MLP Pipeline

Code
mlp_pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("clf", MLPClassifier(
        hidden_layer_sizes=(64,64,),     # <- the only change
        activation="relu",
        alpha=1e-4,        # L2 regularization
        max_iter=500,
        random_state=3151
    ))
])

mlp_pipe.fit(X_train, y_train)

y_train_pred_mlp = mlp_pipe.predict(X_train)
y_val_pred_mlp = mlp_pipe.predict(X_val)
y_test_pred_mlp = mlp_pipe.predict(X_test)

mlp2_results = {
    "model": "MLP (2 hidden layers, 64+64 units)",
    "train_acc": accuracy_score(y_train, y_train_pred_mlp),
    "val_acc": accuracy_score(y_val, y_val_pred_mlp),
    "test_acc": accuracy_score(y_test, y_test_pred_mlp),
}

mlp2_results
{'model': 'MLP (2 hidden layers, 64+64 units)',
 'train_acc': 1.0,
 'val_acc': 0.9693593314763231,
 'test_acc': 0.975}

๐Ÿ’ฏ Digits: Side-by-side Performance Table

Code
import pandas as pd

results_df = pd.DataFrame([logreg_results, mlp_results,mlp2_results])
results_df
Digits: logistic regression vs 1-hidden-layer MLP.
model train_acc val_acc test_acc
0 Logistic regression 0.998145 0.972145 0.966667
1 MLP (1 hidden layer, 64 units) 1.000000 0.966574 0.966667
2 MLP (2 hidden layers, 64+64 units) 1.000000 0.969359 0.975000

Important

How many parameters did we just buy?

For digits: input dimension \(d=64\), classes \(K=10\).
For a fully-connected layer with \(n_{\ell-1}\to n_\ell\), params = \((n_{\ell-1}+1)n_\ell\) (weights + biases).

  • Logistic regression (softmax):
    \(W\in\mathbb{R}^{K\times d},\ b\in\mathbb{R}^K \Rightarrow (d+1)K = (64+1)\cdot 10 = 650\)

  • MLP, 1 hidden layer (64):
    \((64+1)\cdot 64 + (64+1)\cdot 10 = 4160 + 650 = 4810\)

  • MLP, 2 hidden layers (64+64):
    \((64+1)\cdot 64 + (64+1)\cdot 64 + (64+1)\cdot 10 = 4160 + 4160 + 650 = 8970\)

Question: Look at your validation/test accuracies.

  • โœ๏ธ Is the gain (if any) worth ~7ร— more parameters for 1-hidden-layer, or ~14ร— for 2-hidden-layerโ€”plus slower training and less interpretability?
  • โœ๏ธ When would you still choose the bigger MLP anyway?

Evaluation & Quality Checks

Evaluation

When using FFNNs, always keep the decision context in mind:

  • What is the task? e.g., classification vs regression
  • What metric matters? Overall accuracy? Class-specific performance? Calibration?

For our examples:

  • ๐ŸŒ™ Moons:
    • Metric: accuracy is fine; weโ€™re mostly visual.
    • Decision context: toy, but good for understanding geometry.
  • ๐Ÿ’ฏ Digits:
    • Metric: accuracy works, but confusion matrix may give more insight.
    • Decision context: toy; in real applications (e.g., postal sorting) might care about specific error types.

Confusion Matrix on Digits

Code
from sklearn.metrics import ConfusionMatrixDisplay

fig, ax = plt.subplots(figsize=(5, 5))
ConfusionMatrixDisplay.from_estimator(
    mlp_pipe, X_val, y_val, ax=ax, cmap="Blues", values_format="d"
)
ax.set_title("MLP confusion matrix (validation)")
plt.tight_layout()

Note

  • Look for:
    • Systematic confusions (e.g., 3 vs 5)
    • Classes that are much worse than others

MLP implementations + practical knobs

PyTorch (build-your-own MLP)

Losses

โ€œSolver choicesโ€ = optimizer choice

Regularization & stability knobs (common)

  • weight_decay (L2 / weight decay) in optimizers
  • Dropout(p=...)
  • Early stopping on a validation set (you implement it)
  • Learning rate + schedule, batch size

scikit-learn

Key hyperparameters (the ones you actually tune)

  • Architecture: hidden_layer_sizes=(...), activation={'relu','tanh','logistic','identity'}
  • Solver: solver={'adam','sgd','lbfgs'}
  • Regularization: alpha (L2)
  • Optimization: learning_rate_init, batch_size, max_iter, tol
  • Generalization: early_stopping=True, validation_fraction, n_iter_no_change

Practical examples

Rule-of-thumb starting point

  • Scale features, start with solver='adam', activation='relu', then tune alpha and learning_rate_init.

Risks & Ethics: Neural Nets in the Wild

Even simple FFNNs can be used in high-stakes settings (finance, health, hiring, etc.).

  • Overfitting & spurious patterns
    • High capacity networks can memorize quirks in small datasets.
  • Opacity / interpretability
    • Harder to explain than linear models or shallow trees.
    • Can make debugging and accountability tricky.
  • Bias & fairness
    • Networks happily amplify biases in training data.
  • Robustness
    • Small perturbations (even adversarial) can cause surprising errors.

Quick Check

Q1 (MCQ)

Which of the following is not a reason we need nonlinear activations in neural networks?

A. To allow the network to represent more complex functions
B. To ensure that stacking layers does more than a single linear model
C. To make sure the loss function is convex
D. To avoid the composition of linear maps collapsing into one linear map

Q2

Consider a feedforward network with input dimension 5, one hidden layer of size 10, and a single output.
How many parameters (weights + biases) does it have?

Assume fully-connected layers and no tricks.

Q3 (MCQ)

On the moons dataset, suppose your MLP achieves:

  • Train accuracy: 0.99
  • Test accuracy: 0.78

Which diagnosis is most plausible?

A. Underfitting
B. Overfitting
C. Data leakage
D. Perfect model; no issues

Q4

On the digits dataset, your MLP and logistic regression have similar test accuracy, but the MLP takes much longer to train.

Give one reasonable explanation for why you might still prefer logistic regression in this setting.

Summary

  • Feedforward neural networks are compositions of linear transforms and nonlinear activations.
  • Without nonlinearity, stacked layers collapse to a single linear model.
  • Capacity is controlled by:
    • Depth (layers) and width (units per layer)
    • Regularization and optimization choices
  • Small FFNNs can already:
    • Capture nonlinear patterns (moons)
    • Compete with linear models on real data (digits)
  • This module sets up:
    • M31: Backprop & autodiff
    • Later modules on deep architectures, stability, and representation learning.

References

Bengio, Yoshua. 2009. โ€œLearning Deep Architectures for AI.โ€ Found. Trends Mach. Learn. 2 (1): 1โ€“127. https://doi.org/10.1561/2200000006.
Cybenko, George. 1989. โ€œApproximation by Superpositions of a Sigmoidal Function.โ€ Math. Control. Signals Syst. 2 (4): 303โ€“14. https://doi.org/10.1007/BF02551274.
Eldan, Ronen, and Ohad Shamir. 2016. โ€œThe Power of Depth for Feedforward Neural Networks.โ€ In 29th Annual Conference on Learning Theory, edited by Vitaly Feldman, Alexander Rakhlin, and Ohad Shamir, 49:907โ€“40. Proceedings of Machine Learning Research. Columbia University, New York, New York, USA: PMLR. https://proceedings.mlr.press/v49/eldan16.html.
Glorot, Xavier, Antoine Bordes, and Yoshua Bengio. 2011. โ€œDeep Sparse Rectifier Neural Networks.โ€ In Proceedings of the Fourteenth International Conference on Artificial Intelligence and Statistics (AISTATS 2011), 15:315โ€“23. JMLR Workshop and Conference Proceedings.
Goodfellow, Ian J., Yoshua Bengio, and Aaron C. Courville. 2016. Deep Learning. Adaptive Computation and Machine Learning. MIT Press. http://www.deeplearningbook.org/.
Hebb, Donald O. 1949. The Organization of Behavior: A Neuropsychological Theory. New York: Wiley.
Hornik, Kurt. 1991. โ€œApproximation Capabilities of Multilayer Feedforward Networks.โ€ Neural Networks 4 (2): 251โ€“57. https://doi.org/10.1016/0893-6080(91)90009-T.
LeCun, Yann, Yoshua Bengio, and Geoffrey E. Hinton. 2015. โ€œDeep Learning.โ€ Nat. 521 (7553): 436โ€“44. https://doi.org/10.1038/NATURE14539.
Lu, Zhou, Hongming Pu, Feicheng Wang, Zhiqiang Hu, and Liwei Wang. 2017. โ€œThe Expressive Power of Neural Networks: A View from the Width.โ€ In Advances in Neural Information Processing Systems 30: Annual Conference on Neural Information Processing Systems 2017, December 4-9, 2017, Long Beach, CA, USA, edited by Isabelle Guyon, Ulrike von Luxburg, Samy Bengio, Hanna M. Wallach, Rob Fergus, S. V. N. Vishwanathan, and Roman Garnett, 6231โ€“39. https://proceedings.neurips.cc/paper/2017/hash/32cbf687880eb1674a07bf717761dd3a-Abstract.html.
McCulloch, Warren S., and Walter Pitts. 1943. โ€œA Logical Calculus of the Ideas Immanent in Nervous Activity.โ€ The Bulletin of Mathematical Biophysics 5 (4): 115โ€“33. https://doi.org/10.1007/BF02478259.
Minsky, Marvin, and Seymour Papert. 1969. Perceptrons: An Introduction to Computational Geometry. Cambridge, MA: MIT Press.
Rosenblatt, Frank. 1958. โ€œThe Perceptron: A Probabilistic Model for Information Storage and Organization in the Brain.โ€ Psychological Review 65 (6): 386โ€“408. https://doi.org/10.1037/h0042519.
Telgarsky, Matus. 2016. โ€œBenefits of Depth in Neural Networks.โ€ In 29th Annual Conference on Learning Theory, edited by Vitaly Feldman, Alexander Rakhlin, and Ohad Shamir, 49:1517โ€“39. Proceedings of Machine Learning Research. Columbia University, New York, New York, USA: PMLR. https://proceedings.mlr.press/v49/telgarsky16.html.