Introduction
nn.Module is the base class that every neural network model and layer in PyTorch is built from — providing the standard structure for organizing learnable parameters, defining a forward pass, and integrating seamlessly with Autograd, optimizers, and the rest of the PyTorch ecosystem. Whether you're using a single built-in layer like nn.Linear or building an entire custom architecture, everything ultimately inherits from and follows the same nn.Module pattern.
This is the direct PyTorch counterpart to the Keras layer/model system covered in the TensorFlow section — while the specific syntax differs, nn.Module serves fundamentally the same purpose: providing a consistent, reusable structure for defining neural network components.
Why Does nn.Module Matter?
nn.Module helps to:
- Provide a standard, consistent structure for defining any neural network component
- Automatically track all learnable parameters (weights and biases) within a model
- Integrate seamlessly with Autograd for gradient computation during training
- Support nesting — modules can contain other modules, building complex architectures from simple building blocks
- Enable easy saving, loading, and moving of models between CPU and GPU
- Serve as the foundation beneath every built-in PyTorch layer and every custom model
The Structure of an nn.Module
Every custom model in PyTorch follows the same two-part pattern:
__init__: create and store the layers the model will use
(this is also where you MUST call super().__init__())
forward: define the actual computation — how input data flows
through the layers defined in __init__ to produce output
This mirrors the same __init__/call structure covered in the
Model Subclassing topic from the TensorFlow section, just with
PyTorch's own naming convention (forward instead of call).A Basic Custom Model
Notice that layers (nn.Linear, nn.ReLU, nn.Sigmoid) are created once in __init__, while forward() defines the actual sequence of operations connecting them — you never call forward() directly; instead, calling model(input) automatically invokes it behind the scenes.
Common Built-In Layers
| Layer | Purpose |
|---|---|
| nn.Linear | A fully connected (dense) layer — the same core operation covered in the Custom Layers topic |
| nn.Conv2d | A 2D convolutional layer, used in CNNs (as covered in the CNN, RNN, LSTM topic) |
| nn.LSTM | A Long Short-Term Memory recurrent layer |
| nn.ReLU / nn.Sigmoid / nn.Softmax | Activation function layers (as covered in the Activation Functions topic) |
| nn.Dropout | A regularization layer that randomly zeroes some values during training |
| nn.BatchNorm1d / nn.LayerNorm | Normalization layers (related to the Layer Normalization topic) |
Automatic Parameter Tracking
Because every layer assigned as an attribute in __init__ is
itself an nn.Module, PyTorch automatically discovers and tracks
ALL learnable parameters across the entire model — this is
what allows a single call like optimizer = Adam(model.parameters())
to correctly gather every trainable weight and bias in the
model, regardless of how deeply nested the architecture is.nn.Sequential: A Simpler Alternative for Simple Stacks
For simple, linear stacks of layers, nn.Sequential provides a more concise alternative to writing a full custom class — directly analogous to Keras's Sequential API covered in the TensorFlow section, offering the same convenience for straightforward architectures.
Nesting Modules Within Modules
This demonstrates a key strength of nn.Module: since a module can contain other modules as attributes, complex architectures can be built by composing smaller, reusable custom blocks — parameter tracking, device movement, and saving/loading all work correctly automatically, no matter how deeply nested the structure becomes.
Moving a Model Between CPU and GPU
Calling .to(device) on an nn.Module moves every parameter within it (including all nested sub-modules) to the specified device — the exact same .to() pattern covered in the earlier Tensors topic, applied here at the whole-model level.
Training Mode vs Evaluation Mode
nn.Module vs TensorFlow's Keras Layer/Model System
| Aspect | PyTorch (nn.Module) | TensorFlow (Keras) |
|---|---|---|
| Forward Pass Method | forward() | call() |
| Base Class | nn.Module (for both layers and models) | keras.layers.Layer and keras.Model (distinct base classes) |
| Parameter Access | model.parameters() | model.trainable_variables |
| Simple Stack Shortcut | nn.Sequential | keras.Sequential |
| Mode Switching | Explicit .train() / .eval() calls | Handled via a training argument passed through call() |
nn.Module Class vs nn.Sequential
| Aspect | Custom nn.Module Class | nn.Sequential |
|---|---|---|
| Flexibility | Full — supports any custom forward logic, branching, loops | Limited to a simple, linear stack of layers |
| Verbosity | More code required | Minimal, concise |
| Best For | Complex or custom architectures | Simple, straightforward layer stacks |
Key Properties of nn.Module
- Every PyTorch layer and model inherits from
nn.Module, providing a consistent structure and behavior. - Layers are defined in
__init__, and the forward pass logic is defined inforward(). nn.Moduleautomatically discovers and tracks all learnable parameters, including those in nested sub-modules.nn.Sequentialoffers a concise shortcut for simple, linear layer stacks, similar to Keras's Sequential API..train()and.eval()explicitly control behavior for layers like Dropout and BatchNorm that differ between training and inference.
Where Is nn.Module Used?
| Field | Application |
|---|---|
| Any PyTorch Model Definition | The universal base for every layer and model, from simple to complex |
| Computer Vision | Defining CNN architectures using nn.Conv2d and related layers |
| Natural Language Processing | Defining RNN, LSTM, or Transformer-based models |
| Research and Custom Architectures | Building novel, deeply nested, or non-standard model structures |
| Production Model Development | The standard way models are defined before training and deployment |
Advantages
- Provides a single, consistent structure for both simple layers and entire complex models
- Automatic parameter tracking eliminates manual bookkeeping of weights across a model
- Supports arbitrary nesting, enabling clean, modular, reusable architecture design
- Seamlessly integrates with Autograd, optimizers, and device placement
nn.Sequentialoffers convenience for simple cases without sacrificing full flexibility when needed
Limitations
- Requires understanding the
__init__/forward()pattern and remembering to callsuper().__init__() - Explicit
.train()/.eval()mode switching is easy to forget, leading to subtle bugs - More verbose than
nn.Sequentialfor genuinely simple architectures - Debugging deeply nested custom modules can require careful tracing through multiple
forward()calls - No built-in automatic shape inference the way some other frameworks provide (input sizes are typically specified explicitly)
Real-World Examples
| Application | nn.Module Use |
|---|---|
| Image Classifiers | Custom nn.Module classes combining nn.Conv2d, pooling, and nn.Linear layers |
| Language Models | Custom or built-in Transformer-based nn.Module architectures |
| Research Paper Implementations | Reproducing novel architectures as custom nn.Module subclasses |
| Reusable Model Components | Custom nn.Module "blocks" reused across multiple larger architectures |
| Production ML Systems | Standard model definitions used throughout training and deployment pipelines |
Best Practices
- Always call
super().__init__()as the first line inside a customnn.Module's__init__method. - Use
nn.Sequentialfor simple, linear architectures, and custom classes when more flexibility is needed. - Remember to call
model.train()before training andmodel.eval()before evaluation or inference. - Build complex architectures by composing smaller, reusable custom
nn.Moduleblocks. - Use
model.parameters()when setting up an optimizer to ensure all trainable weights are correctly included.
Interview Tip
A common interview question is:
"What is nn.Module, and why is calling super().__init__() necessary when defining a custom model?"
A strong answer is:
nn.Moduleis the base class that every PyTorch layer and model inherits from, providing automatic tracking of learnable parameters, integration with Autograd, and support for moving models between devices, saving/loading, and nesting sub-modules. Callingsuper().__init__()is necessary because it runsnn.Module's own internal setup code, which initializes the internal data structures — like the registry that tracks parameters and sub-modules — that PyTorch relies on to make features likemodel.parameters()and.to(device)work correctly; skipping this call would break that essential internal bookkeeping.
Explaining specifically what super().__init__() sets up makes your answer stronger.
Conclusion
nn.Module provides the foundational structure underlying every PyTorch model, standardizing how layers are defined, how parameters are tracked, and how the forward pass is computed, while integrating seamlessly with Autograd and the rest of the training pipeline. With nn.Module now covered, the next topic explores Optimizers — the algorithms responsible for actually using the gradients Autograd computes to update a model's parameters during training.