本系列文章配套代码获取有以下两种途径:
-
通过百度网盘获取:
链接:https://pan.baidu.com/s/1XuxKa9_G00NznvSK0cr5qw?pwd=mnsj
提取码:mnsj
-
前往GitHub获取:
https://github.com/returu/PyTorch
nn.Module是一个Python类,在PyTorch的torch.nn包中定义。它是所有神经网络模块的基类,包括预定义的层(如nn.Linear、nn.Conv2d等)以及用户自定义的模型。
定义自定义层:
以下是一个简单的示例,展示了如何创建一个自定义的全连接层(y=wx+b,其中w和b是可学习参数):
-
自定义层必须继承nn.Module,并在构造函数中调用nn.Module的构造函数(super().__init__()); -
在构造函数__init__()中必须自行定义可学习的参数,并封装成nn.Parameter()。在Pytorch中Parameter是一种特殊的Tensor,默认需要求导(即requires_grad=True); -
前向传播函数forward()的输入可以是一个或多个Tensor; -
nn.Module会利用autograd自动实现反向传播,无须手动编写。
class CustomLinearLayer(nn.Module):
# 继承nn.Module,需要重写构造函数__init__()以及前向传播函数forward()
def __init__(self, input_features, output_features):
super().__init__()
# nn.Parameter()内的参数是网络中可学习的参数
self.weight = nn.Parameter(torch.Tensor(output_features, input_features))
self.bias = nn.Parameter(torch.Tensor(output_features))
def forward(self, x):
# 执行线性变换并添加偏置:y=wx+b
out = torch.mm(x, self.weight.t()) + self.bias
return out
# 实例化自定义的全连接层
custom_linear = CustomLinearLayer(input_features=10, output_features=5)
# 假设输入是一个3x10的张量,即3个样本,每个样本10个特征
input_tensor = torch.randn(3, 10)
output_tensor = custom_linear(input_tensor)
print(output_tensor) # 输出应该是一个3x5的张量
for name,parameter in custom_linear.named_parameters():
print(name,parameter)
# 输出结果:
# weight Parameter containing:
# tensor([[7.1118e-04, 7.3017e+31, 2.8026e-45, 1.8672e+25, 0.0000e+00, 0.0000e+00,
# 5.3992e-14, 1.3556e-19, 2.8026e-45, 2.6354e+23],
# [0.0000e+00, 0.0000e+00, 7.5338e+28, 7.1758e+22, 2.8026e-45, 6.8801e+16,
# 0.0000e+00, 0.0000e+00, 1.2415e+28, 2.7376e+20],
# [2.8026e-45, 1.8524e+28, 0.0000e+00, 0.0000e+00, 1.8390e+25, 5.0714e-14,
# 2.8026e-45, 1.8888e+31, 0.0000e+00, 0.0000e+00],
# [1.3419e+19, 1.7611e+19, 2.8026e-45, 1.3570e-14, 0.0000e+00, 0.0000e+00,
# 3.2745e-12, 7.5555e+31, 2.8026e-45, 1.4605e-19],
# [0.0000e+00, 0.0000e+00, 3.0300e+35, 7.2251e+28, 2.8026e-45, 2.0027e-19,
# 0.0000e+00, 0.0000e+00, 2.4176e-12, 1.7743e+28]], requires_grad=True)
# bias Parameter containing:
# tensor([9.6429e-39, 8.4490e-39, 9.6429e-39, 9.2755e-39, 1.0286e-38],
# requires_grad=True)
-
nn.ParameterList:
class CustomLinearLayer_2(nn.Module):
# 继承nn.Module,需要重写构造函数__init__()以及前向传播函数forward()
def __init__(self, input_features, output_features):
super().__init__()
# 创建一个nn.ParameterList,用于存储网络中的各个参数
self.params = nn.ParameterList([
nn.Parameter(torch.Tensor(output_features, input_features)) , # weight
nn.Parameter(torch.Tensor(output_features)) # bias
])
def forward(self, x):
# 执行线性变换并添加偏置:y=wx+b
out = torch.mm(x, self.params[0].t()) + self.params[1]
return out
构建复杂模型:
-
使用自定义层:
例如,使用上面创建的自定义全连接层创建一个多层感知机模型:
-
在构造函数__init__()中将自定义的全连接层(module)作为当前module对象的一个子module; -
在前向传播函数forward()中加上各层之间的处理函数,例如激活函数。
class Perceptron(nn.Module):
def __init__(self,in_features,hidden_features,out_features):
super().__init__()
# 使用前面自定义的全连接层CustomLinearLayer
self.layer1 = CustomLinearLayer(in_features,hidden_features)
self.layer2 = CustomLinearLayer(hidden_features,out_features)
def forward(self,x):
x = self.layer1(x)
x = torch.sigmoid(x)
x = self.layer2(x)
return x
# 实例化自定义的多层感知机
Perceptron = Perceptron(in_features=10,hidden_features=50,out_features=5)
# 假设输入是一个3x10的张量,即3个样本,每个样本10个特征
input_tensor = torch.randint(low=0, high=100, size=(3, 10),dtype=torch.float32)
output_tensor = Perceptron(input_tensor) # 输出是一个3x5的张量
# 主module能够递归查找子module中的Parameter
for name,parameter in Perceptron.named_parameters():
print(name,parameter.shape)
# 输出结果:
# layer1.weight torch.Size([50, 10])
# layer1.bias torch.Size([50])
# layer2.weight torch.Size([5, 50])
# layer2.bias torch.Size([5])
-
预定义的层和自定义层组合使用:
使用预定义的层和自定义层的组合来实现特定的模型架构。
class CompleteConvNet(nn.Module):
def __init__(self,num_classes=10):
super().__init__()
# 第一个卷积层
self.conv1 = nn.Conv2d(3, 32, kernel_size=3)
self.bn1 = nn.BatchNorm2d(32)
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
# 第二个卷积层
self.conv2 = nn.Conv2d(32, 64, kernel_size=3)
self.bn2 = nn.BatchNorm2d(64)
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
# 第三个卷积层
self.conv3 = nn.Conv2d(64, 128, kernel_size=3)
self.bn3 = nn.BatchNorm2d(128)
# 预定义的全连接层
# self.fc1 = nn.Linear(128 * 4 * 4, 512)
# self.fc2 = nn.Linear(512,num_classes)
# 自定义的全连接层
self.fc1 = CustomLinearLayer(128 * 4 * 4, 512)
self.fc2 = CustomLinearLayer(512,num_classes)
def forward(self, x):
# 第一个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
x = F.relu(self.bn1(self.conv1(x)))
x = self.pool1(x)
# 第二个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
x = F.relu(self.bn2(self.conv2(x)))
x = self.pool2(x)
# 第三个卷积层(卷积 -> 标准化 -> 激活)
x = F.relu(self.bn3(self.conv3(x)))
# 将特征图展平
x = x.view(x.size(0), -1)
# 全连接层
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
# 主module能够递归查找子module中的Parameter
for name,parameter in net.named_parameters():
print(name,parameter.shape)
print('-----------')
conv1.weight torch.Size([32, 3, 3, 3])
-----------
conv1.bias torch.Size([32])
-----------
bn1.weight torch.Size([32])
-----------
bn1.bias torch.Size([32])
-----------
conv2.weight torch.Size([64, 32, 3, 3])
-----------
conv2.bias torch.Size([64])
-----------
bn2.weight torch.Size([64])
-----------
bn2.bias torch.Size([64])
-----------
conv3.weight torch.Size([128, 64, 3, 3])
-----------
conv3.bias torch.Size([128])
-----------
bn3.weight torch.Size([128])
-----------
bn3.bias torch.Size([128])
-----------
fc1.weight torch.Size([512, 2048])
-----------
fc1.bias torch.Size([512])
-----------
fc2.weight torch.Size([10, 512])
-----------
fc2.bias torch.Size([10])
-----------
从输出结果可以看出,对于子module中的Parameter,输出时会在parameter名字前加上当前module的名字。
更多内容可以前往官网查看:
https://pytorch.org/
本篇文章来源于微信公众号: 码农设计师