百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

回顾五个强大的CNN架构介绍及Python示例

toyiye 2024-06-06 22:12 13 浏览 0 评论

让我们来回顾一些强大的卷积神经网络,它们为今天的计算机视觉成就奠定了基础,这些成就是通过深度学习获得的。

LeNet-5

LeNet-5是一个7层卷积神经网络,部署在许多银行系统中,用于识别支票上的手写数字。

LeNet-5 - 架构

手写数字被数字化为像素大小的灰度图像--32×32。那时,计算能力有限,因此该技术无法扩展到大规模图像。

该模型包含7层(不包括输入层)。由于它是一个相对较小的架构,让我们逐层解释:

  1. 第1层:卷积层,核大小为5×5,步长为1×1,总共为6个核。因此,大小为32x32x1的输入图像的输出为28x28x6。层中的总参数= 5 * 5 * 6 + 6(偏差项)
  2. 第2层:具有2×2核大小的池化层,总共2×2和6个核。这个池化层的行为与之前的文章略有不同。将接收器中的输入值相加,然后乘以可训练参数(每个filter 1个),最后将结果加到可训练的偏差(每个filter 1个)。最后,将sigmoid激活应用于输出。因此,来自前一层大小为28x28x6的输入被子采样为14x14x6。层中的总参数= [1(可训练参数)+ 1(可训练偏差)] * 6 = 12
  3. 第3层:与第1层类似,此层是具有相同配置的卷积层,除了它有16个filters而不是6个。因此,前一个大小为14x14x6的输入提供10x10x16的输出。层中的总参数= 5 * 5 * 16 + 16 = 416。
  4. 第4层:与第2层类似,此层是一个池化层,这次有16个filters。请记住,输出通过sigmoid激活函数传递。来自前一层的大小为10x10x16的输入被子采样为5x5x16。层中的总参数=(1 + 1)* 16 = 32
  5. 第5层:卷积层,核大小为5×5,filters为120。由于输入大小为5x5x16,因此我们无需考虑步幅,因此输出为1x1x120。层中的总参数= 5 * 5 * 120 = 3000
  6. 第6层:这是一个包含84个参数的dense层。因此,120个units的输入转换为84个units。总参数= 84 * 120 + 84 = 10164.此处使用的激活函数相当独特。我要说的是,你可以在这里尝试你的任何选择,因为按照今天的标准,这个任务非常简单。
  7. 输出层:最后,使用具有10个units的dense层。总参数= 84 * 10 + 10 = 924。

跳过所使用的损失函数的细节及其使用原因,我建议在最后一层使用softmax激活的交叉熵损失。尝试不同的训练计划和学习率。

LeNet-5 - Python代码

from keras import layers
from keras.models import Model
def lenet_5(in_shape=(32,32,1), n_classes=10, opt='sgd'):
 in_layer = layers.Input(in_shape)
 conv1 = layers.Conv2D(filters=20, kernel_size=5,
 padding='same', activation='relu')(in_layer)
 pool1 = layers.MaxPool2D()(conv1)
 conv2 = layers.Conv2D(filters=50, kernel_size=5,
 padding='same', activation='relu')(pool1)
 pool2 = layers.MaxPool2D()(conv2)
 flatten = layers.Flatten()(pool2)
 dense1 = layers.Dense(500, activation='relu')(flatten)
 preds = layers.Dense(n_classes, activation='softmax')(dense1)
 model = Model(in_layer, preds)
 model.compile(loss="categorical_crossentropy", optimizer=opt,
	 metrics=["accuracy"])
 return model
if __name__ == '__main__':
 model = lenet_5()
 print(model.summary())

AlexNet

2012年,Hinton的深度神经网络将世界上最重要的计算机视觉挑战图像网络中的损失从26%减少到15.3%。

该网络与LeNet非常相似,但更深,拥有大约6000万个参数。

深度卷积神经网络的ImageNet分类

AlexNet - 架构

当然这个数字看起来吓人。这是因为网络被分成两个半部分,每个部分在两个不同的gpu上同时被训练。让我们做一下简单的为我们带来一个更简单的版本的图片:

该架构由5个卷积层和3个全连接层组成。这8个层与当时的两个新概念相结合--MaxPooling和ReLU激活为他们的模型提供了优势。

您可以在上图中看到各种层及其配置。这些层如下表所示:

注意:ReLU激活应用于除最后一个softmax图层之外的每个卷积和全连接层的输出。

作者使用了各种其他技术 - dropout,augmentation 和Stochastic Gradient Descent with momentum。

AlexNet - Python代码

from keras import layers
from keras.models import Model
def alexnet(in_shape=(227,227,3), n_classes=1000, opt='sgd'):
 in_layer = layers.Input(in_shape)
 conv1 = layers.Conv2D(96, 11, strides=4, activation='relu')(in_layer)
 pool1 = layers.MaxPool2D(3, 2)(conv1)
 conv2 = layers.Conv2D(256, 5, strides=1, padding='same', activation='relu')(pool1)
 pool2 = layers.MaxPool2D(3, 2)(conv2)
 conv3 = layers.Conv2D(384, 3, strides=1, padding='same', activation='relu')(pool2)
 conv4 = layers.Conv2D(256, 3, strides=1, padding='same', activation='relu')(conv3)
 pool3 = layers.MaxPool2D(3, 2)(conv4)
 flattened = layers.Flatten()(pool3)
 dense1 = layers.Dense(4096, activation='relu')(flattened)
 drop1 = layers.Dropout(0.5)(dense1)
 dense2 = layers.Dense(4096, activation='relu')(drop1)
 drop2 = layers.Dropout(0.5)(dense2)
 preds = layers.Dense(n_classes, activation='softmax')(drop2)
 model = Model(in_layer, preds)
 model.compile(loss="categorical_crossentropy", optimizer=opt,
	 metrics=["accuracy"])
 return model
if __name__ == '__main__':
 model = alexnet()
 print(model.summary())

VGGNet

2014年imagenet挑战的亚军被命名为VGGNet。由于其简单的统一结构,它以更简单的形式提出了一种更简单的深度卷积神经网络的形式。

VGGNet - 架构

VGGNet有两条简单的经验法则:

  • 每个卷积层都有配置 - 核大小= 3×3,stride = 1×1,padding = same。唯一不同的是filters的数量。
  • 每个Max Pooling层都有配置 - windows size= 2×2和stride = 2×2。因此,我们在每个Pooling层的图像大小减半。

输入图像是224×224像素的RGB图像。所以输入大小= 224x224x3

总参数= 1.38亿。大多数这些参数由全连接层贡献。

  • 第一个FC层= 4096 *(7 * 7 * 512)+ 4096 = 102,764,544
  • 第二个FC层= 4096 * 4096 + 4096 = 16,781,312
  • 第三个FC层= 4096 * 1000 + 4096 = 4,100,096

FC层贡献的总参数= 123,645,952。

VGGNet - 代码

from keras import layers
from keras.models import Model, Sequential
from functools import partial
conv3 = partial(layers.Conv2D,
 kernel_size=3,
 strides=1,
 padding='same',
 activation='relu')
def block(in_tensor, filters, n_convs):
 conv_block = in_tensor
 for _ in range(n_convs):
 conv_block = conv3(filters=filters)(conv_block)
 return conv_block
def _vgg(in_shape=(227,227,3),
 n_classes=1000,
 opt='sgd',
 n_stages_per_blocks=[2, 2, 3, 3, 3]):
 in_layer = layers.Input(in_shape)
 block1 = block(in_layer, 64, n_stages_per_blocks[0])
 pool1 = layers.MaxPool2D()(block1)
 block2 = block(pool1, 128, n_stages_per_blocks[1])
 pool2 = layers.MaxPool2D()(block2)
 block3 = block(pool2, 256, n_stages_per_blocks[2])
 pool3 = layers.MaxPool2D()(block3)
 block4 = block(pool3, 512, n_stages_per_blocks[3])
 pool4 = layers.MaxPool2D()(block4)
 block5 = block(pool4, 512, n_stages_per_blocks[4])
 pool5 = layers.MaxPool2D()(block5)
 flattened = layers.GlobalAvgPool2D()(pool5)
 dense1 = layers.Dense(4096, activation='relu')(flattened)
 dense2 = layers.Dense(4096, activation='relu')(dense1)
 preds = layers.Dense(1000, activation='softmax')(dense2)
 model = Model(in_layer, preds)
 model.compile(loss="categorical_crossentropy", optimizer=opt,
	 metrics=["accuracy"])
 return model
def vgg16(in_shape=(227,227,3), n_classes=1000, opt='sgd'):
 return _vgg(in_shape, n_classes, opt)
def vgg19(in_shape=(227,227,3), n_classes=1000, opt='sgd'):
 return _vgg(in_shape, n_classes, opt, [2, 2, 4, 4, 4])
if __name__ == '__main__':
 model = vgg19()
 print(model.summary())

GoogLeNet / Inception

2014年imagenet竞赛的获胜者 - GoogLeNet(又名Inception v1)。它使用了一个inception 模块,一个新颖的概念,具有较小的卷积,允许将参数数量减少到仅400万。

Inception module

使用这些Inception module的原因:

  1. 每个层类型从输入中提取不同的信息。从3×3层收集的信息将与从5×5层收集的信息不同。我们怎么知道哪一种transformation 是最好的呢?所以我们全部使用它们!
  2. 使用1×1卷积减少尺寸!考虑一个128x128x256输入。如果我们通过20个大小为1×1的过滤器,我们将获得128x128x20的输出。因此,我们在3×3或5×5卷积之前应用它们,以减少用于降维的inception block中这些层的输入filters的数量。

GoogLeNet / Inception - 架构

完整的初始架构:

您可能会在此结构中看到一些带有softmax的“辅助分类器”。在这里引用论文 - “通过增加连接到这些中间层的辅助分类器,我们期望鼓励分类器的较低阶段的区分,增加传播后的梯度信号,并提供附加的正则化。”

但是这是什么意思?基本上他们的意思是:

  1. 在较低阶段的discrimination :我们将在网络中训练较低层,其中梯度来自较早阶段的层以用于输出概率。这可以确保网络在较早的时候对不同的对象有一些区别。
  2. 增加传播回来的梯度信号:在深度神经网络中,通常,回流的梯度(使用反向传播)变得非常小,以至于网络的早期层很难学习。因此,较早的分类层通过传播强梯度信号来训练网络而变得有用。
  3. 提供额外的正规化:深度神经网络往往overfit(或导致高方差)数据,同时小神经网络往往underfit(或导致高偏差)。较早的分类器规范了更深层的过度拟合效果!

辅助分类器的结构:

注意:

#1×1表示inception module中1×1卷积的filters 。

#3×3 reduce表示在inception module中3×3卷积之前的1×1卷积中的filters 。

#5×5 reduce表示在inception module中5×5卷积之前的1×1卷积中的filters 。

#3×3表示inception module中3×3卷积的filters 。

#5×5表示inception module中5×5卷积的filters 。

Pool Proj表示在inception module中Max Pool之前的1×1卷积中的filters 。

GoogLeNet构成了Inception体系结构

它使用了批量归一化,图像失真和RMSprop。

GoogLeNet / Inception - 代码

from keras import layers
from keras.models import Model
from functools import partial
conv1x1 = partial(layers.Conv2D, kernel_size=1, activation='relu')
conv3x3 = partial(layers.Conv2D, kernel_size=3, padding='same', activation='relu')
conv5x5 = partial(layers.Conv2D, kernel_size=5, padding='same', activation='relu')
def inception_module(in_tensor, c1, c3_1, c3, c5_1, c5, pp):
 conv1 = conv1x1(c1)(in_tensor)
 conv3_1 = conv1x1(c3_1)(in_tensor)
 conv3 = conv3x3(c3)(conv3_1)
 conv5_1 = conv1x1(c5_1)(in_tensor)
 conv5 = conv5x5(c5)(conv5_1)
 pool_conv = conv1x1(pp)(in_tensor)
 pool = layers.MaxPool2D(3, strides=1, padding='same')(pool_conv)
 merged = layers.Concatenate(axis=-1)([conv1, conv3, conv5, pool])
 return merged
def aux_clf(in_tensor):
 avg_pool = layers.AvgPool2D(5, 3)(in_tensor)
 conv = conv1x1(128)(avg_pool)
 flattened = layers.Flatten()(conv)
 dense = layers.Dense(1024, activation='relu')(flattened)
 dropout = layers.Dropout(0.7)(dense)
 out = layers.Dense(1000, activation='softmax')(dropout)
 return out
def inception_net(in_shape=(224,224,3), n_classes=1000, opt='sgd'):
 in_layer = layers.Input(in_shape)
 conv1 = layers.Conv2D(64, 7, strides=2, activation='relu', padding='same')(in_layer)
 pad1 = layers.ZeroPadding2D()(conv1)
 pool1 = layers.MaxPool2D(3, 2)(pad1)
 conv2_1 = conv1x1(64)(pool1)
 conv2_2 = conv3x3(192)(conv2_1)
 pad2 = layers.ZeroPadding2D()(conv2_2)
 pool2 = layers.MaxPool2D(3, 2)(pad2)
 inception3a = inception_module(pool2, 64, 96, 128, 16, 32, 32)
 inception3b = inception_module(inception3a, 128, 128, 192, 32, 96, 64)
 pad3 = layers.ZeroPadding2D()(inception3b)
 pool3 = layers.MaxPool2D(3, 2)(pad3)
 inception4a = inception_module(pool3, 192, 96, 208, 16, 48, 64)
 inception4b = inception_module(inception4a, 160, 112, 224, 24, 64, 64)
 inception4c = inception_module(inception4b, 128, 128, 256, 24, 64, 64)
 inception4d = inception_module(inception4c, 112, 144, 288, 32, 48, 64)
 inception4e = inception_module(inception4d, 256, 160, 320, 32, 128, 128)
 pad4 = layers.ZeroPadding2D()(inception4e)
 pool4 = layers.MaxPool2D(3, 2)(pad4)
 aux_clf1 = aux_clf(inception4a)
 aux_clf2 = aux_clf(inception4d)
 inception5a = inception_module(pool4, 256, 160, 320, 32, 128, 128)
 inception5b = inception_module(inception5a, 384, 192, 384, 48, 128, 128)
 pad5 = layers.ZeroPadding2D()(inception5b)
 pool5 = layers.MaxPool2D(3, 2)(pad5)
 avg_pool = layers.GlobalAvgPool2D()(pool5)
 dropout = layers.Dropout(0.4)(avg_pool)
 preds = layers.Dense(1000, activation='softmax')(dropout)
 model = Model(in_layer, [preds, aux_clf1, aux_clf2])
 model.compile(loss="categorical_crossentropy", optimizer=opt,
	 metrics=["accuracy"])
 return model
if __name__ == '__main__':
 model = inception_net()
 print(model.summary())

ResNet

2015年的imagenet竞赛带来了Top-5的错误率3.57%。这是由于使用了ResNet(残差网络)模型。该网络引入了一种称为“skip connections”的新方法。

Residual learning: a building block

该想法作为一种解决办法来解决一个深度神经网络的问题,因为我们一直在添加层。但直觉来说,不应该这样。如果具有k层的网络执行为y,则具有k + 1层的网络应该至少执行y。

观察一个假设:直接映射很难学习。因此,不是学习层的输出和它的输入之间的映射,而是学习它们之间的差异 - 学习残差。

比方说,x是输入,H(x)是学习输出。所以,我们需要学习F(x)= H(x) - x。我们可以通过首先创建一个层来学习F(x)然后将x添加到F(x)来实现这一点,从而实现H(x)。结果,我们在下一层发送与之前一样的H(x)!这导致我们在上面看到的残差块。

结果令人惊讶,因为通常使深度神经网络的消失梯度问题被消除了。我们可以这样说,skip connections或shortcuts为前面的层提供了梯度的捷径,跳过了一堆层。

ResNet - 架构

论文提到了更深层ResNets的瓶颈使用 - 50/101/152。不使用上述残差块,网络使用1×1卷积来增加和减少信道数量的维数。

ResNet - 代码

from keras import layers
from keras.models import Model
def _after_conv(in_tensor):
 norm = layers.BatchNormalization()(in_tensor)
 return layers.Activation('relu')(norm)
def conv1(in_tensor, filters):
 conv = layers.Conv2D(filters, kernel_size=1, strides=1)(in_tensor)
 return _after_conv(conv)
def conv1_downsample(in_tensor, filters):
 conv = layers.Conv2D(filters, kernel_size=1, strides=2)(in_tensor)
 return _after_conv(conv)
def conv3(in_tensor, filters):
 conv = layers.Conv2D(filters, kernel_size=3, strides=1, padding='same')(in_tensor)
 return _after_conv(conv)
def conv3_downsample(in_tensor, filters):
 conv = layers.Conv2D(filters, kernel_size=3, strides=2, padding='same')(in_tensor)
 return _after_conv(conv)
def resnet_block_wo_bottlneck(in_tensor, filters, downsample=False):
 if downsample:
 conv1_rb = conv3_downsample(in_tensor, filters)
 else:
 conv1_rb = conv3(in_tensor, filters)
 conv2_rb = conv3(conv1_rb, filters)
 if downsample:
 in_tensor = conv1_downsample(in_tensor, filters)
 result = layers.Add()([conv2_rb, in_tensor])
 return layers.Activation('relu')(result)
def resnet_block_w_bottlneck(in_tensor,
 filters,
 downsample=False,
 change_channels=False):
 if downsample:
 conv1_rb = conv1_downsample(in_tensor, int(filters/4))
 else:
 conv1_rb = conv1(in_tensor, int(filters/4))
 conv2_rb = conv3(conv1_rb, int(filters/4))
 conv3_rb = conv1(conv2_rb, filters)
 if downsample:
 in_tensor = conv1_downsample(in_tensor, filters)
 elif change_channels:
 in_tensor = conv1(in_tensor, filters)
 result = layers.Add()([conv3_rb, in_tensor])
 return result
def _pre_res_blocks(in_tensor):
 conv = layers.Conv2D(64, 7, strides=2, padding='same')(in_tensor)
 conv = _after_conv(conv)
 pool = layers.MaxPool2D(3, 2, padding='same')(conv)
 return pool
def _post_res_blocks(in_tensor, n_classes):
 pool = layers.GlobalAvgPool2D()(in_tensor)
 preds = layers.Dense(n_classes, activation='softmax')(pool)
 return preds
def convx_wo_bottleneck(in_tensor, filters, n_times, downsample_1=False):
 res = in_tensor
 for i in range(n_times):
 if i == 0:
 res = resnet_block_wo_bottlneck(res, filters, downsample_1)
 else:
 res = resnet_block_wo_bottlneck(res, filters)
 return res
def convx_w_bottleneck(in_tensor, filters, n_times, downsample_1=False):
 res = in_tensor
 for i in range(n_times):
 if i == 0:
 res = resnet_block_w_bottlneck(res, filters, downsample_1, not downsample_1)
 else:
 res = resnet_block_w_bottlneck(res, filters)
 return res
def _resnet(in_shape=(224,224,3),
 n_classes=1000,
 opt='sgd',
 convx=[64, 128, 256, 512],
 n_convx=[2, 2, 2, 2],
 convx_fn=convx_wo_bottleneck):
 in_layer = layers.Input(in_shape)
 downsampled = _pre_res_blocks(in_layer)
 conv2x = convx_fn(downsampled, convx[0], n_convx[0])
 conv3x = convx_fn(conv2x, convx[1], n_convx[1], True)
 conv4x = convx_fn(conv3x, convx[2], n_convx[2], True)
 conv5x = convx_fn(conv4x, convx[3], n_convx[3], True)
 preds = _post_res_blocks(conv5x, n_classes)
 model = Model(in_layer, preds)
 model.compile(loss="categorical_crossentropy", optimizer=opt,
	 metrics=["accuracy"])
 return model
def resnet18(in_shape=(224,224,3), n_classes=1000, opt='sgd'):
 return _resnet(in_shape, n_classes, opt)
def resnet34(in_shape=(224,224,3), n_classes=1000, opt='sgd'):
 return _resnet(in_shape,
 n_classes,
 opt,
 n_convx=[3, 4, 6, 3])
def resnet50(in_shape=(224,224,3), n_classes=1000, opt='sgd'):
 return _resnet(in_shape,
 n_classes,
 opt,
 [256, 512, 1024, 2048],
 [3, 4, 6, 3],
 convx_w_bottleneck)
def resnet101(in_shape=(224,224,3), n_classes=1000, opt='sgd'):
 return _resnet(in_shape,
 n_classes,
 opt,
 [256, 512, 1024, 2048],
 [3, 4, 23, 3],
 convx_w_bottleneck)
def resnet152(in_shape=(224,224,3), n_classes=1000, opt='sgd'):
 return _resnet(in_shape,
 n_classes,
 opt,
 [256, 512, 1024, 2048],
 [3, 8, 36, 3],
 convx_w_bottleneck)
if __name__ == '__main__':
 model = resnet50()
 print(model.summary())

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码