上一小节.
GitHub地址.
目录
- 1、思路和流程分析
- 2、准备训练集和测试集
- 2.1 torchvision.transform的图形数据处理方法
- (1) torchvision.transform.ToTensor
- (2)torchvision.transforms.Normalize(mean, std)
- (3)torchvision.transforms.Compose(transforms)
- 2.2 准备MNIST数据集的Dataset和DataLoader
- 3 模型的构建
- 3.1 激活函数的使用:
- 3.2 模型中数据的形状:
- 4、模型的训练
- 5、模型的保存和加载
- 5.1 模型的保存
- 5.2 模型的加载
- 6、模型的评估
- 7、完整代码:
- 7.1 代码:
- 7.2 结果:
流程:
准备数据集的方法上一节已经讲过数据加载Dataset和DataLoader的使用,在这里我们使用pytorch自带的mnist数据集来做,也就是说,我们不需要再去写自己的dataset类了,pytorch已经封装好了,我们只需要调用即可。API如下所示:
mnist = MNIST(path, train=True, download=True) # 是个dataset的实例
参数:
path
:表示保存数据的路径;
train
:训练集的数据;
download
:是否下载,因为第一次使用要从官网下载,下载了在之后,就可以设置为False了。
后面还有参数之后再介绍。
但是,调用MNIST返回的结果中图形数据是一个mage对象需要对其进行处理。
为了进行数据的处理,接下来需要学习torchvision.transform
的方法:
作用:把一个取值范围是[0,255]的PIL.Image
或者shape
为(H,W,C)的numpy.ndarray
,转换成形状为[C,H,W] ,取值范围是[0,1. 0]的torch.FloatTensor
。
其中(H,W,C)意思为(高,宽,通道数),黑白图片的通道数只有1,其中每个像素点的取值为
[0,255],彩色图片的通道数为(R,G,B)每个通道的每个像素点的取值为[0,255],三个通道的颜色相互叠加,形成了各种颜色。
示例如下:
from torchvision.datasets import MNIST
from torchvision import transforms
mnist = MNIST("./data", train=True, download=False) # 是个dataset的实例
# mnist[0][0].show() # 由于mnist是一个实例化对象,则可以用[]方式取每一条数据,mnist[0]表示第一条数据,是个元组(image,label)
print(mnist[0]) # 第1条数据,是个元组(image,label)
ret = transforms.ToTensor()(mnist[0][0]) # 将mage对象转换成张量,[28,28,1]->[1,28,28]
print(ret.size())
运行结果:
(<PIL.Image.Image image mode=L size=28x28 at 0x211EE644198>, 5)
torch.Size([1, 28, 28])
注意:
transforms.ToTensor
对象有个__call__
方法,所以可以对其示例能够传入数据获取结果。
作用:标准化张量。
参数:
给定均值: mean
,注意: shape和图片的通道数相同(指的是每个通道的均值);
方差: std
, 注意:和图片的通道数相同(指的是每个通道的方差),将会把Tensor规范化处理。
即: Normalized_ image=(image-mean)/std
.
例如:
from torchvision.datasets import MNIST
from torchvision import transforms
mnist = MNIST("./data", train=True, download=False) # 是个dataset的实例
# mnist[0][0].show() # 由于mnist是一个实例化对象,则可以用[]方式取每一条数据,mnist[0]表示第一条数据,是个元组(image,label)
print(mnist[0]) # 第1条数据,是个元组(image,label)
ret = transforms.ToTensor()(mnist[0][0])
print(ret.size())
norm_img = transforms.Normalize(mean=[0.1307], std=[0.3081])(ret) # 进行规范化处理
print(norm_img)
运行结果:
作用:将多个transforms组合起来使用。
例如:
transforms.Compose([transforms.ToTensor()(), # 先转化为Tensor
transforms.Normalize(mean, std)]) # 再进行正则化
# 1、准备数据集
def get_dataloader(train=True):
# 准备数据集,其中0.1307, 0.3081为MNIST数据集的均值和标准差,这样操作能够对其进行标准化
# 因为MNIST只有一个通道(黑白色),所以列表中只有一个
transforms_fn = transform=Compose([ToTensor(), # 先转化为Tensor
Normalize(mean=[0.1307], std=[0.3081])]) # 再进行正则化
dataset = MNIST("./data", train=train, download=False, transform=transforms_fn)
# 准备数据迭代器
data_loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True)
return data_loader
注意:要是首次,需要
download=True
补充:全连接层:当前一层的神经元和前一层的神经元相互连接,其核心操作就是 y = w x y = wx y=wx,即矩阵的乘法,实现对前一层的数据的变换模型的构建使用了一个三层的神经网络,其中包括两个全连接层和一个输出层,第一个全连接层会经过激活函数的处理,将处理后的结果交给下一个全连接层,进行变换后输出结果那么在这个模型中有两个地方需要注意:
常用的激活函数为Relu激活函数,他的使用非常简单,Relu激活函数由import torch.nn . functional as F
提供,F.relu(x)
即可对x进行处理
例如:
In [30]: b
out[30]: tensor([-2, -1, 0, 1, 2])
In [31]: import torch.nn.functiona1 as F
In [32]: F.re1u(b)
out[32]: tensor([0, 0, 0, 1, 2])
1、原始输入数据为的形状:
[batch_size ,1,28,28]
;
2、进行形状的修改:[batch_size,28*28]
,(全连接层是在进行矩阵的乘法操作);
3、第一个全连接层的输出形状:[batch_size,28]
,这里的28是个,人设定的,你也可以设置为别的;
4、激活函数不会修改数据的形状;
5、第二个全连接层的输出形状:[batch_size , 10]
,因为手写数字有10个类别。
构建模型的代码如下:
# 2、构建模型
class MnistModel(nn.Module):
def __init__(self):
super(self, MnistModel).__init__()
self.fc1 = nn.Linear(1*28*28, 28)
self.fc2 = nn.Linear(28, 10)
def forward(self, input):
"""
:param x:[batch_size, 1, 28, 28]
:return:
"""
# 1、修改形状
x = input.view(input.size(0), 1*28*28)
# input = input.view(-1, 1*28*28)
# 2、进行全连接操作
x = self.fc1(x)
# 3、进行激活函数的处理,形状不会变
F.relu(x)
# 4、输出层
out = self.fc2(x)
return F.log_softmax(out)
训练的流程:
def train(epoch):
"""实现训练过程"""
mode = True
model.train(mode=mode ) # 模型设置为训练模式
data_loader = get_dataloader()
for idx, (input, target) in enumerate(data_loader): # 每一轮里面的数据进行遍历
optimizer.zero_grad() # 梯度清零
output = model(input) # 调用模型,得到预测值
loss = F.nll_loss(output, target) # 得到损失
loss.backward() # 反向传播
optimizer.step() # 梯度更新
if idx % 10 == 0:
print("epoch:",epoch," idx:",idx, " loss:",loss.item())
if __name__ == "__main__":
for i in range(3): # 训练三轮
train(i)
torch.save(mnist_net.state_dict(), "mode1/mnist_net.pt") #保存模型参数
torch.save(optimizer.state_dict(),'results/mnist_optimizer.pt' #保存优化器参数
mnist_net.1oad_state_dict(torch.1oad("mode1/mnist_net.pt")) # 加载模型的参数
optimizer.1oad_state_dict(torch.1oad("resu1ts/mnist_optimizer.pt")) # 加载优化器的参数
评估的过程和训练的过程相似,但是:
def test():
loss_list = []
acc_list = []
test_dataloader = get_dataloader(train=False)
for inx, (input, target) in enumerate(test_dataloader):
with torch.no_grad():
output = model(input)
cur_loss = F.nll_loss(output, target)
loss_list.append(cur_loss)
# print(type(output))
# 计算准确率
# output [batch_size, 10], target [batch_size]
pred = output.max(dim=-1)[-1] # tensor的max/min返回两个值:第一个是值,第二个是对应的索引,因此加[-1]表示取索引,和argmax一样
# print(pred)
cur_acc = pred.eq(target).float().mean()
acc_list.append(cur_acc)
print("平均准确率:", np.mean(acc_list), "平均损失:", np.mean(loss_list))
import os
import torch
import numpy as np
from torchvision.transforms import Compose, ToTensor, Normalize
import torch.nn as nn
import torch.nn.functional as F
from torch.optim import Adam
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
BATCH_SIZE = 128
# 1、准备数据集
def get_dataloader(train=True):
# 准备数据集,其中0.1307, 0.3081为MNIST数据集的均值和标准差,这样操作能够对其进行标准化
# 因为MNIST只有一个通道(黑白色),所以列表中只有一个
transforms_fn = transform=Compose([ToTensor(), # 先转化为Tensor
Normalize(mean=[0.1307], std=[0.3081])]) # 再进行正则化
dataset = MNIST("./data", train=train, download=False, transform=transforms_fn)
# 准备数据迭代器
data_loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True)
return data_loader
# 2、构建模型
class MnistModel(nn.Module):
def __init__(self):
super(MnistModel, self).__init__()
self.fc1 = nn.Linear(1*28*28, 28)
self.fc2 = nn.Linear(28, 10)
def forward(self, input):
"""
:param x:[batch_size, 1, 28, 28]
:return:
"""
# 1、修改形状
x = input.view(input.size(0), 1*28*28)
# input = input.view(-1, 1*28*28)
# 2、进行全连接操作
x = self.fc1(x)
# 3、进行激活函数的处理,形状不会变
F.relu(x)
# 4、输出层
out = self.fc2(x)
return F.log_softmax(out, dim=-1)
model = MnistModel()
optimizer = Adam(model.parameters(), lr=0.001)
if os.path.exists("./model/model.pkl"):
# 加载模型参数
model.load_state_dict(torch.load("./model/model.pkl"))
# 加载优化器的参数
optimizer.load_state_dict(torch.load("./model/optimizer.pkl"))
def train(epoch):
"""实现训练过程"""
mode = True
model.train(mode=mode ) # 模型设置为训练模式
data_loader = get_dataloader()
for idx, (input, target) in enumerate(data_loader): # 每一轮里面的数据进行遍历
optimizer.zero_grad() # 梯度清零
output = model(input) # 调用模型,得到预测值
loss = F.nll_loss(output, target) # 得到损失
loss.backward() # 反向传播
optimizer.step() # 梯度更新
if idx % 10 == 0:
print("epoch:",epoch," idx:",idx, " loss:",loss.item())
# 模型的保存
if idx % 100 == 0:
torch.save(model.state_dict(), "./model/model.pkl")
torch.save(optimizer.state_dict(), "./model/optimizer.pkl")
def test():
loss_list = []
acc_list = []
test_dataloader = get_dataloader(train=False)
for inx, (input, target) in enumerate(test_dataloader):
with torch.no_grad():
output = model(input)
cur_loss = F.nll_loss(output, target)
loss_list.append(cur_loss)
# print(type(output))
# 计算准确率
# output [batch_size, 10], target [batch_size]
pred = output.max(dim=-1)[-1] # tensor的max/min返回两个值:第一个是值,第二个是对应的索引,因此加[-1]表示取索引,和argmax一样
# print(pred)
cur_acc = pred.eq(target).float().mean()
acc_list.append(cur_acc)
print("平均准确率:", np.mean(acc_list), "平均损失:", np.mean(loss_list))
if __name__ == "__main__":
# for i in range(3): # 训练三轮
# train(i)
test()
平均准确率: 0.9193038 平均损失: 0.2860677