当前位置: 首页 > article >正文

Pytorch框架03-网络的搭建(nn.Module/卷积层/池化层/非线性激活/线性层/CIFAR-10分类模型搭建)

三、Pytorch网络的搭建

3.1 nn.Module-神经网络基本骨架

import torch
import torch.nn as nn

"""
1.nn.Module:Base class for all neural network modules.Your models should also subclass this class.
2.一般要实现__init__(self)和forward(self)方法
"""
class sinFunction(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self, input):
        return torch.sin(input)

sf = sinFunction()
input = torch.tensor([0.25 * torch.pi])
print(sf(input)) #这一行代码调用了 sf(input),即调用了 sinFunction 类的 forward 方法,并将输入 input 传递给它。

3.2 神将网络-卷积层

  • torch.nn.functional中卷积

torch.nn.functional.conv2d(*input*, *weight*, *bias=None*, *stride=1*, *padding=0*, *dilation=1*, *groups=1*) → Tensor

参数

image-20250219113100833

**minibatch:**批次大小,即一次处理的样本数量。
**in_channels:**输入通道的数量(例如RGB图像为3)。
**iH:**输入图像或特征图的高度。
**iW:**输入图像或特征图的宽度。

image-20250219113143552

**out_channels:**输出通道的数量,也就是卷积核的数量,这决定了输出张量的深度。
**groups:**用于分组卷积的参数。如果 groups = 1,则为标准卷积。如果 groups > 1,则输入通道和卷积核将被分成多个组,每组独立进行卷积操作。
**in_channels:**输入通道的数量(与输入张量中的 in_channels 对应)。
**kH:**卷积核的高度(即卷积核的大小)。
**kW:**卷积核的宽度(即卷积核的大小)。

image-20250219113216167

bias 是一个可选的张量,在卷积操作之后加到输出上。它的形状为:bias = (out_channels)
这个张量的大小与输出通道的数量相同。默认情况下,bias 是 None,如果提供了,它将加到卷积结果上。

image-20250219113229473

stride 定义了卷积核滑动时每次移动的步幅。步幅可以是一个单独的数值,也可以是一个元组:
1.如果是单一的数值(例如 stride = 1),则步幅在高度和宽度上都是相同的。
2.如果是元组 (sH, sW),则 sH 表示垂直方向上的步幅,sW 表示水平方向上的步幅。

image-20250219114513263

import torch
import torch.nn.functional as F

# torch.nn.functional.conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1) →

# 输入参数:(input) = (minibatch, in_channels, iH, iW)
input = torch.tensor([[1, 2, 0, 3, 1],
                      [0, 1, 2, 3, 1],
                      [1, 2, 1, 0, 0],
                      [5, 2, 3, 1, 1],
                      [2, 1, 0, 1, 1]])
input = torch.reshape(input, (1, 1, 5, 5))  # 一次处理一个样本,一个通道

# 卷积核:(weight) = (out_channels, in_channels/groups, kH, kW)
weight = torch.tensor([[1, 2, 1],
                       [0, 1, 0],
                       [2, 1, 0]])
weight = torch.reshape(weight, (1, 1, 3, 3))  # 一个卷积盒,标准卷积

d = F.conv2d(
    input=input,
    weight=weight,
    stride=1,  # 步长为1
    padding=0,  # 不做填充
    bias=None  # 不添加偏振
)
print(d)
  • torch.nn中卷积

卷积的作用:

  • 特征提取:卷积帮助网络从原始图像中提取低级到高级的特征(如边缘、纹理、形状等)。
  • 局部感知与共享权重:卷积通过局部连接和权重共享,使得特征学习更高效且具有平移不变性。
  • 效率:卷积操作能够在减少计算复杂度的同时提取图像中的重要信息,适合处理大规模图像数据。

1.方法

torch.nn.Conv2d(*in_channels*, *out_channels*, *kernel_size*, *stride=1*, *padding=0*, *dilation=1*, *groups=1*, *bias=True*, *padding_mode='zeros'*, *device=None*, *dtype=None*)

2.公式:

image-20250219114906462

no_padding_no_strides

3.常用参数

  • **in_channels (int):**这是输入图像的通道数。
例如,对于RGB图像,in_channels 为 3,因为每个图像有3个通道(红、绿、蓝)。如果是灰度图像,in_channels 为 1。
  • **out_channels (int):**这是卷积操作生成的输出通道数。
它表示卷积层会生成多少个不同的特征图(每个输出通道对应一个特征图)。

例如,如果 out_channels = 64,那么卷积操作会生成64个不同的输出特征图。
  • kernel_size (int 或 tuple):(随机初始化)
卷积核的大小,控制每次卷积操作时核的覆盖范围。如果设置为一个整数 k,则卷积核的大小为 k x k。如果设置为元组 (kH, kW),则分别表示卷积核的高度和宽度。

例如,kernel_size = 3 表示3x3卷积核,kernel_size = (3, 5) 表示高度为3、宽度为5的卷积核。
  • stride (int 或 tuple, optional):
步幅控制卷积核在输入数据上的滑动速度。步幅越大,输出特征图的尺寸越小。默认值为1,表示每次卷积核滑动一个像素。

如果设置为一个整数 s,则步幅在高度和宽度上都是 s。
如果设置为元组 (sH, sW),则分别控制卷积核在垂直方向和水平方向上的步幅。
  • padding (int, tuple 或 str, optional):
填充用于在输入数据的边缘添加额外的像素,通常是为了保持输出的空间尺寸或者避免特征丢失。
如果设置为整数 p,则表示在四个边上都添加 p 个像素的零填充。
如果设置为元组 (pH, pW),则表示垂直方向和水平方向上的填充分别为 pH 和 pW。

有些框架也允许使用字符串 same 或 valid 来自动确定填充:
'same':填充使得输出特征图的空间尺寸与输入相同。
'valid':不使用填充,输出特征图的尺寸会减小。

4.案例

from PIL import Image
import torch.nn as nn
from torch.nn import Conv2d
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms

"""
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, 
dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)
"""
# 将图片变为6通道展示
# 1.将图片转换为torch
img = Image.open("data/tyut.png")
to_tensor = transforms.ToTensor()
img_tensor = to_tensor(img)


# 2.创建网络
class net(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = Conv2d(
            in_channels=3,  # 输入通道数
            out_channels=6,  # 6层卷积核
            kernel_size=3,  # 卷积核大小3*3
            stride=1  # 步长为1
        )

    def forward(self, x):
        return self.conv1(x)

# 3.输出结果
n = net()
output = n(img_tensor)
print(output.shape)

3.3 神将网络-最大池化层

最大池化(Max Pooling)是一种常用于卷积神经网络(CNN)中的降维操作

  • 作用:
  1. 减少计算量:通过池化操作,减少了特征图的尺寸,降低了后续层的计算量和内存消耗。
  2. 防止过拟合:池化层通过去除一些细节信息,增强了模型的泛化能力,防止过拟合。
  3. 保留重要特征:最大池化通过选取局部区域的最大值,保留了图像中的显著特征(如边缘、角点等),减少了不重要的噪声。
  4. 空间不变性:通过池化,模型对图像的微小平移、旋转等变换更加鲁棒,即具有一定的空间不变性。
  • 方法
torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
  • 参数:
1.kernel_size (Union[int, Tuple[int, int]])
作用:定义池化窗口的大小。它指定了池化操作时考虑的区域的大小。

2.stride (Union[int, Tuple[int, int]])
作用:定义池化窗口滑动的步幅,即每次池化窗口移动的距离。默认情况下,步幅等于 kernel_size,即池化窗口每次移动的距离与窗口大小相同。

3.padding (Union[int, Tuple[int, int]])
作用:在输入张量的边缘添加零填充。这个参数用来处理边缘像素,保证池化操作可以覆盖到输入的边缘部分。对于最大池化来说,这种填充通常是隐式的。

4.dilation (Union[int, Tuple[int, int]])
作用:控制池化窗口元素之间的间距。通过增加窗口中元素的间距,池化操作会跳过一些中间值,从而能够“扩展”池化窗口的覆盖范围。

5.ceil_mode (bool)
作用:控制池化输出尺寸的计算方法。当 ceil_mode=True 时,输出尺寸使用向上取整的方式计算;否则,使用向下取整(默认行为)。
  • 过程:

    1.将池化核与覆盖数据部分作比较,取其最大部分作为输出

    2.从第0行0列进行,依次按先行后列的顺序,按照步长为stride(默认与kernel_size一致)

    3.对于输入数据包含四个维度 (N,C,Hin,Win)【批次大小,通道数,高度,宽度】

  • **案例1:**将下述矩阵分别做池化操作(ceil_mode=True或ceil_mode=False)

image-20250222101111032

import torch
from torch import nn as nn

data = torch.tensor([[1, 2, 0, 3, 1],
                     [0, 1, 2, 3, 1],
                     [1, 2, 1, 0, 0],
                     [5, 2, 3, 1, 1],
                     [2, 1, 0, 1, 1]], dtype=torch.float32)
re_data = torch.reshape(data, (1, 1, 5, 5))


class MaxPool(nn.Module):
    def __init__(self):
        super().__init__()
        self.maxPool = nn.MaxPool2d(kernel_size=3, ceil_mode=True)

    def forward(self, data):
        return self.maxPool(data)


maxPool = MaxPool()
res = maxPool(re_data)
print(res)
/*
tensor([[[[2., 3.],
          [5., 1.]]]])
*/
  • **案例2:**对CIFAR10的测试集的图片做最大池化操作
import torchvision
from torch import nn as nn
from torch.utils.data.dataloader import DataLoader
from torch.utils.tensorboard import SummaryWriter

# 1.加载数据
data = torchvision.datasets.CIFAR10('data-train', train=False, transform=torchvision.transforms.ToTensor(), download=True)
data_loader = DataLoader(data, batch_size=64)

# 2.池化层网络
class MaxPool(nn.Module):
    def __init__(self):
        super().__init__()
        self.maxPool = nn.MaxPool2d(kernel_size=3, ceil_mode=True)

    def forward(self, data):
        return self.maxPool(data)

# 3.将数据加载到网络
writer = SummaryWriter('logs')
maxPool = MaxPool()
step = 0
for x in data_loader:
    imgs, labels = x
    pool = maxPool(imgs)
    writer.add_images('Pooling', pool, step)
    step += 1
writer.close()

image-20250222111215681

3.4 神经网络-非线性激活

神经网络中的非线性激活函数用于引入非线性因素,从而使得神经网络能够学习和表示复杂的模式和关系。没有非线性激活函数,神经网络即使拥有多个层,也只会表现为一个简单的线性变换,无法解决复杂的任务。

非线性激活函数有多个,如ReLU函数,ELU函数等,下面介绍ReLU函数的非线性激活:

ReLU函数:torch.nn.ReLU(inplace=False)]
f ( x ) = m a x ( 0 , x ) f(x) = max(0,x) f(x)=max(0,x)
image-20250222162119298

  • 使用方法:
import torch
from torch import nn as nn

input = torch.tensor([[-1, 0.2, -0.3], [1, 3, -2], [2, -1, -2]])

class ReLUTest(nn.Module):
    def __init__(self):
        super().__init__()
        self.relu = nn.ReLU()

    def forward(self, input):
        return self.relu(input)

relu = ReLUTest()
res = relu(input)
print(res)

#结果:
tensor([[0.0000, 0.2000, 0.0000],
        [1.0000, 3.0000, 0.0000],
        [2.0000, 0.0000, 0.0000]])
  • **案例:**使用Sigmoid对CIFAR10的测试集的图片做非线性激活
import torchvision
from torch.utils.data.dataloader import DataLoader
from torch.utils.tensorboard import SummaryWriter
from torch import nn as nn

# 1.加载数据集
data = torchvision.datasets.CIFAR10('data-train', train=False, transform=torchvision.transforms.ToTensor(),
                                    download=True)
data_loader = DataLoader(data, batch_size=64)


# 2.构建网络
class SigmoidTest(nn.Module):
    def __init__(self):
        super().__init__()
        self.sigmoid = nn.Sigmoid()

    def forward(self, input):
        return self.sigmoid(input)

# 3.得出结果
net = SigmoidTest()
writer = SummaryWriter('logs')
step = 0
for x in data_loader:
    imgs, labels = x
    res = net(imgs)
    writer.add_images('非线性激活', res, step)
    step += 1
writer.close()

image-20250222164143467

3.5 神经网络-线性层

线性层(Linear Layer)是神经网络中的基本构建模块之一,也常被称为全连接层(Fully Connected Layer)。它的主要作用是对输入数据进行线性变换,通过一组权重和偏置来生成输出。

  • 线性层的数学表达式:

假设输入为向量 x,权重矩阵为 W,偏置为 b,输出为 y,那么线性层的运算可以表示为:
y = W x + b y=Wx+b y=Wx+b

  • 线性变换

线性层对输入进行线性变换,即通过矩阵乘法和加法运算将输入映射到一个新的空间。通过训练过程中的权重更新,网络能够学习到如何将输入数据映射到合适的输出空间。

  • 方法:torch.nn.Linear(in_features, out_features, bias=True)
in_features:这个参数表示输入特征的数量,也就是每个输入样本的特征维度。例如,如果输入是一个包含10个特征的向量,那么in_features就是10。

out_features:这个参数表示输出特征的数量,也就是该层输出的维度或神经元的数量。比如,如果希望该层的输出是一个包含5个特征的向量,那么out_features就是5。

bias:是否添加偏振

在进行线性变换时,它们的权重W和偏置b由下方公式生成:

image-20250222171311249

image-20250222171405236

通过上述图片的计算,可以得到新维度下的特征值

  • 输入数据与输出数据

image-20250222172626445

  • **案例:**将图片中的特征值个数通过线性变换得到10
import torch
import torchvision
from PIL import Image
from torch import nn as nn

img = Image.open('data/tyut.png')
to_tensor = torchvision.transforms.ToTensor()
tensor_img = to_tensor(img)

print(tensor_img.shape)  # torch.Size([3, 1280, 1276])
print(torch.reshape(tensor_img, (1, -1)).shape) # torch.Size([1, 4899840])

class LinearTest(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(4899840, 10)

    def forward(self, input):
        return self.linear(input)


test = LinearTest()
print(test(torch.reshape(tensor_img, (1, -1))).shape) #torch.Size([1, 10])

3.6 CIFAR-10分类模型搭建

CIFAR-10 是一个广泛使用的计算机视觉数据集,包含 10 个类别的 60,000 张彩色图像。每个类别有 6,000 张图像,图像的大小是 32x32 像素。这个数据集常用来训练和评估机器学习模型,尤其是卷积神经网络(CNN)。

image-20250222180135152

上述为网络的构建过程:

  1. 3@32x32 -> 32@32x32 的卷积操作
nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=2)
"""
1.输入通道为3, 输出通道为32,卷积核的大小为5,dilation=1(非空洞卷积)
2.通过下述公式可以计算padding和stride
  32 = (32 + 2*p -1*(5-1)-1)/s +1  =》 31*s = 27 + 2*p
结果:s=1 p=2 / s=2 p=17.5(舍去)
"""

image-20250222181518807

  1. 32@32x32 -> 32@16x16的池化操作
nn.MaxPool2d(kernel_size=2, stride=1, padding=2)
"""
1. 池化核大小为2
"""
  1. 32@16x16 -> 32@16x16的卷积操作
nn.Conv2d(in_channels=16, out_channels=16, kernel_size=5, stride=1, padding=2),
"""
1.输入通道为32, 输出通道为32,卷积核的大小为5,dilation=1(非空洞卷积)
2.通过下述公式可以计算padding和stride
  16 = (16 + 2*p -1*(5-1)-1)/s +1  =》 15*s = 11 + 2*p
结果:s=1 p=2 / s=2 p=9.5(舍去)
"""
  1. 32@16x16 -> 32@8x8的池化操作
nn.MaxPool2d(kernel_size=2)
  1. 32@8x8 -> 64@8x8的卷积操作
nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2)
  1. 64@8x8 -> 64@4x4的池化操作
nn.MaxPool2d(kernel_size=2)
  1. 64@4x4变为1维张量
nn.Flatten()
"""
shape=64*4*4=1024
"""
  1. 线性池将1024特征 -> 64特征 -> 10特征
nn.Linear(in_features=1024, out_features=64),
nn.Linear(in_features=64, out_features=20)
  • 网络搭建源码
import torchvision
from torch import nn as nn
from torch.utils.data import DataLoader


# 1.搭建网络
class CIFAR10_test(nn.Module):
    def __init__(self):
        super().__init__()
        self.model = nn.Sequential(
            nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=2),
            nn.MaxPool2d(kernel_size=2),
            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
            nn.MaxPool2d(kernel_size=2),
            nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
            nn.MaxPool2d(kernel_size=2),
            nn.Flatten(),
            nn.Linear(in_features=1024, out_features=64),
            nn.Linear(in_features=64, out_features=10)
        )

    def forward(self, input):
        return self.model(input)


# 2.加载数据集
data = torchvision.datasets.CIFAR10('data-train', train=False, transform=torchvision.transforms.ToTensor(),
                                    download=True)
data_loader = DataLoader(data, batch_size=64)

# 3.输出结果
testNet = CIFAR10_test()
for x in data_loader:
    imgs, labels = x
    res = testNet(imgs)
    print(res.shape)

http://www.kler.cn/a/557595.html

相关文章:

  • 分页功能组件开发
  • 1688代采下单API接口使用指南:实现商品采集与自动化下单
  • 深度学习-5.卷积网络
  • npm、pnpm和yarn有什么区别
  • Matplotlib 高级图表绘制与交互式可视化(ipywidgets)
  • 【Windows系统node_modules删除失败(EPERM)问题解析与应对方案】
  • mysql之规则优化器RBO
  • 关于 Grok-3 大语言模型的研究
  • Web Worker终极优化指南:4秒卡顿→0延迟的实战蜕变
  • 【AcWing】动态规划-线性DP -选数异或
  • MapReduce 读取 Hive ORC ArrayIndexOutOfBoundsException: 1024 异常解决
  • python脚本(一):飞书机器人实现新闻抓取与推送
  • socket()函数的概念和使用案例
  • Android:权限permission申请示例代码
  • C++ 设计模式-模板方法模式
  • 【Python】Python顺序语句经典题合集
  • java开发——为什么要使用动态代理?
  • hot100_74. 搜索二维矩阵
  • Unity FBXExport导出的FBX无法在Blender打开
  • ZT7 小红的排列构造