Pytorch(笔记8神经网络nn)

1、nn.Module

torch.nn是专门为深度学习而设计的模块。torch.nn的核心数据结构是Module,它是一个抽象的概念,既可以表示神经网络中的某个层(layer),也可以表示一个包含很多层的神经网络。在实际使用中,最常见的做法是继承nn.Module,从而编写自己的网络/层。下面先来看看如何用nn.Module实现自己的全连接层。Y=AX+B

import torch as t
import torch.nn as nn

class network(nn.Module):
    def __init__(self, input, output):
        super().__init__()
        # 定义权重矩阵a,它是一个可训练的参数,形状为(input, output)
        self.a = nn.Parameter(t.randn(input, output))
        # 定义偏置向量b,它也是一个可训练的参数,形状为(output,)
        # 注意:偏置向量的长度应与输出特征的维度相匹配
        self.b = nn.Parameter(t.randn(output))

    def forward(self, x):
        """
        定义前向传播过程

        参数:
            x (torch.Tensor): 输入数据,形状应为(batch_size, input)

        返回:
            torch.Tensor: 输出数据,形状为(batch_size, output)
        """
        # 首先,使用权重矩阵a对输入x进行线性变换
        # x@self.a执行矩阵乘法,x的每一行与a相乘,结果形状为(batch_size, output)
        x = x @ self.a
        # 然后,将偏置向量b扩展(通过broadcasting)到与x相同的形状,并加到x上
        # self.b.expand_as(x)将b的形状从(output,)扩展到(batch_size, output)
        # x + self.b.expand_as(x)将偏置加到每个样本的输出上
        x = x + self.b.expand_as(x)
        # 返回变换后的输出
        return x


a = network(4, 3)
# 创建输入数据,形状为(6, 4),表示有6个样本,每个样本有4个特征
input = t.rand(6, 4)
# 通过网络前向传播得到输出
output = a(input)
# 打印输出,形状应为(6, 3),表示有6个样本,每个样本的输出特征维度为3
print(output)
  • 自定义层network必须继承nn.Module,并且在其构造函数中需调用nn.Module的构造函数,即super().init ()或nn.Module.init(self),推荐使用第一种用法;
  • 在构造函数__init__中必须自行定义可学习的参数,并封装成Parameter,如在本例中我们把w和b封装成Parameter。Parameter是一种特殊的Tensor,但其默认需要求导(requires_grad
    = True)
  • forward函数实现前向传播过程,其输入可以是一个或多个Tensor;
  • 无需写反向传播函数,nn.Module能够利用autograd自动实现反向传播,这点比Function简单许多;
  • Module中的可学习参数可以通过named_parameters()或者parameters()返回一个迭代器,前者会给每个parameter都附上名字,使其更具有辨识度。

2、常用神经网络层

2.1 图像相关层

图像相关层主要包括卷积层(Conv)、池化层(Pool)等,这些层在实际使用中可分为一维(1D)、二维(2D)和三维(3D),池化方式又分为平均池化(AvgPool)、最大值池化(MaxPool)、自适应池化(AdaptiveAvgPool)等。而卷积层除了常用的前向卷积之外,还有逆卷积(TransposeConv)等等。下面将举例说明。

  • 特征提取
  • 保持数据空间结构
  • 引入非线性变换 在卷积操作之后,通常会应用一个激活函数(如ReLU、Sigmoid或Tanh)来引入非线性变换。这些激活函数能够增加CNN的表达能力,使其能够学习更加复杂的非线性关系。
  • 提高计算效率通过卷积操作和池化层的结合使用,卷积层能够降低特征图的空间维度,从而减少计算量并提高模型的计算效率。同时,池化层还能够增强特征的平移不变性,使模型对输入数据的小变化更加鲁棒。

卷积层

在深度学习中,与图像处理相关的网络结构中最重要的便是卷积层(Conv)。卷积神经网络的本质就是卷积层、池化层、激活层以及其他层的叠加,所以理解卷积层的工作原理是极其重要的,下面将举例说明卷积操作的具体过程。

# 导入PyTorch库  
import torch  
import torch.nn as nn  
  
# 从torchvision.transforms导入ToTensor和ToPILImage,用于图像张量和PIL图像之间的转换  
from torchvision.transforms import ToTensor, ToPILImage  
  
# 从PIL(Python Imaging Library,Pillow是其一个分支)导入Image模块,用于处理图像文件  
from PIL import Image  
  
# 使用PIL的Image.open函数打开指定路径的图片文件,并通过.convert("L")将其转换为灰度图像(单通道)  
img = Image.open("H:\\PYTHON_Proj\\handlearnpytorch\\OIP-C.jpg").convert("L")  
  
# 实例化ToTensor转换对象,用于将PIL图像转换为PyTorch张量  
to_tensor = ToTensor()  
  
# 实例化ToPILImage转换对象,用于将PyTorch张量转换回PIL图像  
to_PIL = ToPILImage()  
  
# 使用to_tensor将PIL图像转换为PyTorch张量,并通过.unsqueeze(0)在批次大小维度上增加一个维度,使其形状变为(1, 1, H, W)  
img = to_tensor(img).unsqueeze(0)  
  
# 创建一个3x3的卷积核(滤波器),初始时所有元素都被设置为-1/9,然后将中心元素设置为1  
kernel = torch.ones(3, 3) / (-9.0)  
kernel[1][1] = 1  
  
# 创建一个Conv2d层,指定输入通道数为1(因为是灰度图像),输出通道数也为1,卷积核大小为3x3,步长为1,填充为1(保持输出尺寸与输入相同),且不使用偏置项  
conv = nn.Conv2d(1, 1, 3, 1, 1, bias=False)  
  
# 将之前定义的卷积核赋值给Conv2d层的权重,注意要调整形状以匹配Conv2d层的期望(out_channels, in_channels, kernel_size[0], kernel_size[1])  
conv.weight.data = kernel.reshape(1, 1, 3, 3)  
  
# 对图像应用卷积操作,此时img是一个四维张量,Conv2d层会处理它并返回一个新的四维张量  
img = conv(img)  
  
# 使用to_PIL将卷积后的PyTorch张量转换回PIL图像,并通过.squeeze(0)移除批次大小维度  
img = to_PIL(img.squeeze(0))  
  
# 使用PIL的.show()方法显示图像  
img.show()

池化层

池化层可以看作是一种特殊的卷积层,其主要用于下采样,增加池化层可以在保留主要特征的同时降低参数量,从而一定程度上防止了过拟合。池化层没有可学习参数,它的weight是固定的。在torch.nn工具箱中封装好了各种池化层,常见的有最大池化(MaxPool)和平均池化(AvgPool),池化层(Pooling Layer)在卷积神经网络(CNN)中扮演着非常重要的角色。它的主要用处可以归纳为以下几点:

  • 降维(减少计算量):池化层通过减少数据的空间大小(即高度和宽度),来降低后续层的计算量和参数数量。这对于防止过拟合和加快计算速度是非常有益的。
  • 特征不变性:池化层能够使得模型学习到更加鲁棒的特征表示,即对输入数据的微小变化(如平移、旋转等)具有不变性。这是因为池化操作(如最大池化、平均池化等)会选取区域内的代表性特征,而不是依赖于具体的位置信息。
  • 提取主要特征:通过池化操作,可以提取出图像中最主要的特征,而忽略掉一些不重要的细节信息。这对于后续的卷积层进一步提取高层次的特征是有帮助的。
  • 扩大感受野:随着网络层数的增加,池化层能够逐步扩大后续层中每个神经元对应的输入区域(即感受野)。这有助于网络学习到更加全局的特征信息。
  • 减少过拟合:由于池化层通过减少数据的空间维度来降低参数数量,这在一定程度上可以减少模型的复杂度,从而有助于防止过拟合。

常见的池化操作包括:

  • 最大池化(Max Pooling):在池化窗口内选取最大值作为输出。这种方式有助于保留图像的边缘和纹理信息。
  • 平均池化(Average Pooling):在池化窗口内计算所有值的平均值作为输出。这种方式有助于保留图像的背景信息。
  • 随机池化(Stochastic Pooling):根据池化窗口内各元素的值大小,按概率随机选择元素作为输出。这种方式结合了最大池化和平均池化的优点,但计算复杂度较高。

总之,池化层是卷积神经网络中不可或缺的一部分,它通过减少数据的空间维度、提取主要特征、扩大感受野以及防止过拟合等方式,为整个网络的学习能力和性能提供了重要的支持。

# 导入PyTorch库  
import torch  
  
# 导入PyTorch的神经网络模块,用于构建和训练神经网络  
import torch.nn as nn  
  
# 从torchvision.transforms模块导入ToTensor和ToPILImage,这两个转换工具用于图像数据的预处理和后处理  
from torchvision.transforms import ToTensor, ToPILImage  
  
# 从PIL库导入Image模块,用于图像的打开、显示等操作  
from PIL import Image  
  
# 创建一个ToTensor的实例,用于将PIL图像或numpy.ndarray转换为FloatTensor,并归一化到[0.0, 1.0]  
to_tensor = ToTensor()  
  
# 创建一个ToPILImage的实例,用于将Tensor或ndarray转换为PIL图像  
to_pil = ToPILImage()  
  
# 使用PIL的Image.open方法打开指定路径的图像文件,并将其转换为灰度图像('L'模式)  
img = Image.open("H:\\PYTHON_Proj\\handlearnpytorch\\OIP-C.jpg").convert('L')  
  
# 使用PIL的show方法显示图像  
img.show()  
  
# 使用ToTensor转换将PIL图像转换为Tensor,并增加一个维度使其成为[1, H, W]形状,即增加一个批次维度  
img = to_tensor(img).unsqueeze(0)  
  
# 创建一个平均池化层实例,使用2x2的窗口大小和步长为2进行池化  
pool = nn.AvgPool2d(2, 2)  
  
# 对图像Tensor应用平均池化层,然后移除批次维度(squeeze(0)),使其变回[H', W']形状  
img = pool(img).squeeze(0)  
  
# 将Tensor转换回PIL图像以便显示  
img = to_pil(img)  
  
# 再次使用PIL的show方法显示经过池化处理的图像  
img.show()

其他层

除了卷积层和池化层,深度学习中还将常用到以下几个层:

  • Linear:全连接层;
  • BatchNorm:批标准化层,分为1D、2D和3D。除了标准的BatchNorm之外,还有在风格迁移中常用到的InstanceNorm层;
  • Dropout:Dropout层,用来防止过拟合,同样分为1D、2D和3D。

3、初始化策略

在深度学习中参数的初始化十分重要,良好的初始化能让模型更快地收敛,并达到更高水平,而糟糕的初始化则可能使得模型迅速崩溃。PyTorch中nn.Module的模块参数都采取了较为合理的初始化策略,因此一般不用我们考虑。当然我们也可以用自定义初始化来代替系统的默认初始化。而当我们在使用Parameter时,自定义初始化则尤为重要,这是因为torch.Tensor()返回的是内存中的随机数,很可能会有极大值,这在实际训练网络中会造成溢出或者梯度消失。PyTorch中nn.init模块就是专门为初始化而设计的一个模块,其中实现了常用的初始化策略。如果某种初始化策略nn.init不提供,用户也可以自己直接初始化。

import torch  
from torch.nn import init  
from torch import nn  
  
# 创建一个线性层,其权重和偏置会被随机初始化(与torch.manual_seed无关,因为这是在调用torch.manual_seed之前发生的)  
linear = nn.Linear(3, 4)  
  
# 打印层创建时默认初始化的权重  
print("默认初始化的权重:")  
print(linear.weight)  
  
# 设置随机数生成的种子,以确保接下来的随机数生成是可重复的  
torch.manual_seed(2021)  
  
# 使用Xavier正态分布重新初始化权重  
# 这个初始化是受torch.manual_seed(2021)影响的  
init.xavier_normal_(linear.weight)  
  
# 打印重新初始化后的权重  
print("Xavier正态分布初始化后的权重:")  
print(linear.weight)
相关推荐
四口鲸鱼爱吃盐1 小时前
Pytorch | 从零构建GoogleNet对CIFAR10进行分类
人工智能·pytorch·分类
冷眼看人间恩怨1 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
落魄君子1 小时前
ELM分类-单隐藏层前馈神经网络(Single Hidden Layer Feedforward Neural Network, SLFN)
神经网络·分类·数据挖掘
leaf_leaves_leaf2 小时前
win11用一条命令给anaconda环境安装GPU版本pytorch,并检查是否为GPU版本
人工智能·pytorch·python
夜雨飘零12 小时前
基于Pytorch实现的说话人日志(说话人分离)
人工智能·pytorch·python·声纹识别·说话人分离·说话人日志
四口鲸鱼爱吃盐3 小时前
Pytorch | 从零构建MobileNet对CIFAR10进行分类
人工智能·pytorch·分类
苏言の狗3 小时前
Pytorch中关于Tensor的操作
人工智能·pytorch·python·深度学习·机器学习
是Dream呀3 小时前
Python从0到100(七十八):神经网络--从0开始搭建全连接网络和CNN网络
网络·python·神经网络
Hejjon7 小时前
SpringBoot 整合 SQLite 数据库
笔记
四口鲸鱼爱吃盐8 小时前
Pytorch | 利用VMI-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击
人工智能·pytorch·python