天池实战-街景字符编码识别-task3模型建立

写在前面的话

前两节我们进行了数据的预处理,介绍了赛题的相关背景:天池实战-街景字符编码识别-task1赛题理解,通过Pytorch 批量读取图像数据并进行图像预处理:天池实战-街景字符编码识别-task2数据预处理

这节我们通过CNN模型完成字符识别功能,并且介绍如何在Pytorch 下进行模型的建立



关于CNN模型

想必在使用Pytorch 之前你应该对CNN有一个基本的了解,最少得了解CNN一般都包括哪些层,每层都是用来干嘛的这些,至于说建模和调参这些你可以先不用知道,在Pytorch 的使用中你会看到。



Pytorch 模型创建

在Pytorch 中建立模型只需要定义好模型的参数和正向传播即可,Pytorch会根据正向传播自动计算反向传播。

那么我们的问题就可以优化成建立CNN模型的正向传播,在Pytorch 中包括这两步:

  1. 构建子模块(比如LeNet里面的卷积层,池化层,全连接层)
  2. 拼接子模块(有了子模块,我们把子模块按照一定的顺序,逻辑进行拼接起来得到最终的模型)

构建子模块

这一步主要是建立自己的模型,继承nn.Module,并在 __init__()方法中实现相关子模块的构建

说到这不知道你有没有想起上一节在读取读取的时候,同样是继承了Pytorch 的基类Dataset和DataLoader,不同的是这里需要集成nn.Module这个基类。


拼接子模块

基于上面创建的模型在模型的forward()方法中进行模块的拼接,相当于我们需要定义前向传播的每个层

不同的是在module中会自己计算反向传播,我们只需要定义前向传播即可。


nn.Module 类

在nn.Module中,有8个以有序字典形式存在的属性,用于管理整个模型

  • _parameters: 存储管理属于nn.Parameter类的属性,例如权值,偏置等参数
  • _modules: 存储管理nn.Module类,比如模型中构建的子模块、卷积层、池化层等会存储在modules中
  • _buffers: 存储管理缓冲属性
  • *_hooks: 存储管理钩子函数(5个钩子函数)

子模块的构建机制如下:

  • 创建一个大的Module 继承nn.Module 这个基类,并且在这个大的基类中有很多的子模块,子模块同样继承于nn.Module,在Module 的__init__方法中,会先通过调用父类的初始化方法进行8个属性的初始化。
  • 然后在构建每个子模块的时候,先初始化然后被__setattr__方法通过判断value 的类型将其保存到相应的属性字典中(例如module 类型、parameter类型),然后通过赋值的方式赋给相应的子模块的成员。

通过上述两步便完成了一个子模块的构建,我们通过构建一个个这样的子模块,最终构成整个Module。


总结一下Module:

  • 一个module可以包含多个子module(卷积层、池化层、全连接层等)
  • 一个module相当于一个运算,必须实现forware()函数(前向传播),module会自己计算反向传播
  • 每个module都有8个字典管理它的属性(最常用的是_parameters, _modules

上节在学习Dataset的时候了解到DataLoader,可以实现对图像的批处理,那么同样的道理我们已经创建了一个个的子Module,也会存在一个模型容器Containers。



模型容器Containers

模型容器中包含3个子模块,分别是nn.Sequential,nn.ModuleList和nn.ModuleDict

nn.Sequential

这是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是满足以下规律的:

  • 顺序性:各网络层之间严格按照顺序构建,这时候一定要注意前后层数据的关系
  • forward:自带的forward里,通过for循环依次执行前向传播运算

nn.ModuleList

nn.ModuleList是nn.module的容器, 用于包装一组网络层, 以迭代方式调用网络层。 主要方法:

  • append(): 在ModuleList后面添加网络层
  • extend(): 拼接两个ModuleList
  • insert(): 指定在ModuleList中位置插入网络层

这个方法的作用其实类似于我们的列表,只不过元素换成网络层而已


nn.ModuleDict

nn.ModuleDict是nn.module的容器, 用于包装一组网络层, 以索引方式调用网络层。主要方法:

  • clear(): 清空ModuleDict
  • items(): 返回可迭代的键值对(key-value pairs)
  • keys(): 返回字典的键(key)
  • values(): 返回字典的值(value)
  • pop(): 返回一对键值对, 并从字典中删除

总结一下Sequential
  • nn.Sequential:顺序性,各网络层之间严格按顺序执行
  • nn.ModuleList:迭代性,常用于大量重复网构建,通过for循环即可实现
  • nn.ModuleDict:索引性,常用于可选择的网络层



实操代码

在本次项目的特征提取模块,通过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个全连接层实现分类的目的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章