使用 TensorFlow 创建 DenseNet 121

一、说明

本篇示意DenseNet如何在tensorflow上实现,DenseNet与ResNet有类似的地方,都有层与层的"短路"方式,但两者对层的短路后处理有所不同,本文遵照原始论文的技术路线,完整复原了DenseNet的全部网络。

图1:DenseNet中的各种块和层(来源:原始DenseNet论文)

二、DenseNet综述

DenseNet(密集卷积网络)是一种架构,专注于使深度学习网络更深入,但同时通过在层之间使用更短的连接来提高它们的训练效率。DenseNet 是一个卷积神经网络,其中每一层都连接到网络中更深的所有其他层,即第一层连接到第 2、3、4 层等,第二层连接到第 3、4、5 层等。这样做是为了在网络各层之间实现最大的信息流。为了保持前馈特性,每一层从前面的所有层获取输入,并将自己的特征图传递给它将要到达的所有层。与 Resnets 不同,它不是通过求和来组合特征,而是通过连接它们来组合特征。因此,"ith"层具有"i"输入,并且由其所有先前卷积块的特征图组成。它自己的特征图被传递到所有下一个"I-i"层。这在网络中引入了"(I(I+1)))/2"连接,而不是像传统深度学习架构中那样只是"I"连接。因此,与传统的卷积神经网络相比,它需要的参数更少,因为不需要学习不重要的特征图。

DenseNet由两个重要的块组成,而不是基本的卷积层和池化层。它们是密集块和过渡层。

接下来,我们看看所有这些块和层的外观,以及如何在 python 中实现它们。

图2:DenseNet121框架(来源:DenseNet原始论文,由作者编辑)

DenseNet从基本的卷积和池化层开始。然后有一个密集块,然后是一个过渡层,另一个密集块后跟一个过渡层,另一个密集块后跟一个过渡层,最后是一个密集块,然后是一个分类层。

第一个卷积块有 64 个大小为 7x7 的过滤器,步幅为 2。接下来是最大池化为 3x3 且步幅为 2 的 MaxPooling 层。这两行可以在 python 中用以下代码表示。

ax 复制代码
input = Input (input_shape)
x = Conv2D(64, 7, strides = 2, padding = 'same')(input)
x = MaxPool2D(3, strides = 2, padding = 'same')(x)

2.1 定义卷积块 ---

输入后的每个卷积块具有以下顺序:批处理归一化,然后是 ReLU 激活,然后是实际的 Conv2D 层。为了实现这一点,我们可以编写以下函数。

ax 复制代码
#batch norm + relu + conv
def bn_rl_conv(x,filters,kernel=1,strides=1):
        
    x = BatchNormalization()(x)
    x = ReLU()(x)
    x = Conv2D(filters, kernel, strides=strides,padding = 'same')(x)
    return x

图3.密集块(来源:DenseNet论文-作者编辑)

2.2 定义密集块 ---

如图 3 所示,每个密集块都有两个卷积,具有 1x1 和 3x3 大小的内核。在密集块 1 中,重复 6 次,在密集块 2 中重复 12 次,在密集块 3 中重复 24 次,最后在密集块 4 中重复 16 次。

在密集块中,每个 1x1 卷积都有 4 倍的滤波器数量。所以我们使用 4*过滤器,但 3x3 过滤器只存在一次。此外,我们必须将输入与输出张量连接起来。

每个块分别运行 6、12、24、16 次重复,使用 'for 循环'。

ax 复制代码
def dense_block(x, repetition):
        
   for _ in range(repetition):
        y = bn_rl_conv(x, 4*filters)
        y = bn_rl_conv(y, filters, 3)
        x = concatenate([y,x])
   return x

图4:过渡层(来源:DenseNet论文,作者编辑)

2.3 定义过渡层

--- 在过渡层中,我们将通道数减少到现有通道的一半。有一个 1x1 卷积层和一个 2x2 平均池化层,步幅为 2。bn_rl_conv,函数中已经设置了 1x1 的内核大小,因此我们不需要明确地再次定义它。

在过渡层中,我们必须将通道删除到现有通道的一半。我们有输入张量x,我们想找到有多少个通道,我们需要得到其中的一半。因此,我们可以使用 Keras 后端 (K) 获取张量 x 并返回一个维度为 x 的元组。而且,我们只需要该形状的最后一个数字,即过滤器的数量。所以我们加上 -1。最后,我们可以将这个数量的过滤器除以 2 以获得所需的结果。

ax 复制代码
def transition_layer(x):
        
    x = bn_rl_conv(x, K.int_shape(x)[-1] //2 )
    x = AvgPool2D(2, strides = 2, padding = 'same')(x)
    return x

因此,我们完成了定义密集块和过渡层的工作。现在我们需要将密集块和过渡层堆叠在一起。所以我们写了一个 for 循环来运行 6,12,24,16 次重复。因此,循环运行 4 次,每次使用 6、12、24 或 16 中的值之一。这样就完成了 4 个密集块和过渡层。

ax 复制代码
for repetition in [6,12,24,16]:
        
    d = dense_block(x, repetition)
    x = transition_layer(d)

最后,是GlobalAveragePooling,然后是最终的输出层。正如我们在上面的代码块中看到的,密集块由"d"定义,而在最后一层,在密集块 4 之后,没有过渡层 4,而是直接进入分类层。因此,"d"是应用GlobalAveragePooling的连接,而不是"x"。另一种选择是从上面的代码中删除"for"循环,并将层一个接一个地堆叠,而不使用最终的过渡层。

ax 复制代码
x = GlobalAveragePooling2D()(d)
output = Dense(n_classes, activation = 'softmax')(x)

现在我们已经将所有块放在一起,让我们将它们合并以查看整个DenseNet架构。

三、完整的 DenseNet 121 架构

ax 复制代码
import tensorflow as tf
from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Dense
from tensorflow.keras.layers import AvgPool2D, GlobalAveragePooling2D, MaxPool2D
from tensorflow.keras.models import Model
from tensorflow.keras.layers import ReLU, concatenate
import tensorflow.keras.backend as K
# Creating Densenet121
def densenet(input_shape, n_classes, filters = 32):
    
    #batch norm + relu + conv
    def bn_rl_conv(x,filters,kernel=1,strides=1):
        
        x = BatchNormalization()(x)
        x = ReLU()(x)
        x = Conv2D(filters, kernel, strides=strides,padding = 'same')(x)
        return x
    
    def dense_block(x, repetition):
        
        for _ in range(repetition):
            y = bn_rl_conv(x, 4*filters)
            y = bn_rl_conv(y, filters, 3)
            x = concatenate([y,x])
        return x
        
    def transition_layer(x):
        
        x = bn_rl_conv(x, K.int_shape(x)[-1] //2 )
        x = AvgPool2D(2, strides = 2, padding = 'same')(x)
        return x
    
    input = Input (input_shape)
    x = Conv2D(64, 7, strides = 2, padding = 'same')(input)
    x = MaxPool2D(3, strides = 2, padding = 'same')(x)
    
    for repetition in [6,12,24,16]:
        
        d = dense_block(x, repetition)
        x = transition_layer(d)
    x = GlobalAveragePooling2D()(d)
    output = Dense(n_classes, activation = 'softmax')(x)
    
    model = Model(input, output)
    return model
input_shape = 224, 224, 3
n_classes = 3
model = densenet(input_shape,n_classes)
model.summary()

输出:(假设 3 个最终类 --- 模型摘要的最后几行)

四、 查看体系结构关系图

可以使用以下代码。

ax 复制代码
from tensorflow.python.keras.utils.vis_utils import model_to_dot
from IPython.display import SVG
import pydot
import graphviz

SVG(model_to_dot(
    model, show_shapes=True, show_layer_names=True, rankdir='TB',
    expand_nested=False, dpi=60, subgraph=False
).create(prog='dot',format='svg'))

输出 --- 图表的前几个块

这就是我们如何实现DenseNet 121架构。

五、引用

  1. 黄高、刘壮、劳伦斯·范德马滕和基利安·温伯格,密集连接的卷积网络,arXiv 1608.06993 (2016)
    阿琼·萨卡尔

2 密网论文链接:https://arxiv.org/pdf/1608.06993.pdf

相关推荐
Seoyoneh1 分钟前
2026年呼叫中心选型技术指南:架构、API与高可用维度的评估清单
人工智能·信息与通信·通信
xierui1231235 分钟前
AI 视频横版改竖版:先保住操作动作,再调整裁切中心
人工智能
东莞市云毅网络有限公司6 分钟前
用标准库做网页正文抽取:从 HTML 到结构化字段的轻量实现
python·sqlite·自动化运维·geo·数据监测
高级程序源11 分钟前
django校企合作实习基地管理系统82506-计算机课程设计、毕业设计
vue.js·后端·python·mysql·django·课程设计·pygame
东方芷兰12 分钟前
Agent 技术摘要 02 —— 区块链、比特币、挖矿、ETF、比特币疯涨事件、以太坊、以太币、显卡荒事件
人工智能·笔记·python
万物智能信息科技14 分钟前
GPIO控制状态灯—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
人工智能·华为·开源·harmonyos·鸿蒙
像豆芽一样优秀16 分钟前
用 Codex + 阿里云 DataWorks MCP 实现数仓自动化
大数据·人工智能
FII工业富联科技服务20 分钟前
从台积电微通道散热看 AI 热管理:热量究竟如何从芯片走向机房?
人工智能·ai
贝猫说python23 分钟前
阿里云部署qwen 大模型从0-1,第2步:阿里云平台创建ubuntu实例
python
峥嵘life23 分钟前
2026华为AI码道 CodeArts 使用分享:Windows端 + 服务器CLI 实战总结
android·大数据·开发语言·python