深度学习_AlexNet,VGGNet,GoogleNet和ResNet

1.AlexNet

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

该网络的特点是:

  • AlexNet包含8层变换,有5层卷积和2层全连接隐藏层,以及1个全连接输出层
  • AlexNet第一层中的卷积核形状是1111 。第二层中的卷积核形状减小到55 ,之后全采用3*3 。所有的池化层窗口大小为 、步幅为2的最大池化。
  • AlexNet将sigmoid激活函数改成了ReLU激活函数,使计算更简单,网络更容易训练
  • AlexNet通过dropOut来控制全连接层的模型复杂度。
  • AlexNet引入了大量的图像增强,如翻转、裁剪和颜色变化,从而进一步扩大数据集来缓解过拟合。

在tf.keras中实现AlexNet模型:

python 复制代码
# 构建AlexNet模型
net = tf.keras.models.Sequential([
# 卷积层:96个卷积核,卷积核为11*11,步幅为4,激活函数relu
tf.keras.layers.Conv2D(filters=96,kernel_size=11,strides=4,activation='relu'),
# 池化:窗口大小为3*3、步幅为2
tf.keras.layers.MaxPool2D(pool_size=3, strides=2),
# 卷积层:256个卷积核,卷积核为5*5,步幅为1,padding为same,激活函数relu
tf.keras.layers.Conv2D(filters=256,kernel_size=5,padding='same',activation='relu'
# 池化:窗口大小为3*3、步幅为2
tf.keras.layers.MaxPool2D(pool_size=3, strides=2),
# 卷积层:384个卷积核,卷积核为3*3,步幅为1,padding为same,激活函数relu
tf.keras.layers.Conv2D(filters=384,kernel_size=3,padding='same',activation='relu'
# 卷积层:384个卷积核,卷积核为3*3,步幅为1,padding为same,激活函数relu
tf.keras.layers.Conv2D(filters=384,kernel_size=3,padding='same',activation='relu'
# 卷积层:256个卷积核,卷积核为3*3,步幅为1,padding为same,激活函数relu
tf.keras.layers.Conv2D(filters=256,kernel_size=3,padding='same',activation='relu'
# 池化:窗口大小为3*3、步幅为2
tf.keras.layers.MaxPool2D(pool_size=3, strides=2),
# 伸展为1维向量
tf.keras.layers.Flatten(),
# 全连接层:4096个神经元,激活函数relu
tf.keras.layers.Dense(4096,activation='relu'),
# 随机失活
tf.keras.layers.Dropout(0.5),
# 全链接层:4096个神经元,激活函数relu
tf.keras.layers.Dense(4096,activation='relu'),
# 随机失活
tf.keras.layers.Dropout(0.5),
# 输出层:10个神经元,激活函数softmax
tf.keras.layers.Dense(10,activation='softmax')
])
python 复制代码
# 我们构造一个高和宽均为227的单通道数据样本来看一下模型的架构:
# 构造输入X,并将其送入到net网络中
X = tf.random.uniform((1,227,227,1)
y = net(X)
# 通过net.summay()查看网络的形状
net.summay()

代码实现:

python 复制代码
# 获取手写数字数据集
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
# 训练集数据维度的调整:N H W C
train_images = np.reshape(train_images,(train_images.shape[0],train_images.shape[1],train_images.shape[2],1))
# 测试集数据维度的调整:N H W C
test_images = np.reshape(test_images,(test_images.shape[0],test_images.shape[1],test_images.shape[2],1))


# 由于使用全部数据训练时间较长,我们定义两个方法获取部分数据,并将图像调整为227*227大小,进行模型训练:
# 定义两个方法随机抽取部分样本演示
# 获取训练集数据
def get_train(size):
	# 随机生成要抽样的样本的索引
	index = np.random.randint(0, np.shape(train_images)[0], size)
	# 将这些数据resize成227*227大小
	resized_images = tf.image.resize_with_pad(train_images[index],227,227,)
	# 返回抽取的
	return resized_images.numpy(), train_labels[index]
# 获取测试集数据
def get_test(size):
	# 随机生成要抽样的样本的索引
	index = np.random.randint(0, np.shape(test_images)[0], size)
	# 将这些数据resize成227*227大小
	resized_images = tf.image.resize_with_pad(test_images[index],227,227,)
	# 返回抽样的测试样本
	return resized_images.numpy(), test_labels[index]

# 获取训练样本和测试样本
train_images,train_labels = get_train(256)
test_images,test_labels = get_test(128)

# 数据展示:将数据集的前九个数据集进行展示
for i in range(9):
	plt.subplot(3,3,i+1)
	# 以灰度图显示,不进行插值
	plt.imshow(train_images[i].astype(np.int8).squeeze(), cmap='gray',interpolation='none')
	# 设置图片的标题:对应的类别
	plt.title("数字{}".format(train_labels[i]))

# 模型编译
# 指定优化器,损失函数和评价指标
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01, momentum=0.0,nesterov=False)
net.compile(optimizer=optimizer,loss='sparse_categorical_crossentropy',metrics=['accuracy'])


# 模型训练:指定训练数据,batchsize,epoch,验证集
net.fit(train_images,train_labels,batch_size=128,epochs=3,verbose=1,validation_sp

# 指定测试数据
net.evaluate(test_images,test_labels,verbose=1)

# 这边只使用了部分图片进行训练,测试集正确率不到10%,如果使用整个数据训练,结果会好很多

2.VGGNet

2014年,牛津大学计算机视觉组(Visual Geometry Group)和Google DeepMind公司的研究员一起研发出了新的深度卷积神经网络:VGGNet,并取得了ILSVRC2014比赛分类项目的第二名,主要贡献是使用很小的卷积核(3×3)构建卷积神经网络结构,能够取得较好的识别精度,常用来提取图像特征的VGG-16和VGG-19。

VGG可以看成是加深版的AlexNet,整个网络由卷积层和全连接层叠加而成,和AlexNet不同的是,VGG中使用的都是小尺寸的卷积核(3×3),其网络架构如下图所示:

VGGNet使用的全部都是3x3的小卷积核和2x2的池化核,通过不断加深网络来提升性能。VGG可以通过重复使用简单的基础块来构建深度模型。

核心代码:

python 复制代码
# 定义VGG网络中的卷积块:卷积层的个数,卷积层中卷积核的个数
def vgg_block(num_convs, num_filters):
# 构建序列模型
	blk = tf.keras.models.Sequential()
	# 遍历所有的卷积层
	for _ in range(num_convs):
		# 每个卷积层:num_filter个卷积核,卷积核大小为3*3,padding是same,激活函数是
		relu
		blk.add(tf.keras.layers.Conv2D(num_filters,kernel_size=3,
		VGG16网络有5个卷积块,前2块使用两个卷积层,而后3块使用三个卷积层。第一块的输出通道
		是64,之后每次对输出通道数翻倍,直到变为512。
		因为这个网络使用了13个卷积层和3个全连接层,所以经常被称为VGG-16,通过制定conv_arch得
		到模型架构后构建VGG16:
		我们构造一个高和宽均为224的单通道数据样本来看一下模型的架构:
		网络架构如下:
		padding='same',activation='relu'))
		# 卷积块最后是一个最大池化,窗口大小为2*2,步长为2
		blk.add(tf.keras.layers.MaxPool2D(pool_size=2, strides=2))
	return blk
	
# 定义5个卷积块,指明每个卷积块中的卷积层个数及相应的卷积核个数
conv_arch = ((2, 64), (2, 128), (3, 256), (3, 512), (3, 512))

# 定义VGG网络
def vgg(conv_arch):
	# 构建序列模型
	net = tf.keras.models.Sequential()
	# 根据conv_arch生成卷积部分
	for (num_convs, num_filters) in conv_arch:
	net.add(vgg_block(num_convs, num_filters))
	# 卷积块序列后添加全连接层
	net.add(tf.keras.models.Sequential([
	# 将特征图展成一维向量
	tf.keras.layers.Flatten(),
	# 全连接层:4096个神经元,激活函数是relu
	tf.keras.layers.Dense(4096, activation='relu'),
	# 随机失活
	tf.keras.layers.Dropout(0.5),
	# 全连接层:4096个神经元,激活函数是relu
	tf.keras.layers.Dense(4096, activation='relu'),
	# 随机失活
	tf.keras.layers.Dropout(0.5),
	# 全连接层:10个神经元,激活函数是softmax
	tf.keras.layers.Dense(10, activation='softmax')]))
	return net
# 网络实例化
net = vgg(conv_arch)

# 定义VGG网络
def vgg(conv_arch):
	# 构建序列模型
	net = tf.keras.models.Sequential()
	# 根据conv_arch生成卷积部分
		for (num_convs, num_filters) in conv_arch:
		net.add(vgg_block(num_convs, num_filters))
		# 卷积块序列后添加全连接层
		net.add(tf.keras.models.Sequential([
		# 将特征图展成一维向量
		tf.keras.layers.Flatten(),
		# 全连接层:4096个神经元,激活函数是relu
		tf.keras.layers.Dense(4096, activation='relu'),
		# 随机失活
		tf.keras.layers.Dropout(0.5),
		# 全连接层:4096个神经元,激活函数是relu
		tf.keras.layers.Dense(4096, activation='relu'),
		# 随机失活
		tf.keras.layers.Dropout(0.5),
		# 全连接层:10个神经元,激活函数是softmax
		tf.keras.layers.Dense(10, activation='softmax')]))
	return net

# 网络实例化
net = vgg(conv_arch)
# 构造输入X,并将其送入到net网络中
X = tf.random.uniform((1,224,224,1))
y = net(X)
# 通过net.summay()查看网络的形状
net.summay()

3.GoogleNet

GoogLeNet的名字不是GoogleNet,而是GoogLeNet,这是为了致敬LeNet。GoogLeNet和AlexNet/VGGNet这类依靠加深网络结构的深度的思想不完全一样。GoogLeNet在加深度的同时做了结构上的创新,引入了一个叫做Inception的结构来代替之前的卷积加激活的经典组件。

GoogLeNet在ImageNet分类比赛上的Top-5错误率降低到了6.7%。。

3.1 Inception块

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

Inception块在结构比较复杂,如下图所示:

Inception块里有4条并行的线路。前3条线路使用窗口大小分别是1 × 1、3 × 3和5 × 5的卷积层来抽取不同空间尺寸下的信息,其中中间2个线路会对输入先做1 × 1卷积来减少输入通道数,以降低模型复杂度。第4条线路则使用3 × 3最大池化层,后接1 × 1卷积层来改变通道数。4条线路都使用了合适的填充来使输入与输出的高和宽一致。最后我们将每条线路的输出在通道维上连结,并向后进行传输。

以inception模块为例,来说明1x1的卷积如何来减少模型参数:

(a)是未加入1x1卷积的inception模块,(b)是加入了1x1 卷积的inception模块。

我们以3x3卷积线路为例,假设输入的特征图大小为(28x28x192),输出特征图的通道数是128:

(a)图中该线路的参数量为:3x3x192x128 = 221184

(b)图中加入1x1卷积后通道为96,再送入3x3卷积中的参数量为:(1x1x192x96)+(3x3x96x128)=129024.

对比可知,加入1x1卷积后参数量减少了。

在tf.keras中实现Inception模块:

python 复制代码
# 定义Inception模块
class Inception(tf.keras.layers.Layer):
	# 输入参数为各个卷积的卷积核个数
	def __init__(self, c1, c2, c3, c4):
		super().__init__()
		# 线路1:1 x 1卷积层,激活函数是RELU,padding是same
		self.p1_1 = tf.keras.layers.Conv2D(
		c1, kernel_size=1, activation='relu', padding='same')
		# 线路2,1 x 1卷积层后接3 x 3卷积层,激活函数是RELU,padding是same
		self.p2_1 = tf.keras.layers.Conv2D(
		c2[0], kernel_size=1, padding='same', activation='relu')
		self.p2_2 = tf.keras.layers.Conv2D(c2[1], kernel_size=3,
		padding='same',
		activation='relu')
		# 线路3,1 x 1卷积层后接5 x 5卷积层,激活函数是RELU,padding是same
		self.p3_1 = tf.keras.layers.Conv2D(
		c3[0], kernel_size=1, padding='same', activation='relu')
		self.p3_2 = tf.keras.layers.Conv2D(c3[1], kernel_size=5,
		padding='same',
		activation='relu')
		# 线路4,3 x 3最大池化层后接1 x 1卷积层,激活函数是RELU,padding是same
		self.p4_1 = tf.keras.layers.MaxPool2D(
		pool_size=3, padding='same', strides=1)
		self.p4_2 = tf.keras.layers.Conv2D(
		c4, kernel_size=1, padding='same', activation='relu')
	# 完成前向传播过程
	def call(self, x):
		# 线路1
		p1 = self.p1_1(x)
		# 线路2
		p2 = self.p2_2(self.p2_1(x))
		# 线路3
		p3 = self.p3_2(self.p3_1(x))
		# 线路4
		p4 = self.p4_2(self.p4_1(x))
		# 在通道维上concat输出
		outputs = tf.concat([p1, p2, p3, p4], axis=-1)
		return outputs
		
Inception(64, (96, 128), (16, 32), 32)
3.2 B1~B5模块
3.2.1 B1模块

第一模块使用一个64通道的7 × 7卷积层。

python 复制代码
# 定义模型的输入
inputs = tf.keras.Input(shape=(224,224,3),name = "input")
# b1 模块
# 卷积层7*7的卷积核,步长为2,pad是same,激活函数RELU
x = tf.keras.layers.Conv2D(64, kernel_size=7, strides=2, padding='same',
activation='relu')(inputs)
# 最大池化:窗口大小为3*3,步长为2,pad是same
x = tf.keras.layers.MaxPool2D(pool_size=3, strides=2, padding='same')(x)
3.2.2 B2模块

第二模块使用2个卷积层:首先是64通道的1 × 1卷积层,然后是将通道增大3倍的3 × 3卷积层。

python 复制代码
# b2 模块
# 卷积层1*1的卷积核,步长为2,pad是same,激活函数RELU
x = tf.keras.layers.Conv2D(64, kernel_size=1, padding='same',
activation='relu')(x)
# 卷积层3*3的卷积核,步长为2,pad是same,激活函数RELU
x = tf.keras.layers.Conv2D(192, kernel_size=3, padding='same',
activation='relu')(x)
# 最大池化:窗口大小为3*3,步长为2,pad是same
x = tf.keras.layers.MaxPool2D(pool_size=3, strides=2, padding='same')(x)
3.2.3 B3模块

第三模块串联2个完整的Inception块。

第一个Inception块的输出通道数为 64+ 128+32+32 = 256.

第二个Inception块输出通道数增至 128 + 192 + 96 + 64 = 480.

python 复制代码
# b3 模块
# Inception
x = Inception(64, (96, 128), (16, 32), 32)(x)
# Inception
x = Inception(128, (128, 192), (32, 96), 64)(x)
# 最大池化:窗口大小为3*3,步长为2,pad是same
x = tf.keras.layers.MaxPool2D(pool_size=3, strides=2, padding='same')(x)
3.2.4 B4模块

第四模块更加复杂。它串联了5个Inception块,其输出通道数分别是

192 + 208 + 48 + 64 = 512 ,160 + 224 + 64 + 64 = 512,

128 + 256 + 64 + 64 = 512, 112 + 288 + 64 + 64 = 528 和 256 + 320 + 128 + 128 = 832。

并且增加了辅助分类器,根据实验发现网络的中间层具有很强的识别能力,为了利用中间层抽象的特征,在某些中间层中添加含有多层的分类器,如下图所示:

python 复制代码
def aux_classifier(x, filter_size):
	#x:输入数据,filter_size:卷积层卷积核个数,全连接层神经元个数
	# 池化层
	x = tf.keras.layers.AveragePooling2D(
	pool_size=5, strides=3, padding='same')(x)
	# 1x1 卷积层
	x = tf.keras.layers.Conv2D(filters=filter_size[0], kernel_size=1,
	strides=1,
	padding='valid', activation='relu')(x)
	# 展平
	x = tf.keras.layers.Flatten()(x)
	# 全连接层1
	x = tf.keras.layers.Dense(units=filter_size[1], activation='relu')(x)
	# softmax输出层
	x = tf.keras.layers.Dense(units=10, activation='softmax')(x)
	return x


# b4 模块
# Inception
x = Inception(192, (96, 208), (16, 48), 64)(x)
# 辅助输出1
aux_output_1 = aux_classifier(x, [128, 1024])
# Inception
x = Inception(160, (112, 224), (24, 64), 64)(x)
# Inception
x = Inception(128, (128, 256), (24, 64), 64)(x)
# Inception
x = Inception(112, (144, 288), (32, 64), 64)(x)
# 辅助输出2
aux_output_2 = aux_classifier(x, [128, 1024])
# Inception
x = Inception(256, (160, 320), (32, 128), 128)(x)
# 最大池化
x = tf.keras.layers.MaxPool2D(pool_size=3, strides=2, padding='same')(x)
3.2.5 B5模块

第五模块有输出通道数为 256 + 320 + 128 + 128 = 832 和 384 + 384 + 128 + 128 = 1024 的两个Inception块。后面紧跟输出层,该模块使用全局平均池化层(GAP)来将每个通道的高和宽变成1。最后输出变成二维数组后接输出个数为标签类别数的全连接层。
全局平均池化层(GAP)

用来替代全连接层前的Flatten,将特征图每一通道中所有像素值相加后求平均,得到就是GAP的结果,在将其送入后续网络中进行计算

python 复制代码
# b5 模块
# Inception
x = Inception(256, (160, 320), (32, 128), 128)(x)
# Inception
x = Inception(384, (192, 384), (48, 128), 128)(x)
# GAP
x = tf.keras.layers.GlobalAvgPool2D()(x)
# 输出层
main_outputs = tf.keras.layers.Dense(10,activation='softmax')(x)

最终使用model来创建模型:

python 复制代码
# 使用Model来创建模型,指明输入和输出
model = tf.keras.Model(inputs=inputs, outputs=[main_outputs,aux_output_1,
aux_output_2])
model.summary()

4.ResNet

⽹络越深,获取的信息就越多,特征也越丰富。但是在实践中,随着⽹络的加深,优化效果反⽽越差,测试数据和训练数据的准确率反⽽降低了。

针对这⼀问题,何恺明等⼈提出了残差⽹络(ResNet)在2015年的ImageNet图像识别挑战赛夺魁,并深刻影响了后来的深度神经⽹络的设计。

4.1 残差块

假设 F(x) 代表某个只包含有两层的映射函数, x 是输⼊, F(x)是输出。假设他们具有相同的维度。在训练的过程中我们希望能够通过修改⽹络中的 w和b去拟合⼀个理想的 H(x)(从输⼊到输出的⼀个理想的映射函数)。也就是我们的⽬标是修改F(x) 中的 w和b逼近 H(x) 。如果我们改变思路,⽤F(x) 来逼近 H(x)-x ,那么我们最终得到的输出就变为 F(x)+x(这⾥的加指的是对应位置上的元素相加,也就是element-wise addition),这⾥将直接从输⼊连接到输出的结构也称为shortcut,那整个结构就是残差块,ResNet的基础模块。

ResNet沿⽤了VGG全3 × 3卷积层的设计。残差块⾥⾸先有2个有相同输出通道数的3 × 3卷积层。每个卷积层后接BN层和ReLU激活函数,然后将输⼊直接加在最后的ReLU激活函数前,这种结构⽤于层数较少的神经⽹络中,⽐如ResNet34。若输⼊通道数⽐较多,就需要引⼊1 × 1卷积层来调整输⼊的通道数,这种结构也叫作瓶颈模块,通常⽤于⽹络层数较多的结构中。如下图所⽰:

上图左中的残差块的实现如下,可以设定输出通道数,是否使⽤1*1的卷积及卷积层的步幅。

python 复制代码
# 导入相关的工具包
import tensorflow as tf
from tensorflow.keras import layers, activations
# 定义ResNet的残差块
class Residual(tf.keras.Model):
# 指明残差块的通道数,是否使用1*1卷积,步长
def __init__(self, num_channels, use_1x1conv=False, strides=1):
	super(Residual, self).__init__()
	# 卷积层:指明卷积核个数,padding,卷积核大小,步长
	self.conv1 = layers.Conv2D(num_channels,padding='same',	kernel_size=3,	strides=strides)
	# 卷积层:指明卷积核个数,padding,卷积核大小,步长
	self.conv2 = layers.Conv2D(num_channels, kernel_size=3,	padding='same')
	if use_1x1conv:
		self.conv3 = layers.Conv2D(num_channels,	kernel_size=1,	strides=strides)
	else:
		self.conv3 = None
		# 指明BN层
		self.bn1 = layers.BatchNormalization()
		self.bn2 = layers.BatchNormalization()
	
# 定义前向传播过程
def call(self, X):
	# 卷积,BN,激活
	Y = activations.relu(self.bn1(self.conv1(X)))
	# 卷积,BN
	Y = self.bn2(self.conv2(Y))
	# 对输入数据进行1*1卷积保证通道数相同
	if self.conv3:
	X = self.conv3(X)
	# 返回与输入相加后激活的结果
	return activations.relu(Y + X)

1*1卷积⽤来调整通道数。

4.2 ResNet模型

ResNet⽹络中按照残差块的通道数分为不同的模块。第⼀个模块前使⽤了步幅为2的最⼤池化层,所以⽆须减⼩⾼和宽。之后的每个模块在第⼀个残差块⾥将上⼀个模块的通道数翻倍,并将⾼和宽减半。

下⾯我们来实现这些模块。注意,这⾥对第⼀个模块做了特别处理。

python 复制代码
# ResNet网络中模块的构成
class ResnetBlock(tf.keras.layers.Layer):
	# 网络层的定义:输出通道数(卷积核个数),模块中包含的残差块个数,是否为第一个模块
	def __init__(self,num_channels, num_residuals, first_block=False):
	super(ResnetBlock, self).__init__()
	# 模块中的网络层
	self.listLayers=[]

	# 遍历模块中所有的层
	for i in range(num_residuals):
		# 若为第一个残差块并且不是第一个模块,则使用1*1卷积,步长为2(目的是减小特征图,并增大通道数)
		if i == 0 and not first_block:
			self.listLayers.append(Residual(num_channels,use_1x1conv=True, strides=2))
	   # 否则不使用1*1卷积,步长为1
		else:
			self.listLayers.append(Residual(num_channels))
			
# 定义前向传播过程
def call(self, X):
	# 所有层依次向前传播即可
	for layer in self.listLayers.layers:
	X = layer(X)
	return X

ResNet的前两层跟之前介绍的GoogLeNet中的⼀样:在输出通道数为64、步幅为2的 卷积层后接步幅为2的 的最⼤池化层。不同之处ResNet每个卷积层后增加了BN层,接着是所有残差模块,最后,与GoogLeNet⼀样,加⼊全局平均池化层(GAP)后接上全连接层输出。

python 复制代码
# 构建ResNet网络
class ResNet(tf.keras.Model):
	# 初始化:指定每个模块中的残差快的个数
	def __init__(self,num_blocks):
		super(ResNet, self).__init__()
			# 输入层:7*7卷积,步长为2
			self.conv=layers.Conv2D(64, kernel_size=7, strides=2, padding='same')
			# BN层
			self.bn=layers.BatchNormalization()
			# 激活层
			self.relu=layers.Activation('relu')
			# 最大池化层
			self.mp=layers.MaxPool2D(pool_size=3, strides=2, padding='same')
			# 第一个block,通道数为64
			self.resnet_block1=ResnetBlock(64,num_blocks[0], first_block=True)
			# 第二个block,通道数为128
			self.resnet_block2=ResnetBlock(128,num_blocks[1])
			# 第三个block,通道数为256
			self.resnet_block3=ResnetBlock(256,num_blocks[2])
			# 第四个block,通道数为512
			self.resnet_block4=ResnetBlock(512,num_blocks[3])
			# 全局平均池化
			self.gap=layers.GlobalAvgPool2D()
			# 全连接层:分类
			self.fc=layers.Dense(units=10,activation=tf.keras.activations.softmax)
	
# 前向传播过程
def call(self, x):
	# 卷积
	x=self.conv(x)
	# BN
	x=self.bn(x)
	# 激活
	x=self.relu(x)
	# 最大池化
	x=self.mp(x)
	# 残差模块
	x=self.resnet_block1(x)
	x=self.resnet_block2(x)
	x=self.resnet_block3(x)
	x=self.resnet_block4(x)
	# 全局平均池化
	x=self.gap(x)
	# 全链接层
	x=self.fc(x)
	return x
# 模型实例化:指定每个block中的残差块个数
mynet=ResNet([2,2,2,2])

这⾥每个模块⾥有4个卷积层(不计算 1×1卷积层),加上最开始的卷积层和最后的全连接层,共计18层。这个模型被称为ResNet-18。通过配置不同的通道数和模块⾥的残差块数可以得到不同的ResNet模型,例如更深的含152层的ResNet-152。虽然ResNet的主体架构GoogLeNet的类似,但ResNet结构更简单,修改也更⽅便。这些因素都导致了ResNet迅速被⼴泛使⽤。 在训练ResNet之前,我们来观察⼀下输⼊形状在ResNe的架构:

python 复制代码
X = tf.random.uniform(shape=(1, 224, 224 , 1))
y = mynet(X)
mynet.summary()
相关推荐
a187927218312 小时前
【教程】打通本地 IDE AI 与云端 AI 的记忆壁垒:基于 COS 的跨 AI 终端记忆共享与通信系统
人工智能·ai·ai编程·claude·mem·agents·vibe coding
jkyy20142 小时前
智慧座舱新维度:汽车领域健康管理如何重塑驾乘体验?
人工智能·汽车·健康医疗
PNP机器人2 小时前
具身大型语言模型让机器人玩转复杂未知场景
人工智能·语言模型·机器人·kinova机械臂
电商API_180079052472 小时前
企业级应用:京东商品详情 API 的高可用架构与多级缓存设计
开发语言·人工智能·python·数据分析·网络爬虫·php
MoonBit月兔2 小时前
MoonBit 0.8.3版本更新
开发语言·人工智能·算法·ai编程·moonbit
云蝠呼叫大模型联络中心2 小时前
金融智能外呼合规技术实现与数据安全架构
大数据·人工智能·#金融科技·#智能外呼合规·#云蝠智能·#ai语音外呼·#数据安全架构
Fibocom广和通2 小时前
MWC 2026 | 广和通发布 AI ECR 解决方案,以端侧 AI 能力开启无人零售新纪元
人工智能·无人零售·ai收银机·自助收银机
dblens 数据库管理和开发工具2 小时前
从 Prompt 到系统工程:AI / Agent 系统架构设计要点
人工智能·系统架构·prompt
安逸sgr2 小时前
16-OpenClaw数据分析与可视化
人工智能·数据挖掘·数据分析·大模型·aigc·agent·openclaw