【深度学习基础模型】深度残差网络(Deep Residual Networks, DRN)详细理解并附实现代码。

【深度学习基础模型】深度残差网络(Deep Residual Networks, DRN)详细理解并附实现代码。

【深度学习基础模型】深度残差网络(Deep Residual Networks, DRN)详细理解并附实现代码。



参考地址:https://www.asimovinstitute.org/neural-network-zoo/
论文地址:https://arxiv.org/pdf/1512.03385

欢迎宝子们点赞、关注、收藏!欢迎宝子们批评指正!
在这里插入图片描述

1. 算法提出

深度残差网络(DRN)最初由何凯明等人于2015年在论文“Deep Residual Learning for Image Recognition”中提出。该算法的核心思想是通过残差块(Residual Block)来解决深层神经网络训练中的退化问题

传统神经网络在层数增加时,随着网络变深,训练误差反而会上升,这种现象被称为梯度消失/爆炸问题DRN通过引入跳跃连接(Skip Connection),将前几层的输入直接传递到后几层,从而有效缓解了这个问题

2. 概述

DRN的核心结构是残差块。一个典型的残差块包含一个跳跃连接,将输入直接加到输出上,如下所示:

y = F ( x ) + x y=F(x)+x y=F(x)+x

其中, x x x是残差块的输入, F ( x ) F(x) F(x)是经过几层非线性变换后的输出。通过将输入 x x x直接添加到输出 F ( x ) F(x) F(x),残差网络实际上是在学习一个残差函数。这种结构使得网络能够更容易训练,并且即使网络层数增加,网络也不会出现退化现象。

残差网络的优点在于:

  • 更深的网络结构:传统前馈神经网络(Feedforward Neural Networks, FFNN)的层数通常在几层到几十层,而DRN可以扩展到上百层甚至更深(如ResNet-152)。
  • 稳定的训练过程:通过引入跳跃连接,梯度可以更好地传播,从而缓解了梯度消失问题。

3. 发展

自2015年提出以来,残差网络成为了许多深度学习模型的基础架构。随着研究的深入,残差网络的变种也被提出,例如:

  • ResNet:最早的残差网络版本,适用于图像分类等任务。
  • ResNeXt:将残差块中的卷积运算拆分为多个并行的路径,提高了模型的可扩展性。
  • DenseNet:一种变体,进一步增加了层之间的密集连接。

4. 应用

DRN被广泛应用于各种深度学习任务中,特别是在计算机视觉领域表现出色。典型的应用包括:

  • 图像分类:ResNet在ImageNet分类任务中取得了极好的效果,常用于图像分类任务。
  • 目标检测:许多目标检测模型(如Faster R-CNN)都基于残差网络作为主干结构。
  • 语义分割:在语义分割任务中,残差网络作为特征提取器也广泛使用。

5. 优缺点

优点:

  • 有效的深度学习:DRN能够有效训练非常深的网络(可达150层甚至更多),而不会出现明显的性能退化。
  • 跳跃连接:通过跳跃连接,DRN能够更好地传播梯度,解决梯度消失问题,从而加快训练速度。
  • 强大的表达能力:可以通过残差学习获得更高的模型表达能力,适用于复杂的学习任务。

缺点:

  • 计算复杂性高:随着网络深度的增加,计算资源需求显著增加,训练时间可能较长。
  • 模型可解释性差:深度模型的复杂性可能导致难以解释其内部机制和决策过程。
  • 需要大量数据:有效训练深度残差网络通常需要大量标注数据,以防止过拟合。

6. Python代码实现

以下是一个使用深度残差网络进行图像分类的示例,基于PyTorch框架:

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 定义残差块
class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        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.relu = nn.ReLU(inplace=True)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)
        
        # 如果输入维度和输出维度不匹配,通过1x1卷积进行匹配
        self.shortcut = nn.Sequential()
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels)
            )
        
    def forward(self, x):
        out = self.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)  # 跳跃连接
        out = self.relu(out)
        return out

# 定义ResNet模型
class ResNet(nn.Module):
    def __init__(self, block, num_blocks, num_classes=10):
        super(ResNet, self).__init__()
        self.in_channels = 64
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)
        self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)
        self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
        self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
        self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(512, num_classes)
    
    def _make_layer(self, block, out_channels, num_blocks, stride):
        layers = []
        layers.append(block(self.in_channels, out_channels, stride))
        self.in_channels = out_channels
        for _ in range(1, num_blocks):
            layers.append(block(self.in_channels, out_channels))
        return nn.Sequential(*layers)
    
    def forward(self, x):
        out = self.relu(self.bn1(self.conv1(x)))
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        out = self.avg_pool(out)
        out = out.view(out.size(0), -1)
        out = self.fc(out)
        return out

# 实例化ResNet18模型
def ResNet18():
    return ResNet(ResidualBlock, [2, 2, 2, 2])  # 定义ResNet18结构

# 数据预处理
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
])

# 加载CIFAR-10数据集
train_dataset = datasets.CIFAR10(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.CIFAR10(root='./data', train=False, transform=transform, download=True)
train_loader = DataLoader(train_dataset, batch_size=100, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=100, shuffle=False)

# 定义设备、损失函数和优化器
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = ResNet18().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
def train_model(num_epochs=5):
    for epoch in range(num_epochs):
        model.train()
        for images, labels in train_loader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            loss = criterion(outputs, labels)
            
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        
        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

# 评估模型
def test_model():
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        for images, labels in test_loader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    print(f'测试集准确率: {100 * correct / total:.2f}%')

# 运行训练和测试
train_model(num_epochs=5)
test_model()

代码解释:

  • ResidualBlock:实现了残差块,其中包括卷积层、批量归一化(Batch Normalization)、ReLU激活函数和跳跃连接。通过跳跃连接,将输入直接加到输出中,以实现残差学习。
  • ResNet:定义了ResNet模型结构,包括多个残差块的堆叠。_make_layer方法用于构建每一层的残差块。
  • 数据预处理:使用transforms.Compose对CIFAR-10数据集进行转换,进行标准化处理。
  • 模型训练:在train_model函数中,模型通过多轮训练,不断优化损失函数。
  • 模型评估:在test_model函数中,模型评估在测试集上的性能,并输出准确率。

该代码实现了基于深度残差网络的图像分类任务,展示了DRN在实际应用中的有效性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值