PyTorch入门
1.读取数据类Dataset
Dataset将数据和label进行组织编号0 1 2 3……,使得可以根据编号读取数据;需获取每一个数据及其label以及数据总数 ,要实现 len() 方法和 getitem() 方法。
len() 方法返回数据集的样本数量;getitem() 方法根据给定的索引返回对应的数据样本;
通过继承Dataset类class MyData(Dataset),实现__len__和__getitem__方法,可以自定义自己的数据集类以适应不同的数据源和格式
class MyData(Dataset):#root:根目录 label:标签(ants/bees)def __init__(self,root_dir,label_dir):self.root_dir = root_dirself.label_dir = label_dirself.path=os.path.join(self.root_dir,self.label_dir)#os.listdir()获取指定目录下的所有条目self.img_path=os.listdir(self.path)def __getitem__(self,idx):img_name=self.img_path[idx]img_item_name=os.path.join(self.root_dir,self.label_dir,img_name)img=Image.open(img_item_name)label=self.label_dirreturn img,labeldef __len__(self):return len(self.img_path)
2.TensorBoard使用
(1)add_scalar方法:通常用于记录训练过程中的各种指标,如损失值、准确率等。它将这些数值与对应的步数关联起来,以便后续进行可视化分析
def add_scalar(self,tag,scalar_value,global_step=None,walltime=None,new_style=False,double_precision=False,):
<font style="color:rgb(163, 21, 21);background-color:rgba(0, 0, 0, 0.1);">tag</font>
: 字符串类型,用于标识数据的标签或名称,通常用于在可视化时区分不同的数据曲线<font style="color:rgb(163, 21, 21);background-color:rgba(0, 0, 0, 0.1);">scalar_value</font>
: 要记录的标量值,通常是数字<font style="color:rgb(163, 21, 21);background-color:rgba(0, 0, 0, 0.1);">global_step</font>
: 可选参数,表示训练的步数或迭代次数,用于x轴的刻度
(2)利用TensorBoard绘制y=x图像
from torch.utils.tensorboard import SummaryWriter#保存的目录路径
writer=SummaryWriter("logs")#y=x
for i in range(100):writer.add_scalar("y=x",i,i)writer.close()
(3)启动命令
在终端中输入如下命令
tensorboard --logdir=logs #logs是上面指定在writer = SummaryWriter("logs")中指定的文件夹名,日志文件存储在此文件中
(4)add_image方法:将图像数据添加到TensorBoard日志中
def add_image(self, tag, img_tensor, global_step=None, walltime=None, dataformats="CHW"):
- tag:图像的标识符,用于在TensorBoard中区分不同的图像
- img_tensor:图像数据,支持torch.Tensor、numpy.ndarray或字符串/blobname格式
- global_step:记录的全局步数,用于时间轴上的定位
- walltime:可选的时间戳,默认使用当前时间
- dataformats:图像数据格式,如CHW(通道-高度-宽度)、HWC等
(5)add_image使用
import numpy as np
from PIL import Image
from torch.utils.tensorboard import SummaryWriterwriter=SummaryWriter("logs")
image_path="dataset/train/ants/0013035.jpg"
img_PIL=Image.open(image_path)
#利用numpy将PIL格式转换为numpy数组
img_array=np.array(img_PIL)
print(type(img_array))
print(img_array.shape)
#HWC表示Height(高度)× Width(宽度)× Channels(通道数)
writer.add_image("test",img_array,2,dataformats='HWC')writer.close()
3.Transforms学习
用于对图像进行预处理和数据增强操作,如调整图像大小、中心裁剪、随机裁剪、随机水平翻转、归一化、将 PIL 图像转换为 Tensor 等等
(1)ToTensor()使用:PyTorch的torchvision模块中的一个转换类,它的主要作用是将PIL图像或NumPy数组转换为PyTorch张量
#1.创建具体的工具
tensor_trans=transforms.ToTensor()
#2.转换
tensor_img=tensor_trans(img)writer.add_image("Tensor_img",tensor_img)
(2)Normalize: 对图像进行归一化,对每个通道进行归一化
trans_norm=transforms.Normalize([3,5,8],[3,2,1])
img_norm=trans_norm(img_tensor)
writer.add_image("Normalize",img_norm,2)
(3)Resize()使用:可以处理 PIL 图像对象,也可以处理张量类型的数据
trans_resize=transforms.Resize((800,800))
img_resize=trans_resize(img)
img_resize=trans_totensor(img_resize)
writer.add_image("Resize",img_resize,2)
(4)Compose()使用: 组合多个图像转换(transform)操作,通过 Compose,可以创建一个转换流程,这个流程可以按顺序执行多个图像处理操作,这些操作可以包括缩放、裁剪、归一化等,其需要的参数是一个列表,其元素类型是transforms类型。
trans_resize = transforms.Resize(512)
trans_totensor = transforms.ToTensor()
trans_compose = transforms.Compose([trans_resize,trans_totensor])
img_resize_2 = trans_compose(img)
4.Torchvision数据集使用
(1)DataSet使用
#将数据集中的每张图片转换为tensor类型
dataset_transform=torchvision.transforms.Compose([torchvision.transforms.ToTensor()
])torch_set=torchvision.datasets.CIFAR10(root="./dataset",train=True,transform=dataset_transform,download=True)
test_set=torchvision.datasets.CIFAR10(root="./dataset",train=False,transform=dataset_transform,download=True)
<font style="color:rgb(163, 21, 21);background-color:rgba(0, 0, 0, 0.1);">root="./dataset"</font>
: 指定数据集存储的根目录为当前路径下的"dataset"文件夹<font style="color:rgb(163, 21, 21);background-color:rgba(0, 0, 0, 0.1);">train=True</font>
: 表示加载的是训练集(CIFAR-10中有50000张训练图像)<font style="color:rgb(163, 21, 21);background-color:rgba(0, 0, 0, 0.1);">transform=dataset_transform</font>
: 对数据应用的预处理/变换操作<font style="color:rgb(163, 21, 21);background-color:rgba(0, 0, 0, 0.1);">download=True</font>
: 如果数据集不存在于指定目录,则自动下载
(2)Dataloader使用: PyTorch 提供的数据加载器,用于批量加载数据集。
#transform参数指定了数据转换方式
test_data=torchvision.datasets.CIFAR10(root="./dataset",train="False",transform=torchvision.transforms.ToTensor())test_loader=DataLoader(dataset=test_data,batch_size=4,shuffle=True,num_workers=0,drop_last=False)img,target=test_data[0]
print(img.shape)
print(target)for data in test_loader:imgs,targets=dataprint(imgs.shape)print(targets)
<font style="color:rgb(163, 21, 21);background-color:rgba(0, 0, 0, 0.1);">dataset=test_data</font>
:指定要加载的数据集<font style="color:rgb(163, 21, 21);background-color:rgba(0, 0, 0, 0.1);">batch_size=4</font>
:每个批次包含4个样本<font style="color:rgb(163, 21, 21);background-color:rgba(0, 0, 0, 0.1);">shuffle=True</font>
:在每个epoch开始时打乱数据顺序<font style="color:rgb(163, 21, 21);background-color:rgba(0, 0, 0, 0.1);">drop_last=False</font>
:如果最后一批数据小于batch_size,仍然保留
5.神经网络基本骨架nn.Moduel使用
对于定义的模型都需要集成Moduel类,实现__init__和forward函数
class Module(nn.Module):def __init__(self) :super().__init__()def forward(self,input):output=input+1return outputmodule=Module()
x=torch.tensor(1.0)
output=module(x)
print(output)
当调用一个 nn.Module 的实例时,例如 module(x),PyTorch 会自动触发该实例的 forward 方法。这是因为 nn.Module 类在 Python 中被视为一个可调用对象,这是通过在 nn.Module 类中实现特殊方法__call__()来实现的。
call 方法在 nn.Module 中被定义为调用 forward 方法的包装器,像函数一样调用一个 nn.Module 实例时,实际上是在执行 forward 方法,并将传入的参数(在这个例子中是 x)作为输入传递给 forward 方法
6.卷积层的使用
dataset=torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)dataloader=DataLoader(dataset,batch_size=64)class Module(nn.Module):def __init__(self):super(Module,self).__init__()#创建二维卷积层self.conv1=Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0)def forward(self,x):x=self.conv1(x)return xmodule=Module()writer=SummaryWriter("./logs")step=0
for data in dataloader:imgs,targets=dataoutput=module(imgs)print(imgs.shape)print(output.shape)#torch。Size([64,3,32,32])writer.add_images("input",imgs,step)#torch.Size([64,6,30,30])#卷积之后图像的形状 torch.Size([64, 6, 30, 30])是6个通道的 而add_images只能接收3通道的输入output=torch.reshape(output,(-1,3,30,30))writer.add_images("output",output,step)step+=1
7.最大池化层
保留输入的特征,同时减少数据量 加快训练速度
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))
print(input.shape)class Module(nn.Module):def __init__(self):super(Module,self).__init__()#ceil_mode=True:当输入尺寸不能被池化窗口整除时,使用向上取整的方式计算输出尺寸self.maxpool1=MaxPool2d(kernel_size=3,ceil_mode=True)def forward(self,input):output=self.maxpool1(input)return output
8.非线性激活层
引入非线性的特性,使得神经网络具有更强的表达能力和适应能力
(1)ReLU:当输入大于0时,输出等于输入,当输入小于等于0时,输出为0
input=torch.tensor([[1,-0.5],[-1,3]
])input=torch.reshape(input,(-1,1,2,2))
print(input.shape)class Module(nn.Module):def __init__(self):super(Module,self).__init__()self.relu=ReLU()def forward(self,input):output=self.relu(input)return output
(2)Sigmod:用于隐层神经单元输出,取值范围为(0,1),它可以将一个实数映射到(0,1)的区间,可以用来做二分类。在特征相差比较复杂或者相差不是特别大的时候效果比较好。
9.线性层及其他层
Linear:
output=input×weightT+bias
<font style="color:#DF2A3F;">input</font>
:输入数据(形状<font style="color:#000000;">[batch_size, input_dim]</font>
)<font style="color:#DF2A3F;">weight</font>
:权重矩阵(形状<font style="color:#000000;">[output_dim, input_dim]</font>
)<font style="color:#DF2A3F;">bias</font>
:偏置向量(形状<font style="color:#000000;">[output_dim]</font>
)
import torch
import torch.nn as nn# 定义一个线性层:输入100维,输出64维
linear_layer = nn.Linear(100, 64)# 随机生成输入数据(batch_size=5, 输入维度=100)
input_data = torch.randn(5, 100)# 前向传播
output = linear_layer(input_data)
print("输出形状:", output.shape) # 输出: torch.Size([5, 64])
10.flatten层
将输入张量扁平化(flatten)的函数,它将输入张量沿着指定的维度范围进行扁平化处理,并返回一个一维张量作为结果
t = torch.tensor([[[1, 2],[3, 4]],[[5, 6],[7, 8]]])
torch.flatten(t)
11.Sequential的使用
以按照添加的顺序依次执行包含的各个模块,torch.nn.Sequential提供了一种简单的方式来构建神经网络模型,代码十分简洁。
class Module(nn.Module):def __init__(self):super(Module,self).__init__()#Sequential使用self.model1=Sequential(Conv2d(3,32,5,padding=2),MaxPool2d(2),Conv2d(32,32,5,padding=2),MaxPool2d(2),Conv2d(32, 64, 5, padding=2),MaxPool2d(2),Flatten(),Linear(1024, 64),Linear(64, 10))def forward(self,x):x=self.model1(x)return xmodule=Module()
print(module)#网络结构检测
input=torch.ones((64,3,32,32))
output=module(input)
print(output.shape)
12.损失函数
损失函数(Loss Function)用于衡量模型的预测输出与实际标签之间的差异或者误差,损失越小越好,根据loss调整参数(反向传播),更新输出,减小损失
inputs=torch.tensor([1,2,3],dtype=torch.float32)
targets=torch.tensor([1,2,5],dtype=torch.float32)inputs=torch.reshape(inputs,(1,1,1,3))
targets=torch.reshape(targets,(1,1,1,3))#平均绝对误差
loss=L1Loss(reduction="sum")
result=loss(inputs,targets)#均方误差
loss_mse=MSELoss()
result_mse=loss_mse(inputs,targets)print(result,result_mse)
分类问题常用损失:CrossEntropyLoss交叉熵损失函数
dataset=torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader=DataLoader(dataset=dataset,batch_size=64)class Module(nn.Module):def __init__(self):super(Module,self).__init__()self.model1 = Sequential(Conv2d(3, 32, 5, padding=2),MaxPool2d(2),Conv2d(32, 32, 5, padding=2),MaxPool2d(2),Conv2d(32, 64, 5, padding=2),MaxPool2d(2),Flatten(),Linear(1024, 64),Linear(64, 10))def forward(self, x):x = self.model1(x)return x#使用交叉熵损失函数
loss=nn.CrossEntropyLoss()
module=Module()
for data in dataloader:imgs,targets=dataoutputs=module(imgs)result_loss=loss(outputs,targets)#执行反向传播,计算损失相对于模型参数的梯度,这些梯度将用于后续的参数更新result_loss.backward()print(result_loss)
反向传播:在张量上进行操作时,PyTorch 会自动跟踪操作并构建计算图,可以使用 .backward() 方法(反向传播)计算梯度,然后通过 .grad 属性获取梯度值
13.优化器使用
在深度学习中,optimizer.zero_grad()是一个非常重要的操作,它的含义是将模型参数的梯度清零。
在训练神经网络时,通常采用反向传播算法来计算损失函数关于模型参数的梯度,并利用优化器来更新模型参数以最小化损失函数。在每次反向传播计算梯度后,梯度信息会被累积在对应的参数张量(tensor)中。如果不清零梯度,在下一次计算梯度时,这些梯度将会被新计算的梯度累加,导致梯度信息错误。
#创建一个计算交叉损失熵函数
loss=nn.CrossEntropyLoss()
module=Module()
#创建一个随机梯度下降(SGD)优化器
optim=torch.optim.SGD(module.parameters(),lr=0.01)for epoch in range(20):running_loss=0.0for data in dataloader:imgs,targets=dataoutputs=module(imgs)result_loss=loss(outputs,targets)#将模型中所有参数(parameters)的梯度清零。optim.zero_grad()#反向传播result_loss.backward()#optim.step()是优化器对象的一个方法,用于根据计算得到的梯度更新模型的参数optim.step()running_loss=running_loss+result_lossprint(running_loss)
14.网络模型使用
(1)模型下载、添加、修改
#vgg16网络模型下载
vgg16_false=torchvision.models.vgg16(pretrained=False)
vgg16_true=torchvision.models.vgg16(pretrained=True)#现有网络模型添加
vgg16_true.classifier.add_module("add_linear",nn.Linear(1000,10))#现有网络模型修改
vgg16_false.classifier[6]=nn.Linear(4096,10)
print(vgg16_false)
(2)模型保存
vgg16=torchvision.models.vgg16(weights=None)
#保存方式1,模型结构+模型参数
torch.save(vgg16,"vgg16_method1.pth")#保存方式2(推荐),模型参数
torch.save(vgg16.state_dict(),"vgg16_method2.pth")
(3)模型加载
#加载模型方式1
model=torch.load("vgg16_method1.pth")
print(model)#加载模型方式2
#创建一个未预训练的VGG16模型,weights=None表示不加载预训练权重
vgg16=torchvision.models.vgg16(weights=None)
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
print(vgg16)
15.完整网络模型训练与测试套路
import torch.optim
import torchvision.datasets
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriterfrom model import *
from nn_optim import epoch#使用GPU进行训练
device=torch.device("cuda")train_data=torchvision.datasets.CIFAR10(root="./dataset",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_data=torchvision.datasets.CIFAR10(root="./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)
#length长度
train_data_size=len(train_data)
test_data_size=len(test_data)
print("训练数据长度为{}".format(train_data_size))
print("测试数据长度为{}".format(test_data_size))train_dataloader=DataLoader(train_data,batch_size=64)
test_dataloader=DataLoader(test_data,batch_size=64)#创建网络模型
module=Module()
module=module.to(device)#损失函数
loss_fn=nn.CrossEntropyLoss()
loss_fn=loss_fn.to(device)#优化器
learning_rate=1e-2
optimizer=torch.optim.SGD(module.parameters(),lr=learning_rate)#设置训练网络的一些参数
#记录训练次数
total_train_step=0
#记录测试次数
total_test_step=0
#训练轮数
epoch=10#添加tensorboard
writer=SummaryWriter("./logs_train")for i in range(epoch):print("--------第{}轮训练开始----------".format(i+1))#训练步骤for data in train_dataloader:imgs,targets=dataimgs=imgs.to(device)targets=targets.to(device)outputs=module(imgs)loss=loss_fn(outputs,targets)#优化器优化模型optimizer.zero_grad()loss.backward()optimizer.step()total_train_step=total_train_step+1if total_train_step%100==0:print("训练次数:{},loss:{}".format(total_train_step,loss))writer.add_scalar("train_loss",loss,total_train_step)#测试步骤total_test_loss=0total_accuracy=0with torch.no_grad():for data in test_dataloader:imgs,targets=dataimgs = imgs.to(device)targets = targets.to(device)outputs=module(imgs)loss=loss_fn(outputs,targets)total_test_loss=total_test_loss+lossaccuracy = (outputs.argmax(1) == targets).sum()total_accuracy=total_accuracy+accuracyprint("整体测试集上的Loss:{}".format(total_test_loss))print("整体测试集上的正确率:{}".format(total_accuracy/test_data_size))writer.add_scalar("test_loss",total_test_loss,total_test_step)writer.add_scalar("test_accuracy",total_accuracy/test_data_size,total_test_step)total_test_step=total_test_step+1#保存网络模型torch.save(module,"moduel_{}.pth".format(i))print("模型已保存")
writer.close()
在推理或评估模型时使用
torch.no_grad()
,表明当前计算不需要反向传播使用之后,强制后边的内容不进行计算图的构建,
with 语句是 Python 中的一个语法结构,用于包裹代码块的执行,并确保在代码块执行完毕后,能够自动执行一些清理工作。
model.train()开启训练模式,模型会跟踪所有层的梯度,以便在优化器进行梯度下降时更新模型的权重。
model.eval():开启评估模式,在评估模式下,模型不会跟踪梯度,这有助于减少内存消耗并提高计算效率。
16.使用GPU进行训练
(1)对于网络模型,数据,以及损失函数,都要调用cuda()
(2)(推荐)xx=xx.to(device)
device=torch.device("cuda")#模型
module=module.to(device)
#损失函数
loss_fn=loss_fn.to(device)
#数据
imgs=imgs.to(device)
targets=targets.to(device)