Neural Networks: Foundations & Architectures
Master the computational structure of neural networks. Move from a single artificial neuron and affine linear transformations (y = xAᵀ + b) to stacked dense layers, multi-dimensional tensor shape propagation, parameter counting formulas, and building scalable PyTorch models with nn.Module and nn.Sequential.
What is a Neural Network?
A parameterized mathematical function mapping input features to continuous or categorical representations.
At its core, a neural network is not a mystical thinking machine; it is a parameterized computation graph composed of stacked affine transformations and non-linear mappings that converts an input vector x into a prediction or latent embedding y.
W · x + b). They learn numerical parameters via gradient updates rather than using manually written conditional rules.Consider a tabular customer churn predictor:
[age, monthly_spend, support_tickets][h₁, h₂, h₃, h₄] (Intermediate representation space)[churn_score] (Probability or raw logit score)Hidden Layer (Learned Representation)
Performs learned affine transformations on previous representations. The word "hidden" denotes that these activations are internal to the model and not directly dictated by the outside dataset. Contains learnable weights W and biases b.
From a Single Neuron to a Network
The fundamental unit of computation: a weighted sum shifted by an additive bias.
Before assembling large networks, understand the atomic building block: the artificial neuron (often historically termed a perceptron). An artificial neuron takes multiple numerical inputs x₁, x₂, ..., xₙ, multiplies each by a corresponding learned weight w₁, w₂, ..., wₙ, sums the results, and adds a scalar bias b:
z = (w₁ · x₁) + (w₂ · x₂) + (w₃ · x₃) + ... + (wₙ · xₙ) + b = ∑ (wᵢ · xᵢ) + b = wᵀx + b
Why weights matter:
- Large positive weight: Strong positive correlation; when this feature increases, the neuron fires higher.
- Negative weight: Inhibitory effect; when this feature increases, the neuron activation decreases.
- Near-zero weight: Feature is largely ignored by this neuron during the forward pass.
Weights & Biases
Distinguishing learnable parameters from architectural hyperparameters.
Every neural network operates with two distinct classes of numbers:
| Category | Who Sets It? | When is it Defined? | Examples |
|---|---|---|---|
| Parameters | Learned automatically via optimization | Updated continuously during training loop | Weights (W), Biases (b) |
| Hyperparameters | Engineer / System Architect | Configured before training begins | Layer count, hidden dimensions, learning rate, batch size |
z = w₁x₁ + w₂x₂), then whenever the input vector is all zeros (x₁ = 0, x₂ = 0), the output z would be forced to zero. The bias term b shifts the activation function along the axis, allowing the model to fit patterns that do not pass through the origin.Layers: Stacking Transformations
Organizing neurons into sequential ranks to compute hierarchical representations.
Individual neurons are limited: a single linear neuron can only compute a flat hyperplane. By grouping neurons intolayers, an architecture computes multiple simultaneous projections:
- Input Layer: Holds raw features. Size is governed strictly by the dataset (e.g. 5 features).
- Hidden Layers: Perform learned affine transformations. You configure their count (depth) and width (neuron count).
- Output Layer: Produces the task prediction. Size is governed strictly by target format (1 for regression, K for multi-class).
Fully Connected / Linear Layers (nn.Linear)
The fundamental affine building block of PyTorch models.
A layer is described as fully connected (or dense) because every input feature connects to every neuron in the layer. In PyTorch, this is instantiated as torch.nn.Linear(in_features, out_features, bias=True).
# Official PyTorch affine transformation formula: y = x · Aᵀ + b # Tensor Dimensions: # x: Shape (*, in_features) # A (weight): Shape (out_features, in_features) <-- Transposed during multiplication! # b (bias): Shape (out_features) # y (output): Shape (*, out_features)
layer = nn.Linear(in_features=4, out_features=3, bias=True) layer.weight.shape -> torch.Size([3, 4]) # (3 rows, 4 cols) layer.bias.shape -> torch.Size([3]) Total parameters -> 15 (12 weights + 3 biases)
Tensor Shapes Through a Network
Tracing multi-dimensional shape transformations and diagnosing dimension mismatches.
In production deep learning, 90% of architectural bugs are tensor shape mismatches. PyTorch models process mini-batches of inputs. The tensor flow through stacked linear layers must strictly adhere to the inner-dimension matching rule of matrix multiplication:
(B, D_in)Linear(D_in, D_h1) → Output shape: (B, D_h1)Linear(D_h1, D_out) → Output shape: (B, D_out)(32, 5) → Layer 1: (32, 10) → Layer 2: (32, 4) → Output: (32, 2)The Forward Pass (Forward Propagation)
Tracing data flow from raw inputs to final output logits without backpropagation.
In deep learning, forward propagation (or the forward pass) is the calculation that moves data forward through each layer of the network to produce a model prediction:
x (input tensor) ↓ Layer 1: z₁ = x · W₁ᵀ + b₁ ↓ [Activation Placeholder: a₁ = f(z₁)] <-- Covered in next topic! ↓ Layer 2: z₂ = a₁ · W₂ᵀ + b₂ ↓ Output: ŷ (logits / prediction tensor)
[0.50, -1.20] (Shape: [1, 2])h₁ = 0.50×0.4 + -1.20×(-0.6) + 0.10 = 1.020
h₂ = 0.50×0.8 + -1.20×(0.2) - 0.20 = -0.040
h₃ = 0.50×(-0.5) + -1.20×(0.9) + 0.05 = -1.280
Shape:
torch.Size([1, 3])y = 1.02×1.1 + -0.04×(-0.7) + -1.28×0.4 + 0.30 = 0.938
Shape:
torch.Size([1, 1])Network Architecture & Parameter Count
Mathematical formula for calculating the memory and weight footprint of feed-forward architectures.
Every linear layer connecting n_in inputs to n_out neurons has:
Weights = n_in × n_out Biases = n_out Total Parameters = (n_in + 1) × n_out
PyTorch Implementation: torch.nn.Module
The foundational OOP base class for all neural networks in PyTorch.
In PyTorch, custom neural networks subclass torch.nn.Module. Building a model requires two essential steps:
__init__(self): Define your layers (e.g.self.fc1 = nn.Linear(...)). Always callsuper().__init__()first!forward(self, x): Define how data moves through the layers. Never callmodel.forward(x)directly in your code; always invoke the instance asmodel(x), which triggers PyTorch hooks and autograd mechanisms.
import torch
import torch.nn as nn
class CustomClassifier(nn.Module):
def __init__(self, in_features=4, hidden_dim=8, num_classes=2):
super().__init__()
# Define layer transformations
self.fc1 = nn.Linear(in_features, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, num_classes)
def forward(self, x: torch.Tensor) -> torch.Tensor:
# Input tensor shape: (batch_size, 4)
x = self.fc1(x) # -> (batch_size, 8)
# Activation placeholder: e.g., torch.relu(x)
x = self.fc2(x) # -> (batch_size, 2)
return x
# Instantiate model
model = CustomClassifier()
print(model)
# Test forward pass with synthetic batch
sample_batch = torch.randn(16, 4)
logits = model(sample_batch)
print("Output shape:", logits.shape) # torch.Size([16, 2])PyTorch nn.Sequential Container
Streamlining straightforward linear cascades without boilerplate forward methods.
When your neural network is a straight, unbranching line of transformations where the output of Layer N flows directly into Layer N+1, torch.nn.Sequential eliminates boilerplate code:
| Feature | torch.nn.Module (Custom Class) | torch.nn.Sequential |
|---|---|---|
| Best For | Complex architectures, branching, skip connections, multi-input models | Simple feed-forward pipelines, MLPs, stacked linear blocks |
| Forward Method | Manually written in forward(self, x) | Handled automatically under the hood |
| Boilerplate | Requires class declaration, super().__init__() | Minimal: single-line initialization |
import torch.nn as nn
# Linear sequential feed-forward container
model = nn.Sequential(
nn.Linear(4, 8),
nn.ReLU(),
nn.Linear(8, 2),
)
print(model)Model Parameters & Inspection
Auditing weights and biases using model.parameters() and model.named_parameters().
PyTorch automatically registers learnable parameters when layers are assigned to self inside __init__(). You can inspect them via two primary methods:
| Method | Returns | Primary Use Case |
|---|---|---|
model.parameters() | Iterator over raw nn.Parameter tensors | Passing directly to optimizers: optim.SGD(model.parameters(), lr=0.01) |
model.named_parameters() | Iterator over (name: str, param: Tensor) tuples | Debugging layer dimensions, gradient auditing, layer freezing, parameter logging |
| Parameter Name | Tensor Shape | Count |
|---|---|---|
layer1.weight | torch.Size([8, 4]) | 32 |
layer1.bias | torch.Size([8]) | 8 |
layer2.weight | torch.Size([2, 8]) | 16 |
layer2.bias | torch.Size([2]) | 2 |
Complete Forward-Pass Laboratory
Configure custom architectures and run live forward passes with real matrix multiplications.
Use this interactive sandbox to configure network depth, width, and input features. When you click Run Forward Pass, the workbench calculates the exact affine transformations across every layer using genuine vector-matrix operations:
Neural Network Architecture Debugging Lab
Diagnose and remediate 8 real-world runtime exceptions encountered in PyTorch development.
Select a broken deployment or model implementation below, inspect the traceback, diagnose the root architectural bug, and apply the correct PyTorch fix:
The model expects an incoming feature tensor of shape (batch, 10), but the dataset loader delivers features with dimension 8.
model = nn.Linear(in_features=10, out_features=4) x = torch.randn(32, 8) y = model(x) # Throws RuntimeError!
RuntimeError: mat1 and mat2 shapes cannot be multiplied (32x8 and 10x4)
How should this architecture or input tensor be corrected?
Mini Project: Customer Churn Tabular Network
Build an end-to-end feed-forward neural architecture for a production customer churn system.
In this practical project, you configure an enterprise tabular neural network that evaluates customer retention. The input comprises 5 normalized numerical features:[Age, Monthly Spend, Support Tickets, Months Active, Usage Frequency]. The output computes two classification logits: [score_retained, score_churn].
import torch.nn as nn
# Churn prediction architecture
churn_model = nn.Sequential(
nn.Linear(in_features=5, out_features=8),
nn.Linear(in_features=8, out_features=2) # [stay_logit, churn_logit]
)Architecture Capacity Comparison
Comparing depth vs width: analyzing compute, memory, and representational capacity.
No single neural architecture is universally "best." Architecture selection involves balancing representational capacity against latency budgets and memory constraints:
| Metric | Network A (Compact) | Network B (Standard) | Network C (Deep) |
|---|---|---|---|
| Architecture | 4 → 4 → 1 | 4 → 8 → 1 | 4 → 8 → 8 → 1 |
| Hidden Layers | 1 Layer | 1 Layer (Wider) | 2 Layers (Deeper) |
| Total Parameters | (4×4+4) + (4×1+1) = 25 | (4×8+8) + (8×1+1) = 49 | (4×8+8) + (8×8+8) + (8×1+1) = 121 |
| Inference Latency | Minimal (< 0.1 ms) | Very Low (~0.15 ms) | Moderate (~0.35 ms) |
| Risk of Overfitting | Low (High inductive bias) | Balanced | Higher on small datasets |
Common Mistakes & Anti-Patterns
Critical architectural pitfalls and their corresponding correct mental models.
| Mistake / Anti-Pattern | Why It Causes Failure | Correct Engineering Practice |
|---|---|---|
| Confusing Parameters with Hyperparameters | Attempting to manually code weight matrices instead of letting the optimizer learn them. | Define layer architectures (hyperparameters); let PyTorch update parameters via autograd. |
| Omitting the Leading Batch Dimension | Passing 1D tensors (features,) into nn.Linear causes batch-processing errors in DataLoader pipelines. | Ensure inputs have shape (batch_size, in_features); use tensor.unsqueeze(0) for single samples. |
Instantiating Layers in forward() | Creates brand new random weights on every single forward pass; parameters are never preserved or optimized. | Instantiate all layers in __init__(); invoke them inside forward(). |
Calling model.forward(x) Directly | Bypasses PyTorch registered forward hooks and specialized profiling hooks. | Always invoke the model as a callable: output = model(x). |
| Assuming More Layers Always Means Better Results | Excessive depth without adequate training data leads to vanishing gradients and severe overfitting. | Start with a compact baseline model; scale depth only when validation error demonstrates capacity underfitting. |
The AI Engineering Connection
How neural network foundations power modern computer vision, NLP, and LLM systems.
Every modern AI architecture—from Vision Transformers (ViT) and CNNs to Large Language Models (GPT-4, Llama 3) and diffusion image generators—is built upon the exact same principles you learned in this module:
Learning Notes & Key Mental Models
Concise glossary of essential neural architecture terminology.
z = ∑ wᵢxᵢ + b.y = xAᵀ + b.(in_features + 1) × out_features.What You Should Know Now
Confirm your core competencies before progressing to Activation & Loss Functions.
Knowledge Assessment Quiz
Test your architectural understanding across 8 scenario-based questions.