nn ModuleList vs. Sequential
import torch
import torch.nn as nn
import torch.nn.functional as F
nn.Module
- Defines the base class for all neural network
- We MUST subclass it
Example
class Net(nn.Module):
def __init__(self, in_c, n_classes):
super().__init__()
self.conv1 = nn.Conv2d(in_c, 32, kernel_size=3, stride=1, padding=1)
self.bn1 = nn.BatchNorm2d(32)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
self.bn2 = nn.BatchNorm2d(32)
self.fc1 = nn.Linear(32 * 28 * 28, 1024)
self.fc2 = nn.Linear(1024, n_classes)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = F.relu(x)
x = self.conv2(x)
x = self.bn2(x)
x = F.relu(x)
x = x.view(-1, 32 * 28 * 28) # flat
x = self.fc1(x)
x = F.sigmoid(x)
x = self.fc2(x)
return x
model = Net(1, 10)
model
Net(
(conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(bn1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(bn2): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(fc1): Linear(in_features=25088, out_features=1024, bias=True)
(fc2): Linear(in_features=1024, out_features=10, bias=True)
)
nn.Sequential
Sequential is a container of Modules that can be stacked together and run at the same time.
- The
nn.Module
’s stored innn.Sequential
are connected in a cascaded way nn.Sequential
has aforward()
method- Have to make sure that the output size of a block matches the input size of the following block.
- Basically, it behaves just like a
nn.Module
Example
class NetSequential(nn.Module):
def __init__(self, in_c, n_classes):
super().__init__()
self.conv_block1 = nn.Sequential(
nn.Conv2d(in_c, 32, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(32),
nn.ReLU()
)
self.conv_block2 = nn.Sequential(
nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(64),
nn.ReLU()
)
self.decoder = nn.Sequential(
nn.Linear(32 * 28 * 28, 1024),
nn.Sigmoid(),
nn.Linear(1024, n_classes)
)
def forward(self, x):
x = self.conv_block1(x)
x = self.conv_block2(x)
x = x.view(-1, 32 * 28 * 28)
x = self.decode(x)
return x
model = NetSequential(1, 10)
model
NetSequential(
(conv_block1): Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(conv_block2): Sequential(
(0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(decoder): Sequential(
(0): Linear(in_features=25088, out_features=1024, bias=True)
(1): Sigmoid()
(2): Linear(in_features=1024, out_features=10, bias=True)
)
)
nn.ModuleList
Holds submodules in a list.
ModuleList
can be indexed like a regular Python list, but modules it contains are properly registered, and will be visible by allModule
methods.
- Does NOT have a
forward()
method, because it does not define any neural network, that is, there is no connection between each of thenn.Module
’s that it stores. - We may use it to store
nn.Module
’s, just like you use Python lists to store other types of objects (integers, strings, etc). And Pytorch is “aware” of the existence of thenn.Module
’s inside annn.ModuleList
- Execution order of
nn.Modules
stored innn.ModuleList
is defined inforward()
, which we have to implement explicitly by ourselves.
Example
class NetModuleList(nn.Module):
def __init__(self, in_c, n_classes):
super().__init__()
self.module_list = nn.ModuleList([
nn.Conv2d(in_c, 32, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.Flatten(),
nn.Linear(32 * 28 * 28, 1024),
nn.Sigmoid(),
nn.Linear(1024, n_classes)
])
def forward(self, x):
for module in self.module_list:
x = module(x)
return x
model = NetModuleList(1, 10)
model
NetModuleList(
(module_list): ModuleList(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
(3): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU()
(6): Flatten(start_dim=1, end_dim=-1)
(7): Linear(in_features=25088, out_features=1024, bias=True)
(8): Sigmoid()
(9): Linear(in_features=1024, out_features=10, bias=True)
)
)
nn.Sequential
vs. nn.ModuleList
nn.Sequential | nn.ModuleList | |
---|---|---|
Has forward() ? | ✅ | ❌ |
Connection between nn.Modules stored inside? | ✅ | ❌ |
Execution order = stored order? | ✅ | ❌ |
Advantages | succinct | flexible |
When to use which?
- Use
Module
when we have a big block compose of multiple smaller blocks - Use
Sequential
when we want to create a small block from layers - Use
ModuleList
when we need to iterate through some layers or building blocks and do something