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 torchclass 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 Variablebatch_n = 100  # 一个批次输入数据的数量
hidden_layer = 100
input_data = 1000  # 每个数据的特征为1000
output_data = 10x = 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-6for epoch in range(epoch_n):h1 = x.mm(w1)  # (100,1000)*(1000,100)-->100*100print(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.dataw2.data -= lr * w2.grad.dataw1.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 xdef 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=30for 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.dataw2.data -= lr*w2.grad.dataw1.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 = 10x = 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 nnresnet18 = 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,这里设置为64shuffle=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(),  # 将图像转换为tensortransforms.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填充设置为16.1.2:nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1): 这是第二层卷积层,与第一层类似,但输出通道数增加到128,进一步提取更复杂的特征。64个滤波器,每个滤波器都会生成一个不同的特征图(feature map),kernel_size=3,这表示每个滤波器的尺寸是3x36.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,填充为1nn.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,输出1024nn.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 = 3for epoch in range(n_epochs):running_loss = 0.0running_correct = 0print("Epoch {}/{}".format(epoch, n_epochs))  # 打印轮数print("-" * 10)for data in data_loader_train:X_train, y_train = dataprint("训练数据X_train,y_train:")print(X_train, y_train)X_train, y_train = Variable(X_train).to(device), Variable(y_train).to(device)  # 将数据转移到GPUprint("转为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 = 0for data in data_loader_test:  # 使用测试集来监控训练进程,有助于及时调整训练策略或提前停止训练,从而节省资源并避免过拟合。X_test, y_test = dataX_test, y_test = Variable(X_test).to(device), Variable(y_test).to(device)  # 将数据转移到GPUoutputs = 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()  # 确保图片显示

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ一、RabbitMQ的介绍与安装(docker)

一、RabbitMQ相关名词解释 MQ MQ全称Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。 多用于系统之间的异步通信。 常见的两种通信方式&#xff1a; 同步通信&#xff1a;同步通信相当于两个人当面对话&#xff0c;你一言我…

Qt | QSplitter(分离器或分隔符)、QSplitterHandle 类(分界线)

​01、一、QSplitter 类(分离器) 1、QSplitter 类继承自 QFrame 类,也就是说该类是一个带有边框的可视部件。 2、QSplitter 类实现分离器,分离器用于分离两个部件,用户可通过拖动部件之间的分界线来调整子部件的大小。 3、QSplitter 的原理(见上图):QSplitter 的实现原理…

css 中clip 属性和替代方案 clip-path属性使用

clip clip 属性概述 作用&#xff1a;clip 属性用于定义一个裁剪区域&#xff0c;该区域外的元素内容将不可见。适用元素&#xff1a;clip 属性只对绝对定位&#xff08;position: absolute&#xff09;或固定定位&#xff08;position: fixed&#xff09;的元素有效&#xf…

机器学习第四十一周周报 JTFT

文章目录 week41 JTFT摘要Abstract1. 题目2. Abstract3. 网络架构3.1 JTFT3.2 具有可学习频率的稀疏FD表示3.3 用于提取跨渠道依赖关系的低阶注意力层 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程 5. 结论小结参考文献 week41 JTFT 摘要 本周阅读了题为A Joint Time-…

太空音响器

目录 1&#xff0e;课程设计项目 2.任务和要求 3.总体功能设计与仿真 3.1.元器件汇总 3.2.总体方案设计 3.3 总体电路仿真 4.单元模块设计及电路仿真 4.1 互补型振荡器电路 5.组装&#xff0c;调试与测试 6.分析与总结 7.参考文献 1&#xff0e;课程设…

【scikit-learn010】sklearn算法模型清单实战及经验总结(已更新)

1.一直以来想写下基于scikit-learn训练AI算法的系列文章,作为较火的机器学习框架,也是日常项目开发中常用的一款工具,最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下scikit-learn框架模型算法包相关技术点及经验。 3.欢迎批评指正,欢迎互三,跪谢一键…

【简单介绍下K-means聚类算法】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

rpmbuild多进程批量编译脚本

脚本用法 使用手册 bash spec.sh --help Please ensure the directory ~/rpmbuild exists, And the script can execute in any directory.Usage: bash spec.sh [OPTION]... [*.spec]... [OPTION]...or: bash spec.sh [*.spec]... [OPTION]... [*.spec]... rpmbuild all sp…

机器学习各个算法的优缺点!(上篇) 建议收藏。

下篇地址&#xff1a;机器学习各个算法的优缺点&#xff01;&#xff08;下篇&#xff09; 建议收藏。-CSDN博客 纯干货&#xff01;&#xff01; 回归 正则化算法 集成算法 决策树算法 支持向量机 降维算法 聚类算法 贝叶斯算法 人工神经网络 深度学习 感兴趣的朋友…

硬币检测电路设计

一、来源&#xff1a;凡亿教育 第一场&#xff1a;硬币检测装置原理分析、电路设计以及器件选型_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Zh4y1V7Px/?p1&vd_source43eb1cb50ad3175d7f3b9385905cd88f 二、开发软件&#xff1a;KEIL MDK 三、主控芯片&#…

PostgreSQL常用插件

PostgreSQL 拥有许多常用插件&#xff0c;这些插件可以大大增强其功能和性能。以下是一些常用的 PostgreSQL 插件&#xff1a; 性能监控和优化 pg_stat_statements 1.提供对所有 SQL 语句执行情况的统计信息。对调优和监控非常有用。 2.安装和使用&#xff1a; pg_stat_k…

数据库之函数、存储过程

函数、存储过程 1.函数 函数&#xff0c;常用于对一个或多个输入参数进行操作&#xff0c;主要目的是返回一个结果值&#xff0c;就是一种方法&#xff0c;在postgre里存放的位置叫function&#xff0c;比如创建一个计算长方面积的函数。 举例&#xff1a;建立一个计算长方形…

kali-搭建Pikachu漏洞练习平台

环境&#xff1a;kali 压缩包&#xff1a;pikachu-master.zip &#xff08;下载地址&#xff1a;GitHub - zhuifengshaonianhanlu/pikachu: 一个好玩的Web安全-漏洞测试平台&#xff09; 1.mysql 1.开启kali中自带的mysql systemctl restart mysql #重启MySQL服务 systemc…

Java面试题:Redis2_解决Redis缓存击穿问题

缓存击穿 当一个key过期时,需要对这个key进行数据重建 在重建的时间内如果有大量的并发请求进入,就会绕过缓存进入数据库,会瞬间击垮DB 重建时间可能因为数据是多个表的混合结果需要分头统计而延长,从而更容易出现缓存击穿问题 缓存击穿的解决方案 添加互斥锁 先查询缓存…

Ubuntu系统配置DDNS-GO【笔记】

DDNS-GO 是一个基于 Go 语言的动态 DNS (DDNS) 客户端&#xff0c;用于自动更新你的 IP 地址到 DNS 记录上。这对于经常变更 IP 地址的用户&#xff08;如使用动态 IP 的家庭用户或者小型服务器&#xff09;非常有用。 此文档实验环境为&#xff1a;ubuntu20.04.6。 在Ubuntu…

GIt快速入门(一文学会使用Git)

GIt快速入门 文章目录 GIt快速入门一、为什么要学习Git二、Git的安装1.安装Git2.下载GUI 三、Git的概念1、版本控制2、集中式控制3、分布式控制4、多人协作开发1.并行开发2.分支管理3.冲突解决4.代码审查5.分布式特性 四、Git客户端操作1.界面介绍2.提交操作3.创建分支4.合并分…

vscode编辑器创建分支注意事项?!

最近在公司开发项目时&#xff0c;不小心将自己分支的东西提交到公司的master的分支&#xff0c;大家看看是什么情况&#xff1f; 先上图&#xff1a; 从图上看&#xff0c;我这边用了GITLENS这个插件&#xff0c;在创建分支时&#xff0c;有个create branch from&#xff0c;有…

Zynq学习笔记--AXI4-Stream 图像数据从仿真输出到图像文件

目录 1. 简介 2. 构建工程 2.1 Vivado 工程 2.2 TestBench 代码 2.3 关键代码分析 3. VPG Background Pattern ID (0x0020) Register 4. 总结 1. 简介 使用 SystemVerilog 将 AXI4-Stream 图像数据从仿真输出到图像文件 (PPM)。 用到的函数包括 $fopen、$fwrite 和 $f…

2024 第三届 AIGC 中国开发者大会:多模态大模型的发展与趋势

引言 在2024年第三届AIGC中国开发者大会上&#xff0c;零一万物联合创始人潘欣分享了多模态大模型的发展与趋势。潘欣对多模态大模型的历史、现状和未来进行了详细回顾和深刻思考&#xff0c;为我们揭示了该领域的发展路径和技术前景。本文将详细解读潘欣的分享内容&#xff0…

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

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