前两节我们进行了数据的预处理,介绍了赛题的相关背景:天池实战-街景字符编码识别-task1赛题理解,通过Pytorch 批量读取图像数据并进行图像预处理:天池实战-街景字符编码识别-task2数据预处理。
这节我们通过CNN模型完成字符识别功能,并且介绍如何在Pytorch 下进行模型的建立
想必在使用Pytorch 之前你应该对CNN有一个基本的了解,最少得了解CNN一般都包括哪些层,每层都是用来干嘛的这些,至于说建模和调参这些你可以先不用知道,在Pytorch 的使用中你会看到。
在Pytorch 中建立模型只需要定义好模型的参数和正向传播即可,Pytorch会根据正向传播自动计算反向传播。
那么我们的问题就可以优化成建立CNN模型的正向传播,在Pytorch 中包括这两步:
这一步主要是建立自己的模型,继承nn.Module,并在 __init__()
方法中实现相关子模块的构建
说到这不知道你有没有想起上一节在读取读取的时候,同样是继承了Pytorch 的基类Dataset和DataLoader,不同的是这里需要集成nn.Module这个基类。
基于上面创建的模型在模型的forward()
方法中进行模块的拼接,相当于我们需要定义前向传播的每个层
不同的是在module中会自己计算反向传播,我们只需要定义前向传播即可。
在nn.Module中,有8个以有序字典形式存在的属性,用于管理整个模型
_parameters:
存储管理属于nn.Parameter类的属性,例如权值,偏置等参数_modules:
存储管理nn.Module类,比如模型中构建的子模块、卷积层、池化层等会存储在modules中_buffers:
存储管理缓冲属性*_hooks:
存储管理钩子函数(5个钩子函数)子模块的构建机制如下:
__init__
方法中,会先通过调用父类的初始化方法进行8个属性的初始化。__setattr__
方法通过判断value 的类型将其保存到相应的属性字典中(例如module 类型、parameter类型),然后通过赋值的方式赋给相应的子模块的成员。通过上述两步便完成了一个子模块的构建,我们通过构建一个个这样的子模块,最终构成整个Module。
总结一下Module:
_parameters, _modules
)上节在学习Dataset的时候了解到DataLoader,可以实现对图像的批处理,那么同样的道理我们已经创建了一个个的子Module,也会存在一个模型容器Containers。
模型容器中包含3个子模块,分别是nn.Sequential,nn.ModuleList和nn.ModuleDict
这是nn.module的容器,用于按顺序包装一组网络层
在深度学习中,弱化了特征工程部分,偏向于让网络自己提取特征,然后进行分类或者回归任务。
所以一般选择以全连接层为界限,将网络模型划分为特征提取模块和分类模块,在模型前面加上卷积层让网络自己学习提取特征,在通过全连接层进行分类等任务。
那么我们在创建模型的时候就可以这样定义:
class LeNetSequential(nn.Module):
def __init__(self, classes):
super(LeNetSequential, self).__init__()
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),)
def forward(self, x):
x = self.features(x)
x = x.view(x.size()[0], -1)
x = self.classifier(x)
return x
上面是创建了一个LeNet的模型,其中模型包括两部分,分别是features进行特征提取和classifier进行分类,然后通过Sequential包装起来,相比而言forward函数只需要:features、形状变换、classifier
我们可以发现其实Sequential是满足以下规律的:
nn.ModuleList是nn.module的容器, 用于包装一组网络层, 以迭代方式调用网络层。 主要方法:
这个方法的作用其实类似于我们的列表,只不过元素换成网络层而已
nn.ModuleDict是nn.module的容器, 用于包装一组网络层, 以索引方式调用网络层。主要方法:
在本次项目的特征提取模块,通过resnet18模型作特征提取模块,也就相当于我们不需要再构建子模块,直接使用resnet18的子模块即可
代码如下:
class SVHN_Model1(nn.Module):
def __init__(self):
super(SVHN_Model1, self).__init__()
model_conv = models.resnet18(pretrained=True)
model_conv.avgpool = nn.AdaptiveAvgPool2d(1)
model_conv = nn.Sequential(*list(model_conv.children())[:-1])
self.cnn = model_conv
self.fc1 = nn.Linear(512, 11)
self.fc2 = nn.Linear(512, 11)
self.fc3 = nn.Linear(512, 11)
self.fc4 = nn.Linear(512, 11)
self.fc5 = nn.Linear(512, 11)
def forward(self, img):
feat = self.cnn(img)
# print(feat.shape)
feat = feat.view(feat.shape[0], -1)
c1 = self.fc1(feat)
c2 = self.fc2(feat)
c3 = self.fc3(feat)
c4 = self.fc4(feat)
c5 = self.fc5(feat)
return c1, c2, c3, c4, c5
本节通过对Pytorch下模型构建流程的分析,介绍了Pytorch下Module、Containers的用法和相关构建流程。在本次项目中通过resnet18对特征进行提取,通过5个全连接层实现分类的目的