一.模型构建
Pytorch中模型均问类Module的子类的实例,一个特定的网络层是一个Module子类的实例,而整个网络也是一个Module子类的实例,例子如下:
构建一个LeNet网络
net = LeNet(classes=2)
实际上这一步是使用Module类的子类LeNet构建了一个LeNet类net,LeNet类则由我们自己定义(这里Pytorch库已经帮我们定义好了,但我们自己搭建网络的时候需要自己定义,这个定义过程就是构建网络的过程)
LeNet类的定义
class LeNet(nn.Module):
def __init__(self, classes):
#使用super调用父类中的init函数对LeNet进行初始化,初始化后LeNet类的8个nn.Module属性都是空的
super(LeNet, self).__init__()
#构建网络子模块,存储到LeNet的module属性中
#这种conv1的卷积层和fc1的全连接层也是一个Module的子类,其中已经预定好了相关的Module属性
#这种子模块的module属性一般为空,即其中不再包含更小的子模块,而parameters中则存储了相关的权值等属性
#nn.Conv2d(3,6,5)只是实现了一个卷积层的实例化,而赋值给self.conv1才实现了该卷积层到LeNet的module属性中的添加
#这里进行赋值时其实会被setattr函数拦截,对赋值的数据类型进行判断,如果是module类或者其子类,则赋值给LeNet的module中
#module这个属性其实是一个字典,赋值操作就是想该字典中添加键值对,键就是conv1,值就是nn.Conv2d(3,6,5)这个卷积层实例
#赋值时候如果判断数据类型为Parameter类,则会存储到LeNet中的Parameter字典(即Parameter属性)中
self.conv1 = nn.Conv2d(3, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16*5*5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, classes)
#按照每层网络结构写处forward函数,即如何进行前向传播
def forward(self, x):
out = F.relu(self.conv1(x))
out = F.max_pool2d(out, 2)
out = F.relu(self.conv2(out))
out = F.max_pool2d(out, 2)
out = out.view(out.size(0), -1)
out = F.relu(self.fc1(out))
out = F.relu(self.fc2(out))
out = self.fc3(out)
return out
#进行权值初始化
def initialize_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.xavier_normal_(m.weight.data)
if m.bias is not None:
m.bias.data.zero_()
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
m.bias.data.zero_()
elif isinstance(m, nn.Linear):
nn.init.normal_(m.weight.data, 0, 0.1)
m.bias.data.zero_()
使用父类Module中的init函数进行LeNet的初始化,super(LeNet, self).init(),初始化函数如下:
class Module:
...
def __init__(self):
"""
Initializes internal Module state, shared by both nn.Module and ScriptModule.
"""
torch._C._log_api_usage_once("python.nn_module")
#对Module类对象的八个属性进行初始化
self.training = True
self._parameters = OrderedDict()
self._buffers = OrderedDict()
self._non_persistent_buffers_set = set()
self._backward_hooks = OrderedDict()
self._forward_hooks = OrderedDict()
self._forward_pre_hooks = OrderedDict()
self._state_dict_hooks = OrderedDict()
self._load_state_dict_pre_hooks = OrderedDict()
self._modules = OrderedDict()
其中,对于self.conv1 = nn.Conv2d(3, 6, 5)这一句,其实也是在使用Module子类Conv2d实例化了一个2维卷积层conv1,Module类与Conv2d类的关系如下:
Module类->_ConvNd类->Conv2d类
二.模型容器
1.Sequential
# ============================ Sequential
#===============================网络层不带名称的Sequential容器===============
class LeNetSequential(nn.Module):
def __init__(self, classes):
super(LeNetSequential, self).__init__()
#将卷积池化包装为特征提取器
#此时多个网络层组成一个列表输入到Sequential函数中,会默认以序号0,1,2..索引
self.features = nn.Sequential(
nn.Conv2d(3, 6, 5),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, 5),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),)
#讲全连接包装成分类器
self.classifier = nn.Sequential(
nn.Linear(16*5*5, 120),
nn.ReLU(),
nn.Linear(120, 84),
nn.ReLU(),
nn.Linear(84, classes),)
#包装后,forward就非常简单,只要在包装后的模块间传递就可以,不用每层的传递都要写
def forward(self, x):
x = self.features(x)
x = x.view(x.size()[0], -1)
x = self.classifier(x)
return x
#===============================网络层带名称的Sequential容器===============
class LeNetSequentialOrderDict(nn.Module):
def __init__(self, classes):
super(LeNetSequentialOrderDict, self).__init__()
#在大型网络中,使用序号来对网络进行索引是很麻烦的
#我们可以讲多个网络组成一个"有序的字典"进行输入,可以在实现打包的基础上,使用名称来对某一个网络层进行索引
self.features = nn.Sequential(OrderedDict({
'conv1': nn.Conv2d(3, 6, 5),
'relu1': nn.ReLU(inplace=True),
'pool1': nn.MaxPool2d(kernel_size=2, stride=2),
'conv2': nn.Conv2d(6, 16, 5),
'relu2': nn.ReLU(inplace=True),
'pool2': nn.MaxPool2d(kernel_size=2, stride=2),
}))
self.classifier = nn.Sequential(OrderedDict({
'fc1': nn.Linear(16*5*5, 120),
'relu3': nn.ReLU(),
'fc2': nn.Linear(120, 84),
'relu4': nn.ReLU(inplace=True),
'fc3': nn.Linear(84, classes),
}))
def forward(self, x):
x = self.features(x)
x = x.view(x.size()[0], -1)
x = self.classifier(x)
return x
2.ModuleList
# ============================ ModuleList
#采用ModuleList构建一个20层的全连接层神经网路
class ModuleList(nn.Module):
def __init__(self):
super(ModuleList, self).__init__()
#通过迭代构建模型,这里构建了一个20层的全连接层
self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(20)])
def forward(self, x):
#对于ModuleList,可以通过迭代进行前向传播
for i, linear in enumerate(self.linears):
x = linear(x)
return x
3.ModuleDict
# ============================ ModuleDict
#通过ModuleDict构建可以选择的网络层
class ModuleDict(nn.Module):
def __init__(self):
super(ModuleDict, self).__init__()
#构建一个choices的网络层字典,具备conv和pool两个可选择的网络层
self.choices = nn.ModuleDict({
'conv': nn.Conv2d(10, 10, 3),
'pool': nn.MaxPool2d(3)
})
#构建一个activations的网络层字典,具备ReLU()和PReLU两个可选择的激活函数
self.activations = nn.ModuleDict({
'relu': nn.ReLU(),
'prelu': nn.PReLU()
})
def forward(self, x, choice, act):
#激活函数需要额外的参数以指定使用哪个网络
x = self.choices[choice](x)
x = self.activations[act](x)
return x
net = ModuleDict()
fake_img = torch.randn((4, 10, 32, 32))
#在对ModuleDIct网络进行前向传播时,需要传入对应字典键名来选择对相的网络
output = net(fake_img, 'conv', 'relu')
print(output)