Densely connected convolutional networks(DenseNet)论文解读以及运行(Caffe)

DenseNet网络通过密集连接方式解决了传统网络中的梯度消失问题,增强了特征传播并减少了参数数量。本文介绍了DenseNet的基本原理,包括其不同版本如DenseNet-121、DenseNet-169等,并提供了实验结果对比。此外,还讨论了在网络训练过程中遇到的具体问题及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DenseNet网络是应用由于分类任务中,在目前较好的分类网络ResNets,他们之前存在着很多的不同。下面先来说一说。
先来看一看网络模型结构:
这里写图片描述
DenseNet主要解决的问题是缓解了消失梯度的问题,增强了特征的传播,促进了特征再利用,大大减少了参数的数量,使网络模型模型进行缩减。
这篇文章中作者主要用了DenseNet-121(k=32),DenseNet-169(k=32),DenseNet-201(k=32),DenseNet-161(k=48),在三个数据集中进行测试。下图是测试结果。
这里写图片描述
DenseNet是一种密集的连接模式的直观效果,它需要的参数比传统的娟娟网络少,无需重新学习冗余的特征图,改善看网络中的信息流和梯度。对于ResNet是采用合并之前所有层的特征到一层的方法。
下面是图片输入后进行操作,值得注意的是Dense里面每一张特征图谱的连接的前提条件必须是特征图谱的大小一致,不然的话也是不可能连接的在文章中作者说的并不是很清楚,我们可以从提供的代码中进行解读,以及对于图像是如何进行降维的在文中我们可以知道两个Dense之间是有一个卷积层和一个pooling层,降维的操作主要是在pooling层中进行,卷积层并没有。
这里写图片描述
下面是文章的链接以及代码的链接:
https://arxiv.org/pdf/1608.06993.pdf
https://github.com/liuzhuang13/DenseNet
一个博客对文章的翻译:
https://blog.youkuaiyun.com/tumi678/article/details/78667966?locationNum=7&fps=1

代码实现(Caffe)

后续pytorch以及keras等代码实现之后补充
在利用本文提供的代码对自己的数据进行训练:网络结构的prototxt文件地址:https://github.com/shicai/DenseNet-Caffe
在利用的时候出现的问题:
Question:https://github.com/shicai/DenseNet-Caffe/issues/10
fine-tuning:https://github.com/shicai/DenseNet-Caffe/issues/8
[libprotobuf ERROR google/protobuf/text_format.cc:245] Error parsing text-format caffe.NetParameter: 54:14: Message type “caffe.PoolingParameter” has no field named “ceil_mode”.
解决办法:https://github.com/BVLC/caffe/pull/3057/files
其中,include/caffe/vision_layers.hpp,由于caffe结构已经修改,没有这个文件了,经过查看之后,发现该文件是:/home/ccf/caffe/include/caffe/layers/pooling_layer.hpp
第一步:/home/ccf/caffe/include/caffe/layers/pooling_layer.hpp

   int height_, width_;
   int pooled_height_, pooled_width_;
   bool global_pooling_;
+  bool ceil_mode_;
   Blob<Dtype> rand_idx_;
   Blob<int> max_idx_;
 };

第二步: src/caffe/layers/pooling_layer.cpp

       || (!pool_param.has_stride_h() && !pool_param.has_stride_w()))
       << "Stride is stride OR stride_h and stride_w are required.";
   global_pooling_ = pool_param.global_pooling();
+  ceil_mode_ = pool_param.ceil_mode();
   if (global_pooling_) {
     kernel_h_ = bottom[0]->height();
     kernel_w_ = bottom[0]->width();
@@ -90,10 +91,18 @@ void PoolingLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
     kernel_h_ = bottom[0]->height();
     kernel_w_ = bottom[0]->width();
   }
-  pooled_height_ = static_cast<int>(ceil(static_cast<float>(
-      height_ + 2 * pad_h_ - kernel_h_) / stride_h_)) + 1;
-  pooled_width_ = static_cast<int>(ceil(static_cast<float>(
-      width_ + 2 * pad_w_ - kernel_w_) / stride_w_)) + 1;
+  // Specify the structure by ceil or floor mode
+  if (ceil_mode_) {
+    pooled_height_ = static_cast<int>(ceil(static_cast<float>(
+        height_ + 2 * pad_h_ - kernel_h_) / stride_h_)) + 1;
+    pooled_width_ = static_cast<int>(ceil(static_cast<float>(
+        width_ + 2 * pad_w_ - kernel_w_) / stride_w_)) + 1;
+  } else {
+    pooled_height_ = static_cast<int>(floor(static_cast<float>(
+        height_ + 2 * pad_h_ - kernel_h_) / stride_h_)) + 1;
+    pooled_width_ = static_cast<int>(floor(static_cast<float>(
+        width_ + 2 * pad_w_ - kernel_w_) / stride_w_)) + 1;
+  }
   if (pad_h_ || pad_w_) {
     // If we have padding, ensure that the last pooling starts strictly
     // inside the image (instead of at the padding); otherwise clip the last.

第三步:src/caffe/proto/caffe.proto

   // If global_pooling then it will pool over the size of the bottom by doing
   // kernel_h = bottom->height and kernel_w = bottom->width
   optional bool global_pooling = 12 [default = false];
+  // Specify floor/ceil mode
+  optional bool ceil_mode = 13 [default = true];
 }

 message PowerParameter {

之后对caffe重新编译make -j128

### Densely Connected Convolutional Networks (DenseNet) 的架构与实现 #### 架构解释 DenseNet 是一种密集连接的卷积神经网络,其核心思想在于通过层间的密集连接来增强特征重用并减少冗余参数。在传统卷积网络中,每一层仅与其相邻的一层相连;而在 DenseNet 中,每层不仅接受前一层的输入,还接受之前所有层的输出作为额外输入[^3]。 这种设计使得 DenseNet 能够显著降低模型复杂度和计算成本,因为各层无需重复学习相同的特征图。具体而言,DenseNet 将每一层的输出视为全局状态的一部分,并允许后续层访问这些状态。因此,最终分类器能够利用整个网络中的所有特征图进行预测[^5]。 #### 实现方法 以下是 DenseNet 的基本构建单元及其 Python 实现: 1. **Dense Block**: 密集块由多个卷积层组成,其中每一层都将自身的输出与其他层的输出拼接在一起。 2. **Transition Layer**: 这些层用于控制特征图的数量以及缩小图像尺寸,通常包括批量归一化(Batch Normalization)、ReLU 和平均池化操作。 下面是基于 PyTorch 的简单实现示例: ```python import torch.nn as nn import torch class Bottleneck(nn.Module): """Bottleneck 层""" def __init__(self, in_channels, growth_rate): super(Bottleneck, self).__init__() inter_channel = 4 * growth_rate self.bn1 = nn.BatchNorm2d(in_channels) self.conv1 = nn.Conv2d(in_channels, inter_channel, kernel_size=1, bias=False) self.relu = nn.ReLU(inplace=True) self.bn2 = nn.BatchNorm2d(inter_channel) self.conv2 = nn.Conv2d(inter_channel, growth_rate, kernel_size=3, padding=1, bias=False) def forward(self, x): out = self.conv1(self.relu(self.bn1(x))) out = self.conv2(self.relu(self.bn2(out))) out = torch.cat((x, out), dim=1) # 特征图拼接 return out class Transition(nn.Module): """过渡层""" def __init__(self, in_channels, out_channels): super(Transition, self).__init__() self.bn = nn.BatchNorm2d(in_channels) self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False) self.avg_pool = nn.AvgPool2d(kernel_size=2, stride=2) def forward(self, x): out = self.conv(self.bn(x)) out = self.avg_pool(out) return out class DenseNet(nn.Module): """DenseNet 主体结构""" def __init__(self, num_init_features, growth_rate, block_config, num_classes=1000): super(DenseNet, self).__init__() self.features = nn.Sequential( nn.Conv2d(3, num_init_features, kernel_size=7, stride=2, padding=3, bias=False), nn.BatchNorm2d(num_init_features), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=3, stride=2, padding=1) ) num_features = num_init_features for i, num_layers in enumerate(block_config): dense_block = self._make_dense_block(growth_rate, num_layers, num_features) self.features.add_module(f"denseblock{i + 1}", dense_block) num_features += num_layers * growth_rate if i != len(block_config) - 1: trans_layer = Transition(num_features, num_features // 2) self.features.add_module(f"transition{i + 1}", trans_layer) num_features //= 2 self.classifier = nn.Linear(num_features, num_classes) def _make_dense_block(self, growth_rate, n_layers, input_channels): layers = [] for _ in range(n_layers): layers.append(Bottleneck(input_channels, growth_rate)) input_channels += growth_rate return nn.Sequential(*layers) def forward(self, x): features = self.features(x) out = nn.functional.adaptive_avg_pool2d(features, (1, 1)) out = torch.flatten(out, 1) out = self.classifier(out) return out ``` 上述代码定义了一个基础版本的 DenseNet 模型,其中包括瓶颈层(`Bottleneck`)和过渡层(`Transition`)。通过调整 `growth_rate` 和 `block_config` 参数,可以灵活配置不同的 DenseNet 变种。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值