目录
2.1. 根据CIFAR-10数据集完成分类任务的操作如下:
2.4.2. 定义显示函数imshow(),去显示加载器中的图像。
2.4.3. 定义迭代器iter()去读一次迭代数据(4张图,batch_size=4)
前提:了解深度学习中的前向传播、反向传播、梯度更新
PyTorch是一个开源的Python机器学习框架,提供了丰富的工具和库,使得构建和训练深度学习模型变得更加简单和高效。
1. 网络初学:(即2.5. 定义卷积神经网络)
W:宽高;F:卷积核大小;P:padding,向图片外面补边,默认为0; S:步长,即该卷积核遍历图片的步长是多少,默认为1
1.1. 导入torch相关包
import torch
import torch.nn as nn
import torch.nn.functional as F
1.2. 搭建网络
pytorch中搭建网络通常用类来管理,同时还需要继承nn.Module这个类。搭建一个网络一般有两个函数:初始化函数、前向传播函数
1.2.1. 初始化函数:初始化是在实例化时自动执行的一部分,即在初始化函数中放置网络需要初始化的内容,同时还要进行一个多继承(相当于把module中继承的类以及全部类的方法都继承下来供net去使用)的操作,使用super函数实现了多继承。网络搭建的基本模板如下:
class Net(nn.Module):
def __init__(self):
super (Net,self).__init__()
def forward(self,x)
对于下采样只需要2*2的和,不需要任何参数设置;对于卷积,涉及到输入输出以及很多参数的设置,需要初始化;对于全连接层也需要初始化。
1.2.2. forward函数里面要实现前向回归逻辑 ,该网络设计卷积、下采样(最大池化下采样方法)、全连接。
- pytorch处理的都是张量(tensor,pytorch中张量是一种数据结构,可以是一个标量、一个向量、一个矩阵,甚至是更高维度的数组)数据。1. PyTorch中的基本数据类型——张量 - 知乎 (zhihu.com)
- pytorch的
nn.Linear()
是用于设置网络中的全连接层。Pytorch nn.Linear的基本用法与原理详解_iioSnail的博客-CSDN博客 - F.relu():激活函数,是函数调用,一般使用在forward函数里。激活函数后会让网络具有非线性的分裂能力。【精选】深入理解ReLU函数(ReLU函数的可解释性)-CSDN博客
- 卷积和下采样操作结束后需要把多维度的tensor展平成一维,再进行全连接操作。在pytorch中的view()函数就是用来改变tensor的形状的,其中-1表示会自适应的调整剩余的维度。
- numel()函数获取tensor中一共包含多少个元素
class Net(nn.Module):
def __init__(self):
super (Net,self).__init__()
#卷积=>第一个参数:输入的通道数;第二个参数:输出的通道数;第三个参数:卷积核大小
# 32-F+1=28=>F=5
self.conv1=nn.Conv2d(1,6,5)
# 14-F+1=10=>F=5
self.conv2=nn.Conv2d(6,16,5)
#全连接层=>第一个参数:输入特征的维数大小;第二个参数:输出特征维数大小
self.fc1=nn.Linear(16*5*5,120)
self.fc2=nn.Linear(120,84)
self.fc3=nn.Linear(84,10)
def forward(self,x)
#tensor[batch(批次信息),channel(通道数),H,W]
#卷积操作
x = self.conv1(x)
x = F.relu(x)
#下采样操作
x = F.max_pool2d(x,(2,2))
#再一次卷积、下采样
x = F.max_pool2d(F.relu(self.conv2(x)),2)
# [1:]是切片数据,切掉了batch信息.展平操作如下
x = x.view(-1,x.size()[1:].numel())
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
定义一个网络是希望有可学习参数,经过输入数据的训练,让其参数不断更新,梯度下降更新到一个合适值后,再输入,就能进行分类或者预测。
1.3. 对网络进行实例化
net = Net()
print(net)
1.4. 输入输出
input = torch.randn(1,1,32,32)
print(input)
out = net(input)
print(out)
其输出是十个参数,即可分类十个类别
2. 第一个分类任务学习——十个类别进行分类
2.1. 根据CIFAR-10数据集完成分类任务的操作如下:
- 使用torch自带的torchvision(视觉工具,包含了常用数据集以及数据转化工具等)加载初始化数据集
- 定义卷积神经网络
- 定义损失函数,得到损失才能去更新网络的参数
- 根据训练数据去训练网络
- 根据测试数据去测试网络
2.2. 导包
import torch
import torchvision
import torchvision.transforms as transforms
2.3. 加载数据集
Dataset:定义好数据的格式和数据变换形式。Dataset是DataLoader实例化的一个参数。
Dataloader:用iterative(迭代)的方式不断读入批次数据(增加网络效率)。这种数据集主要用于数据大小未知,或者以流的形式的输入,本地文件不固定的情况,需要以迭代的方式来获取样本索引。
两文读懂PyTorch中Dataset与DataLoader(一)打造自己的数据集 - 知乎 (zhihu.com)
2.3.1. 定义数据变换格式
——定义数据加载进来的一个初始化操作(叫做transform):用定义好的transforms工具去进行处理,Compose()相当于把所有要处理的操作给打包起来。
- 首先进行ToTensor操作。pytorch处理的都是tensor数据,so读入进来的图片格式需要转换成tensor,tensor比普通图片三通道多了一个通道——batch通道。
- 进行归一化。可加速网络的收敛、防止梯度消失、梯度爆炸等等。(归一化?)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))
])
2.3.2. 定义数据格式
第一个参数:数据集加载到哪里;第二个参数:是否是训练数据;第三个参数:本地是否下载该数据集;第四个参数:做哪些变化。
trainset = torchvision.datasets.CIFAR10(root='./data',train=True,
download=True,transform=transform)
testset = torchvision.datasets.CIFAR10(root='./data',train=False,
download=True,transform=transform)
2.3.3. 读入批次数据
第一个参数:数据;第二个参数:batch_size,即一次从所有数据中拿多少数据;第三个参数:是否打乱数据;第四个参数:几线程去读取数据,Windows默认0
trainloader = torch.utils.data.DataLoader(trainset,batch_size=4,
shuffle=True,num_workers=0)
testloader = torch.utils.data.DataLoader(trainset,batch_size=4,
shuffle=False,num_workers=0)
2.3.4. 类别名的转换,即字典映射
该数据集读取进来其分类是0(对应airplane)、1(对应automobile)、2 ··· 9,不直观,可进行转换类别名操作。
classes = ('airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck')
2.4. 验证数据加载是否成功
2.4.1. 导包
import matp1otlib.pyp1ot as plt
import numpy as np
2.4.2. 定义显示函数imshow(),去显示加载器中的图像。
- 需要反归一化去恢复为正常图片,再将tensor数据(四通道)转换成矩阵数据(numpy格式)以供plt去显示,即img.numpy()。转换后剩余channel、H、W三个通道数据。pytorch: tensor与numpy之间的转换_tensor转化为numpy-CSDN博客
- tensor显示顺序:tensor[batch,channel,H,W];正常图片显示顺序:[H,W,channel]。so用np.transpose()进行通道转换。
- 显示图片
def imshow(img):
img = img/2 + 0.5 #unnormalize,恢复归一化
npimg = img.numpy() #tensor转numpy
plt.imshow(np.transpose(npimg,(1,2,0))) #通道顺序转换
plt.show()
2.4.3. 定义迭代器iter()去读一次迭代数据(4张图,batch_size=4)
Python迭代器基本方法iter()及其魔法方法__iter__()原理详解_涛涛ALG的博客-CSDN博客
- 先定义迭代器,训练数据中包含图片信息和标签信息, 再使用dataiter.next()获取图片和标签
-
torchvision.utils.make_grid() 将多张图片拼成一张Pytorch: torchvision.utils.make_grid函数的说明-CSDN博客
-
打印 观察标签信息对应的是什么
dataiter = iter(trainloader) #定义迭代器
images,labels = dataiter.__next__() #获得图片和标签数据
imshow(torchvision.utils.make_grid(images))
print(labels) #打印标签
print(labels[0],classes[labels[0]]) #打印第一个
print(' '.join(classes[labels[j]] for j in range(4)))
结果:
2.5. 定义卷积神经网络
具体操作如目录1. 网络初学:部分
2.6. 定义损失函数criterion
2.6.1. 定义损失函数
criterion = nn.CrossEntropyLoss()
2.6.2. 定义优化器 optimizer
- 导包
- 调用包里的SGD,第一个参数:所更新的网络参数,该网络继承nn.Module类,包含net.perameters()方法;第二个参数:学习率;第三个参数:动量
import torch.optim as optim
optimizer = optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
2.7. 训练网络
3.7.1. 定义epoch对数据集遍历几次
通常用for循环进行管理,去遍历整个数据集,遍历名称通常叫epoch,eopch从0开始,一个epoch代表对整个数据集遍历了一次。代表对整个数据学习2遍,for opoch in range(2)。
3.7.2. 读批次信息
- enumerate(第一个参数:数据内容即trainloader , 第二个参数:0,即i从0开始记录批次信息)。
- 读数据。一个trainloader包含图片(inputs)和标签labels()信息,从data中读出来。
- 读出来后进行梯度清零操作。pytorch中会对梯度信息累加,学习过程中是希望对每一次数据进行更新,so需要清零,清零后才能对网络进行权重更新。
3.7.3. 网络策略学习
前向传播获取网络输出信息,做损失函数计算,做后向传播获取梯度信息去更新、优化(根据SGD策略),让输出结果不断靠近网络真实标签结果。
- 通过预测值和正确值算损失函数,衡量网络该去哪更新。
- 后向传播,获得梯度信息做梯度更新
3.7.4. 看训练效果
每次迭代够2000次/轮(根据数据集大小决定)时,让其输出,打印 (当前训练遍历数据是第几轮,显示迭代到第几批次数据,损失函数求平均值)。
for epoch in range(2):
running_loss = 0.0 #running_loss值越低,学习效果越好,损失值越低
for i,data in enumerate(trainloader,0):
inputs,labels = data
optimizer.zero_grad() #梯度清零
# forward + loss + backward + optimizer
outputs = net(inputs) #预测值outputs
loss = criterion(outputs,labels) #第一个是输入参数,第二个是正确值。算损失函数
loss.backward() #后向传播
optimizer.step() #梯度更新
running_loss += loss.item()
if i % 2000 == 1999:
print('[%d,%5d] loss:%.3f' % (epoch +1,i+1,running_loss/2000))
running_loss = 0.0
print("Finish")
3.7.5. 保存学习参数,下次直接调用模型完成预测
torch.save() 第一个参数:要保存的梯度,把学习好的参数读取出来net.state_dict();第二个参数:保存路径。
PATH='./cifar_net.pth'
torch.save(net.state_dict(),PATH)
3.8. 测试网络
3.8.1. 先从测试数据集中读取一个批次数据
定义迭代器,获得图片和标签信息,打印。拿该数据集去预测。
# 测试网络部分
dataiter = iter(testloader) #定义迭代器
images,labels = dataiter.__next__() #获得图片和标签数据
imshow(torchvision.utils.make_grid(images)) #把图片拼接起来
#打印标签信息,每个标签长度5个字符大小,一次读4张图so对for循环做4次遍历,去读4次标签信息再映射
print('GroundTruth:',' '.join('%5s'% classes[labels[j]] for j in range(4)))
3.8.2. 加载学习好的权重
先实例化网络,再读取学习好的权重文件。
net = Net() #实例化网络
PATH='./cifar_net.pth'
net.load_state_dict(torch.load(PATH)) #让网络加载学习好的权重文件,是使用torch.load()函数读取进来的
3.8.3. 做预测
outputs = net(images)
print(outputs) #输出:每一行是一张图片的预测值
#加了_:获取最大值的标签位置;不加_:获取最大值
_,predicted = torch.max(outputs,1) #预测结果。 1:找出行最大值去返回;如果是0,则是列最大去返回
print('Predicted:',' '.join('%5s'% classes[predicted[j]] for j in range(4)))
在整个数据集上的预测结果:54%左右
torch.no_grad()关闭梯度。torch在预测中会进行梯度累加,做预测不需要保留梯度,训练中要保留梯度更新参数。
correct = 0 #正确率,即在整个数据集上预测正确的有几个
total = 0 #整个数据集的大小
with torch.no_grad():
#遍历整个测试数据集
for data in testloader:
images,labels = data
outputs = net(images)
_,predicted = torch.max(outputs,1) #找出预测信息
total += labels.size(0) #记录读了几张图片
correct += (predicted == labels).sum().item() #预测成功的照片数
correctGailv = 100*(correct / total)
print(correctGailv)