model_py篇
python中采用驼峰书写法且首字母大写的变量符号一般表示类名。
学习网络步骤:看原论文+看别人对原论文的理解,学习网络结构,看损失函数计算,看数据集,看别人写的代码,复现代码。
经历以上步骤我们便可以选择合适的框架复现代码,这里使用PyTorch复现网络结构。我们用PyTorch搭建网络可以分为以下几个module,数据处理dataloader.py
,网络模型model.py
,训练模块train.py
,工具模块utils.py
,预测模块predict.py
。接下来我们将以LeNet为例进行讲解。
目录如下:
- model.py 模块综述
- def init(self):
torch.nn.Conv2d();torch.nn.MaxPool2d();torch.nn.Linear() - def forward(self, x):
self.conv1(x);torch.nn.functional.relu();torch.Tensor.view() - 测试
- 源码
参考文章如下:
- Python通过对象名调用方法(对象名后面括号和参数)
https://blog.csdn.net/MustangJy/article/details/93760708
- Python类__call__()方法
https://www.cnblogs.com/lyu454978790/p/8630215.html
- Python类__getitem__()方法
https://www.cnblogs.com/lyu454978790/p/8625888.html
model.py 模块综述
在这个模块中,我们根据网络模型结构搭建模型。在这一步骤中,我们往往需要torch.nn
这个目录和torch.nn.functional
这个.py文件,我们需要import这两个模块。
import torch.nn as nn
import torch.nn.functional as F
然后我们用class[模型名]
创建该模型的类,该模型类必须继承自torch.nn.Module
类并根据我们要搭建的网络结构重写类的__init__()
方法和forword()
方法。
需要注意的是,PyTorch接受的Tensor的通道排列顺序是:[batch, channel, height, width]
。
接下来我们对多种PyTorch内置的重要函数进行记录:
def init(self):
一定要重写父类的__init__方法:
super(LeNet, self).__init__()
torch.nn.Conv2d()
该函数即创建卷积层,他的函数声明如下:
torch.nn.Conv2d(in_channels, out_channels, kernel_size,
stride=1, padding=0, dilation=1, groups=1, bias=True,
padding_mode='zeros', device=None, dtype=None)
- in_channels即输入特征矩阵维度,由上一层输出矩阵决定,等于该层卷积核的channels(维数)
- out_channels即输出特征矩阵维度,由该层卷积核的numbers(个数)决定。
- kernel_size(卷积核大小),stride(步长),padding(填补)三因素再加in_size(输入特征矩阵大小)共同决定out_size(输出特征矩阵大小),公式为
out_size = ( in_size - kernel_size + 2 * padding ) / stride +1
torch.nn.MaxPool2d()
该函数即创建最大池化层,下采样层的一种,它的函数声明如下:
torch.nn.MaxPool2d(kernel_size,
stride=None, padding=0, dilation=1,
return_indices=False, ceil_mode=False)
- 该层不改变channels,只影响输出矩阵的大小out_size(stride一般默认等于kernel_size),计算公式为
out_size = ( in_size - kernel_size + 2 * padding ) / stride + 1
torch.nn.Linear()
该函数即创建全连接层,全连接层是一维的。在书写该函数时我们先要通过公式计算最终离全连接层最近的那一层的输出矩阵的out_size为多少,即输入全连接层的自变量个数为多少。Linear()函数的声明如下:
torch.nn.Linear(in_features, out_features,
bias=True, device=None, dtype=None)
- in_features 表示输入全连接层的参数个数
- out_features 表示输出全连接层的参数个数
def forward(self, x):
重写forward方法,即正向传播过程,其中x即输入,x的通道排列顺序为PyTorch接受的Tensor的通道排列顺序:[batch, channel, height, width]
。
self.conv1(x)
self.conv1是前面在__init__()
函数中定义的对象,为何对象名后能直接加小括号添加变量呢?这里其实调用了__call__
函数。
call():Python中,只要在创建类型的时候定义了__call__()方法,这个类型就是可调用的。Python中的所有东西都是对象,其中包括int/str/func/class这四类,它们都是对象,都是从一个类创建而来的。元类就是创建这些对象的东西,type就是Python的内建元类。其中,self.conv1是可调用的对象,说明在创建它的类型(父类或它本身)的时候,定义了__call__()方法。
# 下面两种调用方法是等价的
x = torch.nn.functional.relu(self.conv1.__call__(x))
x = torch.nn.functional.relu(self.conv1(x))
self.pool1(x)
同理也调用了__call__()
torch.nn.functional.relu()
函数定义如下,传入tensor进行relu处理后,传出tensor。需要注意的是relu()是不需要训练参数的。
torch.nn.functional.relu(input, inplace=False) → Tensor
torch.Tensor.view()
view函数即展平操作,在进入全连接层前需要进行展平处理view(x, y),其中y为你要接受的input参数,如3255,根据Tensor通道排序,x为batch值,我们往往将x=-1进行自动推理。
测试
增加if __name__ == "__main__":
,传入一个符合规则的Tensor进行测试,如果程序成功运行,则模型搭建成功
源码
import torch.nn
import torch.nn.functional
class LeNet(torch.nn.Module):
"""model
Args:
torch (_type_): _description_
"""
def __init__(self):
"""build model
"""
super(LeNet, self).__init__() # parent's init
self.conv1 = torch.nn.Conv2d(3, 16, (5, 5))
self.pool1 = torch.nn.MaxPool2d((2, 2), 2)
self.conv2 = torch.nn.Conv2d(16, 32, 5)
self.pool2 = torch.nn.MaxPool2d(2, 2)
self.fc1 = torch.nn.Linear(32 * 5 * 5, 120) # full connect to 1 dimension
self.fc2 = torch.nn.Linear(120, 84)
self.fc3 = torch.nn.Linear(84, 10)
def forward(self, x):
"""forward propagation
Args:
x (Tensor): [batches, channels, height, width]
"""
x = torch.nn.functional.relu(self.conv1.__call__(x)) # input[batch, 3, 32, 32] output[batch, 16, 28, 28]
x = self.pool1(x) # output(16, 14, 14) height and width become 1/2
x = torch.nn.functional.relu(self.conv2.__call__(x)) # output[batch, 32, 10, 10]
x = self.pool2(x) # output[batch, 32, 5, 5]
x = x.view(-1, 32 * 5 * 5) # output(32*5*5) # -1 means Automated reasoning
x = torch.nn.functional.relu(self.fc1(x)) # output(120)
x = torch.nn.functional.relu(self.fc2(x)) # output(84)
x = self.fc3(x) # output(10)
return x
if __name__ == "__main__":
import torch
my_model = LeNet()
print(my_model)
my_tensor = torch.randn(32, 3, 32, 32)
my_model.forward(my_tensor)