卷积神经网络:ResNet

        Residual Network(ResNet)是一种深度卷积神经网络架构,由 Kaiming He 等人在 2015 年提出,并在 ImageNet 挑战赛中取得了显著的成绩。其核心思想是通过引入“残差学习”来解决深度网络训练中的问题,特别是梯度消失和网络退化的问题。

一、主要特点

1. 残差学习

        ResNet 的基本构建块是“残差块”(Residual Block),其中输入直接与经过若干卷积层处理后的输出相加。这种结构允许网络学习到“残差”函数而不是直接学习目标函数,使得在堆叠多层时保持高效的特征学习。

2. 深层架构

        ResNet 可以构建非常深的网络,如 ResNet-50、ResNet-101 和 ResNet-152,分别具有 50、101 和 152 层的深度。通过残差连接,使得这些深层网络能够顺利训练。

3. 跳跃连接(Skip Connections)

        跳跃连接允许信息在网络中跳过几个层级,从而减轻梯度消失现象,并促进更好的信号传递。这种设计显著提高了网络的训练效果和准确性。

4. 批量归一化

        每个卷积层后通常会使用批量归一化(Batch Normalization)来提高训练的稳定性和加快收敛速度。

5. 全局平均池化

        在模型的最后,通常用全局平均池化层替代传统的全连接层,这样可以显著减少模型的参数数量并降低过拟合风险。

二、网络结构

        Residual Network(ResNet) 的设计理念是通过引入残差学习,使得深层网络在训练时更加高效和稳定。下面是 ResNet 的网络结构和主要组成部分的详细介绍。

1. 残差块(Residual Block)

残差块是 ResNet 的基本构建单元。每个残差块通常包含以下几个部分:

        卷积层: 通常使用两个或三个连续的卷积层。
        批量归一化: 在每个卷积层后应用批量归一化,以提高训练的稳定性。
        激活函数: 通常使用 ReLU 激活函数。
        跳跃连接: 输入直接与卷积层的输出相加,形成残差学习。

残差块的结构示意图:

Input ----> [Conv2D] ---> [BatchNorm] ---> [ReLU] ----> [Conv2D] ---> [BatchNorm] ----> Output  
      |                                                             |  
      +-------------------------------------------------------------+

2. 网络深度

ResNet 的深度可以根据需要进行扩展,常见的版本包括:

        ResNet-50: 50 层深度,使用 16 个残差块。
        ResNet-101: 101 层深度,使用 33 个残差块。
        ResNet-152: 152 层深度,使用 50 个残差块。

这种深度的设计使得 ResNet 能够学习到更复杂的特征。

3. 网络结构

以下是 ResNet-50 的具体结构:

3.1 输入层: 输入图像(224x224x3)。
3.2 卷积层: 7x7 卷积,步长为 2,输出 64 个特征图。
3.3 最大池化层: 3x3 最大池化,步长为 2。
3.4 残差块:
        第一组:3 个残差块,输出 256 个特征图。
        第二组:4 个残差块,输出 512 个特征图。
        第三组:6 个残差块,输出 1024 个特征图。
        第四组:3 个残差块,输出 2048 个特征图。
3.5 全局平均池化层: 计算每个特征图的平均值。
3.6 全连接层: 输出分类结果。

三、示例代码

示例1:

下面是如何在 TensorFlow Keras 中使用 `ResNet` 的示例代码。

import tensorflow as tf  
from tensorflow.keras.applications import ResNet50  
from tensorflow.keras.preprocessing.image import ImageDataGenerator  
from tensorflow.keras import layers, models  

# 设定超参数  
img_height, img_width = 224, 224  
batch_size = 32  
num_classes = 10  # 替换为你的分类数量  
epochs = 10  

# 加载 ResNet50 模型,不包含顶层(即全连接层)  
base_model = ResNet50(weights='imagenet', include_top=False, input_shape=(img_height, img_width, 3))  

# 冻结基础模型的层  
for layer in base_model.layers:  
    layer.trainable = False  

# 添加自定义分类头  
model = models.Sequential()  
model.add(base_model)  
model.add(layers.GlobalAveragePooling2D())  
model.add(layers.Dense(256, activation='relu'))  
model.add(layers.Dropout(0.5))  # 添加 Dropout 防止过拟合  
model.add(layers.Dense(num_classes, activation='softmax'))  # num_classes 是你的分类数  

# 编译模型  
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])  

# 数据准备  
train_datagen = ImageDataGenerator(  
    rescale=1.0/255,  
    rotation_range=40,  
    width_shift_range=0.2,  
    height_shift_range=0.2,  
    shear_range=0.2,  
    zoom_range=0.2,  
    horizontal_flip=True,  
    fill_mode='nearest'  
)  

# 替换为你的训练数据目录  
train_generator = train_datagen.flow_from_directory(  
    'path_to_train_directory',  # 确保此目录包含子文件夹,每个子文件夹代表一个类别  
    target_size=(img_height, img_width),  
    batch_size=batch_size,  
    class_mode='categorical'  
)  

# 训练模型  
model.fit(train_generator, epochs=epochs)  

# 评估模型  
val_datagen = ImageDataGenerator(rescale=1.0/255)  
val_generator = val_datagen.flow_from_directory(  
    'path_to_validation_directory',  # 替换为你的验证数据目录  
    target_size=(img_height, img_width),  
    batch_size=batch_size,  
    class_mode='categorical'  
)  

# 评估  
loss, accuracy = model.evaluate(val_generator)  
print(f"Validation accuracy: {accuracy * 100:.2f}%")

代码说明

        导入库: 引入所需的 TensorFlow 和 Keras 模块。
        超参数设定:设置图像高度和宽度、批量大小和训练轮数。
        加载模型: 加载预训练的 ResNet50 模型,去掉顶层。
        模型构建: 在基础模型上添加全局平均池化层、自定义的全连接层和 Dropout 层。
        编译模型: 使用 Adam 优化器与 categorical_crossentropy 损失函数编译模型。
        数据准备: 使用 `ImageDataGenerator` 创建训练数据生成器并进行数据增强。
        训练模型: 训练模型,调用 `fit` 方法。
        评估模型: 准备验证数据集并评估模型准确率。

示例2:

ResNet模型构建。

# 导入模块
import tensorflow as tf
from tensorflow.keras import layers,activations

# 残差块
class Residual(tf.keras.Model):
    # 定义网络结构
    def __init__(self,num_channels,use_1x1conv=False,strides=1):
        super(Residual,self).__init__()
        # 卷积层
        self.conv1 = layers.Conv2D(num_channels,padding='same',kernel_size=3,strides=strides)
        # 卷积层
        self.conv2 = layers.Conv2D(num_channels,kernel_size=3,padding='same')
        # 是否使用1*1的卷积
        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):
        Y = activations.relu(self.bn1(self.conv1(x)))
        Y = self.bn2(self.conv2(Y))
        if self.conv3:
            x = self.conv3(x)
        outputs = activations.relu(Y+x)
        return outputs

# 残差模块
class ResnetBlock(tf.keras.layers.Layer):
    # 定义所需的网络结构
    def __init__(self,num_channels,num_res,first_block=False):
        super(ResnetBlock,self).__init__()
        # 存储残差块
        self.listLayers=[]
        # 遍历残差数目生成模块
        for i in range(num_res):
            # 如果是第一个残差块而且不是第一个模块时
            if i ==0 and not first_block:
                self.listLayers.append(Residual(num_channels,use_1x1conv=True,strides=2))
            else:
                self.listLayers.append(Residual(num_channels))
    # 定义前向传播
    def call(self,X):
        for layer in self.listLayers.layers:
            X = layer(X)
        return X

# 构建resNet网络
class ResNet(tf.keras.Model):
    # 定义网络的构成
    def __init__(self, num_blocks):
        super(ResNet, self).__init__()
        # 输入层
        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")
        # 残差模块
        self.res_block1 = ResnetBlock(64, num_blocks[0], first_block=True)
        self.res_block2 = ResnetBlock(128, num_blocks[1])
        self.res_block3 = ResnetBlock(256, num_blocks[2])
        self.res_block4 = ResnetBlock(512, num_blocks[3])
        # GAP
        self.gap = layers.GlobalAvgPool2D()
        # 全连接层
        self.fc = layers.Dense(
            units=10, activation=tf.keras.activations.softmax)
    # 定义前向传播过程

    def call(self, x):
        # 输入部分的传输过程
        x = self.conv(x)
        x = self.bn(x)
        x = self.relu(x)
        x = self.mp(x)
        # block
        x = self.res_block1(x)
        x = self.res_block2(x)
        x = self.res_block3(x)
        x = self.res_block4(x)
        # 输出部分的传输
        x = self.gap(x)
        x = self.fc(x)
        return x

# 实例化
mynet = ResNet([2,2,2,2])
X = tf.random.uniform((1,224,224,1))
y = mynet(X)
mynet.summary()

四、特点与优势

        有效的梯度传播: 残差连接使得梯度能够更有效地传播,减轻了梯度消失的问题。
        易于训练: 更深的网络可以通过残差学习更容易地训练,从而提高模型性能。
        强大的特征提取能力: ResNet 在图像分类和其他计算机视觉任务中的表现非常出色。

五、应用领域

        图像分类: 在各种标准数据集(如 ImageNet)上进行训练和评估。
        目标检测: 作为基础网络(backbone)用于 Faster R-CNN、YOLO 等检测模型。
        语义分割: 用于像素级别的分类任务。

六、影响力

        ResNet 的提出改变了人们对深度网络结构的认识,并引领了深度学习研究中的许多新的思路和架构设计,从而在业界和学术界影响深远。

七、总结

        ResNet 是一种高效且强大的深度学习模型,通过创新的残差学习机制,成功地训练了极深的神经网络并在多个标准数据集上取得了突破性的成绩。这种架构的成功为以后的卷积神经网络(CNN)设计开辟了新的方向。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

00&00

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值