【CNN】MobileNet——卷积神经网络中轻量级的经典

news2024/11/24 9:43:04

前言

MobileNet 系列 是 Andrew G. Howard(Google Inc.) 等人于 2017 年(其实是 2016 年先于 Xception 已经提出,但是直到 2017 年才挂到 arXiv 上)在 MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications 中提出的一种网络结构,这种网络结构的特点是模型小,计算速度快,适合部署到移动端或者嵌入式系统中。 之后原作者团队对模型进行了进一步改进,提出V2和V3版本,如GoogLeNet一样,也是一项系列文章。

MobileNetV1论文下载链接:
https://arxiv.org/pdf/1704.04861.pdf%EF%BC%89
MobileNetV2论文下载链接:
https://openaccess.thecvf.com/content_cvpr_2018/papers/Sandler_MobileNetV2_Inverted_Residuals_CVPR_2018_paper.pdf
MobileNetV3论文:
http://openaccess.thecvf.com/content_ICCV_2019/papers/Howard_Searching_for_MobileNetV3_ICCV_2019_paper.pdf

一、Motivation

卷积神经网络(CNN)已经普遍应用在计算机视觉领域,并且已经取得了不错的效果。但是,近几年来CNN在ImageNet竞赛的表现可以看到,为了追求分类准确度,模型深度越来越深,模型复杂度也越来越高,如深度残差网络(ResNet)其层数已经多达152层。然而,在某些真实的应用场景如移动或者嵌入式设备,如此大而复杂的模型是难以被应用的。

首先是模型过于庞大,面临着内存不足的问题,其次一些场景要求低延迟,或者说响应速度要快,想象一下自动驾驶汽车的行人检测系统如果速度很慢会发生什么可怕的事情。所以,研究小而高效的CNN模型在这些场景至关重要,至少目前是这样,尽管未来硬件也会越来越快。

目前的研究总结来看分为两个方向:

一是对训练好的复杂模型进行压缩得到小模型,也就是业界常说的模型量化;
二是直接设计小模型并进行训练。

不管如何,其目标在保持模型性能(accuracy)的前提下降低模型大小(parameters size),同时提升模型速度(speed, low latency)。

本文的主角MobileNet系列的网络属于后者,是Google最近提出的一种小巧而高效的CNN模型,其在accuracy和latency之间做了折中。下面对MobileNet做详细的介绍。

二、MobileNetV1

1.深度可分离卷积

MobileNetV1之所以轻量,与深度可分离卷积的关系密不可分。
在这里插入图片描述
如上图所示,模型推理中卷积操作占用了大部分的时间,因此MobileNetV1使用了深度可分离卷积对卷积操作做了进一步的优化,具体解释如下:

常规卷积操作

对于5x5x3的输入,如果想要得到3x3x4的feature map,那么卷积核的shape为3x3x3x4;如果padding=1,那么输出的feature map为5x5x4,如下图:
在这里插入图片描述
卷积层共4个Filter,每个Filter包含一个通道数为3(与输入信息通道相同),且尺寸为3×3的kernel。因此卷积层的参数数量可以用如下公式来计算(即:卷积核W x 卷积核H x 输入通道数 x 输出通道数):

N_std = 4 × 3 × 3 × 3 = 108

计算量可以用如下公式来计算(即:卷积核W x 卷积核H x (图片W-卷积核W+1) x (图片H-卷积核H+1) x 输入通道数 x 输出通道数):

C_std =3*3*(5-2)*(5-2)*3*4=972

深度可分离卷积 主要是两种卷积变体组合使用,分别为逐通道卷积Depthwise Convolution)和逐点卷积Pointwise Convolution)。

逐通道卷积(Depthwise Convolution)

Depthwise Convolution的一个卷积核只有一个通道,输入信息的一个通道只被一个卷积核卷积,这个过程产生的feature map通道数和输入的通道数完全一样,如下图所示:
在这里插入图片描述
一张5×5像素、三通道彩色输入图片(shape为5×5×3),Depthwise Convolution每个卷积核只负责计算输入信息的某一个通道。卷积核的数量与输入信息的通道数相同。所以一个三通道的图像经过运算后一定是生成了3个Feature map。卷积核的shape即为:(卷积核W x 卷积核H x 输入通道数)。

此时,卷积部分的参数个数计算如下( 卷积核Wx卷积核Hx输入通道数),即:

N_depthwise = 3 × 3 × 3 = 27

计算量为( 卷积核W x 卷积核H x (图片W-卷积核W+1) x (图片H-卷积核H+1) x 输入通道数)即:

C_depthwise=3x3x(5-2)x(5-2)x3=243

Depthwise Convolution完成后的Feature map数量与输入层的通道数相同,无法在通道维度上扩展或压缩Feature map的数量。而且这种运算对输入层的每个通道独立进行卷积运算,没有有效的利用不同通道在相同空间位置上的features的相关性。简而言之,虽然减少了计算量,但是失去了通道维度上的信息交互。因此需要Pointwise Convolution来将这些Feature maps进行组合生成新的Feature maps。

逐点卷积(Pointwise Convolution)

Pointwise Convolution的运算与常规卷积运算非常相似,其实就是1X1的卷积。它的卷积核的尺寸为 1×1×M,M为上一层输出信息的通道数。所以这里Pointwise Convolution的每个卷积核会将上一步的feature maps在通道方向上进行加权组合,生成新的feature map。有几个卷积核就有几个新的feature maps输出。
在这里插入图片描述
由于采用的是1×1卷积的方式,此步中卷积涉及到的参数个数可以计算为(1 x 1 x 输入通道数 x 输出通道数):

N_pointwise = 1 × 1 × 3 × 4 = 12

计算量则为(1 x 1 特征层W x 特征层H x 输入通道数 x 输出通道数):

C_pointwise = 1 × 1 × 3 × 3 × 3 × 4 = 108

经过Pointwise Convolution之后,由四个卷积核输出了4张Feature map,与常规卷积的输出维度相同。

参数对比

回顾一下,常规卷积的参数个数为:

N_std = 4 × 3 × 3 × 3 = 108

Depthwise Separable Convolution的参数由两部分相加得到:

N_depthwise = 3 × 3 × 3 = 27

N_pointwise = 1 × 1 × 3 × 4 = 12

N_separable = N_depthwise + N_pointwise = 39

相同的输入,同样是得到4张Feature map,Separable Convolution的参数个数是常规卷积的约1/3。因此,在参数量相同的前提下,采用Depthwise Separable Convolution的神经网络层数可以做的更深

计算量对比

回顾一下,常规卷积的计算量为:

C_std =33(5-2)*(5-2)34=972

Depthwise Separable Convolution的计算量由两部分相加得到:

C_depthwise=3x3x(5-2)x(5-2)x3=243

C_pointwise = 1 × 1 × 3 × 3 × 3 × 4 = 108

C_separable = C_depthwise + C_pointwise = 351

相同的输入,同样是得到4张Feature map,Separable Convolution的计算量也是常规卷积的约1/3。因此,在计算量相同的情况下,Depthwise Separable Convolution可以将神经网络层数可以做的更深

2. MBconv

前面讲述了depthwise separable convolution,这是MobileNet的基本操作,但是在真正应用中会加入batchnorm,并使用ReLU激活函数,所以depthwise separable convolution的基本结构如下图右面所示, 左面是正常的conv:
在这里插入图片描述
整体网络就是通过不断堆叠MBconv组件组成的,另外值得提一句的是深度可分卷积并不是 MobileNet 首次提出的,仅仅是利用这一变换来达到减少参数量和计算量的目的。

3.Model Architecture

MobileNet的网络结构如表1所示。首先是一个3x3的标准卷积,然后后面就是堆积depthwise separable convolution,并且可以看到其中的部分depthwise convolution会通过strides=2进行down sampling。经过 卷积提取特征后再采用average pooling将feature变成1x1,根据预测类别大小加上全连接层,最后是一个softmax层。

如果单独计算depthwise convolution和pointwise convolution,整个网络有28层(这里Avg Pool和Softmax不计算在内)。我们还可以分析整个网络的参数和计算量分布,如下面第二张图所示。
在这里插入图片描述
可以看到整个计算量基本集中在1x1卷积上,如果你熟悉卷积底层实现的话,你应该知道卷积一般通过一种im2col方式实现,其需要内存重组,但是当卷积核为1x1时,其实就不需要这种操作了,底层可以有更快的实现。对于参数也主要集中在1x1卷积,除此之外还有就是全连接层占了一部分参数。
在这里插入图片描述

4. 小结

以上简单介绍了Google提出的移动端模型MobileNetV1模型,其核心是采用了可分解的depthwise separable convolution,其不仅可以降低模型计算复杂度,而且可以大大降低模型的参数量。另外,值得一提的是,文中将激活函数从Relu替换成Relu6。在真实的移动端应用场景,像MobileNet这样类似的网络将是持续研究的重点。

三、MobileNetV2

Andrew G. Howard 等于 2018 年在 MobileNet V1 的基础上又提出了改进版本 MobileNet V2。具体可以参考原始论文 MobileNetV2: Inverted Residuals and Linear Bottlenecks。

从标题我们就可以看出,V2 中主要用到了 Inverted Residuals 和 Linear Bottlnecks。

1. Inverted Residuals

上一篇我们看到 V1 的网络结构还是非常传统的直桶模型(没有旁路),但是 ResNet 在模型中引入旁路并取得了很好的效果,因此到了 V2 的时候,作者也想引入进来,这就有了我们要探讨的问题了。

首先我们看下ResNet BottleNeck Block。下图可以看到,采用 1x1 的卷积核先将 256 维度降到 64 维,经过 3x3 的卷积之后,然后又通过 1x1 的卷积核恢复到 256 维。
在这里插入图片描述
我们要把 ResNet BottleNeck Block 运用到 MobileNet 中来的话,如果我们还是采用相同的策略显然是有问题的,因为 MobileNet 中由于逐通道卷积,本来 feature 的维度就不多,如果还要先压缩的话,会使模型太小了,所以作者提出了 Inverted Residuals,即先扩展(6倍)后压缩,这样就不会使模型被压缩的太厉害。 下图对比了原始残差和反转残差结构:
在这里插入图片描述

2. Linear Bottlnecks

Linear Bottlnecks 听起来很高级,其实就是把上面的 Inverted Residuals block 中的 bottleneck 处的 ReLU 去掉。通过下面的图片对比就可以很容易看出,实际上就是去掉了最后一个 1x1 卷积后面的 ReLU。整体的网络模型就是由堆叠下图右图的Bottlenecks搭建成的。
在这里插入图片描述
那为什么要去掉呢?而且为什么是去掉最后一个1X1卷积后面的 ReLU 呢?因为在训练 MobileNet V1 的时候发现最后 Depthwise 部分的 kernel 训练容易失去作用,最终再经过ReLU出现输出为0的情况。作者发现是因为ReLU 会对 channel 数较低的张量造成较大的信息损耗,因此执行降维的卷积层后面不会接类似于ReLU这样的非线性激活层。说人话就是:1X1卷积降维操作本来就会丢失一部分信息,而加上 ReLU 之后那是雪上加霜,所以去掉 ReLU 缓一缓。

3. Model Architecture

完整的MobileNetV2的网络结构参数如下:
t代表反转残差中第一个1X1卷积升维的倍数;c代表通道数;n代表堆叠bottleneck的次数;s代表DWconv的幅度(1或2),不同的步幅对应了不同的模块,详见上图(d)MobilenetV2。
在这里插入图片描述
效果上,在 ImageNet 图像分类的任务中,相比 V1 参数量减少了,效果也更好了,详见下图:
在这里插入图片描述

4. 小结

MobileNetV2最大的贡献就是改进了通道数较少的网络运用残差连接的方式:设计了反转残差(Inverted Residuals)的结构。另外,提出了Linear Bottlnecks的模型设计技巧。

四、MobileNetV3

V3 保持了一年一更的节奏,Andrew G. Howard 等在 2019 年又提出了 MobileNet V3。文中提出了两个网络模型, MobileNetV3-Small 与 MobileNetV3-Large 分别对应对计算和存储要求低和高的版本。具体可以参考原始论文 Searching for MobileNetV3。

这回的标题(Searching for MobileNetV3)说的不是 V3 里面有什么,而是说的 V3 是怎么来的。Searching 说的是网络架构搜索技术(NAS),即 V3 是通过搜索和网络优化而来。

这里我们不详细讨论 NAS网络搜索技术,虽然这是论文的一大亮点。原因是这个技术不是一般人玩得起的…它相当于训练的不是模型参数,而是模型架构。说白了就是设计一个网络模型结构的集合,通过不同网络层的排列组合可以组合出许多许多的模型,再通过NAS搜索技术搜索出最佳的网络结构。这相当于大力出奇迹嘛,将调参工作交给NAS技术去做,实属一种降维打击。

当然,随之而来的缺点也很明显,这需要大量的计算资源才能完成,恐怕只有想GoogLe,Baidu这种可以买显卡当买白开水的公司才有财力去搞这些研究。而且,由于搜索过程中最关注网络的性能,因此最优的的网络结构可能长得五花八门,换一种说法就是层级结构的排列可能比较混乱。

这就导致了两个缺点:
一:网络的可解释性更差,没办法说为啥这么排列性能好,只能说实验得出…
二:这种不规律的模型层级排列也不利于模型的部署。因此经过NAS搜索后的模型一般需要人为的进行进一步调整,让它长的规矩一些。也是性能和部署之间的trade-off(折中)。

当然,NAS网络结构搜索技术还是很强的,一方面是通过设计不同的搜索空间,NAS可以设计不同的搜索目标 。这算是一个比较前沿的方向了。

1. 对 V2 最后几层的修改

作者实验发现 V2 网络最后一部分结构可以优化,如Figure5所示,原始的结构用 1x1 的卷积来调整 feature 的维度,从而提高预测的精度,但是这一部分也会造成一定的延时,为了减少延时,作者把 average pooling 提前,这样的话,这样就提前把 feature 的 size 减下来了(pooling 之后 feature size 从 7x7 降到了 1x1)。这样一来延时减小了,但是试验证明精度却几乎没有降低。
在这里插入图片描述

2. h-swish

这个得先说说 swish(也是 google 自家人搞出来的),说是这个激活函数好用,替换 ReLU 可以提高精度,但是这个激活函数(主要是 σ ( x ) \sigma(x)σ(x) 部分)在移动端设备上显得太耗资源,所以作者又提出了一个新的 h-swish 激活函数来取代 swish,效果跟 swish 差不多,但是计算量却大大减少。
在这里插入图片描述
具体的数学公式如下:
在这里插入图片描述
其中,说明一下ReLU6,卷积之后通常会接一个ReLU非线性激活,在Mobile v1里面使用ReLU6,ReLU6就是普通的ReLU但是限制最大输出值为6(对输出值做clip),这是为了在移动端设备float16的低精度的时候,也能有很好的数值分辨率,如果对ReLU的激活范围不加限制,输出范围为0到正无穷,如果激活值非常大,分布在一个很大的范围内,则低精度的float16无法很好地精确描述如此大范围的数值,带来精度损失。

3. squeeze-and-excite(SE)

Squeeze-and-Excitation Networks(SENet)是由自动驾驶公司Momenta在2017年公布的一种全新的图像识别结构,它通过对特征通道间的相关性进行建模,把重要的特征进行强化来提升准确率。这个结构是2017 ILSVR竞赛的冠军,top5的错误率达到了2.251%,比2016年的第一名还要低25%,可谓提升巨大。SE也一下成为基于通道的注意力机制。

与MobileNetV2相比,MobileNetV3 中增加了 SE 结构,并且将含有 SE 结构部分的 expand layer 的 channel 数减少为原来的 1/4 以减少延迟(但是,从计算时间上看,貌似只是减少了1/2),试验发现这样不仅提高了模型精度,而且整体上延迟也并没有增加。
在这里插入图片描述

4. Model Architecture

MobileNetV3的网络模型如下,其中Table1对应着MobileNetV3_Large版的网络结构参数;Table2对应着MobileNetV3_Small版;
在这里插入图片描述
值得注意的是:对比 V2 还可以发现, V3模型开始的 conv2d 部分的输出feature map数量减少为原来的一般了,试验发现延迟有所降低,精度没有下降。

至于效果,相比 V2 1.0 来说, V3-Small 和 V3-Large 在性能和精度上各有优势。但是在工程实际中,特别是在移动端上 V2 用的更为广泛,因为 V2 结构更简单,移植更方便,速度也更有优势。
在这里插入图片描述

5. 小结

(1)利用NAS网络搜索结构优化了网络架构
(2)使用h-swish激活函数
(3)加入SE模块

五、代码实现

这里给出V1模型搭建的python代码(基于pytorch实现)。

import time
import torch
import torch.nn as nn
import torch.backends.cudnn as cudnn
import torchvision.models as models
from torch.autograd import Variable

class MobileNet(nn.Module):
    def __init__(self, n_class=1000):
        super(MobileNet, self).__init__()
        self.nclass = n_class

        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, self.nclass)

    def forward(self, x):
        x = self.model(x)
        x = x.view(-1, 1024)
        x = self.fc(x)
        return 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/28275.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

干货 | 如何在子查询和联接之间选择

在 联接与子查询:哪个更快?文章我们了解到连接往往比子查询执行得更快。话虽如此,这并不是一条通用准则,因此你可能不希望自动假设联接更可取。正如那篇文章中提到的,如果你需要在查询添加许多联接,数据库服…

分治算法Divide and Conquer

评价 它可以减少运行的时间,很多问题如果暴力求解需要O(n^2)的复杂度,而通过分治可以减少到O(nlogn) 当与随机化技术相结合时,分治的功能很强大 分治算法的步骤 1.先将大的问题分解为一个个小的子问题 2.对每一个子…

swift内存绑定

swift提供了3种不同的API来绑定/重新绑定指针 assumingMemoryBound(to:)bindMemory(to: capacity:)withMemoryRebound(to: capacity: body:) 绕过编译器检查 - assumingMemoryBound 就是假定内存绑定 func testPointer(_ p: UnsafePointer<Int>) {print(p) } let tup…

Tomcat运行流程、Servlet运行原理以及常用API

文章目录Servlet原理Tomcat 的定位Tomcat 的伪代码Tomcat 初始化流程Tomcat处理请求总结Servlet的核心APIHttpServletHttpServletRequestHttpServletResponseCookie 和 SessionServlet原理 Servlet终究是属于应用层&#xff0c;它是在应用层进行的一系列操作&#xff0c;它的底…

Prometheus Operator 实战 监控 etcd 集群

上节课和大家讲解了 Prometheus Operator 的安装和基本使用方法&#xff0c;这节课给大家介绍如何在 Prometheus Operator 中添加一个自定义的监控项。 除了 Kubernetes 集群中的一些资源对象、节点以及组件需要监控&#xff0c;有的时候我们可能还需要根据实际的业务需求去添…

Java 后端 本地调试-获取微信公众号 openId

Java 后端 本地调试-获取微信公众号 openId申请测试微信公众号内网穿透工具配置公众号获取用户 openId申请测试微信公众号 微信测试公众号 内网穿透工具 netapp 配置公众号 搜索网页账号选项 点击修改&#xff0c;填写内网穿透的域名 获取用户 openId 1 第一步&#xff…

国家高新技术企业的好处

国家高新技术企业的好处&#xff1a;享受税收减免优惠政策&#xff1b;国家科研经费支持和财政拨款&#xff1b;国家级的资质认证硬招牌&#xff1b;提升企业品牌形象&#xff1b;促进企业科技转型&#xff1b;提高企业市场价值&#xff1b;提高企业资本价值&#xff1b;吸引市…

【电脑讲解】电脑如何实现双系统

核心提示&#xff1a;电脑双系统&#xff0c;大家应该不会太陌生&#xff0c;有的网吧就装的是双系统&#xff0c;双系统可以满足不同人群的需要&#xff0c;可以这样说&#xff0c;一个系统可以专门工作使用&#xff0c;另一个可以供玩游戏使用&#xff0c;&#xff08;电脑硬…

[Linux打怪升级之路]-环境变量

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。 目录 一、认识环…

一文详解JVM的内存结构

目录 前言 内存结构 程序计数器 虚拟机栈 本地方法栈 堆内存 方法区 内部组成 前言 Java的JVM解决的问题是跨操作系统问题。程序员只需要专注于代码的编写&#xff0c;这些代码能够在不同的操作系统Mac&#xff0c;Linux和Windows运行的前提是JVM。JVM还提供了垃圾回收机制…

Linux终端操作-Xshell和Xftp(家庭版)

目录一&#xff0c;终端操作二&#xff0c;软件安装1&#xff0c;Xshell, Xftp下载2&#xff0c;Xshell安装3&#xff0c;Xftp安装三&#xff0c;使用1&#xff0c;Xshell建立连接2&#xff0c;Xftp上传文件一&#xff0c;终端操作 上一篇博客记录了如何本地安装虚拟机并实现本…

SpringSecurity(十五)---OAuth2的运行机制(上)-OAuth2概念和授权码模式讲解

一、前言 鸽了很久&#xff0c;其实也因为自己确实比较忙&#xff0c;加之自己在造demo的时候也遇到了很多问题&#xff0c;并且网上这方面的解答非常之少&#xff0c;不过也正是因为少&#xff0c;才更加让我想写这样的知识分享&#xff0c;最终&#xff0c;在一篇博客的解答…

中文drupal教程(4)Session会话系统

Session&#xff08;会话&#xff09;在网站中扮演非常重要的角色&#xff0c;储存临时用户数据、登录数据等等都用到了它&#xff0c;Drupal使用到了Symfony的Session组件&#xff0c;该组件非常强大灵活&#xff0c;drupal在此基础上有所改造和扩展&#xff0c;要理解Symfony…

企业微信接口测试实战(一)

本文为在霍格沃兹测试开发学社中学习到的一些技术,写出来分享给大家,希望有志同道合的小伙伴可以一起交流技术,一起进步~ 霍格沃茨启发: 测试开发进阶班>接口自动化测试>企业微信接口测试实战 企业微信接口测试实战 一、准备环境二、脚本实现2.1、 获得access_token2…

防火墙用户管理理论+实验

目录 注&#xff1a;实验需要有安全策略配置、NAT配置基础 一、防火墙用户管理重要知识点 用户管理 访问控制策略 NGFW下一代防火墙 AAA 鉴别方式——认证 用户认证的分类&#xff1a; 上网用户上线流程&#xff1a; 二、用户认证实验&#xff1a; 实验拓扑 先配置防…

pmp考试是什么?

PMP是一个全球资格认证&#xff0c;也是目前项目管理领域大家公认的证书&#xff0c;相当于项目管理的入门证书。 一、PMP 是什么 pmp 中文叫项目管理专业人士资格认证&#xff0c;目前项目管理领域大家公认的证书&#xff0c;是一个用来评估项目管理人员的知识技能是否已经达…

D. Divide and Summarize(BFS+二分+预处理)

Problem - 1461D - Codeforces 迈克收到一个长度为n的数组作为生日礼物&#xff0c;决定测试一下它的漂亮程度。 如果有一种方法可以通过一定数量&#xff08;可能是零&#xff09;的切片操作得到一个元素总和为si的数组&#xff0c;那么这个数组将通过第i次漂亮度测试。 一个…

红红火火的VB,悄然离去,新型中文编程,如日中天

“悲哀&#xff01;现在用VB连1200都赚不到。”一位VB程序员有感而发。曾经红红火火的VB编程语言&#xff0c;如今却徘徊在被淘汰边缘&#xff0c;让人惋惜。 依稀记得&#xff0c;读大学时候&#xff0c;有一位财务专业同学&#xff0c;特别喜欢计算机&#xff0c;有空就自学V…

密码学引论 | DES

文章目录DES算法1 算法流程2 算法细节&#xff08;1&#xff09;子密钥的产生&#xff08;2&#xff09;初始置换IPIPIP&#xff08;3&#xff09;加密函数&#xff08;4&#xff09;逆初始置换IP−1IP^{-1}IP−1例题DES算法 1 算法流程 64位密钥经子密钥产生算法产生出16个子…

用 TypeScript 类型运算实现一个五子棋游戏

之前有看到有大佬用类型运算实现了中国象棋程序 和 Lisp 解释器 甚是震惊&#xff0c;之前不太会看不懂。 最近也学了点类型体操的内容想着自己也玩一下。选择五子棋的原因是相对来说规则是更简单一些的也比较好实现。此实现没有考虑性能上优化和最佳实现方式只关注功能的实现…