PyTorch神经网络构建实战:从nn.Sequential到nn.MaxPool2d的完整流程解析

PyTorch学习笔记(1)nn.Sequential、nn.Conv2d、nn.BatchNorm2d、nn.ReLU和nn.MaxPool2d 这里写目录标题一、nn.Sequential二、nn.Conv2d 一、nn.Sequential torch.nn.Sequential是一个Sequential容器,模块将按照构造函数中传递的顺序添加到模块中。另外,也可以传入一个有序模块。 为了更容易理解,官方给出了一些案例: # Sequential使用实例 model = nn.Sequential( nn.Conv2d(1,20,5), nn.ReLU(), nn.Conv2d(20, 阅读详情

1. 从零开始:为什么选择PyTorch和nn.Sequential?

如果你刚接触深度学习,面对TensorFlow、PyTorch这些框架可能会有点懵。我刚开始那会儿也这样,总觉得这些东西门槛很高。但实际用下来,尤其是PyTorch,给我的感觉就像在用Python写脚本一样自然。它最大的魅力在于动态计算图,你可以像调试普通Python代码一样,随时打印中间变量的值,看看网络到底在“想”什么。这对于我们理解神经网络内部运作,尤其是调试模型,简直是“神器”。

今天我们要聊的,就是用PyTorch里最核心、最常用的一些“积木块”,快速搭出一个能处理图像的神经网络。这些“积木块”就是nn.Sequential、nn.Conv2d、nn.BatchNorm2d、nn.ReLU和nn.MaxPool2d。你可能在无数教程里见过它们,但把它们串起来,理解每一步为什么这么做,才是从“看懂”到“会用”的关键。

我们先从最省心的nn.Sequential说起。你可以把它想象成一个乐高管道。你把不同的神经网络层(比如卷积、激活函数)按顺序扔进这个管道,数据从一头进去,就会自动按你设定的顺序,一层一层地流过这些层,最后从另一头出来。这种方式搭建网络,代码极其简洁明了,特别适合构建那种一层接一层的、顺序执行的网络结构,比如我们常见的VGG、AlexNet的前半部分。

我举个最简单的例子。假设我们要处理一张灰度手写数字图片(比如MNIST数据集),图片大小是28x28,通道数是1。我们想先用一个卷积层提取特征,然后接一个激活函数让它具备非线性,最后再做一个池化。用nn.Sequential来写,三行代码搞定:

import torch.nn as nn

model = nn.Sequential(
    nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3), # 第一层:卷积
    nn.ReLU(), # 第二层:激活函数
    nn.MaxPool2d(kernel_size=2) # 第三层:最大池化
)

你不需要自己写forward函数(前向传播逻辑),nn.Sequential帮你自动完成了。打印这个model,你会看到清晰的结构:

Sequential(
  (0): Conv2d(1, 16, kernel_size=(3, 3), stride=(1, 1))
  (1): ReLU()
  (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)

这种方式对于快速原型验证、教学或者构建不那么复杂的子模块来说,效率非常高。但它的缺点是不够灵活,比如你想在网络中间加一个分支(像ResNet的残差连接),或者根据条件跳过某些层,nn.Sequential就有点力不从心了。这时候,我们就需要用到更基础的nn.Module类,通过继承它来定义我们自己的网络类,在forward函数里自由编排数据流动的路径。不过对于新手入门,nn.Sequential绝对是让你快速获得成就感、理解网络流水线的最佳起点。

2. 核心积木详解(一):nn.Conv2d与nn.BatchNorm2d

2.1 nn.Conv2d:图像的“特征提取器”

卷积层是深度学习处理图像的基石。你可以把它理解成一个局部特征的扫描仪。nn.Conv2d这个扫描仪,带着一组可学习的“滤镜”(卷积核),在图像上滑动。每个“滤镜”负责提取一种特定的局部模式,比如边缘、纹理、角点。

它的参数看起来多,但常用的就几个。我们结合代码来看:

conv_layer = nn.Conv2d(
    in_channels=3,   # 输入通道数:RGB图像就是3
    out_channels=64,  # 输出通道数:即使用多少个不同的“滤镜”,提取多少种特征
    kernel_size=3,    # 卷积核大小:3x3的扫描窗口
    stride=1,         # 步长:每次滑动1个像素
    padding=1,        # 填充:在图像边缘补一圈0,使得输出尺寸和输入尺寸一致(当kernel_size=3, padding=1时)
    bias=False        # 是否添加偏置项。通常后面接BatchNorm时,可以设为False
)

这里有几个实战经验分享:

  • kernel_size:最常用的是3x3和5x5。3x3卷积感受野小,参数少,可以堆叠多层来获得大感受野,是现在的主流。5x5卷积有时在网络的较浅层使用,一次性看更大的区域。
  • stride:默认是1。如果想对特征图进行下采样(缩小尺寸),可以把stride设为2。这样扫描仪每次跳两格,输出特征图的高和宽就大约减半了。
  • padding:这是新手容易忽略但非常重要的参数。如果不做填充(padding=0),一个3x3卷积会让特征图每边缩小1个像素。为了在深层网络中保持特征图尺寸可控,我们经常设置padding=(kernel_size-1)//2,这样就能保证输入输出尺寸不变。
  • bias:当卷积层后面紧跟批归一化层(nn.BatchNorm2d)时,因为批归一化本身包含一个可学习的偏移参数(beta),所以卷积层的偏置项就多余了。将其设为False可以稍微减少参数数量,且不影响模型性能。

2.2 nn.BatchNorm2d:训练过程的“稳定器”

批归一化(BatchNorm)是我认为过去十年深度学习领域最重要的技巧之一。没有它之前,训练深一点的网络非常痛苦,学习率不敢设大,初始化要小心翼翼。BatchNorm的出现极大地缓解了这个问题。

它到底做了什么?简单说,它强制让一个批次(Batch) 的数据,在通过某一层之后,其每个通道的特征分布都保持均值为0、方差为1的标准状态。想象一下,每一层神经网络就像一个小加工厂,前一层的产品(数据)质量忽高忽低(分布不稳定),下一层工人就很难高效工作。BatchNorm就是每个工厂门口的质检员,把进来的半成品都标准化一下,让下一道工序的工人总是在稳定的输入条件下工作,大大加快了整个流水线(训练过程)的效率。

它的使用非常简单,几乎总是紧跟在卷积层(或全连接层)之后、激活函数之前:

conv_layer = nn.Conv2d(3, 64, 3, padding=1, bias=False)
bn_layer = nn.BatchNorm2d(64) # 参数是num_features,必须等于上一层卷积的输出通道数
relu = nn.ReLU()

这里的关键点是:nn.BatchNorm2d(64)里的64,必须和前面nn.Conv2d层的out_channels完全一致。因为它是对每个通道单独进行归一化。

在实际训练中,BatchNorm会计算当前这个小批次数据的均值和方差来做归一化。同时,它还会维护一个全局的移动平均均值和方差。在训练模式(model.train())下,它使用当前批次的统计量,并更新这个移动平均。在评估/测试模式(model.eval())下,它就固定使用训练过程中积累下来的移动平均统计量,这样保证模型行为一致。所以,千万记得,在训练和测试时要用model.train()和model.eval()来正确切换模型状态,否则BatchNorm的行为会错乱,严重影响性能。

我踩过的一个坑是:当训练时batch_size设得很小(比如1或2),BatchNorm的统计会非常不准,因为一个小批次的分布不能代表整个数据集。这时候模型效果可能会很差。对于小批量训练,可以考虑使用GroupNorm(组归一化)等其他归一化方法作为替代。

3. 核心积木详解(二):nn.ReLU与nn.MaxPool2d

3.1 nn.ReLU:引入非线性的“开关”

如果神经网络只有卷积、全连接这些线性操作,那么无论堆多少层,整个网络等效于一个单层的线性模型,根本无法拟合复杂的数据。激活函数就是用来给网络加入非线性能力的,让网络可以逼近任意复杂的函数。

nn.ReLU(Rectified Linear Unit,修正线性单元)是目前最流行、最常用的激活函数。它的规则简单粗暴:f(x) = max(0, x)。输入为正数,原样输出;输入为负数,输出为零。

为什么是它?我总结有三大好处:

  1. 计算极其简单:就是和0比大小,没有指数、除法等复杂运算,在反向传播求梯度时也简单(大于0导数为1,小于0导数为0),训练速度飞快。
  2. 缓解梯度消失:相比sigmoid、tanh函数在两端的饱和区梯度接近于0,ReLU在正区间的梯度恒为1,能有效地将梯度传递到更深的层,这是训练深层网络的关键。
  3. 带来稀疏性:ReLU会让一部分神经元的输出为0,这使得网络变得稀疏。稀疏的网络通常被认为具有更好的泛化能力,并且减少了神经元之间的相互依赖,有点像一种隐式的正则化。

在PyTorch中使用时,你可能会看到两种形式:

# 方式一:作为网络层模块(推荐在nn.Sequential或__init__中使用)
self.relu = nn.ReLU(inplace=True)
# 在前向传播中调用
x = self.relu(x)

# 方式二:作为函数调用(推荐在自定义forward函数中使用)
import torch.nn.functional as F
x = F.relu(x)

两者在数学上完全等价。区别在于,nn.ReLU()是一个nn.Module子类,需要被注册到模型中(作为模型的一部分);F.relu()是一个纯函数。inplace=True参数意味着直接修改输入张量来节省内存,但如果你后续还需要用到修改前的张量(比如要计算多个分支),就不要用inplace操作,否则原数据会被覆盖。

3.2 nn.MaxPool2d:信息的“浓缩器”

池化层,特别是最大池化(MaxPooling),通常跟在卷积+激活之后。它的主要目的有两个:降低特征图的空间尺寸(宽和高) 和 引入一定程度的空间不变性。

降低尺寸很好理解,就是把图片缩小,这样后续层的计算量就小了,也能扩大后面卷积层的感受野。空间不变性指的是,比如图片里有一只猫,这只猫在图像中稍微平移了一点,经过最大池化后,提取到的特征可能变化不大,这有助于模型更关注“有没有猫”而不是“猫在精确的哪个像素位置”。

nn.MaxPool2d的操作就是在一个小窗口(比如2x2)里,只保留数值最大的那个像素,丢弃其他。就像一个“最强代表”选举,每个小区域只选一个特征最强的点代表该区域。

pool_layer = nn.MaxPool2d(
    kernel_size=2,   # 池化窗口大小:2x2
    stride=2,        # 步长:通常和kernel_size一样,实现不重叠的池化
    padding=0,       # 池化一般不加padding
    ceil_mode=False  # 默认为False,表示当窗口无法完全覆盖时,舍弃多余部分
)

假设输入特征图是4x4,经过kernel_size=2, stride=2的最大池化后,输出就变成了2x2。计算量直接降到原来的1/4。

这里有个参数ceil_mode值得注意。默认ceil_mode=False是向下取整模式。比如一个5x7的特征图,用kernel_size=2, stride=2去池化,在宽度方向(7),窗口滑动范围是[0,2), [2,4), [4,6),最后多出来的第7列就被舍弃了。如果设置ceil_mode=True,它会向上取整,最后一个窗口会包含第6列和第7列(即使不够2列),这样输出尺寸会大一点。具体用哪种,需要和网络整体设计保持一致。在经典的VGG网络中,就是通过多个stride=2的池化层,逐步将224x224的输入压缩到7x7。

4. 实战:组装一个图像分类小网络

现在,我们把前面所有的“积木”组合起来,搭建一个用于CIFAR-10图像分类的微型卷积神经网络(CNN)。CIFAR-10图片尺寸是32x32,有10个类别。我们的目标是构建一个流程清晰、易于理解的范例。

4.1 使用nn.Sequential快速搭建

我们先采用最直观的nn.Sequential方式,把网络定义成一条直线。这个网络包含两个卷积块,每个块都是“卷积 -> 批归一化 -> ReLU -> 池化”的标准结构,最后接上全连接层进行分类。

import torch
import torch.nn as nn
import torch.nn.functional as F

class SimpleCNN_Sequential(nn.Module):
    def __init__(self, num_classes=10):
        super(SimpleCNN_Sequential, self).__init__()
        
        # 特征提取部分:使用Sequential容器
        self.features = nn.Sequential(
            # 第一个卷积块:输入3通道(RGB),输出32通道
            nn.Conv2d(3, 32, kernel_size=3, padding=1, bias=False), # 输出尺寸: 32x32x32
            nn.BatchNorm2d(32),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2), # 输出尺寸: 16x16x32
            
            # 第二个卷积块:输入32通道,输出64通道
            nn.Conv2d(32, 64, kernel_size=3, padding=1, bias=False), # 输出尺寸: 16x16x64
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2), # 输出尺寸: 8x8x64
        )
        
        # 分类器部分
        self.classifier = nn.Sequential(
            # 将三维特征图展平成一维向量:8 * 8 * 64 = 4096
            nn.Flatten(),
            # 全连接层
            nn.Linear(64 * 8 * 8, 128),
            nn.ReLU(inplace=True),
            nn.Dropout(p=0.5), # Dropout防止过拟合
            nn.Linear(128, num_classes)
        )
    
    def forward(self, x):
        x = self.features(x) # 数据流过特征提取管道
        x = self.classifier(x) # 数据流过分类器管道
        return x

# 实例化模型
model = SimpleCNN_Sequential()
print(model)

通过打印模型,你可以清晰地看到nn.Sequential带来的结构可视化好处。整个前向传播逻辑在forward里只有两行,非常简洁。这种写法特别适合教学和快速验证网络主体结构。

4.2 使用继承nn.Module的经典写法

虽然nn.Sequential很方便,但在实际项目中,网络结构往往更复杂,可能需要分支、跳跃连接(如ResNet)或多尺度特征融合。这时,继承nn.Module并自定义forward函数是更主流、更灵活的方式。我们用同样的网络结构,换一种写法:

class SimpleCNN_Module(nn.Module):
    def __init__(self, num_classes=10):
        super(SimpleCNN_Module, self).__init__()
        
        # 定义每一层
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(32)
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
        
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(64)
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
        
        self.flatten = nn.Flatten()
        self.fc1 = nn.Linear(64 * 8 * 8, 128)
        self.dropout = nn.Dropout(p=0.5)
        self.fc2 = nn.Linear(128, num_classes)
        
        # 注意:这里没有定义ReLU层,我们将在forward中使用F.relu函数
        
    def forward(self, x):
        # 第一个卷积块
        x = self.conv1(x)
        x = self.bn1(x)
        x = F.relu(x, inplace=True) # 使用函数式API
        x = self.pool1(x)
        
        # 第二个卷积块
        x = self.conv2(x)
        x = self.bn2(x)
        x = F.relu(x, inplace=True)
        x = self.pool2(x)
        
        # 分类器
        x = self.flatten(x)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.dropout(x)
        x = self.fc2(x)
        return x

这种写法的优势在于,你对网络数据流的控制力是百分之百的。你可以在forward里任意组合、分支、循环这些层。例如,想加一个残差连接,只需要out = F.relu(x + residual)。想对中间特征进行可视化,可以轻松地将x在某个阶段保存下来。这种灵活性是nn.Sequential无法比拟的。

4.3 两种写法的对比与选择

我把两种方式的核心区别和适用场景总结了一下:

特性nn.Sequential 写法nn.Module 继承写法
代码简洁度极高,结构一目了然相对冗长,需要手动写forward
灵活性低,严格顺序执行,难以实现分支、跳跃连接极高,可以自由定义任意数据流
可调试性一般,中间变量获取稍麻烦好,可以在forward中任何位置打印或返回中间特征
适用场景1. 简单的顺序模型
2. 大型网络中的重复子模块
3. 快速原型、教学示例
1. 复杂的网络结构(ResNet, DenseNet, U-Net等)
2. 需要自定义前向逻辑的项目
3. 生产环境的主流写法

我的个人习惯是:在构建一个大的、复杂的网络时,我会用nn.Sequential来封装其中重复出现的、顺序的子模块(比如一个包含Conv-BN-ReLU的“基础块”),然后在主网络类(继承nn.Module)里,像搭积木一样调用这些子模块,并在forward里组织它们的关系。这样既保持了代码的整洁,又拥有了足够的灵活性。

5. 关键参数调优与避坑指南

网络搭起来了,但想让它跑得好,参数设置里的门道可不少。这里分享几个我实战中总结的经验和容易踩的坑。

5.1 卷积核与步长的协同设计

卷积层的kernel_size和stride共同决定了输出特征图的尺寸。公式是:输出尺寸 = floor((输入尺寸 + 2*padding - kernel_size) / stride) + 1。为了在设计网络时保持尺寸可控,一个常见的技巧是:当stride=1时,设置padding = (kernel_size - 1) // 2。这样能保证输入和输出的空间尺寸完全相同。对于kernel_size=3,padding就是1;对于kernel_size=5,padding就是2。

当你想让特征图尺寸减半时,通常有两种做法:

  1. 使用stride=2的卷积层(如nn.Conv2d(..., stride=2))。
  2. 使用stride=1的卷积层,后面接一个stride=2的池化层(如nn.MaxPool2d(2, stride=2))。

第一种方法(带步长的卷积)现在更流行,因为它用一个操作同时完成了特征提取和下采样,参数效率可能更高。第二种方法(卷积+池化)是更传统的做法,池化层提供了额外的平移不变性。在ResNet等现代网络中,通常使用第一种。

5.2 BatchNorm层的陷阱与模式切换

这是新手,甚至是有经验的开发者都容易出错的地方。nn.BatchNorm2d在训练和评估时的行为是不同的。

  • 训练时 (model.train()):使用当前mini-batch数据的均值和方差进行归一化,并更新内部维护的全局移动平均统计量。
  • 评估时 (model.eval()):停止更新移动平均统计量,并固定使用训练阶段积累下来的全局均值和方差进行归一化。

踩坑实录:有一次我训练完模型,在测试集上评估准确率,忘记调用model.eval(),结果准确率比预期低了很多。排查了半天才发现,BatchNorm层还在用当前测试批次的统计量(测试批次通常很小,分布和训练集不同),导致特征分布被错误地归一化。所以,务必养成习惯:在训练循环开始前调用model.train(),在验证和测试前调用model.eval()。

另一个相关参数是track_running_stats。通常我们保持其默认值True即可,让BatchNorm跟踪整个训练过程的统计量。只有在一些特殊场景,比如你使用预训练模型进行微调,且新数据和原数据分布差异极大时,才可能需要考虑调整。

5.3 池化层的选择与尺寸压缩

nn.MaxPool2d的kernel_size最常见的选择是2或3,stride通常等于kernel_size以实现不重叠的池化。池化层会不可逆地丢弃一部分信息(只保留最大值),所以不宜过度使用。在早期的CNN(如AlexNet)中池化层用得多,而在更深的网络(如ResNet)中,倾向于用带步长的卷积来代替池化,以减少信息损失。

ceil_mode参数在输入尺寸不是kernel_size的整数倍时起作用。例如,输入尺寸为5x5,kernel_size=2, stride=2。当ceil_mode=False(默认)时,输出尺寸为2x2(舍弃边缘)。当ceil_mode=True时,输出尺寸为3x3(包含边缘不足的部分)。你需要根据整个网络的设计来决定,确保所有层的尺寸计算能对齐,避免出现尺寸不匹配的错误。一个稳妥的做法是,在设计网络时,先手动计算或写个小脚本模拟一下各层尺寸的变化。

6. 完整训练流程示例与调试技巧

光有模型定义还不够,我们得把它放到一个完整的训练循环里跑起来,看看效果。这里我给出一个最精简但完整的训练流程框架,并附上几个实用的调试技巧。

6.1 数据准备、模型、损失与优化器

首先,我们需要准备数据。这里以CIFAR-10为例,使用PyTorch内置的数据加载工具。

import torchvision
import torchvision.transforms as transforms

# 数据预处理:转换为Tensor,并做归一化(使用CIFAR-10的均值和标准差)
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616))
])

# 加载训练集和测试集
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False, num_workers=2)

# 定义模型、损失函数和优化器
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = SimpleCNN_Module().to(device) # 使用我们之前定义的模型
criterion = nn.CrossEntropyLoss() # 分类任务常用交叉熵损失
optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # 使用Adam优化器

6.2 训练循环与验证

接下来是标准的训练循环。每个epoch包含训练和验证两个阶段。

num_epochs = 10

for epoch in range(num_epochs):
    # 训练阶段
    model.train()  # 至关重要:设置为训练模式
    running_loss = 0.0
    for i, (inputs, labels) in enumerate(trainloader):
        inputs, labels = inputs.to(device), labels.to(device)
        
        # 清零梯度
        optimizer.zero_grad()
        
        # 前向传播
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        
        # 反向传播与优化
        loss.backward()
        optimizer.step()
        
        running_loss += loss.item()
        
    print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {running_loss/len(trainloader):.4f}')
    
    # 验证阶段
    model.eval()  # 至关重要:设置为评估模式
    correct = 0
    total = 0
    with torch.no_grad():  # 验证时不计算梯度,节省内存和计算
        for inputs, labels in testloader:
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    
    print(f'Test Accuracy: {100 * correct / total:.2f} %')

6.3 实用调试技巧:可视化与尺寸检查

网络不work?损失不下降?别急着调参,先做最基本的检查。

技巧一:打印网络每层的输出尺寸。 这是我调试网络的第一步。在forward函数的关键位置插入print(x.shape),或者写一个辅助函数:

def check_model_output_shape(model, input_shape=(1, 3, 32, 32)):
    """检查模型各层输出尺寸"""
    model.eval()
    x = torch.randn(input_shape) # 生成一个随机输入
    print(f"Input shape: {x.shape}")
    with torch.no_grad():
        # 如果是Sequential写法,可以逐层打印
        if isinstance(model.features, nn.Sequential):
            for name, layer in model.features.named_children():
                x = layer(x)
                print(f"After {name}: {x.shape}")
        # 对于自定义forward的模型,可能需要手动在forward里加打印

运行这个函数,确保从输入到输出,每一层的尺寸变化都符合你的预期。常见的错误就是卷积或池化后尺寸算错了,导致最后全连接层输入维度对不上。

技巧二:使用TensorBoard或简单绘图观察特征图。 直观感受卷积层到底学到了什么。你可以取出第一层卷积的权重(model.conv1.weight)进行可视化,看看它们是不是像边缘检测器。也可以将中间某层的特征图(比如x)选择几个通道,用matplotlib画出来,看看网络在关注图像的哪些部分。

技巧三:过拟合一个极小数据集。 如果模型在大数据集上效果不好,先别怀疑人生。用5~10张图片组成一个微型训练集,关掉所有正则化(如Dropout),看模型能否快速将训练损失降到接近0。如果能,说明模型有能力学习,问题可能出在数据、优化器或超参数上。如果不能,那很可能就是模型结构本身有缺陷(比如梯度无法传播),需要回头检查网络设计。这个技巧能帮你快速定位问题是出在模型容量还是训练过程上。

从‘自适应’到‘全局’:深入理解PyTorch中AvgPool2d与AdaptiveAvgPool2d的核心差异与选用时机 本文深入解析PyTorch中AvgPool2d与AdaptiveAvgPool2d的核心差异与适用场景。通过对比两种池化函数的数学本质、计算复杂度和典型应用,帮助开发者在构建卷积神经网络时做出明智选择。重点探讨自适应平均池化在动态尺寸处理中的优势,以及传统池化在固定模式下的高效表现。 阅读详情

相关推荐

活用QueryWrapper与@Select注解,构建MybatisPlus动态多表查询方案

本文深入探讨了如何结合MybatisPlus的QueryWrapper与@Select注解,构建高效灵活的动态多表关联查询方案。通过解析${ew.customSqlSegment}占位符机制,并设计通用的Service层封装,解决了复杂条件下多表查询的SQL拼接难题,显著提升了后台管理系统列表查询的开发效率与可维护性。

weixin_29291069的博客 271

PyTorch核心模块实战指南:从nn.Sequential到nn.MaxPool2d的深度解析

本文深入解析PyTorch核心模块,从nn.Sequential到nn.MaxPool2d的实战应用。通过详细代码示例和实用技巧,帮助开发者高效构建神经网络,涵盖nn.Conv2d、nn.BatchNorm2d和nn.ReLU等关键模块的使用与优化策略,提升深度学习模型的开发效率。

weixin_30420305的博客 255

创客学院嵌入式驱动开发——学习资料汇总

1.创客学院知识巩固-01C语言回顾_C/C++_Set_Mode的博客-优快云博客https://blog.csdn.net/Set_Mode/article/details/94431050 2.创客学院知识巩固-02数据结构_Set_Mode的博客-优快云博客https://blog.csdn.net/Set_Mode/article/details/94431087 3.创客学院知...

snaking616 5116

剖解pytorch的CNN例子,了解函数Sequential,Conv2d,Maxpool2d,Flatten

数据加载:使用DataLoader加载 CIFAR-10 数据集,并分批处理。模型定义:定义了一个包含卷积层、池化层和全连接层的 CNN 模型。训练过程使用作为损失函数,SGD作为优化器。通过 10 个 epoch 的训练,模型逐渐优化。优化与可视化:每完成一个 epoch,打印当前的损失值,用于监控训练过程。这段代码是一个简单的深度学习训练示例,适用于初学者理解和学习 CNN 的基本原理和 PyTorch 的使用。解析函数Sequential 的使用场景和原理。

qq_62585870的博客 1142

STM32—时钟树(结合系统时钟函数理解)

时钟树的概念: 我们可以把MCU的运行比作人体的运行一样,人最重要的是什么?是心跳! 心脏的周期性收缩将血液泵向身体各处。心脏对于人体好比时钟对于MCU,微控制器(MCU)的运行要靠周期性的时钟脉冲来驱动,而这个脉冲的始源往往是由外部晶体振荡器提供时钟输入,最终转换为多个外部设备的周期性运作。这种时钟“能量”的传递路径犹如大树的养分由主干流向个分支,因此称为时钟树。 STM32时钟: 在STM32...

肥宅快乐博客 1万+

如何在离线的电脑上部署一个本地Deepseek(使用ollama)

例如,如果您的显卡显存为 16GB,官方推荐使用 32b 大小的 DeepSeek 模型。请根据您的硬件配置选择适合的模型版本,以获得最佳性能。首先我们需要将ollama和chatbox的安装包,Deepseek模型文件通过U盘或者光盘的形式导入到这台不能联网的电脑上。按照时间排序,找到最大的那个文件,就是gguf格式的模型,复制出来,改名为qwen2.5-coder0.5b.gguf。:基于开源模型(如 QWEN 和 LLAMA)微调,参数量从 1.5B 到 70B 不等,适合本地硬件部署。

qq_41854911的博客 1万+

别光调参!深入理解PyTorch CNN中Conv2d、MaxPool2d的输入输出形状计算

本文深入解析PyTorch中CNN的Conv2d和MaxPool2d层的输入输出形状计算,从数学原理到代码实现,帮助开发者彻底掌握卷积神经网络的核心参数关系。通过可视化计算和实战案例,详细讲解padding、stride等参数对特征图尺寸的影响,提升网络设计效率。

weixin_30596023的博客 350

模仿学习(Imitation Learning)概述

什么是模仿学习呢?简单来说,模仿学习(Imitation Learning),就是要训练机器能够复制人类的连续动作,进而达到模仿的目的。其实,Imitation Learning的实用性很高,假设今天有一个训练场景,你不知道该怎么定奖励值(reward),但是你可以收集到专家的示范数据(expert demonstration data),你就可以考虑用Imitation Learning这个方法。因此,模仿学习也被寄予很高的期待,成为下一代强人工智能的关键技术。......

qq_22749225的博客 2万+

PyTorch实战:从VGG到ResNet,手把手搭建经典CNN网络

卷积神经网络(CNN)是计算机视觉领域的核心架构,其通过卷积层自动提取图像特征,为图像识别、目标检测等任务奠定了基础。其核心原理在于利用局部连接和权值共享,高效处理网格状数据(如图像)。随着网络深度增加,模型表征能力增强,但也会引发梯度消失和网络退化等问题。VGG网络通过堆叠小型卷积核,在增加深度的同时保持了参数效率;ResNet则创新性地引入残差连接,通过恒等映射解决了深度网络的优化难题,使训练数百层的网络成为可能。这些经典架构在图像分类、目标检测等场景中广泛应用。本文将以PyTorch为工具,深入解析V

weixin_34413802的博客 306

Linux下基于AHCI controller模块实现SATA Platform驱动附代码详细流程

本文主要是针对ARM A55集成了AHSATA controller模块开发sata驱动,通过Platform的形式在linux识别到SSD,本文详细讲述整个开发过程,同时对SATA Platform驱动进行讲解。1、配置SATA Phy的初始化;2、打开SATA Platform相关编译开关;3、配置设备树;4、linux启动识盘。

Luckiers的博客 2497

别再只用线性回归了!用sklearn的核岭回归搞定非线性数据(附RBF核调参实战)

本文详细介绍了如何使用sklearn的核岭回归处理非线性数据,特别是RBF核的应用与调参实战。通过网格搜索找到最佳参数组合,核岭回归能有效捕捉数据中的复杂波动,显著提升模型性能。适合需要解决非线性问题的数据科学家和机器学习工程师。

weixin_34246529的博客 337

Pytorch搭建自定义神经网络

Pytorch搭建自定义神经网络

柠檬的博客~~~ 2555

基于STM32L4XX、HAL库的ICM-42670-P姿态传感器驱动程序设计

sprintf(buffer, "温度: %.1f °C\r\n\r\n", sensor_data.temperature);sprintf(buffer, "陀螺仪: X=%.2f Y=%.2f Z=%.2f rad/s\r\n",sprintf(buffer, "加速度: X=%.2f Y=%.2f Z=%.2f m/s²\r\n",sprintf(buffer, "开始校准IMU,请保持传感器静止...\r\n");sprintf(buffer, "读取传感器数据失败\r\n");

zhusl6688的博客 642

pytorch_nn.Module

nn.Module模型创建步骤torch.nnnn.Module属性模型容器nn.Sequentialnn.ModuleListnn.ModuleDict代码示例AlexNet 模型创建步骤 torch.nn nn.Module属性 模型容器 nn.Sequential nn.ModuleList nn.ModuleDict 代码示例 import torch import to...

沉梦昂志 274

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

pytorch模型搭建函数整理——model文件

pytorch模型搭建函数整理——model文件

threewsl的博客 1401

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

PyTorch中F.relu与nn.ReLU的深度解析:从函数与模块差异到实战选择

在深度学习模型构建中,激活函数是神经网络的核心组件之一,负责引入非线性,使模型能够学习复杂模式。其原理是通过对输入进行非线性变换,决定神经元是否被激活,从而影响信息流动与梯度传播。正确的选择与使用对训练稳定性、收敛速度和模型性能至关重要,尤其在处理梯度消失、爆炸等问题时价值凸显。在计算机视觉、自然语言处理等应用场景中,ReLU及其变体因其计算高效性而被广泛采用。本文聚焦于PyTorch框架下激活函数的具体实现差异,深入探讨函数式接口`F.relu`与模块化层`nn.ReLU`在计算图管理、`inplace`

weixin_34014555的博客 433

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

关于pytorch中VGG16微调的详解self.RCNN_base = nn.Sequential(*list(vgg.features._modules.values())[:-1])

先总结一下,self.RCNN_base = nn.Sequential(*list(vgg.features._modules.values())[:-1]),详解如下: vgg是已经构建好的vgg模型。 vgg.features是取出vgg16网络中的features大层。其中vgg网络可以分为3大层,一层是(features),一层是(avgpool),最后一层是(classifier...

a1103688841的博客 6757

别再死记硬背了!用PyTorch实战图解Conv1d、Conv2d、Conv3d的核心区别

本文通过PyTorch实战和可视化方法,深入解析Conv1d、Conv2d和Conv3d的核心区别。从生活场景出发,结合代码示例展示不同维度卷积的应用场景和操作方式,帮助读者建立直觉理解,避免死记硬背。特别适合深度学习初学者掌握卷积神经网络的关键概念。

weixin_30279671的博客 353

vgg16中 nn.Sequential(*list(vgg.classifier._modules.values())[:-1])

原博:https://blog.csdn.net/a1103688841/article/details/89383215 先总结一下,self.RCNN_base = nn.Sequential(*list(vgg.features._modules.values())[:-1]),详解如下: vgg是已经构建好的vgg模型。 vgg.features是取出vgg16网络中的features大层...

qq_42278791的博客 4280

VGGNet架构解析与PyTorch实现实战

卷积神经网络(CNN)作为计算机视觉的基础模型,其核心在于局部感知和参数共享机制。VGGNet通过堆叠3×3小卷积核,在保持感知野的同时显著减少参数量,配合ReLU激活函数提升非线性表达能力。这种设计在ImageNet等大规模图像分类任务中展现出优异性能,其PyTorch实现涉及卷积层配置、参数初始化和显存优化等关键技术。工程实践中,VGG常作为特征提取器应用于迁移学习、风格迁移等场景,结合CUDA加速和混合精度训练可有效提升计算效率。

weixin_33913332的博客 373

PyTorch深度学习实战:从模型构建到部署的完整技术思考

在当今人工智能快速发展的时代,PyTorch已经成为深度学习领域最受欢迎的框架之一。但真正掌握PyTorch不仅仅是学会API调用,更重要的是理解其背后的设计哲学和工程实践。本文将深入探讨PyTorch深度学习项目的完整技术路径,从模型架构设计到实际部署的全流程思考。 ## 技术选型深度解析:为什么PyTorch成为研究者的首选? 当我们站在深度学习框架选择的十字路口时,为什么越来越多的研究者

gitblog_01033的博客 723

小土堆Pytorch笔记

小土堆Pytorch笔记,有部分改进,并记录遇到的问题

q1111111的博客 4577

PyTorch模型封装实战:从nn.Module到训练循环的工程化指南

在深度学习工程实践中,模型封装是构建可维护、可扩展代码的核心基础。其核心原理在于通过模块化设计,将网络结构、参数管理与训练逻辑解耦,从而提升代码的复用性和调试效率。从技术价值看,良好的封装范式能系统性地解决状态管理混乱、参数迁移繁琐、代码复用性差等工程痛点,直接关系到模型从实验原型到生产部署的平滑过渡。具体到应用场景,无论是简单的图像分类任务,还是涉及多GPU训练、模型剪枝等进阶需求,一套清晰的封装模式都是确保项目稳健迭代的关键。本文以PyTorch的nn.Module为切入点,深入探讨如何将模型、损失函数

weixin_30650039的博客 296

PyTorch深度学习环境搭建与实战:从零开始掌握神经网络开发

深度学习作为人工智能的核心技术,通过神经网络模拟人脑学习机制实现智能决策。其原理基于多层非线性变换提取数据特征,具备强大的模式识别能力。在技术价值层面,深度学习显著提升了图像识别、自然语言处理等任务的准确率,成为推动AI产业化的关键引擎。应用场景涵盖计算机视觉、语音识别、推荐系统等多个领域,其中PyTorch框架凭借其动态计算图和易用性优势,成为深度学习开发的首选工具。通过PyTorch的自动微分机制和丰富的预训练模型库,开发者能够快速构建神经网络,实现从环境配置到模型部署的全流程实战。本文基于PyTorc

weixin_30376083的博客 386
上一篇: Hologres vs MaxCompute:如何用阿里云双引擎构建高性价比实时数仓?
下一篇: Mem0大模型记忆库实战:从Python到JavaScript的跨语言安装指南
lambda
博客等级 码龄23年 49粉丝 1303原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值