介绍过去几年中数个在 ImageNet 竞赛(一个著名的计算机视觉竞赛)取得优异成绩的深度卷积神经网络。

LeNet

LeNet 证明了通过梯度下降训练卷积神经网络可以达到手写数字识别的最先进的结果。这个奠基性的工作第一次将卷积神经网络推上舞台,为世人所知。

net = nn.Sequential()
net.add(
nn.Conv2D(channels=6, kernel_size=5, activation='sigmoid'),
nn.MaxPool2D(pool_size=2, strides=2),
nn.Conv2D(channels=16, kernel_size=5, activation='sigmoid'),
nn.MaxPool2D(pool_size=2, strides=2),
# Dense 会默认将(批量大小,通道,高,宽)形状的输入转换成
#(批量大小,通道 x 高 x 宽)形状的输入。
nn.Dense(120, activation='sigmoid'),
nn.Dense(84, activation='sigmoid'),
nn.Dense(10)
) X = nd.random.uniform(shape=(1, 1, 28, 28))
net.initialize()
for layer in net:
X = layer(X)
print(layer.name, 'output shape:\t', X.shape)
# output
('conv0', 'output shape:\t', (1L, 6L, 24L, 24L))
('pool0', 'output shape:\t', (1L, 6L, 12L, 12L))
('conv1', 'output shape:\t', (1L, 16L, 8L, 8L))
('pool1', 'output shape:\t', (1L, 16L, 4L, 4L))
('dense0', 'output shape:\t', (1L, 120L))
('dense1', 'output shape:\t', (1L, 84L))
('dense2', 'output shape:\t', (1L, 10L))

训练:

batch_size = 256
train_iter, test_iter = gb.load_data_fashion_mnist(batch_size=batch_size) lr = 0.8
num_epochs = 5
net.initialize(force_reinit=True, ctx=mx.cpu(), init=init.Xavier())
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': lr})
loss = gloss.SoftmaxCrossEntropyLoss()
# net, train_iter, test_iter, loss, num_epochs, batch_size, params=None, lr=None, trainer=None
gb.train_cpu(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, trainer)
# output
epoch 1, loss 2.3203, train acc 0.103, test acc 0.102
epoch 2, loss 2.3032, train acc 0.108, test acc 0.102
epoch 3, loss 2.2975, train acc 0.121, test acc 0.158
...
epoch 8, loss 0.1189, train acc 0.964, test acc 0.972
epoch 9, loss 0.1023, train acc 0.969, test acc 0.975
epoch 10, loss 0.0906, train acc 0.972, test acc 0.976

AlexNet

2012 年 AlexNet [1],名字来源于论文一作姓名 Alex Krizhevsky,横空出世,它使用 8 层卷积神经网络以很大的优势赢得了 ImageNet 2012 图像识别挑战赛。它首次证明了学习到的特征可以超越手工设计的特征,从而一举打破计算机视觉研究的前状。

AlextNet 与 LeNet 的设计理念非常相似。但也有非常显著的区别。

  • 与相对较小的 LeNet 相比,AlexNet 包含 8 层变换,其中有五层卷积和两层全连接隐含层,以及一个输出层。
  • 将 sigmoid 激活函数改成了更加简单的 relu 函数 f(x)=max(x,0)。它计算上更简单,同时在不同的参数初始化方法下收敛更加稳定。
  • 通过丢弃法来控制全连接层的模型复杂度。
  • 引入了大量的图片增广,例如翻转、裁剪和颜色变化,进一步扩大数据集来减小过拟合。

AlexNet 跟 LeNet 结构类似,但使用了更多的卷积层和更大的参数空间来拟合大规模数据集 ImageNet。它是浅层神经网络和深度神经网络的分界线。虽然看上去 AlexNet 的实现比 LeNet 也就就多了几行而已。但这个观念上的转变和真正优秀实验结果的产生,学术界整整花了 20 年

VGG

虽然 AlexNet 指明了深度卷积神经网络可以取得很高的结果,但并没有提供简单的规则来告诉后来的研究者如何设计新的网络。

VGG它名字来源于论文作者所在实验室 Visual Geometry Group。VGG 提出了可以通过重复使用简单的基础块来构建深层模型。

我们使用 vgg_block 函数来实现这个基础块,它可以指定使用卷积层的数量和其输出通道数。

def vgg_block(num_convs, num_channels):
blk = nn.Sequential()
for _ in range(num_convs):
blk.add(nn.Conv2D(
num_channels, kernel_size=3, padding=1, activation='relu'))
blk.add(nn.MaxPool2D(pool_size=2, strides=2))
return blk

我们根据架构实现 VGG 11: 8 个卷积层和 3 个全连接层。

def vgg(conv_arch):
net = nn.Sequential()
# 卷积层部分。
for (num_convs, num_channels) in conv_arch:
net.add(vgg_block(num_convs, num_channels))
# 全连接层部分。
net.add(nn.Dense(4096, activation='relu'), nn.Dropout(0.5),
nn.Dense(4096, activation='relu'), nn.Dropout(0.5),
nn.Dense(10))
return net conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))
net = vgg(conv_arch)

NiN

它提出了另外一个思路,即串联多个由卷积层和“全连接”层(1x1卷积层)构成的小网络来构建一个深层网络。

NiN 中的一个基础块由一个卷积层外加两个充当全连接层的 1×1 卷积层构成。

def nin_block(num_channels, kernel_size, strides, padding):
blk = nn.Sequential()
blk.add(nn.Conv2D(num_channels, kernel_size,
strides, padding, activation='relu'),
nn.Conv2D(num_channels, kernel_size=1, activation='relu'),
nn.Conv2D(num_channels, kernel_size=1, activation='relu'))
return blk

除了使用 NiN 块外,NiN 还有一个重要的跟 AlexNet 不同的地方:NiN 去掉了最后的三个全连接层,取而代之的是使用输出通道数等于标签类数的卷积层,然后使用一个窗口为输入高宽的平均池化层来将每个通道里的数值平均成一个标量直接用于分类。这个设计好处是可以显著的减小模型参数大小,从而能很好的避免过拟合,但它也可能会造成训练时收敛变慢。

net = nn.Sequential()
net.add(
nin_block(96, kernel_size=11, strides=4, padding=0),
nn.MaxPool2D(pool_size=3, strides=2),
nin_block(256, kernel_size=5, strides=1, padding=2),
nn.MaxPool2D(pool_size=3, strides=2),
nin_block(384, kernel_size=3, strides=1, padding=1),
nn.MaxPool2D(pool_size=3, strides=2), nn.Dropout(0.5),
# 标签类数是 10。
nin_block(10, kernel_size=3, strides=1, padding=1),
# 全局平均池化层将窗口形状自动设置成输出的高和宽。
nn.GlobalAvgPool2D(),
# 将四维的输出转成二维的输出,其形状为(批量大小,10)。
nn.Flatten())
X = nd.random.uniform(shape=(1, 1, 224, 224))
net.initialize()
for layer in net:
X = layer(X)
print(layer.name, 'output shape:\t', X.shape)
# output
sequential1 output shape: (1, 96, 54, 54)
pool0 output shape: (1, 96, 26, 26)
sequential2 output shape: (1, 256, 26, 26)
pool1 output shape: (1, 256, 12, 12)
sequential3 output shape: (1, 384, 12, 12)
pool2 output shape: (1, 384, 5, 5)
dropout0 output shape: (1, 384, 5, 5)
sequential4 output shape: (1, 10, 5, 5)
pool3 output shape: (1, 10, 1, 1)
flatten0 output shape: (1, 10)

虽然因为精度和收敛速度等问题 NiN 并没有像本章中介绍的其他网络那么被广泛使用,但 NiN 的设计思想影响了后面的一系列网络的设计。

GoogleLeNet

在 2014 年的 Imagenet 竞赛中,一个名叫 GoogLeNet [1] 的网络结构大放光彩。它虽然在名字上是向 LeNet 致敬,但在网络结构上已经很难看到 LeNet 的影子。

GoogLeNet 吸收了 NiN 的网络嵌套网络的想法,并在此基础上做了很大的改进。

在随后的几年里研究人员对它进行了数次改进,本小节将介绍这个模型系列的第一个版本。

GoogLeNet 中的基础卷积块叫做 Inception,得名于同名电影《盗梦空间》(Inception),寓意梦中嵌套梦。

class Inception(nn.Block):
# c1 - c4 为每条线路里的层的输出通道数。
def __init__(self, c1, c2, c3, c4, **kwargs):
super(Inception, self).__init__(**kwargs)
# 线路 1,单 1 x 1 卷积层。
self.p1_1 = nn.Conv2D(c1, kernel_size=1, activation='relu')
# 线路 2,1 x 1 卷积层后接 3 x 3 卷积层。
self.p2_1 = nn.Conv2D(c2[0], kernel_size=1, activation='relu')
self.p2_2 = nn.Conv2D(c2[1], kernel_size=3, padding=1,
activation='relu')
# 线路 3,1 x 1 卷积层后接 5 x 5 卷积层。
self.p3_1 = nn.Conv2D(c3[0], kernel_size=1, activation='relu')
self.p3_2 = nn.Conv2D(c3[1], kernel_size=5, padding=2,
activation='relu')
# 线路 4,3 x 3 最大池化层后接 1 x 1 卷积层。
self.p4_1 = nn.MaxPool2D(pool_size=3, strides=1, padding=1)
self.p4_2 = nn.Conv2D(c4, kernel_size=1, activation='relu') def forward(self, x):
p1 = self.p1_1(x)
p2 = self.p2_2(self.p2_1(x))
p3 = self.p3_2(self.p3_1(x))
p4 = self.p4_2(self.p4_1(x))
# 在通道维上合并输出。
return nd.concat(p1, p2, p3, p4, dim=1)

第一个模块

b1 = nn.Sequential()
b1.add(
nn.Conv2D(64, kernel_size=7, strides=2, padding=3, activation='relu'),
nn.MaxPool2D(pool_size=3, strides=2, padding=1)
)

第二个模块

b2 = nn.Sequential()
b2.add(
nn.Conv2D(64, kernel_size=1),
nn.Conv2D(192, kernel_size=3, padding=1),
nn.MaxPool2D(pool_size=3, strides=2, padding=1)
)

第三个模块

b3 = nn.Sequential()
b3.add(
Inception(64, (96, 128), (16, 32), 32),
Inception(128, (128, 192), (32, 96), 64),
nn.MaxPool2D(pool_size=3, strides=2, padding=1)
)

第四个模块

b4 = nn.Sequential()
b4.add(
Inception(192, (96, 208), (16, 48), 64),
Inception(160, (112, 224), (24, 64), 64),
Inception(128, (128, 256), (24, 64), 64),
Inception(112, (144, 288), (32, 64), 64),
Inception(256, (160, 320), (32, 128), 128),
nn.MaxPool2D(pool_size=3, strides=2, padding=1)
)

第五个模块

b5 = nn.Sequential()
b5.add(
Inception(256, (160, 320), (32, 128), 128),
Inception(384, (192, 384), (48, 128), 128),
nn.GlobalAvgPool2D()
) net = nn.Sequential()
net.add(b1, b2, b3, b4, b5, nn.Dense(10))

演示各个模块之间的输出形状变化

X = nd.random.uniform(shape=(1, 1, 96, 96))
net.initialize()
for layer in net:
X = layer(X)
print(layer.name, 'output shape:\t', X.shape)
# output
sequential0 output shape: (1, 64, 24, 24)
sequential1 output shape: (1, 192, 12, 12)
sequential2 output shape: (1, 480, 6, 6)
sequential3 output shape: (1, 832, 3, 3)
sequential4 output shape: (1, 1024, 1, 1)
dense0 output shape: (1, 10)

Inception 块相当于一个有四条线路的子网络,它通过不同窗口大小的卷积层和最大池化层来并行抽取信息,并使用 1×1 卷积层减低通道数来减少模型复杂度。

GoogLeNet 将多个精细设计的 Inception 块和其他层串联起来。其通道分配比例是在 ImageNet 数据集上通过大量的实验得来。

ResNet

class Residual(nn.Block):
def __init__(self, num_channels, use_1x1conv=False, strides=1, **kwargs):
super(Residual, self).__init__(**kwargs)
self.conv1 = nn.Conv2D(num_channels, kernel_size=3, padding=1,
strides=strides)
self.conv2 = nn.Conv2D(num_channels, kernel_size=3, padding=1)
if use_1x1conv:
self.conv3 = nn.Conv2D(num_channels, kernel_size=1,
strides=strides)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm()
self.bn2 = nn.BatchNorm() def forward(self, X):
Y = nd.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(X)
return nd.relu(Y + X)

查看输出形状

X = nd.random.uniform(shape=(4, 3, 6, 6))
blk = Residual(3)
blk.initialize()
blk(X).shape
# (4,3,6,6)
blk = Residual(6, use_1x1conv=True, strides=2)
blk.initialize()
blk(X).shape
#(4,6,3,3)

ResNet 则是使用四个由残差块组成的模块,每个模块使用若干个同样输出通道的残差块。第一个模块的通道数同输入一致,同时因为之前已经使用了步幅为 2 的最大池化层,所以也不减小高宽。之后的每个模块在第一个残差块里将上一个模块的通道数翻倍,并减半高宽。

def resnet_block(num_channels, num_residuals, first_block=False):
blk = nn.Sequential()
for i in range(num_residuals):
if i == 0 and not first_block:
blk.add(Residual(num_channels, use_1x1conv=True, strides=2))
else:
blk.add(Residual(num_channels))
return blk

ResNet18的网络结构:

net = nn.Sequential()
net.add(nn.Conv2D(64, kernel_size=7, strides=2, padding=3),
nn.BatchNorm(), nn.Activation('relu'),
nn.MaxPool2D(pool_size=3, strides=2, padding=1))
net.add(resnet_block(64, 2, first_block=True),
resnet_block(128, 2),
resnet_block(256, 2),
resnet_block(512, 2))
net.add(nn.GlobalAvgPool2D(), nn.Dense(10))
X = nd.random.uniform(shape=(1, 1, 224, 224))
net.initialize()
for layer in net:
X = layer(X)
print(layer.name, 'output shape:\t', X.shape)
# output
conv5 output shape: (1, 64, 112, 112)
batchnorm4 output shape: (1, 64, 112, 112)
relu0 output shape: (1, 64, 112, 112)
pool0 output shape: (1, 64, 56, 56)
sequential1 output shape: (1, 64, 56, 56)
sequential2 output shape: (1, 128, 28, 28)
sequential3 output shape: (1, 256, 14, 14)
sequential4 output shape: (1, 512, 7, 7)
pool1 output shape: (1, 512, 1, 1)
dense0 output shape: (1, 10)

DenseNet

DenseNet 的主要构建模块是稠密块和过渡块,前者定义了输入和输出是如何合并的,后者则用来控制通道数不要过大。

定义Resnet时已经“改良“的卷积块

def conv_block(num_channels):
blk = nn.Sequential()
blk.add(nn.BatchNorm(), nn.Activation('relu'),
nn.Conv2D(num_channels, kernel_size=3, padding=1))
return blk

稠密块由多个 conv_block 组成,每块使用相同的输出通道数。但在正向传播时,我们将每块的输出在通道维上同其输出合并进入下一个块。

class DenseBlock(nn.Block):
def __init__(self, num_convs, num_channels, **kwargs):
super(DenseBlock, self).__init__(**kwargs)
self.net = nn.Sequential()
for _ in range(num_convs):
self.net.add(conv_block(num_channels)) def forward(self, X):
for blk in self.net:
Y = blk(X)
# 在通道维上将输入和输出合并。
X = nd.concat(X, Y, dim=1)
return Y

我们定义一个有两个输出通道数为 10 的卷积块,使用通道数为 3 的输入时,我们会得到通道数为 3+2×10=23 的输出。

卷积块的通道数控制了输出通道数相对于输入通道数的增长,因此也被称为增长率(growth rate)

blk = DenseBlock(2, 10)
blk.initialize()
X = nd.random.uniform(shape=(4,3,8,8))
Y = blk(X)
Y.shape

过渡块(transition block)则用来控制模型复杂度。它通过 1×1 卷积层来减小通道数,同时使用步幅为 2 的平均池化层来将高宽减半来进一步降低复杂度。

def transition_block(num_channels):
blk = nn.Sequential()
blk.add(nn.BatchNorm(), nn.Activation('relu'),
nn.Conv2D(num_channels, kernel_size=1),
nn.AvgPool2D(pool_size=2, strides=2))
return blk

DenseNet 21模型

net = nn.Sequential()
net.add(nn.Conv2D(64, kernel_size=7, strides=2, padding=3),
nn.BatchNorm(), nn.Activation('relu'),
nn.MaxPool2D(pool_size=3, strides=2, padding=1))
num_channels = 64
growth_rate = 32
num_convs_in_dense_blocks = [4, 4, 4, 4] for i, num_convs in enumerate(num_convs_in_dense_blocks):
net.add(DenseBlock(num_convs, growth_rate))
# 上一个稠密的输出通道数。
num_channels += num_convs * growth_rate
# 在稠密块之间加入通道数减半的过渡块。
if i != len(num_convs_in_dense_blocks) - 1:
net.add(transition_block(num_channels // 2))
net.add(nn.BatchNorm(), nn.Activation('relu'), nn.GlobalAvgPool2D(),
nn.Dense(10))

MXNET:卷积神经网络的更多相关文章

  1. mxnet卷积神经网络训练MNIST数据集测试

    mxnet框架下超全手写字体识别—从数据预处理到网络的训练—模型及日志的保存 import numpy as np import mxnet as mx import logging logging. ...

  2. 经典卷积神经网络(LeNet、AlexNet、VGG、GoogleNet、ResNet)的实现(MXNet版本)

    卷积神经网络(Convolutional Neural Network, CNN)是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出色表现. 其中 文章 详解卷 ...

  3. 使用MXNet远程编写卷积神经网络用于多标签分类

    最近试试深度学习能做点什么事情.MXNet是一个与Tensorflow类似的开源深度学习框架,在GPU显存利用率上效率高,比起Tensorflow显著节约显存,并且天生支持分布式深度学习,单机多卡.多 ...

  4. 使用mxnet实现卷积神经网络LeNet

    1.LeNet模型 LeNet是一个早期用来识别手写数字的卷积神经网络,这个名字来源于LeNet论文的第一作者Yann LeCun.LeNet展示了通过梯度下降训练卷积神经网络可以达到手写数字识别在当 ...

  5. MXNET:卷积神经网络基础

    卷积神经网络(convolutional neural network).它是近年来深度学习能在计算机视觉中取得巨大成果的基石,它也逐渐在被其他诸如自然语言处理.推荐系统和语音识别等领域广泛使用. 目 ...

  6. [DL学习笔记]从人工神经网络到卷积神经网络_3_使用tensorflow搭建CNN来分类not_MNIST数据(有一些问题)

    3:用tensorflow搭个神经网络出来 为什么用tensorflow呢,应为谷歌是亲爹啊,虽然有些人说caffe更适合图像啊mxnet效率更高等等,但爸爸就是爸爸,Android都能那么火,一个道 ...

  7. 卷积神经网络系列之softmax,softmax loss和cross entropy的讲解

    我们知道卷积神经网络(CNN)在图像领域的应用已经非常广泛了,一般一个CNN网络主要包含卷积层,池化层(pooling),全连接层,损失层等.虽然现在已经开源了很多深度学习框架(比如MxNet,Caf ...

  8. XNOR-Net:二值化卷积神经网络

    https://www.jianshu.com/p/f9b015cc4514 https://github.com/hpi-xnor/BMXNet  BMXNet:基于MXNet的开源二值神经网络实现 ...

  9. TensorFlow 一步一步实现卷积神经网络

    欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/,学习更多的机器学习.深度学习的知识! TensorFlow 从入门到精通系列教程: http://www ...

随机推荐

  1. python基础一 ------如何获取多个字典相同的键

    需求: 足球赛第一场进去统计  {"A":3,"B":2,"C":1}足球赛第二场进去统计  {"A":3," ...

  2. Hibernate的核心接口

    Hibernate5个核心接口 所有Hibernate应用中都会访问Hibernate的5个核心接口 Configuration接口:配置Hibernate,根启动Hibernate,创建Sessio ...

  3. [Vijos1130][NOIP2001]数的计数 (递推)

    自己的递推一塌糊涂 考前抱佛脚 #include<bits/stdc++.h> using namespace std; ]; int main() { int n;scanf(" ...

  4. python网络编程(九)

    单进程服务器-非堵塞模式 服务器 #coding=utf-8 from socket import * import time # 用来存储所有的新链接的socket g_socketList = [ ...

  5. 最大流:Dinic算法

    蒟蒻居然今天第一次写网络流 我太弱啦! 最大流问题有很多解法 虽然isap常数巨小 但是连dinic都写挂的本蒟蒻并不会orz 那么我们选用比较好实现的dinic来解决最大流问题 来一段定义:    ...

  6. redis配置(redis.conf)

    1.如果我们刚刚装好 redis 发现Redis Desktop Manager无法连接到redis,       那是因为redis默认配置只让本机访问,我们 vim redis.conf 注释以下 ...

  7. css3 学习 重点 常用

    1 -webkit-    -moz-   -o-浏览器兼容 2 box-sizing:border-box;   两个近乎一样的div一样的样式 平分一个div  定义:属性允许您以确切的方式定义适 ...

  8. 超详细 Spring @RequestMapping 注解使用技巧

    @RequestMapping 是 Spring Web 应用程序中最常被用到的注解之一.这个注解会将 HTTP 请求映射到 MVC 和 REST 控制器的处理方法上. 在这篇文章中,你将会看到 @R ...

  9. JQ01

    JQ01 1.使用js的缺点 innerText的兼容性问题:低版本火狐浏览器不支持 textContent:火狐支持,ie678不支持 2.JQ初体验 1) <!DOCTYPE html> ...

  10. Shader、Draw Call和渲染管线(Rendering Pipeline)

    翻阅了很多资料,也做了不少笔记,决定还是对渲染进行一个总结,以巩固所学的东西. <Real-Time Rendering, Third Edition>   (PDF的配图链接)将一个渲染 ...