Pytorch的学习

1.基本数据:Tensor

Tensor,即张量,是PyTorch中的基本操作对象,可以看做是包含单一数据类型元素的多维矩阵。从使用角度来看,Tensor与NumPy的ndarrays非常类似,相互之间也可以自由转换,只不过Tensor还支持GPU的加速【重点】。

1.1 Tensor的创建

tensor张量的操作

1.2 tensor的操作:

第一部分: 分为FloatTensor,IntTensor,randn,range,zeros,ones,empty

import torch
a = torch.FloatTensor(2,3) # 1.生成两行三列的向量,3代表维度
b = torch.FloatTensor([2,3,4,5]) # 2.生成float类型的四个维度向量
a,b

(tensor([[1.0561e-38, 1.0102e-38, 9.6429e-39],
         [8.4490e-39, 9.6429e-39, 9.1837e-39]]),
 tensor([2., 3., 4., 5.]))

torch.IntTensor: 用于生成数据类型为整型的Tensor,传递给传递给torch.IntTensor的参数可以是列表,也可以是一个维度值。

import torch
a = torch.IntTensor(2,3)
b = torch.IntTensor([2,3,4,5])
a,b

torch.randn: 用于生成数据类型为浮点数且维度指定的随机Tensor,随机生成的浮点数的取值满足均值为0,方差为1的正态分布

import torch
a = torch.randn(2,3)
a
tensor([[-0.0067, -0.0707, -0.6682],
        [ 0.8141,  1.1436,  0.5963]])

torch.zeros: torch.zeros用于生成数据类型为浮点型且维度指定的Tensor

import torch
a = torch.zeros(2,3)
a
tensor([[0., 0., 0.],
        [0., 0., 0.]])

torch.abs 、torch.add、torch.clamp、torch.mm()计算:
torch.clamp()的作用:对tensor中的数据进行裁剪。
torch.mm()的作用:将参数传递到torch.mm后返回输入参数的求积结果作为输出,不过这个求积的方式和之前的torch.mul运算方式不太一样,torch.mm 运用矩阵之间的乘法规则进行计算 ,所以被传入的参数会被当作矩阵进行处理,参数的维度自然也要满足矩阵乘法的前提条件,即前一个矩阵的行数必须和后一个矩阵列数相等

# 这段代码的目的是展示不同类型的张量的使用方法。下面是加上中文注释后的代码:
import torch

# 1.demo1
a = torch.FloatTensor(2, 3)  # 创建一个大小为2x3的浮点型张量
b = torch.FloatTensor([2, 3, 4, 5])  # 创建一个大小为4的浮点型向量
c = torch.Tensor(2, 3)  # 创建一个大小为2x3的任意类型张量

# print(a, b)  # 输出a和b
# print(c)

# 2.demo2
a = torch.IntTensor(2, 3)
b = torch.IntTensor([2, 3, 4, 5])
# print(a, b)

# 3.demo3:生成数据类型为浮点数且维度指定的随机Tensor,取值满足均值为0,方差为1的正态分布
a = torch.randn(2, 3)
b = torch.range(1, 20, 2)  # torch.range用于生成数据类型为浮点型且起始范围和结束范围的Tensor
# print(a)
# print(b)

# 4.demo4: Tensor的计算
a = torch.randn(2, 3)
b = torch.abs(a)  # 绝对值的输出
# print(a)
# print(b)

a = torch.randn(2, 3)
b = torch.randn(2, 3)
c = torch.add(a, b)
# print(c)

d = torch.randn(2, 3)
e = torch.add(d, 10)  # 增加标量
# print(e)

a = torch.randn(2, 3)
# print(a)
b = torch.clamp(a, -0.1, 0.1)  # 卡位操作:限制张量里面每个值都在-0.1到0.1之间,如果<-0.1则设置未-0.1,如果>0.1,则设置未0.1
# print(b)

# 4.demo4:矩阵计算-torch.mm() (2x3)*(3x2)=2x2,如果是矩阵和向量之间的计算,旧用torch.mv()
a = torch.randn(2, 3)
print(a)
b = torch.rand(2, 3)
print(b)
c = torch.mm(a, b.T)
print(c)

torch.mv():
将参数传递到torch.mv后返回输入参数的求积结果作为输出,torch.mv运用矩阵与向量之间的乘法规则进行计算,被传入的第1个参数代表矩阵,第2个参数代表向量,循序不能颠倒。

a = torch.randn(2,3)
a
#我们得到a为:
#tensor([[ 1.0909, -1.1679,  0.3161],
#        [-0.8952, -2.1351, -0.9667]])
b = torch.randn(3)
b
#我们得到b为:
#tensor([-1.4689,  1.6197,  0.7209])
#用产生的a,b进行矩阵乘法操作:
c = torch.mv(a,b)
c
#tensor([-3.2663, -2.8402])

2.神经网络工具箱torch.nn

torch.autograd 库虽然实现了自动求导与梯度反向传播。
torch.nn: 该接口构建于 Autograd 之上,提供了网络模组优化器初始化策略等一系列功能
nn.Module类: nn.Module是PyTorch提供的神经网络类,并在类中实现了网络各层的定义及前向计算反向传播机制。在实际使用时,如果想要实现某个神经网络,只需继承nn.Module,在初始化中定义模型结构与参数,在函数**forward()**中编写网络前向过程即可。

1.nn.Parameter函数

2.forward()函数与反向传播

3.多个Module的嵌套

4.nn.Module与nn.functional库

5.nn.Sequential()模块

import torch.nn as nn
import torch


class MLP(nn.Module):
    """
    实现一个具有三层隐藏层的MLP模型。

    参数:
    - in_dim (int): 输入维度
    - hid_dim1 (int): 第一层隐藏层的维度
    - hid_dim2 (int): 第二层隐藏层的维度
    - out_dim (int): 输出维度

    返回:
    - 无
    """

    def __init__(self, in_dim, hid_dim1, hid_dim2, out_dim):
        super(MLP, self).__init__()  # 1.调用父类 nn.Module 的初始化方法
        # 2.定义一个包含三层线性变换和三层ReLU激活函数的序列模型
        self.layers = nn.Sequential(
            nn.Linear(in_dim, hid_dim1),  # 3.第一层线性变换
            nn.ReLU(),  # 第一层激活函数
            nn.Linear(hid_dim1, hid_dim2),  # 第二层线性变换
            nn.ReLU(),  # 第二层激活函数
            nn.Linear(hid_dim2, out_dim)  # 第三层线性变换
            # 通常在最后一层线性变换后不会再加 ReLU,因为这会影响输出的范围
        )

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

        参数:
        - x (Tensor): 输入的特征张量

        返回:
        - x (Tensor): 经过模型处理后的输出张量
        """
        x = self.layers(x)  # 输入x依次经过self.layers中定义的各层
        return x


# 定义MLP模型输入输出的维度
in_dim = 10
hid_dim1 = 20
hid_dim2 = 15
out_dim = 5

# 实例化MLP模型
model = MLP(in_dim, hid_dim1, hid_dim2, out_dim)

# 得到输入张量x,维度应该和模型model定义的输入张量维度一样的
x = torch.randn(32, in_dim)

# 将输入张量 x 传入模型得到输出的维度,本质就是传递给模型的 forward 方法(或者直接传递给模型实例,因为 forward 方法会被自动调用)
output = model(x)
print(output.shape)

3.搭建简易的神经网络:

3.1下面我们用torch搭一个简易神经网络:
1、【torch.autograd】包的主要功能就是完成神经网络后向传播中的链式求导,手动去写这些求导程序会导致重复造轮子的现象。
2、【自动梯度的功能过程大致为】:先通过输入的Tensor数据类型的变量在神经网络的前向传播过程中生成一张计算图,然后根据这个计算图和输出结果精确计算出每一个参数需要更新的梯度,并通过完成后向传播完成对参数的梯度更新。

import torch
from torch.autograd import Variable

batch_n = 100  # 一个批次输入数据的数量
hidden_layer = 100
input_data = 1000  # 每个数据的特征为1000
output_data = 10

x = Variable(torch.randn(batch_n, input_data), requires_grad=False)
y = Variable(torch.randn(batch_n, output_data), requires_grad=False)
# 用Variable对Tensor数据类型变量进行封装的操作。requires_grad如果是False,表示该变量在进行自动梯度计算的过程中不会保留梯度值。
w1 = Variable(torch.randn(input_data, hidden_layer), requires_grad=True)
w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad=True)

# 学习率和迭代次数
epoch_n = 50
lr = 1e-6

for epoch in range(epoch_n):
    h1 = x.mm(w1)  # (100,1000)*(1000,100)-->100*100
    print(h1.shape)
    h1 = h1.clamp(min=0)
    y_pred = h1.mm(w2)
    # y_pred = x.mm(w1).clamp(min=0).mm(w2)
    loss = (y_pred - y).pow(2).sum()
    print("epoch:{},loss:{:.4f}".format(epoch, loss.data))

    #     grad_y_pred = 2*(y_pred-y)
    #     grad_w2 = h1.t().mm(grad_y_pred)
    loss.backward()  # 后向传播
    #     grad_h = grad_y_pred.clone()
    #     grad_h = grad_h.mm(w2.t())
    #     grad_h.clamp_(min=0)#将小于0的值全部赋值为0,相当于sigmoid
    #     grad_w1 = x.t().mm(grad_h)
    w1.data -= lr * w1.grad.data
    w2.data -= lr * w2.grad.data

    w1.grad.data.zero_()
    w2.grad.data.zero_()

#     w1 = w1 -lr*grad_w1
#     w2 = w2 -lr*grad_w2

3.2重写前向传播函数:
该代码并没有继承父类的model,初始化直接利用父类的model,关键点在于重写了前向传播算法

import torch
from torch.autograd import Variable
batch_n = 64#一个批次输入数据的数量
hidden_layer = 100
input_data = 1000#每个数据的特征为1000
output_data = 10
class Model(torch.nn.Module):#完成类继承的操作
    def __init__(self):
        super(Model,self).__init__()#类的初始化
        
    def forward(self,input,w1,w2):
        x = torch.mm(input,w1)
        x = torch.clamp(x,min = 0)
        x = torch.mm(x,w2)
        return x
    
    def backward(self):
        pass
model = Model()
x = Variable(torch.randn(batch_n,input_data),requires_grad=False)
y = Variable(torch.randn(batch_n,output_data),requires_grad=False)
#用Variable对Tensor数据类型变量进行封装的操作。requires_grad如果是F,表示该变量在进行自动梯度计算的过程中不会保留梯度值。
w1 = Variable(torch.randn(input_data,hidden_layer),requires_grad=True)
w2 = Variable(torch.randn(hidden_layer,output_data),requires_grad=True)

epoch_n=30

for epoch in range(epoch_n):
    y_pred = model(x,w1,w2)
    
    loss = (y_pred-y).pow(2).sum()
    print("epoch:{},loss:{:.4f}".format(epoch,loss.data))
    loss.backward()
    w1.data -= lr*w1.grad.data
    w2.data -= lr*w2.grad.data

    w1.grad.data.zero_()
    w2.grad.data.zero_()
    

3.3 利用torch.nn.Sequential类(一种序列容器)进行神经网络的搭建:
Sequential 类是torch.nn中的一种序列容器,通过在容器中嵌套各种实现神经网络模型的搭建,最主要的是,参数会按照我们定义好的序列自动传递下去。
Linear(input,hidden)的作用就是对输入的数据进行线性变换,以下的权重和偏置会在训练过程中通过反向传播算法进行更新。
在这里插入图片描述
相关链接:
线性回归
分类的基础学习
线性和非线性

import torch
from torch.autograd import Variable
batch_n = 100 #一个批次输入数据的数量
hidden_layer = 100
input_data = 1000 #每个数据的特征为1000
output_data = 10

x = Variable(torch.randn(batch_n,input_data),requires_grad=False)
y = Variable(torch.randn(batch_n,output_data),requires_grad=False)
#用Variable对Tensor数据类型变量进行封装的操作。requires_grad如果是F,表示该变量在进行自动梯度计算的过程中不会保留梯度值。

models = torch.nn.Sequential(
    torch.nn.Linear(input_data,hidden_layer),
    torch.nn.ReLU(),
    torch.nn.Linear(hidden_layer,output_data)
)
#torch.nn.Sequential括号内就是我们搭建的神经网络模型的具体结构,Linear完成从隐藏层到输出层的线性变换,再用ReLU激活函数激活
#torch.nn.Sequential类是torch.nn中的一种序列容器,通过在容器中嵌套各种实现神经网络模型的搭建,
#最主要的是,参数会按照我们定义好的序列自动传递下去。

4.一些计算损失函数的方法

4.1 MSE均方差

torch.nn.MSELoss类使用均方误差函数对损失值进行计算,定义类的对象不用传入任何参数,但在使用实例时需要输入两个维度一样的参数方可进行计算。

用于场景: MSE 是预测值和实际值之间差值的平方的平均值。它主要用于回归问题。
在这里插入图片描述

import torch
from torch.autograd import Variable
loss_f = torch.nn.MSELoss()
x = Variable(torch.randn(100,100)) # 100行100列的向量
y = Variable(torch.randn(100,100))
loss = loss_f(x,y)
loss.data
#tensor(1.9529)

4.2 L1平均绝对误差

torch.nn.L1Loss类使用平均绝对误差函数对损失值进行计算,定义类的对象时不用传入任何参数,但在使用实例时需要输入两个维度一样的参数方可进行计算。
应用场景: L1 损失是预测值与实际值之间差的绝对值的平均值。它对异常值具有较好的鲁棒性。
在这里插入图片描述

import torch
from torch.autograd import Variable
loss_f = torch.nn.L1Loss()
x = Variable(torch.randn(100,100))
y = Variable(torch.randn(100,100))
loss = loss_f(x,y)
loss.data
#tensor(1.1356)

4.3 交叉熵的计算

交叉熵+线性非线性
交叉熵一般就用于多分类的情况,(3,5)表示为3个样本,每个样本为5维的数据,所以我们要给每个样本附上一个标签。

'''
交叉熵:用于多分类任务的损失函数,它需要两个输入:模型的预测输出和目标标签
'''
loss_f = torch.nn.CrossEntropyLoss()
x = Variable(torch.randn(3, 5))  # 创建一个形状为 (3, 5) 的随机张量 x,表示 3 个样本,每个样本有 5 个特征。
y = Variable(torch.LongTensor(3).random_(5))  # 3个0-4的随机数字(表特征),生成了一个包含 3 个随机整数的张量,每个整数的范围在 0 到 4 之间
print(y)
loss = loss_f(x, y)
print(loss.data)

5.使用pytorch搭建神经网络(识别数字)

首先介绍几个常见的包:

5.1 torchvision

torchvision 是PyTorch中专门用来处理图像的库。这个包中有四个大类:

  1. torchvision.datasets
  2. torchvision.models
  3. torchvision.transforms
  4. torchvision.utils

1.torchvision.datasets
torchvision.datasets可以实现对一些数据集的下载加载如MNIST可以用torchvision.datasets.MNIST COCO、ImageNet、CIFCAR等都可用这个方法下载和载入。

这里用torchvision.datasets加载MNIST数据集:

data_train = datasets.MNIST(root="./data/",
                           transform=transform,
                           train = True,
                           download = True)
data_test = datasets.MNIST(root="./data/",
                          transform = transform,
                          train = False)

2.torchvision.models
torchvision.models 中为我们提供了已经训练好的模型.
torchvision.models模块的 子模块中包含以下模型结构。如:

AlexNet VGG ResNet SqueezeNet DenseNet等

import torchvision.models as models
resnet18 = models.resnet18()
alexnet = models.alexnet()
squeezenet = models.squeezenet1_0()
densenet = models.densenet_161()

5.1.1 如何修改这些模型的参数?
  1. 比如修改模型的输入层或输出层。

示例: 修改ResNet18模型以适应具有100个输出类别的任务:

import torchvision.models as models
import torch.nn as nn

resnet18 = models.resnet18(pretrained=True)  # 加载预训练的模型resnet18 
num_ftrs = resnet18.fc.in_features  # 获取最后一个全连接层的输入特征数量
resnet18.fc = nn.Linear(num_ftrs, 100)  # 修改为100个输出类别

  1. 冻结参数,并更新全连接层(迁移学习)。

在迁移学习中,你可能希望冻结模型的一部分,只训练模型的特定层。这通常通过设置参数的requires_grad属性来实现。

示例: 冻结DenseNet161模型的所有参数,只训练最后的分类层:
目的:
1. 利用预训练模型的特征提取能力:一个已经在数据集上训练过的模型(如DenseNet161)通过训练已经学会了如何从图像中提取有用的特征,比如边缘、纹理、形状等。这些特征对于图像识别任务是非常有用的。所以我们可以利用它已经学习到的特征提取能力,利用模型已经理解的复杂和通用的视觉特征,这些特征通常对于大多数视觉识别任务都是有效的。
2.只更新模型的最后部分:尽管预训练模型在特征提取方面表现优秀,但它最初是为处理其他类型的任务(如识别ImageNet数据库中的1000个类别)而训练的。如果我们要处理一个新的任务(例如分类100个不同的类别),我们通常需要调整模型的最后一层,使其输出与新任务的类别数量相匹配。这样的原因:模型的前面几层通常负责提取图像的通用特征,而最后几层则更专注于将这些特征转换成特定的预测输出(比如分类)。
3.训练速度和效果:显著减少需要训练的参数数量,因为只有最后一层是新的或被重新训练的。

densenet = models.densenet161(pretrained=True) # 加载了一个预训练的 DenseNet161 模型。这个模型已经在大型的图像数据集(通常是ImageNet)上进行了训练,学习了很多图像特征。
for param in densenet.parameters():
    param.requires_grad = False  # 遍历 DenseNet161 模型中的所有参数,这些参数在接下来的训练过程中不会被更新(即它们被“冻结”了)

# 仅修改最后的分类层,重新启用梯度
num_ftrs = densenet.classifier.in_features # 获取 DenseNet161 模型中的最后一层全连接层(即为分类层)的输入特征数量
densenet.classifier = nn.Linear(num_ftrs, 100)  # 假设有100个类别
densenet.classifier.weight.requires_grad = True # 重新启用了新分类器层权重的梯度计算。

还需要考虑一个点(迁移学习),即为任务的相似性:
如果新任务与原始任务在视觉上有一定的相似性,比如从识别一般物体到识别特定物体(例如从识别动物到识别特定的动物种类),那么原始模型的特征提取层可能仍然非常有效。然而,如果任务从识别日常物品变为识别细粒度的人脸特征,那么可能需要对这些层进行更细致的调整或重新训练,因为这些任务在视觉特征上的需求可能有较大差异。

  1. 修改架构。

对于一些模型,例如SqueezeNet,你可能需要修改其内部架构,例如改变某些层的激活函数或调整层之间的连接方式。

squeezenet = models.squeezenet1_0(pretrained=True)
# 假设你想修改特定层的属性
squeezenet.features[3].expand3x3.activation = nn.ReLU(inplace=False)

5.1.2 torch.transforms

torch.transforms中有大量数据变换类,如:

5.1.3.1 torchvision.transforms.Resize 用于对载入的图片数据按照我们需求的大小进行缩放。传递的参数可以是一个整型数据,也可以是一个类似于(h,w)的序列。h代表高度,w代表宽度,如果输入的是整型数据那么h和w都等于这个数。

5.1.3.2 torchvision.transforms.Scale 用于对载入的图片数据按照我们需求的大小进行缩放。和Resize类似。

5.1.3.3 torchvision.transforms.CenterCrop 用于对载入的图片以图片中心为参考点,按照我们需要的大小进行裁剪。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h,w)的序列。

5.1.3.4 torchvision.transforms.RandomCrop 用于对载入的图片按照我们需要的大小进行随机裁剪。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h,w)的序列。

5.1.3.5 torchvision.transforms.RandomHorizontalFlip 用于对载入的图片按随机概率进行水平翻转。我们通过传递给这个类的自定义随机概率,如果没有定义,则使用默认的概率为0.5

5.1.3.6 torchvision.transforms.RandomVerticalFlip 用于对载入的图片按随机概率进行垂直翻转。我们通过传递给这个类的自定义随机概率,如果没有定义,则使用默认的概率为0.5

5.1.3.7 torchvision.transforms.ToTensor 用于对载入的图片数据进行类型转换,将之前构成PIL图片数据转换为Tensor数据类型的变量,让PyTorch能够对其进行计算和处理

5.1.3.8 torchvision.transforms.ToPILImage: 用于对Tensor变量的数据转换成PIL图片数据,主要为方便图片显示

#torchvision.transforms: 常用的图片变换,例如裁剪、旋转等;
transform=transforms.Compose(
    [transforms.ToTensor(),#将PILImage转换为张量
     transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))#将[0, 1]归一化到[-1, 1]
     #前面的(0.5,0.5,0.5) 是 R G B 三个通道上的均值, 后面(0.5, 0.5, 0.5)是三个通道的标准差
    ])
#上述代码我们可以将transforms.Compose()看作一种容器,它能够同时对多种数据变换进行组合。
#传入的参数是一个列表,列表中的元素就是对载入数据进行的变换操作。

5.1.4 torch.utils

关于torchvision.utils我们介绍一种用来对数据进行装载的类:torch.utils.data.DataLoader和

dataset 参数指定我们载入的数据集的名称;
batch_size 参数设置每个包中图片的数量; (每个批次的样本数量)
shuffle 设置为True代表在装载的过程会将数据随机打乱顺序并进行打包(True为打乱增强泛化性)。

data_loader_train=torch.utils.data.DataLoader(dataset=data_train,
                                       batch_size=64,#每个batch载入的图片数量,默认为1,这里设置为64
                                        shuffle=True,
                                        #num_workers=2#载入训练数据所需的子任务数
                                       )
data_loader_test=torch.utils.data.DataLoader(dataset=data_test,
                                      batch_size=64,
                                      shuffle=True)
                                      #num_workers=2)

5.1.5 torchvision.utils.make_grid将一个批次的图片构造成网格模式的图片

1、iter和next:获取一个批次的图片数据和其对应的图片标签——>2、再使用torchvision.utils.make_grid:将一个批次的图片构造成网格模式 经过torchvision.utils.make_grid后图片维度变为channel,h,w三维【 因为要用matplotlib将图片显示,我们要使用的数据要是数组且维度为(height,weight,channel)即色彩通道在最后】——> 3、因此我们需要用numpy和transpose完成原始数据类型的转换和数据维度的交换。

#预览
#在尝试过多次之后,发现错误并不是这一句引发的,而是因为图片格式是灰度图只有一个channel,需要变成RGB图才可以,所以将其中一行做了修改:
images,labels = next(iter(data_loader_train))
# dataiter = iter(data_loader_train) #随机从训练数据中取一些数据
# images, labels = dataiter.next()

img = torchvision.utils.make_grid(images)

img = img.numpy().transpose(1,2,0)
std = [0.5,0.5,0.5]
mean = [0.5,0.5,0.5]
img = img*std+mean
print([labels[i] for i in range(64)])
plt.imshow(img)

5.2 CNN网络卷积层搭建

CNN的学习
ResNet的学习

import math
import torch
import torch.nn as nn
class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        
        #构建卷积层之后的全连接层以及分类器
        self.conv1 = nn.Sequential(
        # 输入通道数、输出通道数、卷积核大小[3x3]、卷积核移动步长和paddingde值(用于对边界像素的填充)
                nn.Conv2d(3,64,kernel_size=3,stride=1,padding=1),
                nn.ReLU(),
                nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1),
                nn.ReLU(),
                nn.MaxPool2d(stride=2,kernel_size=2)
                )
        
        self.dense = torch.nn.Sequential(
                nn.Linear(14*14*128,1024),
                nn.ReLU(),
                nn.Dropout(p=0.5), # 防止卷积神经网络在训练过程中发生过拟合,原理是以一定的随机概率将卷积神经网络模型的部分参数归零,以达到减少相邻两层神经连接的目的[本质就是减少参数]
                nn.Linear(1024,10)
            )
        
    def forward(self,x):
        x=self.conv1(x)
        x=x.view(-1,14*14*128)
        x=self.dense(x)
        return x

6.完整代码

import torch  # 用于深度学习和张量计算
import torch.nn as nn
from torchvision import datasets, transforms, utils  # 从Torchvision导入数据集、数据变换工具和utils
import torch.optim as optim
import torchvision  # 导入Torchvision库,用于计算机视觉学习任务。
from torchvision import datasets, transforms  # 从Torchvision库中导入数据集合数据变换工具
from torch.autograd import Variable  # autograd包下自动计算梯度的工具
import numpy as np  # 导入NumPy库,用于数值计算
import matplotlib.pyplot as plt  # 导入Matplotlib库,用于绘图片

'''
1.检查是否可以使用GPU
'''
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print('设备状态:', device)

'''
2.定义数据变换操作:将图像转换为tensor进行标准化
2.1 x 是一个图像张量,可能是一个单通道(灰度图像)的张量
2.2 第一个维度是通道(channels):从 1 重复到 3,这意味着将单通道图像转换为 RGB 三通道图像。
如果你有一个灰度图像,这样做会创建三个相同副本的通道,形成一个彩色图像,每个通道都有相同的灰度值。
2.3 第二个维度是高度(height):1 表示不改变图像的高度,即不重复。
2.4 第三个维度是宽度(width):同样为 1,表示也不改变图像的宽度,不进行重复。
'''
transform = transforms.Compose([
    transforms.ToTensor(),  # 将图像转换为tensor
    transforms.Lambda(lambda x: x.repeat(3, 1, 1)),  # 将单道系统重复成3条道,符合预期输入格式
    transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))  # 标准化操作,将图像像素值范围调整到[-1, 1]之间
])

'''
3.下载并加载MNIST数据集。
3.1 定义数据集位置、数据变换操作、是否训练。
'''
data_train = datasets.MNIST(root='./data/',
                            transform=transform,
                            train=True,
                            download=True)
data_test = datasets.MNIST(root='./data/',
                           transform=transform,
                           train=False,
                           download=False)

'''
4.定义数据加载器。
'''
data_loader_train = torch.utils.data.DataLoader(dataset=data_train,  # 定义训练集位置
                                                batch_size=64,  # 每个批次加载64张图片
                                                shuffle=True)  # shuffle=True,即打乱数据顺序,增强泛化能力

data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
                                               batch_size=64,
                                               shuffle=True)

'''
5.预览数据
5.1:.transpose(1, 2, 0)操作的意义在于:(而matplotlib在展示图像时,它期望的数据格式遵循(height, width, channels)的规则)
把原始张量的第二维度(高度,height)移动到新张量的第一维度。
把原始张量的第三维度(宽度,width)移动到新张量的第二维度。
把原始张量的第一维度(通道,channels)移动到新张量的第三维度。
5.2:反标准化操作的数学表达式:
假设原始图像数据经过了这样的标准化处理:x_normalized = (x - mean) / std,其中x是原始像素值,x_normalized是标准化后的像素值,mean是平均值,std是标准差。
要从标准化后的数据恢复到原始数据,就需要执行反向操作:x = x_normalized * std + mean。
'''
images, labels = next(iter(data_loader_train))  # 获取一个批次的图像和标签
img = utils.make_grid(images)  # 将多个图像images合并为一张大图像,便于可视化
img = img.numpy().transpose(1, 2, 0)  # 转换图片维度将其tensor转为numpy形式,以符合matplotlib的要求
std = [0.5, 0.5, 0.5]
mean = [0.5, 0.5, 0.5]
img = img * std + mean  # 根据归一化公式反标准化图片,以恢复原始图像的像素值范围,方便展示图像。
print([labels[i] for i in range(64)])  # 打印图片的标签,用于检查数据加载是否正确
plt.imshow(img)  # 显示图片
plt.show()  # 确保图片显示

'''
6.定义卷积神经网络
'''


class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()  # 继承父类nn.Module的初始化方法

        '''
        6.1定义卷积层。
        6.1.1:因为之前数据预览的时候(一批次),图层是8x8=64的,我们的这里卷积核是3x3的,并且步长stride=1,所以要充分读取图层信息,故需要将padding填充设置为1
        6.1.2:nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1): 这是第二层卷积层,与第一层类似,但输出通道数增加到128,进一步提取更复杂的特征。
        64个滤波器,每个滤波器都会生成一个不同的特征图(feature map),kernel_size=3,这表示每个滤波器的尺寸是3x3
        6.1.3:用激活函数Rule:第一是缓解了梯度消失的影响,第二是保证正值不变,负值替换为0,帮助网络更好的学习图像特征(关键信息)。
        6.1.4:nn.MaxPool2d(stride=2, kernel_size=2): 这是最大池化层,通常用于降低特征图的空间维度,减少计算量,同时保持重要特征。
        '''
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),  # 第一层卷积,输入3通道,输出64通道,步长为1,填充为1
            nn.ReLU(),  # ReLU激活函数,增加模型的非线性
            nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),  # 第二层卷积,输入64通道,输出128通道
            nn.ReLU(),  # ReLU激活函数
            nn.MaxPool2d(stride=2, kernel_size=2)  # 最大化池化层,减少特征图尺寸,保持重要特征
        )

        '''
        6.2 定义全连接层
        6.2.1:14*14*128:是经过池化层后变为14*14的特征图,然后每个特征有128个通道得到
        1024:是全连接层中隐藏单元数
        6.2.2:nn.Linear(1024, 10): 这是第二个全连接层,也是模型的输出层。输入维度为1024,
        与前一层的输出维度相对应。输出维度为10,对应MNIST数据集的10个类别(0-9的数字)
        '''
        self.dense = nn.Sequential(
            nn.Linear(14 * 14 * 128, 1024),  # 全连接层,输入14*14*128,输出1024
            nn.ReLU(),  # ReLU激活函数
            nn.Dropout(p=0.5),  # Dropout层,防止过拟合
            nn.Linear(1024, 10)  # 输出层,10个类别对应MNIST数据集的10个数字
        )

    '''
    6.3 定义前向传播
    '''

    def forward(self, x):
        x = self.conv1(x)  # 前向传播:卷积层
        x = x.view(-1, 14 * 14 * 128)  # 将特征图转为一维张量
        x = self.dense(x)  # 前向传播:全连接层
        return x  # 返回输出


model = Model().to(device)  # 实例化模型并转移到GPU上。
cost = nn.CrossEntropyLoss()  # 定义交叉熵损失函数,用于多分类任务
optimizer = optim.Adam(model.parameters())  # 定义Adam优化器,用于更新模型参数
print(model)  # 打印模型结构

'''
7.训练模型
'''
n_epochs = 3

for epoch in range(n_epochs):
    running_loss = 0.0
    running_correct = 0
    print("Epoch {}/{}".format(epoch, n_epochs))  # 打印轮数
    print("-" * 10)

    for data in data_loader_train:
        X_train, y_train = data
        print("训练数据X_train,y_train:")
        print(X_train, y_train)
        X_train, y_train = Variable(X_train).to(device), Variable(y_train).to(device)  # 将数据转移到GPU
        print("转为GPU后:")
        print(X_train, y_train)
        outputs = model(X_train)  # 前向传播,计算模型输出
        _, pred = torch.max(outputs.data, 1)  # 获取预测结果
        optimizer.zero_grad()  # 梯度清零,防止累积
        loss = cost(outputs, y_train)  # 计算损失

        loss.backward()  # 反向传播,计算梯度
        optimizer.step()  # 更新参数
        running_loss += loss.data  # 累加损失
        running_correct += torch.sum(pred == y_train.data)  # 计算训练准确率

    testing_correct = 0
    for data in data_loader_test:  # 使用测试集来监控训练进程,有助于及时调整训练策略或提前停止训练,从而节省资源并避免过拟合。
        X_test, y_test = data
        X_test, y_test = Variable(X_test).to(device), Variable(y_test).to(device)  # 将数据转移到GPU
        outputs = model(X_test)  # 前向传播,计算测试集上的模型输出
        _, pred = torch.max(outputs.data, 1)  # 获取预测结果
        testing_correct += torch.sum(pred == y_test.data)  # 计算测试准确率
    print("Loss is:{:4f}, Train Accuracy is:{:.4f}%, Test Accuracy is:{:.4f}".format(running_loss / len(data_train),
                                                                                     100 * running_correct / len(
                                                                                         data_train),
                                                                                     100 * testing_correct / len(
                                                                                         data_test)))  # 输出训练损失和准确率
'''
8.测试模型:
8.1: torch.max(pred, 1)会返回一个包含这些索引的张量[2, 4],其中_会捕获每行的最大值,
但由于前面使用了下划线(_)作为变量名,这意味着我们【忽略了这些最大值】,【只关心索引】
8.2:batch_size=4说明每次处理4个样本,每个样本10维向量【4x10】,如果有1000个样本,则每个epoch需要处理250批次
8.3:X_test 的维度通常是 (batch_size, channels, height, width):batch_size 是每个批次的样本数。channels 表示图像的通道数(对于灰度图像是1,对于彩色图像是3)。height 和 width 表示图像的高度和宽度。
8.4:y_test 的维度通常是 (batch_size,),包含每个图像对应的类别标签
8.5:pred为预测标签
'''
data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
                                               batch_size=4,
                                               shuffle=True)  # 重新定义测试数据加载器,用于测试
X_test, y_test = next(iter(data_loader_test))  # 获取一个批次的测试数据进行预测,X_test是4个样本的张量
inputs = Variable(X_test).to(device)  # 将数据转移到GPU
pred = model(inputs)  # 前向传播,计算预测结果
_, pred = torch.max(pred, 1)  # 获取预测标签
print("预测标签:")
print(pred)

print("Predict Label is:", [i for i in pred.data.cpu()])  # 打印预测标签
print("Real Label is:", [i for i in y_test])  # 打印真实标签
img = torchvision.utils.make_grid(X_test.cpu())  # 将4个测试样本的图像拼接成了一张2x2的网格图,所以你会看到4个数字
img = img.numpy().transpose(1, 2, 0)  # 转换图片维度顺序

std = [0.5, 0.5, 0.5]
mean = [0.5, 0.5, 0.5]
img = img * std + mean  # 反标准化图片
plt.imshow(img)  # 显示测试图片及其标签
plt.show()  # 确保图片显示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/670729.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简单的基于小波分解和独立分量分析的脑电信号降噪(Python)

脑电信号是一种典型的非平稳随机信号且存在一定的非高斯性和非线性。传统的分析处理方法是将脑电信号近似看做线性、准平稳、高斯分布的随机信号&#xff0c;这使得分析结果往往不能令人满意&#xff0c;实用性较差。现代的小波变换方法和独立分量分析方法的提出为有效地分析脑…

LeetCode---字符串

344. 反转字符串 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 代码示例&#xff1a; //时间复杂度: O(n) //空间…

职场思考-在行业坚守中实现个人增值(13)

滚石不生苔&#xff0c;转行不聚财 在自己工作几年后&#xff0c;职业竞争力会由专业能力向行业经验进行转化 如果你不具备足够的行业积累&#xff0c;即使在某个专业上有足够的能力&#xff0c;你也难以得到待遇或职位的提升&#xff0c;陷入高不成低不就的局面 掌握完成岗位工…

使用pikachu Xss后台出现的问题

在进行xss-x漏洞实验的时候&#xff0c;一直出现上述错误&#xff0c;查找了很多&#xff0c;终于找到问题所在 pikachu使用的数据库为同一个数据库&#xff0c;千万别被pkxss误导&#xff0c;以为pikachu还有一个数据库为pkxss,所以在配置的时候写下如下图的

Python知识点5---字符串的使用

提前说一点&#xff1a;如果你是专注于Python开发&#xff0c;那么本系列知识点只是带你入个门再详细的开发点就要去看其他资料了&#xff0c;而如果你和作者一样只是操作其他技术的Python API那就足够了。 Python的字符串在使用上和其他语言的差别不大&#xff0c;常规操作都…

Nginx实战:nginx支持带下划线的header

nginx对header 的名字字符做了限制&#xff0c;默认 underscores_in_headers 为off&#xff0c;表示如果header name中包含下划线&#xff0c;则忽略掉&#xff0c;后端服务就获取不到该请求头。 为了支持header带下划线的参数&#xff0c;可以在http内或者server内设置如下参数…

FreeRTOS基础(七):临界段代码保护及调度器挂起与恢复

上一篇博客我们详细介绍了FreeRTOS是怎么管理中断的&#xff0c;其实&#xff0c;从本质上来讲就是将就是利用的BASEPRI这个寄存器&#xff0c;来屏蔽优先级低于某一个阈值的中断&#xff0c;当设置为0的时候&#xff0c;就是打开所有中断&#xff0c;所有中断都可以响应。这样…

【VMware虚拟机中ubuntu系列】—— 在虚拟机中使用本机摄像头的详细教程与常见问题分析及解决

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、虚拟机调用本机摄像头(1) 启动VMware USB 服务(2) 连接本机摄像头(3) 测试摄像头的连接 二、安装usb驱动二、运行usb_cam.launch时出现select timeout的报错…

希捷硬盘怎么恢复数据? 5 个免费希捷数据恢复软件

希捷已迅速成为全球最大的数字存储提供商。许多人选择并使用希捷外置硬盘来存储他们的媒体文件、学校或工作文件以及其他重要数据。有时&#xff0c;希捷硬盘中的数据会丢失。 如果您丢失了希捷硬盘上的数据&#xff0c;请不要惊慌。在专业的希捷数据恢复软件的帮助下&#xf…

【c++进阶(一)】STL之string接口介绍

&#x1f493;博主CSDN主页:Am心若依旧&#x1f493; ⏩专栏分类c从入门到精通⏪ &#x1f69a;代码仓库:青酒余成&#x1f69a; &#x1f339;关注我&#x1faf5;带你学习更多c   &#x1f51d;&#x1f51d; 1.前言 本章重点 本章着重讲解string中一些重要的接口函数&…

SOUI Combobox 实现半透明弹出下拉框

SOUI默认情况下combobox的弹出框不是半透明的&#xff0c;这个时候如果背景透明时&#xff0c;滚动条会出现黑色背景&#xff0c;这个时候只需要在在combobox下添加一个子节点 <dropdownStyle translucent"1"></dropdownStyle> 这样一个窗口默认即实现…

Nature Communications|柔性自驱动仿生眼(离子凝胶/仿生眼/柔性电子)

2024年4月10日,黄维(Wei Huang)院士、南京工业大学刘举庆(Juqing Liu)教授和刘正东(Zhengdong Liu)副教授课题组,在《Nature Communications》上发布了一篇题为“A bionic self-driven retinomorphic eye with ionogel photosynaptic retina”的论文,罗旭(Xu Luo)、陈晨(…

vscode过滤器@modified(查看配置了哪些设置)

文档 visualstudio•docs•getstarted•settingshttps://code.visualstudio.com/docs/getstarted/settings 说明 使用modified可以过滤出&#xff1a; 配置过的设置&#xff08;和默认值不同&#xff09;&#xff1b; 在 settings.json 文件中配置了值的设置 步骤 1.打开…

Golang省市二级联动实现 从数据收集、清洗到数据存储

1.背景&#xff1a; 最近在写项目&#xff0c;在项目中有一个需求是获取用户的地理位置&#xff0c;一开始是打算让前端使用JSON包的形式去实现&#xff0c;但是考虑到后期可能需要对省市的数据做一些修改和控制操作&#xff0c;所以改为后端实现&#xff0c;并向后台暴露一套…

六一去哪儿,跟着蒙自源开启一段关于童年记忆与美味奇妙旅程

夏日微风轻拂&#xff0c;童心随风起舞。在这个充满欢声笑语的季节里&#xff0c;蒙自源诚挚地邀请您和您的家人&#xff0c;一同参加为六一儿童节精心准备的庆祝活动&#xff0c;共同开启一段关于童年记忆与美味的奇妙旅程。 从5月25日起&#xff0c;蒙自源的各大门店将化身为…

Vue3实战笔记(56)—实战:DefineModel的使用方法细节

文章目录 前言一、实战DefineModel二、思考原理总结 前言 今天写个小例子&#xff0c;实战DefineModel的使用方法细节 一、实战DefineModel 上文官方说的挺清楚&#xff0c;实战验证一下&#xff0c;新建DefineModel.vue&#xff08;这是儿子&#xff09;&#xff1a; <te…

mac油猴Safari浏览器插件:Tampermonkey for Mac下载

Tampermonkey 是一款用于浏览器的用户脚本管理器插件&#xff0c;它允许用户安装、管理和运行用户脚本&#xff0c;从而可以自定义网页的功能和外观。该插件支持在谷歌浏览器、火狐浏览器、Safari等主流浏览器上使用。提供了丰富的用户脚本管理和自定义功能&#xff0c;使用户可…

基于小波分析的一维时间序列多重分形分析(MATLAB R2018a)

分形与小波变换在尺度性能上具有很多相似性&#xff0c;因此小波变换被认为是分析、刻画分形现象一个有力的工具。在分析分形的一般方法中&#xff0c;需要按照“盒维数”的计算思想&#xff0c;首先要将研究序列进行不同长度的分割&#xff0c;然后建立起结构函数&#xff0c;…

【人工智能】第一部分:ChatGPT的基本概念和技术背景

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

【因果推断python】10_分组和虚拟变量回归1

目录 分组数据回归 分组数据回归 并非所有数据点都是一样的。 如果我们再次查看我们的 ENEM 数据集&#xff0c;相比小规模学校的分数&#xff0c;我们更相信规模较大的学校的分数。 这并不是说大型学校更好或什么&#xff0c; 而只是因为它们的较大规模意味着更小的方差。 i…