深度学习camp-第J3周:DenseNet算法实战与解析

我的环境

  • 语言环境:Python 3.12
  • 编译器:Jupyter Lab
  • 深度学习环境:Pytorch 2.4.1 Torchvision 0.19.1
  • 数据集:鸟类分类数据库(和之前的几周一样)

📌 本周任务:
●2.了解并研究 DenseNet与ResNetV 的区别
●3.改进思路是否可以迁移到其他地方呢(自由探索,虽然不强求但是请认真对待这个哦)

一、前言

在计算机视觉领域,卷积神经网络(CNN)已经成为最主流的方法,比如GoogLenet,VGG-16,Incepetion等模型。CNN史上的一个里程碑事件是ResNet模型的出现,ResNet可以训练出更深的CNN模型,从而实现更高的准确度。ResNet模型的核心是通过建立前面层与后面层之间的“短路连接”(shortcuts,skip connection),进而训练出更深的CNN网络。

今天我们要介绍的是DenseNet模型,它的基本思路与ResNet一致,但是它建立的是前面所有层与后面层的密集连接(dense connection),它的名称也是由此而来。DenseNet的另一大特色是通过特征在channel上的连接来实现特征重用(feature reuse)。这些特点让DenseNet在参数和计算成本更少的情形下实现比ResNet更优的性能,DenseNet也因此斩获CVPR 2017的最佳论文奖。

DenseNet(Densely Connected Convolutional Network)是一种深度神经网络架构,于2017年由Gao Huang等人提出。它是深度学习中经典的卷积神经网络模型之一,在图像分类、目标检测等领域表现优异。DenseNet通过高效的信息流传递和参数复用,实现了更高的性能和更少的参数量。
在这里插入图片描述

以下是对DenseNet的核心思想、结构和优点的详细介绍:


核心思想

DenseNet的关键思想是通过密集连接(Dense Connectivity),实现特征的高效利用。具体来说:

  • 在传统的卷积网络中,层与层之间是线性连接的,数据从上一层流向下一层。而在DenseNet中,每一层都与之前所有层直接相连
  • 每一层的输出不仅作为下一层的输入,还会被直接传递给所有后续层。这样可以更好地保留前面层提取的特征,并减轻梯度消失问题。

网络结构

DenseNet的基本构成可以分为以下几个部分:

  1. Dense Block

    • 在一个Dense Block中,每一层都接收所有前面层的特征图作为输入,并将自己的输出传递给后续所有层。
    • 对于第( l )层,其输入是所有之前层的特征的级联,公式为:
      x l = H l ( [ x 0 , x 1 , … , x l − 1 ] ) x_l = H_l([x_0, x_1, \ldots, x_{l-1}]) xl=Hl([x0,x1,,xl1])
      其中, H l H_l Hl表示第 l l l层的非线性变换(如卷积、ReLU等), [ x 0 , x 1 , … , x l − 1 ] [x_0, x_1, \ldots, x_{l-1}] [x0,x1,,xl1]表示特征级联。
  2. Transition Layer

    • 在Dense Blocks之间插入Transition Layer,用于降维和控制模型复杂度。
    • Transition Layer通常由一个 1 × 1 1\times1 1×1卷积层和一个平均池化层(Average Pooling)组成。
  3. Growth Rate

    • DenseNet的一个重要超参数是增长率(Growth Rate, ( k )),表示每一层新增的特征图数量。
    • Growth Rate较小时,模型更轻量化;较大时,模型更有表达能力。
  4. 全局池化和分类层

    • 最终通过全局平均池化层(Global Average Pooling)和全连接层实现分类。

优点

  1. 参数高效

    • DenseNet通过特征复用显著减少了参数量,因为不需要重复计算冗余特征。
  2. 缓解梯度消失

    • DenseNet的直接连接机制使得梯度可以从后层更轻松地传递到前面层,改善了深度网络的训练。
  3. 特征复用

    • 每一层的特征都会被后续层反复利用,显著提高了模型的表达能力。
  4. 更深的网络

    • DenseNet的架构支持更深的网络,而不会带来过高的计算开销。

DenseNet的变体

DenseNet有多个变体,适应不同的任务和场景:

  1. DenseNet-121、DenseNet-169、DenseNet-201、DenseNet-264

    • 基于层数的不同,适用于从轻量级到更复杂的任务。
  2. DenseNet在语义分割、目标检测中的扩展

    • 如在DenseASPP等模型中,与空洞卷积结合,用于语义分割任务。

应用场景

  • 图像分类:如ImageNet、CIFAR-10等数据集上表现优异。
  • 目标检测:DenseNet作为特征提取器,广泛应用于Faster R-CNN等模型中。
  • 医学影像分析:DenseNet因其高效的特征表达能力,常用于CT图像分类、病灶检测等任务。

DenseNet与ResNet的比较

特性 DenseNet ResNet
连接方式 每层与之前所有层连接 仅与前一层的输出相加
参数量 更少 较多
特征复用 高效 较低
梯度流动 更顺畅 较好
适用场景 更深、更复杂的网络 多种场景,适用性广泛

二、设计理念

相比ResNet,DenseNet提出了一种更为激进的密集链接机制:即互相连接所有的层,具体来说就是每个层都会接受前面所有层作为其额外的输入。

图1展示了ResNet的残差连接机制,作为对比,后面图2为DenseNet的密集连接机制。可以看到,ResNet是每个层与前面的某层短路连接在一起,连接方式是元素相加,而DenseNet中,每个层都会与前面所有层在channel维度上concat在一起,元素叠加,并作为下一层的输入。
图一图1
在这里插入图片描述图2

三、网络结构

CNN网络一般要经过Pooling或者Stride > 1的Conv来降低特征图的大小,而DenseNet的密集连接方式需要特征图大小保持一致。为了解决这个问题,DenseNet网络中使用DenseBlock + Transition的结构,其中DenseBlock是包含很多层的模块,每个层的特征图大小相同,层与层之间采用密集连接方式,Transition层是链接两个相邻的DenseBlock,通过Pooling使特征图的大小降低。

在这里插入图片描述

四、网络构建

1、Denselayer的构建

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils import model_zoo


class _DenseLayer(nn.Sequential):
    def __init__(self,num_input_features, growth_rate, bn_size, drop_rate):
        super(_DenseLayer,self).__init__()
        self.add_module('norm1',nn.BatchNorm2d(num_input_features))
        self.add_module('relu1',nn.ReLU(inplace=True))
        self.add_module('conv1',nn.Conv2d(num_input_features, bn_size*growth_rate, kernel_size=1, stride=1,bias = False))
        self.add_module('norm2',nn.BatchNorm2d(bn_size*growth_rate))
        self.add_module('relu2',nn.ReLU(inplace=True))
        self.add_module('conv2',nn.Conv2d(bn_size*growth_rate, growth_rate, kernel_size=3, stride=1,padding=1, bias = False))
        self.drop_rate = drop_rate
        
    def forward(self,x):
        new_features = super(_DenseLayer, self).forward(x)
        if self.drop_rate > 0:
            new_features = F.dropout(new_features, p=self.drop_rate, training=self.training)
            
        return torch.cat([x, new_features],1)

2、Denseblock的构建

class _DenseBlock(nn.Sequential):
    def __init__(self, num_layers, num_input_features, bn_size, growth_rate, drop_rate):
        super(_DenseBlock,self).__init__()
        for i in range(num_layers):
            layer = _DenseLayer(num_input_features+i*growth_rate, growth_rate, bn_size, drop_rate)
            self.add_module('denselayer%d'%(i+1,),layer)

3、Transition的构建

class _Transition(nn.Sequential):
    def __init__(self, num_input_features, num_output_features):
        super(_Transition,self).__init__()
        self.add_module('norm',nn.BatchNorm2d(num_input_features))
        self.add_module('relu',nn.ReLU(inplace=True))
        self.add_module('conv',nn.Conv2d(num_input_features, num_output_features, kernel_size=1,stride=1, bias=False))
        self.add_module('pool',nn.AvgPool2d(2, stride=2))

4、完整网络构建

from collections import OrderedDict

class DenseNet(nn.Module):
    def __init__(self, growth_rate = 32, block_config =(6, 12, 24, 16), num_init_features=64, bn_size = 4, compression_rate = 0.5, drop_rate = 0, num_classes = 1000):
        
        super(DenseNet, self).__init__()
        self.features = nn.Sequential(OrderedDict([
            ('conv0', nn.Conv2d(3, num_init_features, kernel_size=7, stride=2, padding=3, bias=False)),
            ('norm0', nn.BatchNorm2d(num_init_features)),
            ('relu0', nn.ReLU(inplace=True)),
            ('pool0', nn.MaxPool2d(3, stride=2, padding=1))
        ]))
        
        num_features = num_init_features
        for i, num_layers in enumerate(block_config):
            block = _DenseBlock(num_layers, num_features, bn_size, growth_rate, drop_rate)
            self.features.add_module('denseblock%d' % (i + 1), block)
            num_features += num_layers * growth_rate
            if i != len(block_config) - 1:
                transition = _Transition(num_features, int(num_features * compression_rate))
                self.features.add_module('transition%d' % (i + 1), transition)
                num_features = int(num_features * compression_rate)
                
        self.features.add_module('norm5', nn.BatchNorm2d(num_features))
        self.features.add_module('relu5', nn.ReLU(inplace=True))
        
        self.classifier = nn.Linear(num_features, num_classes)
        
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.bias, 0)
                nn.init.constant_(m.weight,1)
            elif isinstance(m, nn.Linear):
                nn.init.constant_(m.bias,0)
        
    
    def forward(self, x):
        features = self.features(x)
        out = F.avg_pool2d(features, kernel_size=features.size()[2:]).view(features.size(0), -1)
        out = self.classifier(out)
        return out

使用densenet121:

import re
from torch.utils import model_zoo

model_urls = {
   
   
    'densenet121': 'https://download.pytorch.org/models/densenet121-a639ec97.pth',
}

def densenet121(pretrained=False, **kwargs):
    model = DenseNet(num_init_features = 64, growth_rate = 32, block_config = (6, 12, 24, 16), **kwargs)
    
    if pretrained:
        pattern = re.compile(r'^(.*denselayer\d+\.(?:norm|relu|conv))\.((?:[12])\.(?:weight|bias|running_mean|running_var))$')
        state_dict = model_zoo.load_url(model_urls['densenet121'])
        for key in list(state_dict.keys()):
            res = pattern.match(key)
            if res:
                new_key = res.group(1)+res.group(2)
                state_dict[new_key] = state_dict[key]
                del state_dict[key]
        
        model.load_state_dict(state_dict)
        return model

我们可以查看网络:

import torchsummary
model = densenet121(pretrained=True).cuda()
x = (3, 224, 224)

torchsummary.summary(model,x)

代码输出:

----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv2d-1         [-1, 64, 112, 112]           9,408
       BatchNorm2d-2         [-1, 64, 112, 112]             128
              ReLU-3         [-1, 64, 112, 112]               0
         MaxPool2d-4           [-1, 64, 56, 56]               0
       BatchNorm2d-5           [-1, 64, 56, 56]             128
              ReLU-6           [-1, 64, 56, 56]               0
            Conv2d-7          [-1, 128, 56, 56]           8,192
       BatchNorm2d-8          [-1, 128, 56, 56]             256
              ReLU-9          [-1, 128, 56, 56]               0
           Conv2d-10           [-1, 32, 56, 56]          36,864
      BatchNorm2d-11           [-1, 96, 56, 56]             192
             ReLU-12           [-1, 96, 56, 56]               0
           Conv2d-13          [-1, 128, 56, 56]          12,288
      BatchNorm2d-14          [-1, 128, 56, 56]             256
             ReLU-15          [-1, 128, 56, 56]               0
           Conv2d-16           [-1, 32, 56, 56]          36,864
      BatchNorm2d-17          [-1, 128, 56, 56]             256
             ReLU-18          [-1, 128, 56, 56]               0
           Conv2d-19          [-1, 128, 56, 56]          16,384
      BatchNorm2d-20          [-1, 128, 56, 56]             256
             ReLU-21          [-1, 128, 56, 56]               0
           Conv2d-22           [-1, 32, 56, 56]          36,864
      BatchNorm2d-23          [-1, 160, 56, 56]             320
             ReLU-24          [-1, 160, 56, 56]               0
           Conv2d-25          [-1, 128, 56, 56]          20,480
      BatchNorm2d-26          [-1, 128, 56, 56]             256
             ReLU-27          [-1, 128, 56, 56]               0
           Conv2d-28           [-1, 32, 56, 56]          36,864
      BatchNorm2d-29          [-1, 192, 56, 56]             384
             ReLU-30          [-1, 192, 56, 56]               0
           Conv2d-31          [-1, 128, 56, 56]          24,576
      BatchNorm2d-32          [-1, 128, 56, 56]             256
             ReLU-33          [-1, 128, 56, 56]               0
           Conv2d-34           [-1, 32, 56, 56]          36,864
      BatchNorm2d-35          [-1, 224, 56, 56]             448
             ReLU-36          [-1, 224, 56, 56]               0
           Conv2d-37          [-
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值