【OCR学习笔记】8、OCR移动端网络汇总与PyTorch实现(一)

本文涉及的产品
个人证照识别,个人证照识别 200次/月
OCR统一识别,每月200次
票据凭证识别,票据凭证识别 200次/月
简介: 【OCR学习笔记】8、OCR移动端网络汇总与PyTorch实现(一)

1 轻量化网络简介


前面所提网络在向着越来越大、越来越深的方向发展,但在实际应用中计算性能有限,但又有着极强的业务需求。对于效率问题,可以想到的方法通常是对模型进行压缩与剪枝,降低网络的参数量,从而降低计算量加快推理速度。相较于对模型进行后处理的方法,轻量化模型设计则是另辟蹊径。

轻量化模型主要是设计更加高效的网络计算方式,在降低网络参数的同时,不损失性能。

常用的四个轻量化模型系列:SqueezeNet、MobileNet、ShuffleNet、Xception。这些模型在实际场景中都得到了广泛的应用。

1.1 SqueezeNet

SqueezeNet是一个人工设计的轻量化网络,它在ImageNet上实现了和AlexNet相同水平的正确率,但是只使用了1/50的参数。更进一步,使用模型压缩技术,可以将SqueezeNet压缩到0.5MB,这是AlexNet的1/510

引入了两个术语CNN微结构CNN宏结构

  • CNN微结构:由层或几个卷积层组成的小模块,如inception模块。
  • CNN宏结构:由层或模块组成的完整的网络结构,此时深度是一个重要的参数。

网络结构的设计策略:

  • (1)代替3x3的滤波器为1x1,这样会减少9倍的参数。
  • (2)减少输入到3x3滤波器的输入通道,这样可以进一步减少参数,本文使用squeeze层来实现。
  • (3)降采样操作延后,可以给卷积层更大的激活特征图,意味着保留的信息更多,可以提升准确率。

策略是减少参数的方案,是在限制参数预算的情况下最大化准确率。如下图所示作者引入了Fire模块来构造CNN,此模块成功地应用了上述的3个策略。

2fc30e1ad78e1f9826e26d2110e8a0dc.png

模块由squeeze层和expand层组成,squeeze层由1x1的卷积层组成,,可以减少输入expand层的特征图的输入通道。expand层由1x1和3x3的卷积混合而成,<,称为扩展了特征图。

PyTorch实现Fire模块如下:

class Fire(nn.Module):
    def __init__(self, inplanes, squeeze_planes, expand1x1_planes, expand3x3_planes):
        super(Fire, self).__init__()
        self.inplanes = inplanes
        self.squeeze = nn.Conv2d(inplanes, squeeze_planes, kernel_size=1)
        self.squeeze_activation = nn.ReLU(inplace=True)
        self.expand1x1 = nn.Conv2d(squeeze_planes, expand1x1_planes, kernel_size=1)
        self.expand1x1_activation = nn.ReLU(inplace=True)
        self.expand3x3 = nn.Conv2d(squeeze_planes, expand3x3_planes, kernel_size=3, padding=1)
        self.expand3x3_activation = nn.ReLU(inplace=True)
    def forward(self, x):
        x = self.squeeze_activation(self.squeeze(x))
        return torch.cat([
            self.expand1x1_activation(self.expand1x1(x)),
            self.expand3x3_activation(self.expand3x3(x))
        ], 1)

整体结构以普通的卷积层(conv1)开始,接着连接8个Fire(2-9)模块,最后以卷积层(conv10)结束。每个Fire模块的filter数量逐渐增加,并且在conv1,Fire4,Fire8和conv10后使用步长为2的max-pooling,这种相对延迟的pooling符合了策略(3)。如下作者对比了添加跳跃层的squeezenet:

PyTorch实现如下:

class SqueezeNet(nn.Module):
    def __init__(self, version=1.0, num_classes=1000):
        super(SqueezeNet, self).__init__()
        if version not in [1.0, 1.1]:
            raise ValueError("Unsupported SqueezeNet version {version}: 1.0 or 1.1 expected".format(version=version))
        self.num_classes = num_classes
        if version == 1.0:
            self.features = nn.Sequential(
                nn.Conv2d(3, 96, kernel_size=7, stride=2),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(96, 16, 64, 64),
                Fire(128, 16, 64, 64),
                Fire(128, 32, 128, 128),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(256, 32, 128, 128),
                Fire(256, 48, 192, 192),
                Fire(384, 48, 192, 192),
                Fire(384, 64, 256, 256),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(512, 64, 256, 256),
            )
        else:
            self.features = nn.Sequential(
                nn.Conv2d(3, 64, kernel_size=3, stride=2),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(64, 16, 64, 64),
                Fire(128, 16, 64, 64),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(128, 32, 128, 128),
                Fire(256, 32, 128, 128),
                nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True),
                Fire(256, 48, 192, 192),
                Fire(384, 48, 192, 192),
                Fire(384, 64, 256, 256),
                Fire(512, 64, 256, 256),
            )
        # Final convolution is initialized differently form the rest
        final_conv = nn.Conv2d(512, self.num_classes, kernel_size=1)
        self.classifier = nn.Sequential(
            nn.Dropout(p=0.5), final_conv, nn.ReLU(inplace=True),
            nn.AdaptiveAvgPool2d((1, 1))
        )
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                if m is final_conv:
                    init.normal_(m.weight, mean=0.0, std=0.01)
                else:
                    init.kaiming_uniform_(m.weight)
                if m.bias is not None:
                    init.constant_(m.bias, 0)
    def forward(self, x):
        x = self.features(x)
        x = self.classifier(x)
        return x.view(x.size(0), self.num_classes)

1.2 MobileNet

mobilenet做的一个工作相当于把卷积核拆分,并提出了逐点卷积depthwise separable convolutions。如下图:

47e29a9e61b31d1c10c2bdd21a9829f1.png

简单解释一下depthwise separable convolutions:假设输入的feature map有C1层,先对每一层先进行3x3的卷积,得到新的C1层的feature map,对新的feature map进行1x1卷积,得到输出的C2层feature map。所以整个过程参数为:3x3xC1+1x1xC1xC2。

class MobileNet(nn.Module):
    def __init__(self):
        super(MobileNet, self).__init__()
        def conv_bn(inp, oup, stride):
            return nn.Sequential(
                nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
                nn.BatchNorm2d(oup),
                nn.ReLU(inplace=True)
            )
        def conv_dw(inp, oup, stride):
            return nn.Sequential(
                nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),
                nn.BatchNorm2d(inp),
                nn.ReLU(inplace=True),
                nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup),
                nn.ReLU(inplace=True),
            )
        self.model = nn.Sequential(
            conv_bn(  3,  32, 2), 
            conv_dw( 32,  64, 1),
            conv_dw( 64, 128, 2),
            conv_dw(128, 128, 1),
            conv_dw(128, 256, 2),
            conv_dw(256, 256, 1),
            conv_dw(256, 512, 2),
            conv_dw(512, 512, 1),
            conv_dw(512, 512, 1),
            conv_dw(512, 512, 1),
            conv_dw(512, 512, 1),
            conv_dw(512, 512, 1),
            conv_dw(512, 1024, 2),
            conv_dw(1024, 1024, 1),
            nn.AvgPool2d(7), )
        self.fc = nn.Linear(1024, 1000)
    def forward(self, x):
        x = self.model(x)
        x = x.view(-1, 1024)
        x = self.fc(x)
        return x

后续的Mobilenet v2针对Mobilenet v1,作者进一步提出了改进方案,作者发现在通道数少的情况下不应该接relu这个激活函数,会造成大量节点变0的情况,因此作者提出了类似于resnet的残差概念,将前面还没有置零的部分直接加和到下一层。除此之外,在通道数较少的层仅仅采用线性函数,而取消了非线性的relu激活。

相关文章
|
1月前
|
PyTorch 算法框架/工具
Pytorch学习笔记(五):nn.AdaptiveAvgPool2d()函数详解
PyTorch中的`nn.AdaptiveAvgPool2d()`函数用于实现自适应平均池化,能够将输入特征图调整到指定的输出尺寸,而不需要手动计算池化核大小和步长。
106 1
Pytorch学习笔记(五):nn.AdaptiveAvgPool2d()函数详解
|
1月前
|
算法 PyTorch 算法框架/工具
Pytorch学习笔记(九):Pytorch模型的FLOPs、模型参数量等信息输出(torchstat、thop、ptflops、torchsummary)
本文介绍了如何使用torchstat、thop、ptflops和torchsummary等工具来计算Pytorch模型的FLOPs、模型参数量等信息。
175 2
|
1月前
|
PyTorch 算法框架/工具
Pytorch学习笔记(六):view()和nn.Linear()函数详解
这篇博客文章详细介绍了PyTorch中的`view()`和`nn.Linear()`函数,包括它们的语法格式、参数解释和具体代码示例。`view()`函数用于调整张量的形状,而`nn.Linear()`则作为全连接层,用于固定输出通道数。
82 0
Pytorch学习笔记(六):view()和nn.Linear()函数详解
|
1月前
|
PyTorch 算法框架/工具
Pytorch学习笔记(四):nn.MaxPool2d()函数详解
这篇博客文章详细介绍了PyTorch中的nn.MaxPool2d()函数,包括其语法格式、参数解释和具体代码示例,旨在指导读者理解和使用这个二维最大池化函数。
115 0
Pytorch学习笔记(四):nn.MaxPool2d()函数详解
|
1月前
|
PyTorch 算法框架/工具
Pytorch学习笔记(三):nn.BatchNorm2d()函数详解
本文介绍了PyTorch中的BatchNorm2d模块,它用于卷积层后的数据归一化处理,以稳定网络性能,并讨论了其参数如num_features、eps和momentum,以及affine参数对权重和偏置的影响。
146 0
Pytorch学习笔记(三):nn.BatchNorm2d()函数详解
|
1月前
|
机器学习/深度学习 PyTorch TensorFlow
Pytorch学习笔记(二):nn.Conv2d()函数详解
这篇文章是关于PyTorch中nn.Conv2d函数的详解,包括其函数语法、参数解释、具体代码示例以及与其他维度卷积函数的区别。
132 0
Pytorch学习笔记(二):nn.Conv2d()函数详解
|
1月前
|
Ubuntu 网络安全 图形学
Ubuntu学习笔记(二):ubuntu20.04解决右上角网络图标激活失败或者消失,无法连接有线问题。
在Ubuntu 20.04系统中解决网络图标消失和无法连接有线网络问题的方法,其中第三种方法通过检查并确保Windows防火墙中相关服务开启后成功恢复了网络连接。
459 0
Ubuntu学习笔记(二):ubuntu20.04解决右上角网络图标激活失败或者消失,无法连接有线问题。
|
8天前
|
数据采集 网络协议 算法
移动端弱网优化专题(十四):携程APP移动网络优化实践(弱网识别篇)
本文从方案设计、代码开发到技术落地,详尽的分享了携程在移动端弱网识别方面的实践经验,如果你也有类似需求,这篇文章会是一个不错的实操指南。
22 1
|
1月前
|
PyTorch 算法框架/工具
Pytorch学习笔记(七):F.softmax()和F.log_softmax函数详解
本文介绍了PyTorch中的F.softmax()和F.log_softmax()函数的语法、参数和使用示例,解释了它们在进行归一化处理时的作用和区别。
381 1
Pytorch学习笔记(七):F.softmax()和F.log_softmax函数详解
|
1月前
|
机器学习/深度学习 PyTorch 算法框架/工具
Pytorch学习笔记(八):nn.ModuleList和nn.Sequential函数详解
PyTorch中的nn.ModuleList和nn.Sequential函数,包括它们的语法格式、参数解释和具体代码示例,展示了如何使用这些函数来构建和管理神经网络模型。
77 1

热门文章

最新文章