Pytorch搭建网络
面向对象编程
类
面向对象编程——类class和对象object
- class是一种类型(Type),object是类的实例(Instance)
- 每一个类方法都必须在参数列表开头有一个固定参数self,当调用该方法时python会自动加上self这个参数。因此如果方法没有参数,也必须加上self
-
init方法:
__init__
方法会在对象被实例化时自动运行(初始化)
继承:
- 要想使用继承,在定义类时我们需要在类后面跟一个包含基类名称的元组。
- 如果子类中定义了
__init__
方法,则基类中的__init__
方法将不会被自动调用,需要手动调用,此时需要显示调用self;相反,如果子类中未定义__init__
方法,则子类的__init__
方法将被自动调用。
class Student(SchoolMember):
'''代表一位学生。'''
def __init__(self, name, age, marks):
SchoolMember.__init__(self, name, age)
- 在子类中调用父方法,都需要显式调用self
- super(),先简单认为成继承父类
super(子类, self).父类方法(参数)
,相当于父类.方法(self, 参数)
类和对象
在面向对象编程中,理解和认出类和对象十分重要
程序中有很多类的结构,一般都要建立该类的对象。例如在LeNet项目中,LeNet5是一个class,在main.py中有net = LeNet5()
,即是创建了LeNet5的对象net。
又如输入时MNIST是类,trainset是对象;Dataloader是类,trainloader是对象等等。
面向对象编程并不是全部由类和对象组建程序的,也有函数,和普通变量
- 变量:有普通数值型变量和Tensor,普通型例如train_loss,大部分都是Tensor型变量
- 函数:例如
torch.max(output)
,可以写作output.max()
,这是函数的应用
总结 - 再看类和对象
class ConvReLU(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=0):
super(ConvReLU, self).__init__()
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding)
def forward(self, x):
x = self.conv(x)
x = F.relu(x)
return x
- 定义了
ConvReLU()
类,继承了nn.Module
父类。 - 定义了
__init()__
方法,在实例化ConvReLU()
类时自动调用__init()__
方法。
2.1 输入参数除了必须有的self
,还有要想实例化一个{ConvReLU()}
的对象需要的参数
2.2 因为子类定义了__init()__
方法,父类的__init()__
方法不再自动执行,需要显式执行,nn.Module
父类的__init()__
方法不需要输入参数,所以super
调用不需要输入参数
2.3 定义了对象变量self.conv
,属性是{nn.Conv2d()}
对象,实际上self.conv
是{nn.Conv2d()}
类的实例化,实例化时需要参数。 - 定义了
forward()
方法,对输入进行操作
3.1 对数据操作需要函数,第一行x = self.conv(x)
实际上为x = self.conv.forward(x)
,调用了nn.Conv2d()
的forward()
函数,由于大家都继承了nn.Module
父类,根据nn.Module
的使用方法,.forward()
不写,直接写object(input)
3.2 第二行x = F.relu(x)
直接调用了函数,对输入进行操作
循环与迭代
batchsize:在深度学习中,一般采用SGD训练,即每次训练在训练集中取batchsize个样本训练;
iteration:中文翻译为迭代,1个iteration等于使用batchsize个样本训练一次;
一个迭代 = 一个正向通过+一个反向通过
epoch:迭代次数,1个epoch等于使用训练集中的全部样本训练一次;
一个epoch = 所有训练样本的一个正向传递和一个反向传递
举个例子,训练集有1000个样本,batchsize=10,那么:
训练完整个样本集需要:
100次iteration,1次epoch。
Moudle class
搭建网络的整个过程都需要继承Module
class
Base class for all neural network modules.
Your models should also subclass this class.
Module
类中最重要的方法是forward
Defines the computation performed at every call.
Should be overridden by all subclasses.
.. note::
Although the recipe for forward pass needs to be defined within
this function, one should call the :class:Module
instance afterwards
instead of this since the former takes care of running the
registered hooks while the latter silently ignores them.
forward
实现了每次调用网络的操作,每个网络子类都必须重写forward
方法。
注意:调用网络操作时应当调用Module()
类而非Module.forward()
方法。
即:output = net(img)
而非output = net.forward(img)
torch.nn.XX VS torch.nn.functional.XX
既有torch.nn.Conv2d
,又有torch.nn.Functional.Conv2d
,区别在于:
nn.Module实现的layer是由class Layer(nn.Module)定义的特殊类,会自动提取可学习参数nn.Parameter;nn.functional中的函数更像是纯函数,由def function(input)定义
- nn.Conv2d是一个类,把卷积当作一个层时使用
- F.conv2d()是一个函数,把卷积当作一个运算函数时使用
- nn.Conv2d的forward()函数实现是用F.conv2d()实现的。
class Conv2d(_ConvNd):
def forward(self, input):
return F.conv2d(input, self.weight, self.bias, self.stride,
self.padding, self.dilation, self.groups)
在调用Conv2d()操作时执行forward
方法,可以看到真正进行操作的还是F.conv2d()
函数
pytorch的图像预处理包
import 和from... import
- 可以import的单位是
package
和module
.py
文件是一个Module
可以from...import...
的单位是package
,module
和class
/function
- 导入文件将导入所有的class,用某个class时需要
package.class
导入类可以直接使用该类
例如:torchvison
pakage有3个package
和一个module
导入class
在导入MNIST数据集时需要用class MNIST
,这个class包含于torchvison.datasets.mnist
文件中,因此正确引用的方式有:
from torchvision.datasets.mnist import MNIST
,使用时直接用MNIST
import torchvision.datasets.mnist as mnist
,使用时用mnist.MNIST
导入module
from torchvision import datasets
import torchvion.datasets as datasets
init.py
此外,通过package的__init__.py
文件,可以将package中的class提到package级别
参考
例如class Dataloader
在torchvision.datasets.folder.py
中,但是因为在torchvision.datasets
package中的__init__.py
中有
from .folder import ImageFolder, DatasetFolder
Datafolder
被提到了package级别,可以在package级别引用
from torchvision.datasets import ImageFolder
# in your __init__.py
from .folder import ImageFolder, DatasetFolder
# now import File from package
from torchvision.datasets import ImageFolder
真正要使用的时候肯定是使用一个class
,没法使用整个package
可以直接引入class,或先引入package再package.class
net中input的维度
应有四个维度:batch_size
, channels
, size_len
, size_width
,如果读取单张图片没有batch_size
维度,需要手动添加
image = image.view(1, image.size(0), image.size(1), image.size(2))
trick - 建一个class ConvReLU()
把Conv2d和ReLU()合起来做
class ConvReLu(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
super(ConvReLu, self).__init__()
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding)
def forward(self, x):
x = self.conv(x)
x = F.relu(x)
return x
使用方法:
class Layer1and2(nn.Module):
def __init__(self):
super(Layer1and2, self).__init__()
self.op = nn.Sequential(
ConvReLu(3, 64, 7, 2, 2),
nn.MaxPool2d(3, 2),
nn.BatchNorm2d(64),
ConvReLu(64, 64, 1, 1),
ConvReLu(64, 192, 3, 1, 1),
nn.BatchNorm2d(192),
nn.MaxPool2d(3, 2)
)
def forward(self, x):
x = self.op(x)
return x
把ConvReLU当作一个整体来用
好处
- 省事,提高代码结构化
- 在调试的时候可以看到卷积之后的效果,只有
forward()
中的操作可以调试跟踪,在__init()__
中nn.Sequential
中的操作是无法跟踪的。
trick - forward函数中尽量别把许多步骤合成一个Sequential
否则没办法调试跟踪每一步之后的结果
nn.Sequential()最后一个元素别加逗号
否则最后一个元素将被忽略,为什么?
Python魔术方法getitem、setitem、delitem、len
见CSDN