0

0

《动手学深度学习》Paddle 版源码-5.12章(DenseNet)

P粉084495128

P粉084495128

发布时间:2025-08-01 10:47:56

|

790人浏览过

|

来源于php中文网

原创

稠密连接网络(DenseNet)是由 ResNet 跨层连接设计引申而来。它的模块间通过在通道维连结输出,实现稠密连接。DenseNet 主要由稠密块和过渡层构成,前者实现输入输出的连结,后者控制通道数。文中还给出了 DenseNet 在 PaddlePaddle 中的实现代码及训练过程。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

《动手学深度学习》paddle 版源码-5.12章(densenet) - php中文网

稠密连接网络(DenseNet)

ResNet中的跨层连接设计引申出了数个后续工作。本节我们介绍其中的一个:稠密连接网络(DenseNet) [1]。 它与ResNet的主要区别如图5.10所示。       

图5.10中将部分前后相邻的运算抽象为模块AA和模块BB。与ResNet的主要区别在于,DenseNet里模块BB的输出不是像ResNet那样和模块AA的输出相加,而是在通道维上连结。这样模块AA的输出可以直接传入模块BB后面的层。在这个设计里,模块AA直接跟模块BB后面的所有层连接在了一起。这也是它被称为“稠密连接”的原因。

DenseNet的主要构建模块是稠密块(dense block)和过渡层(transition layer)。前者定义了输入和输出是如何连结的,后者则用来控制通道数,使之不过大。

稠密块

DenseNet使用了ResNet改良版的“批量归一化、激活和卷积”结构(参见上一节的练习),我们首先在BNConv函数里实现这个结构。

In [1]
import paddleimport paddle.nn as nnimport numpy as npimport warnings
warnings.filterwarnings("ignore", category=Warning) # 过滤报警信息class BNConv(nn.Layer):
    def __init__(self, num_channels, num_filters):
        super(BNConv, self).__init__()
        model = [
            nn.BatchNorm2D(num_channels),
            nn.ReLU(),
            nn.Conv2D(num_channels, num_filters, 3, stride=1, padding=1)
        ]
        self.model = nn.Sequential(*model)    def forward(self, X):
        return self.model(X)
   

稠密块由多个BNConv组成,每块使用相同的输出通道数。但在前向计算时,我们将每块的输入和输出在通道维上连结。

Napkin AI
Napkin AI

Napkin AI 可以将您的文本转换为图表、流程图、信息图、思维导图视觉效果,以便快速有效地分享您的想法。

下载
In [2]
class DenseBlock(nn.Layer):
    def __init__(self, num_channels, num_layers, growth_rate):
        super(DenseBlock, self).__init__()
        self.dense_blocks = []        for i in range(num_layers):
            block = self.add_sublayer(str(i), BNConv(num_channels + i * growth_rate, growth_rate))
            self.dense_blocks.append(block)    def forward(self, X):
        for block in self.dense_blocks:
            X = paddle.concat([X, block(X)], axis=1)        return X
   

在下面的例子中,我们定义一个有2个输出通道数为10的卷积块。使用通道数为3的输入时,我们会得到通道数为3+2×10=233+2×10=23的输出。卷积块的通道数控制了输出通道数相对于输入通道数的增长,因此也被称为增长率(growth rate)。

In [3]
blk = DenseBlock(3, 2, 10)
X = paddle.to_tensor(np.random.uniform(-1., 1., [4, 3, 8, 8]).astype('float32'))
Y = blk(X)print(Y.shape)
       
[4, 23, 8, 8]
       

过渡层

由于每个稠密块都会带来通道数的增加,使用过多则会带来过于复杂的模型。过渡层用来控制模型复杂度。它通过1×11×1卷积层来减小通道数,并使用步幅为2的平均池化层减半高和宽,从而进一步降低模型复杂度。

In [4]
class TransitionLayer(nn.Layer):
    def __init__(self, num_channels, num_filters):
        super(TransitionLayer, self).__init__()
        model = [
            nn.BatchNorm2D(num_channels),
            nn.ReLU(),
            nn.Conv2D(num_channels, num_filters, 1, stride=1),
            nn.AvgPool2D(kernel_size=2, stride=2)
        ]
        self.model = nn.Sequential(*model)    def forward(self, X):
        return self.model(X)
   

对上一个例子中稠密块的输出使用通道数为10的过渡层。此时输出的通道数减为10,高和宽均减半。

In [5]
blk = TransitionLayer(23, 10)
Y2 = blk(Y)print(Y2.shape)
       
[4, 10, 4, 4]
       

DenseNet模型

我们来构造DenseNet模型。

In [6]
class DenseNet(nn.Layer):
    def __init__(self, num_classes=10):
        super(DenseNet, self).__init__()        # DenseNet首先使用同ResNet一样的单卷积层和最大池化层。
        model = [
            nn.Conv2D(1, 64, 7, stride=2, padding=3),
            nn.BatchNorm2D(64),
            nn.ReLU(),
            nn.MaxPool2D(kernel_size=3, stride=2, padding=1)
        ]        # 类似于ResNet接下来使用的4个残差块,DenseNet使用的是4个稠密块。
        # 同ResNet一样,我们可以设置每个稠密块使用多少个卷积层。
        # 这里我们设成4,从而与上一节的ResNet-18保持一致。
        # 稠密块里的卷积层通道数(即增长率)设为32,所以每个稠密块将增加128个通道。
        # ResNet里通过步幅为2的残差块在每个模块之间减小高和宽。这里我们则使用过渡层来减半高和宽,并减半通道数。
        num_channels, growth_rate = 64, 32  # num_channels为当前的通道数
        num_convs_in_dense_blocks = [4, 4, 4, 4]        for i, num_convs in enumerate(num_convs_in_dense_blocks):
            model += [DenseBlock(num_channels, num_convs, growth_rate)]            # 上一个稠密块的输出通道数
            num_channels += num_convs * growth_rate            # 在稠密块之间加入通道数减半的过渡层
            if i != len(num_convs_in_dense_blocks) - 1:
                model += [TransitionLayer(num_channels, num_channels // 2)]
                num_channels //= 2

        # 同ResNet一样,最后接上全局池化层和全连接层来输出。
        model += [
            nn.AdaptiveAvgPool2D(output_size=1),
            nn.Flatten(start_axis=1, stop_axis=-1),
            nn.Linear(num_channels, num_classes),
        ]
        self.model = nn.Sequential(*model)    def forward(self, X):
        Y = self.model(X)        return Y

dn = DenseNet(10)
X = paddle.to_tensor(np.random.uniform(-1., 1., [4, 1, 96, 96]).astype('float32'))
Y = dn(X)print(Y.shape)
       
[4, 10]
       

训练模型

In [7]
import paddleimport paddle.vision.transforms as Tfrom paddle.vision.datasets import FashionMNIST# 数据集处理transform = T.Compose([
    T.Resize(96),
    T.Transpose(),
    T.Normalize([127.5], [127.5]),
])
train_dataset = FashionMNIST(mode='train', transform=transform)
val_dataset = FashionMNIST(mode='test', transform=transform)# 模型定义model = paddle.Model(DenseNet(10))# 设置训练模型所需的optimizer, loss, metricmodel.prepare(
    paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters()),
    paddle.nn.CrossEntropyLoss(),
    paddle.metric.Accuracy(topk=(1, 5)))# 启动训练、评估model.fit(train_dataset, val_dataset, epochs=2, batch_size=64, log_freq=100)
       
The loss value printed in the log is the current step, and the metric is the average value of previous step.
Epoch 1/2
step 100/938 - loss: 0.4821 - acc_top1: 0.7208 - acc_top5: 0.9803 - 2s/step
step 200/938 - loss: 0.3669 - acc_top1: 0.7677 - acc_top5: 0.9875 - 2s/step
step 300/938 - loss: 0.3581 - acc_top1: 0.7905 - acc_top5: 0.9905 - 2s/step
step 400/938 - loss: 0.3215 - acc_top1: 0.8042 - acc_top5: 0.9922 - 2s/step
step 500/938 - loss: 0.3757 - acc_top1: 0.8154 - acc_top5: 0.9933 - 2s/step
step 600/938 - loss: 0.2171 - acc_top1: 0.8244 - acc_top5: 0.9940 - 2s/step
step 700/938 - loss: 0.2634 - acc_top1: 0.8314 - acc_top5: 0.9946 - 2s/step
step 800/938 - loss: 0.5456 - acc_top1: 0.8378 - acc_top5: 0.9950 - 2s/step
step 900/938 - loss: 0.1972 - acc_top1: 0.8429 - acc_top5: 0.9955 - 2s/step
step 938/938 - loss: 0.3683 - acc_top1: 0.8438 - acc_top5: 0.9955 - 2s/step
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 100/157 - loss: 0.2342 - acc_top1: 0.8733 - acc_top5: 0.9972 - 584ms/step
step 157/157 - loss: 0.2860 - acc_top1: 0.8744 - acc_top5: 0.9976 - 582ms/step
Eval samples: 10000
Epoch 2/2
step 100/938 - loss: 0.2843 - acc_top1: 0.8853 - acc_top5: 0.9988 - 2s/step
step 200/938 - loss: 0.3403 - acc_top1: 0.8915 - acc_top5: 0.9990 - 2s/step
step 300/938 - loss: 0.1578 - acc_top1: 0.8936 - acc_top5: 0.9991 - 2s/step
step 400/938 - loss: 0.0841 - acc_top1: 0.8938 - acc_top5: 0.9989 - 2s/step
step 500/938 - loss: 0.3375 - acc_top1: 0.8961 - acc_top5: 0.9988 - 2s/step
step 600/938 - loss: 0.2915 - acc_top1: 0.8964 - acc_top5: 0.9988 - 2s/step
step 700/938 - loss: 0.1506 - acc_top1: 0.8967 - acc_top5: 0.9988 - 2s/step
step 800/938 - loss: 0.4000 - acc_top1: 0.8976 - acc_top5: 0.9988 - 2s/step
step 900/938 - loss: 0.1751 - acc_top1: 0.8983 - acc_top5: 0.9987 - 2s/step
step 938/938 - loss: 0.3205 - acc_top1: 0.8985 - acc_top5: 0.9987 - 2s/step
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 100/157 - loss: 0.1528 - acc_top1: 0.8883 - acc_top5: 0.9989 - 592ms/step
step 157/157 - loss: 0.1586 - acc_top1: 0.8918 - acc_top5: 0.9990 - 596ms/step
Eval samples: 10000
       

相关专题

更多
css3transition
css3transition

css3transition属性用于指定如何从一个CSS样式过渡到另一个CSS样式,本专题为大家提供transition相关的文章、相关下载和相关课程,大家可以免费体验。

231

2023.06.27

css3transition
css3transition

css3transition属性用于指定如何从一个CSS样式过渡到另一个CSS样式,本专题为大家提供transition相关的文章、相关下载和相关课程,大家可以免费体验。

231

2023.06.27

云朵浏览器入口合集
云朵浏览器入口合集

本专题整合了云朵浏览器入口合集,阅读专题下面的文章了解更多详细地址。

20

2026.01.20

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

28

2026.01.20

PS使用蒙版相关教程
PS使用蒙版相关教程

本专题整合了ps使用蒙版相关教程,阅读专题下面的文章了解更多详细内容。

146

2026.01.19

java用途介绍
java用途介绍

本专题整合了java用途功能相关介绍,阅读专题下面的文章了解更多详细内容。

120

2026.01.19

java输出数组相关教程
java输出数组相关教程

本专题整合了java输出数组相关教程,阅读专题下面的文章了解更多详细内容。

41

2026.01.19

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

10

2026.01.19

xml格式相关教程
xml格式相关教程

本专题整合了xml格式相关教程汇总,阅读专题下面的文章了解更多详细内容。

14

2026.01.19

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号