MXNET:卷积神经网络
介绍过去几年中数个在 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:卷积神经网络的更多相关文章
- mxnet卷积神经网络训练MNIST数据集测试
mxnet框架下超全手写字体识别—从数据预处理到网络的训练—模型及日志的保存 import numpy as np import mxnet as mx import logging logging. ...
- 经典卷积神经网络(LeNet、AlexNet、VGG、GoogleNet、ResNet)的实现(MXNet版本)
卷积神经网络(Convolutional Neural Network, CNN)是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出色表现. 其中 文章 详解卷 ...
- 使用MXNet远程编写卷积神经网络用于多标签分类
最近试试深度学习能做点什么事情.MXNet是一个与Tensorflow类似的开源深度学习框架,在GPU显存利用率上效率高,比起Tensorflow显著节约显存,并且天生支持分布式深度学习,单机多卡.多 ...
- 使用mxnet实现卷积神经网络LeNet
1.LeNet模型 LeNet是一个早期用来识别手写数字的卷积神经网络,这个名字来源于LeNet论文的第一作者Yann LeCun.LeNet展示了通过梯度下降训练卷积神经网络可以达到手写数字识别在当 ...
- MXNET:卷积神经网络基础
卷积神经网络(convolutional neural network).它是近年来深度学习能在计算机视觉中取得巨大成果的基石,它也逐渐在被其他诸如自然语言处理.推荐系统和语音识别等领域广泛使用. 目 ...
- [DL学习笔记]从人工神经网络到卷积神经网络_3_使用tensorflow搭建CNN来分类not_MNIST数据(有一些问题)
3:用tensorflow搭个神经网络出来 为什么用tensorflow呢,应为谷歌是亲爹啊,虽然有些人说caffe更适合图像啊mxnet效率更高等等,但爸爸就是爸爸,Android都能那么火,一个道 ...
- 卷积神经网络系列之softmax,softmax loss和cross entropy的讲解
我们知道卷积神经网络(CNN)在图像领域的应用已经非常广泛了,一般一个CNN网络主要包含卷积层,池化层(pooling),全连接层,损失层等.虽然现在已经开源了很多深度学习框架(比如MxNet,Caf ...
- XNOR-Net:二值化卷积神经网络
https://www.jianshu.com/p/f9b015cc4514 https://github.com/hpi-xnor/BMXNet BMXNet:基于MXNet的开源二值神经网络实现 ...
- TensorFlow 一步一步实现卷积神经网络
欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/,学习更多的机器学习.深度学习的知识! TensorFlow 从入门到精通系列教程: http://www ...
随机推荐
- 译:为什么使用 NoSQL 数据库
原文链接:Why NoSQL Database? 向数据时代的转变正在推动 NoSQL 随着各行各业朝着数据时代转变,商业世界正在经历巨大的变革.这是由互联网以及其他二十一世纪新技术--云计算.移动应 ...
- Python接口测试简单框架
用例设计: 执行用例代码: # -*- coding: UTF-8 -*-import xlrd,logging,urllib,urllib2,json,sysfrom pylsy import py ...
- Systick时钟定时
主函数 /* Note:Your choice is C IDE */ #include "stdio.h" #include "led.h" void mai ...
- Ubuntu安装软件时提示依赖项配置错误
在终端中使用dpkg安装软件时有时会出现依赖项配置错误的情况, 解决方法是使用指令 sudo apt-get install -f 安装Ubuntu 16.04新系统不再配有的缺失依赖项,之后再次输入 ...
- 使用Three.js为QQ用户生成3D头像阵列
东西其实比较简单,就是输出某一范围内QQ账号的3D头像 涉及的技术主要是Three.js的基本使用 而后通过腾讯的接口异步并发请求QQ用户头像,Canavs作图生成Texture应用在球体上 需要注意 ...
- 最大流:Dinic算法
蒟蒻居然今天第一次写网络流 我太弱啦! 最大流问题有很多解法 虽然isap常数巨小 但是连dinic都写挂的本蒟蒻并不会orz 那么我们选用比较好实现的dinic来解决最大流问题 来一段定义: ...
- stdio.h cstdio的区别
stdio.h cstdio string.h cstring math.h cmath .h是c语言的习惯,在c++中,替换为在前面加个c
- sencha touch 选择器
1 DOM元素选择器 Ext.DomQuery操作标准DOM元素 Ext.query(selector, [root]) : HTMLElement[] // 调用Ext.dom.Query.sele ...
- 通过System.CommandLine快速生成支持命令行的应用
一直以来,当我们想让我们的控制台程序支持命令行启动时,往往需要编写大量代码来实现这一看起来很简单的功能.虽然有一些库可以简化一些操作,但整个过程仍然是一个相当枯燥而乏味的过程.我之前也写过一些文章简单 ...
- Hexo 博客 github.io MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...