四层网络结构实现数字识别,我们这里对MNIST进行处理,初始的MNIST是 28 * 28,我们把它处理成 96 * 96 的torch.Tensor的格式。
首先导入需要的包。
import torch
import numpy as np
import os #对文件,文件夹执行操作的一个模块。
from torch.utils.data import DataLoader
import torch.nn as nn #torch.nn模块包含torch为我们准备好的各种层,方便我们调用以构建网络。
import torch.nn.functional as F #导入激活函数
from torch.optim import Adam #导入优化器
from torchvision.datasets import MNIST #导入数据集
from torchvision.transforms import ToTensor,Compose,Normalize #导入三个方法
1,准备数据集
先转变图片的格式,将其标准化,再对MNIST实例化,最后调用DataLoader对数据进行处理,批处理和打乱数据,返回处理好的数据。
BATCH_SIZE=128 #定义批处理一批的大小
#1,准备数据集
def get_dataloader(train=True):
transform_fn=Compose([
ToTensor(),#ToTensor()将shape为(H, W, C)的nump.ndarray或img转为shape为(C, H, W)的tensor,其将每一个数值归一化到[0,1]
Normalize(mean=(0.1307,),std=(0.3081,))
])
dataset=MNIST(root='./data',train=train,transform=transform_fn) #实例化
#root表示存放的位置,train表示使用训练集的数据还是测试集,download表示是否下载数据到root目录,transform实现对图片的处理函数
#train=True表示训练集,train=False表示测试集
data_loader=DataLoader(dataset,batch_size=BATCH_SIZE,shuffle=True) #使用DataLoader对数据进行处理,批处理和打乱数据
return data_loader
2,构建模型
构建自己的类,继承自基类nn.Module。
__init__()函数是调用基类的__init__()方法,forward()的作用是向前传播。
#2,构建模型
class MnistModel(nn.Module): #构建自己的类,继承自基类nn.Module
def __init__(self):
super(MnistModel,self).__init__() #调用基类的__init__()方法
self.fc1=nn.Linear(1*28*28,28) #nn.Linear定义一个神经网络的线性层
self.fc2=nn.Linear(28,10)
def forward(self,input):
#1.修改形状
x=input.view([-1,1*28*28]) #-1表示该位置根据后面的形状自动调整
#2.进行全连接的操作
x=self.fc1(x)
#3.经过激活函数的处理
x=F.relu(x)
#4.输出层
out=self.fc2(x)
#对数取值计算softmax并取对数,返回
return F.log_softmax(out,dim=-1)
3,训练
输出的是轮次,索引,损失值(越小越好)
#3,训练
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):
data_loader=get_dataloader() #把函数处理过后的数据拿过来
#这个循环进行训练操作
for idx,(input,target) in enumerate(data_loader):#idx是索引,(input,target)是一个元组,(输入,目标值)
#enumerate这个函数的基本应用就是用来遍历一个集合对象,它在遍历的同时还可以得到当前元素的索引位置。
optimizer.zero_grad() #把梯度置为零
output = model(input) #调用模型,得到预测值output
loss=F.nll_loss(output,target) #得到损失函数
loss.backward() #反向传播
optimizer.step() #梯度更新
if idx%100==0:
print(epoch,idx,loss.item())
#模型的保存
if idx % 100 == 0:
torch.save(model.state_dict(),"./model/model.pkl")
torch.save(optimizer.state_dict(), "./model/optimizer.pkl")
4,测试
输出平均损失和平均准确率。
def text():
#定义两个列表来存放损失和准确率
loss_list=[]
acc_list=[]
text_dataloader=get_dataloader(train=False) #实例化,train=False表示测试集
for idx, (input, target) in enumerate(text_dataloader):
with torch.no_grad():
output=model(input)
cur_loss=F.nll_loss(output,target) #当前的损失
loss_list.append(cur_loss) #存放损失
#计算准确率
pred=output.max(dim=-1)[-1] #预测值
cur_acc=pred.eq(target).float().mean() #float()转化为浮点数,mean()求均值
acc_list.append(cur_acc) #存放准确率
print("平均损失:",np.mean(loss_list),"平均准确率:",np.mean(acc_list))
5,运行
注释的代码是训练的代码。先训练,再测试。
#5,运行
if __name__=='__main__':
# for i in range(3):
# train(i)
for i in range(3):
text()
训练的结果如下图示:
测试的结果如下图示:
我们看到平均损失越来越小,准确率越来越高。