经典卷积网络之ResNet的论文解读及代码实现

文章背景

随着深度学习的快速发展,在一些竞赛或者论文中出现的神经网络层数也越来越深,那么问题来了:得到一个好的网络模型就是简单的堆砌网络层数吗?事实并非如此,实验证明,随着网络深度的增加,会导致梯度爆炸。也有文章提出,解决这个问题的方法是:通过对数据的正则化和初始化以及中间正则化层。这种方法使拥有数十层的神经网络通过反向传播收敛于随机梯度下降(SGD)。但这样会导致另外一个问题:随着网路深度的增加,结果精度在达到饱和之后突然下降,而且这种下降不是过拟合导致的。

思想

一个网络要保证:后面的一层不应该不前面的一层产生更大的误差。所以提出了优化残差的思路:不是将网络层向期望结果的方向优化,而是向实际输出与理想输出残差为零的方向优化,实验证明,这种方式更容易优化,而且”短连接层“不带入参数和增加计算复杂度(只是简单的相加)整个网络仍然可以通过基于反向传播的随机梯度下降的算法来优化。

网络结构

将输入定义为x,理想结果定义为H(x),那么残差为F(x)=H(x)-x;ResNet就是以F(x)=0为目标对模型进行训练,由表达式可以看出,F(x)与x的维度必须一致,一般我们将步长stride设置为1,这样保证输入输出一致,如果stride不为1,那么需要对x进行处理。ResNet的网络结构如下图所示:

在这里插入图片描述

代码实现

Basic Block

在这里插入图片描述

class BasicBlock(layers.layer):
	def __init__(self, filter_num, stride = 1):
		super(BasicBlock, self).__init__()
		#将conv + batchnormalization + 激活函数叫做一层
		self.conv1 = layers.Conv2D(filter_num, kernel_size=[3, 3],
			     stride=stride, padding='same')
		self.bn1 = layers.BatchNormalization()
		self.relu = layers.Activation('relu')

		self.conv1 = layers.Conv2D(filter_num, kernel_size=[3, 3],
       			     stride=1, padding='same')
       		self.bn2 = layers.BatchNormalization()
       		# identity 连接线
       		if stride != 1:
       			self.downsample = Sequential()
       			self.downsample.add(layers.Conv2D(filter_num, 
       					    kernel_size=[1, 1],
      					    stride=stride,
      					    padding='same')
					    )
       			
		else:
			self.downsample = lambda x:x
		
	def call(self, input, training = None):
		# input = [c, h, w, n]
		out = self.conv1(input)
		out = self.bn1(out)
		out = self.relu(out)
		out = self.conv2(out)
		out = self.bn2(out)
		identity = self.dowansample(input)
		output = layers.add([out, identity])
		output = tf.nn.relu(output)
		return output

ResBlock

def build_resblock(self, filter_num, blocks, stride=1):
	res_blocks= Sequential()
	#只在	第一个Basicblock中进行下采样
	res_blocks.add(BasicBlock(filter_num, stride = stride))
	for _ in range(1, blocks):
		res_blocks.add(BasicBlock(filter_num, stride = 1))

ResNet 18

class ResNet(keras.Model):
	def __init__(self, layer_dims, num_class=100):
		super(ResNet, self).__init__()
		# layer_dims=[2,2,2,2]:有四个res_blocks,每个包含两个BasicBlock
		# 第一层:预处理层
		self.stem = Sequential([layers.Conv2D(64,
					kernal_size=[3,3],
					layers.BatchNormalization(),
					layers.Activation('relu'),
					layers.MaxPool2D(pool_size=[2,2]),
					strides=2, padding='same')])	
		# unit 2
		self.layer1 = self.build_resblock(64, layer_dims[0],
						  stride=2)
		# unit 3
 	 	self.layer2 = self.build_resblock(128, layer_dims[1],
        					  stride=2)
        	# unit 4
  		self.layer3 = self.build_resblock(256, layer_dims[2],
               					  stride=2)
         	# unit 5
    		self.layer4 = self.build_resblock(512, layer_dims[3],
                      				  stride=2)
         	#全连接层
         	# output:[b, 512, h, w]
         	self.avgpool=layers.GlobalAveragePooling2D()
         	self.fc = layers.Dense(num_classes)
   	 def call(self, input, training=None):
   	 	x = self.stem(input)
   	 	x = self.layer1(x)
   	 	x = self.layer2(x)
   	 	x = self.layer3(x)
   	 	x = self.layer4(x)
   	 	#[b, c, h, w] -> [b, c]
   	 	x = self.avgpool(x)
   	 	#[b, c] -> [b, num_classes]
   	 	output = self.fc(x)
   	 	return output
 
 

 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是基于 PyTorch 的 ResNet34 的模型代码: ```python import torch.nn as nn import torch.nn.functional as F class ResidualBlock(nn.Module): def __init__(self, in_channels, out_channels, stride=1, downsample=None): super(ResidualBlock, self).__init__() self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(out_channels) self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(out_channels) self.downsample = downsample def forward(self, x): identity = x out = self.conv1(x) out = self.bn1(out) out = F.relu(out) out = self.conv2(out) out = self.bn2(out) if self.downsample is not None: identity = self.downsample(x) out += identity out = F.relu(out) return out class ResNet(nn.Module): def __init__(self, block, layers, num_classes=1000): super(ResNet, self).__init__() self.in_channels = 64 self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False) self.bn1 = nn.BatchNorm2d(64) self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) self.layer1 = self.make_layer(block, 64, layers[0]) self.layer2 = self.make_layer(block, 128, layers[1], stride=2) self.layer3 = self.make_layer(block, 256, layers[2], stride=2) self.layer4 = self.make_layer(block, 512, layers[3], stride=2) self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) self.fc = nn.Linear(512 * block.expansion, num_classes) def make_layer(self, block, out_channels, blocks, stride=1): downsample = None if stride != 1 or self.in_channels != out_channels * block.expansion: downsample = nn.Sequential( nn.Conv2d(self.in_channels, out_channels * block.expansion, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(out_channels * block.expansion) ) layers = [] layers.append(block(self.in_channels, out_channels, stride, downsample)) self.in_channels = out_channels * block.expansion for i in range(1, blocks): layers.append(block(self.in_channels, out_channels)) return nn.Sequential(*layers) def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = F.relu(x) x = self.maxpool(x) x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) x = self.layer4(x) x = self.avgpool(x) x = x.view(x.size(0), -1) x = self.fc(x) return x def ResNet34(): return ResNet(ResidualBlock, [3, 4, 6, 3]) ``` ResNet34 是由4个 ResNet 模块组成,每个模块由多个残差块(Residual Block)组成。其中第一个模块使用1个3x3的卷层,第2到第4个模块使用2个3x3的卷层,卷层后面跟着一个 Batch Normalization 层和 ReLU 激活函数。每个残差块包括两个3x3的卷层,后面也跟着一个 Batch Normalization 层和 ReLU 激活函数。ResNet34 模型最后使用一个全局平均池化层,然后连接一个全连接层输出预测结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值