0

0

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

P粉084495128

P粉084495128

发布时间:2025-07-31 11:06:53

|

1061人浏览过

|

来源于php中文网

原创

残差网络(resnet)由何恺明等人提出,解决了深层神经网络训练误差不降反升的问题。其核心是残差块,通过拟合残差映射简化优化,输入可跨层传播。resnet沿用vgg的3×3卷积设计,含4个残差块模块,结构简单。如resnet-18有18层,还有更深的型号。在fashion-mnist上训练验证了其有效性,深刻影响了深度神经网络设计。

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

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

残差网络(ResNet)

让我们先思考一个问题:对神经网络模型添加新的层,充分训练后的模型是否只可能更有效地降低训练误差?理论上,原模型解的空间只是新模型解的空间的子空间。也就是说,如果我们能将新添加的层训练成恒等映射f(x)=xf(x)=x,新模型和原模型将同样有效。由于新模型可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差。然而在实践中,添加过多的层后训练误差往往不降反升。即使利用批量归一化带来的数值稳定性使训练深层模型更加容易,该问题仍然存在。针对这一问题,何恺明等人提出了残差网络(ResNet) [1]。它在2015年的ImageNet图像识别挑战赛夺魁,并深刻影响了后来的深度神经网络的设计。

残差块

让我们聚焦于神经网络局部。如图5.9所示,设输入为xx。假设我们希望学出的理想映射为f(x)f(x),从而作为图5.9上方激活函数的输入。左图虚线框中的部分需要直接拟合出该映射f(x)f(x),而右图虚线框中的部分则需要拟合出有关恒等映射的残差映射f(x)xf(x)−x。残差映射在实际中往往更容易优化。以本节开头提到的恒等映射作为我们希望学出的理想映射f(x)f(x)。我们只需将图5.9中右图虚线框内上方的加权运算(如仿射)的权重和偏差参数学成0,那么f(x)f(x)即为恒等映射。实际中,当理想映射f(x)f(x)极接近于恒等映射时,残差映射也易于捕捉恒等映射的细微波动。图5.9右图也是ResNet的基础块,即残差块(residual block)。在残差块中,输入可通过跨层的数据线路更快地向前传播。

《动手学深度学习》Paddle 版源码-5.11章(ResNet) - php中文网        

ResNet沿用了VGG全3×33×3卷积层的设计。残差块里首先有2个有相同输出通道数的3×33×3卷积层。每个卷积层后接一个批量归一化层和ReLU激活函数。然后我们将输入跳过这2个卷积运算后直接加在最后的ReLU激活函数前。这样的设计要求2个卷积层的输出与输入形状一样,从而可以相加。如果想改变通道数,就需要引入一个额外的1×11×1卷积层来将输入变换成需要的形状后再做相加运算。

残差块的实现如下。它可以设定输出通道数、是否使用额外的1×11×1卷积层来修改通道数以及卷积层的步幅。

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

下面我们来查看输入和输出形状一致的情况。

In [ ]
blk = Residual(3, 3)
X = paddle.to_tensor(np.random.uniform(-1., 1., [4, 3, 6, 6]).astype('float32'))
Y = blk(X)print(Y.shape)
       
[4, 3, 6, 6]
       

我们也可以在增加输出通道数的同时减半输出的高和宽。

一帧秒创
一帧秒创

基于秒创AIGC引擎的AI内容生成平台,图文转视频,无需剪辑,一键成片,零门槛创作视频。

下载
In [ ]
blk = Residual(3, 6, use_1x1conv=True, stride=2)
X = paddle.to_tensor(np.random.uniform(-1., 1., [4, 3, 6, 6]).astype('float32'))
Y = blk(X)print(Y.shape)
       
[4, 6, 3, 3]
       

GoogLeNet在后面接了4个由Inception块组成的模块。ResNet则使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。第一个模块的通道数同输入通道数一致。由于之前已经使用了步幅为2的最大池化层,所以无须减小高和宽。之后的每个模块在第一个残差块里将上一个模块的通道数翻倍,并将高和宽减半。

下面我们来实现这个模块。注意,这里对第一个模块做了特别处理。

In [ ]
class ResnetBlock(nn.Layer):
    def __init__(self, num_channels, num_filters, num_residuals, first_block=False):
        super(ResnetBlock, self).__init__()
        model = []        for i in range(num_residuals):            if i == 0:                if not first_block:
                    model += [Residual(num_channels, num_filters, use_1x1conv=True, stride=2)]                else:
                    model += [Residual(num_channels, num_filters)]            else:
                model += [Residual(num_filters, num_filters)]
        self.model = nn.Sequential(*model)    def forward(self, X):
        return self.model(X)
   

ResNet模型

In [ ]
class ResNet(nn.Layer):
    def __init__(self, num_classes=10):
        super(ResNet, self).__init__()        # ResNet的前两层跟之前介绍的GoogLeNet中的一样:
        # 在输出通道数为64、步幅为2的7×77卷积层后接步幅为2的3×3的最大池化层。
        # 不同之处在于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加入所有残差块。这里每个模块使用2个残差块。
        model += [
            ResnetBlock(64, 64, 2, first_block=True),
            ResnetBlock(64, 128, 2),
            ResnetBlock(128, 256, 2),
            ResnetBlock(256, 512, 2)
        ]        # 最后,与GoogLeNet一样,加入全局平均池化层后接上全连接层输出。
        model += [
            nn.AdaptiveAvgPool2D(output_size=1),
            nn.Flatten(start_axis=1, stop_axis=-1),
            nn.Linear(512, num_classes),
        ]
        self.model = nn.Sequential(*model)    def forward(self, X):
        Y = self.model(X)        return Y

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

这里每个模块里有4个卷积层(不计算1×11×1卷积层),加上最开始的卷积层和最后的全连接层,共计18层。这个模型通常也被称为ResNet-18。通过配置不同的通道数和模块里的残差块数可以得到不同的ResNet模型,例如更深的含152层的ResNet-152。虽然ResNet的主体架构跟GoogLeNet的类似,但ResNet结构更简单,修改也更方便。这些因素都导致了ResNet迅速被广泛使用。

在训练ResNet之前,我们来观察一下输入形状在ResNet不同模块之间的变化。

In [ ]
resnet = ResNet(10)
param_info = paddle.summary(resnet, (1, 1, 96, 96))print(param_info)
       
-------------------------------------------------------------------------------
   Layer (type)         Input Shape          Output Shape         Param #    
===============================================================================
     Conv2D-26        [[1, 1, 96, 96]]     [1, 64, 48, 48]         3,200     
  BatchNorm2D-22     [[1, 64, 48, 48]]     [1, 64, 48, 48]          256      
      ReLU-12        [[1, 64, 48, 48]]     [1, 64, 48, 48]           0       
    MaxPool2D-2      [[1, 64, 48, 48]]     [1, 64, 24, 24]           0       
     Conv2D-27       [[1, 64, 24, 24]]     [1, 64, 24, 24]        36,928     
  BatchNorm2D-23     [[1, 64, 24, 24]]     [1, 64, 24, 24]          256      
      ReLU-13        [[1, 64, 24, 24]]     [1, 64, 24, 24]           0       
     Conv2D-28       [[1, 64, 24, 24]]     [1, 64, 24, 24]        36,928     
  BatchNorm2D-24     [[1, 64, 24, 24]]     [1, 64, 24, 24]          256      
    Residual-11      [[1, 64, 24, 24]]     [1, 64, 24, 24]           0       
     Conv2D-29       [[1, 64, 24, 24]]     [1, 64, 24, 24]        36,928     
  BatchNorm2D-25     [[1, 64, 24, 24]]     [1, 64, 24, 24]          256      
      ReLU-14        [[1, 64, 24, 24]]     [1, 64, 24, 24]           0       
     Conv2D-30       [[1, 64, 24, 24]]     [1, 64, 24, 24]        36,928     
  BatchNorm2D-26     [[1, 64, 24, 24]]     [1, 64, 24, 24]          256      
    Residual-12      [[1, 64, 24, 24]]     [1, 64, 24, 24]           0       
   ResnetBlock-5     [[1, 64, 24, 24]]     [1, 64, 24, 24]           0       
     Conv2D-31       [[1, 64, 24, 24]]     [1, 128, 12, 12]       73,856     
  BatchNorm2D-27     [[1, 128, 12, 12]]    [1, 128, 12, 12]         512      
      ReLU-15        [[1, 128, 12, 12]]    [1, 128, 12, 12]          0       
     Conv2D-32       [[1, 128, 12, 12]]    [1, 128, 12, 12]       147,584    
  BatchNorm2D-28     [[1, 128, 12, 12]]    [1, 128, 12, 12]         512      
     Conv2D-33       [[1, 64, 24, 24]]     [1, 128, 12, 12]        8,320     
    Residual-13      [[1, 64, 24, 24]]     [1, 128, 12, 12]          0       
     Conv2D-34       [[1, 128, 12, 12]]    [1, 128, 12, 12]       147,584    
  BatchNorm2D-29     [[1, 128, 12, 12]]    [1, 128, 12, 12]         512      
      ReLU-16        [[1, 128, 12, 12]]    [1, 128, 12, 12]          0       
     Conv2D-35       [[1, 128, 12, 12]]    [1, 128, 12, 12]       147,584    
  BatchNorm2D-30     [[1, 128, 12, 12]]    [1, 128, 12, 12]         512      
    Residual-14      [[1, 128, 12, 12]]    [1, 128, 12, 12]          0       
   ResnetBlock-6     [[1, 64, 24, 24]]     [1, 128, 12, 12]          0       
     Conv2D-36       [[1, 128, 12, 12]]     [1, 256, 6, 6]        295,168    
  BatchNorm2D-31      [[1, 256, 6, 6]]      [1, 256, 6, 6]         1,024     
      ReLU-17         [[1, 256, 6, 6]]      [1, 256, 6, 6]           0       
     Conv2D-37        [[1, 256, 6, 6]]      [1, 256, 6, 6]        590,080    
  BatchNorm2D-32      [[1, 256, 6, 6]]      [1, 256, 6, 6]         1,024     
     Conv2D-38       [[1, 128, 12, 12]]     [1, 256, 6, 6]        33,024     
    Residual-15      [[1, 128, 12, 12]]     [1, 256, 6, 6]           0       
     Conv2D-39        [[1, 256, 6, 6]]      [1, 256, 6, 6]        590,080    
  BatchNorm2D-33      [[1, 256, 6, 6]]      [1, 256, 6, 6]         1,024     
      ReLU-18         [[1, 256, 6, 6]]      [1, 256, 6, 6]           0       
     Conv2D-40        [[1, 256, 6, 6]]      [1, 256, 6, 6]        590,080    
  BatchNorm2D-34      [[1, 256, 6, 6]]      [1, 256, 6, 6]         1,024     
    Residual-16       [[1, 256, 6, 6]]      [1, 256, 6, 6]           0       
   ResnetBlock-7     [[1, 128, 12, 12]]     [1, 256, 6, 6]           0       
     Conv2D-41        [[1, 256, 6, 6]]      [1, 512, 3, 3]       1,180,160   
  BatchNorm2D-35      [[1, 512, 3, 3]]      [1, 512, 3, 3]         2,048     
      ReLU-19         [[1, 512, 3, 3]]      [1, 512, 3, 3]           0       
     Conv2D-42        [[1, 512, 3, 3]]      [1, 512, 3, 3]       2,359,808   
  BatchNorm2D-36      [[1, 512, 3, 3]]      [1, 512, 3, 3]         2,048     
     Conv2D-43        [[1, 256, 6, 6]]      [1, 512, 3, 3]        131,584    
    Residual-17       [[1, 256, 6, 6]]      [1, 512, 3, 3]           0       
     Conv2D-44        [[1, 512, 3, 3]]      [1, 512, 3, 3]       2,359,808   
  BatchNorm2D-37      [[1, 512, 3, 3]]      [1, 512, 3, 3]         2,048     
      ReLU-20         [[1, 512, 3, 3]]      [1, 512, 3, 3]           0       
     Conv2D-45        [[1, 512, 3, 3]]      [1, 512, 3, 3]       2,359,808   
  BatchNorm2D-38      [[1, 512, 3, 3]]      [1, 512, 3, 3]         2,048     
    Residual-18       [[1, 512, 3, 3]]      [1, 512, 3, 3]           0       
   ResnetBlock-8      [[1, 256, 6, 6]]      [1, 512, 3, 3]           0       
AdaptiveAvgPool2D-2   [[1, 512, 3, 3]]      [1, 512, 1, 1]           0       
     Flatten-3        [[1, 512, 1, 1]]         [1, 512]              0       
     Linear-2            [[1, 512]]            [1, 10]             5,130     
===============================================================================
Total params: 11,186,186
Trainable params: 11,170,570
Non-trainable params: 15,616
-------------------------------------------------------------------------------
Input size (MB): 0.04
Forward/backward pass size (MB): 10.77
Params size (MB): 42.67
Estimated Total Size (MB): 53.47
-------------------------------------------------------------------------------

{'total_params': 11186186, 'trainable_params': 11170570}
       

训练模型

下面我们在Fashion-MNIST数据集上训练ResNet。

In [ ]
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(ResNet(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.3613 - acc_top1: 0.7612 - acc_top5: 0.9820 - 3s/step
step 200/938 - loss: 0.4060 - acc_top1: 0.7972 - acc_top5: 0.9881 - 3s/step
step 300/938 - loss: 0.3635 - acc_top1: 0.8171 - acc_top5: 0.9908 - 3s/step
step 400/938 - loss: 0.3369 - acc_top1: 0.8292 - acc_top5: 0.9926 - 3s/step
step 500/938 - loss: 0.2733 - acc_top1: 0.8390 - acc_top5: 0.9937 - 3s/step
step 600/938 - loss: 0.1964 - acc_top1: 0.8469 - acc_top5: 0.9943 - 3s/step
       

小结

  • 残差块通过跨层的数据通道从而能够训练出有效的深度神经网络。
  • ResNet深刻影响了后来的深度神经网络的设计。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

16

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

23

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

75

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

95

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

218

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

420

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

168

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

222

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

33

2026.03.03

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
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号