以下使用手写数字识别如何涉及,实现并训练一个标准的前馈神经网络
我们涉及并训练一个三层的神经网络,以数字图像作为输入,经过神经网络的计算就会识别图像中的数字是几,从而实现数字图像的分类
1.神经网络的设计和实现
首先明确输入图像数据的大小和格式,我们要处理分辨率为28*28的灰色通道图像,这样的灰色通道包含了28*28=784个数据点,我们要先将他展平为1*784的向量,再将这个向量输入到神经网络当中。
我们使用一个三层神经网络来处理图片对应的向量x
隐藏层用于特征提取,将输入的特征向量处理为更高级的特征向量,由于手下数字图像并不复杂,将隐藏层的神经个数设置为256,输入层和隐藏层之间就会有一个784*256大小的线性层,该256维的输出向量会继续向前传播到达输出层,由于最终要将数字图像识别为0~9,10种可能的数字,因此输出层需要定义10个神经元,对应着10种数字。这10维的输出向量就代表着十个数字的预测得分。为了继续得到10个数字的预测概率,我们还要将输出层的输出输入到softmax层,softmax层会将10维的向量转换为10个概率值
import torch
from torch import nn
#定义神经网络Network
class Network(nn.Moudle):
def __init__(self):
super().__init__()
#线性层1,输入层和隐藏层之间的线性层
self.layer1 = nn.Linear(784,256)
#线性层2,隐藏层和输出层之间的线性层
self,layer2 = nn.Linear(256,10)
# 在向前传播,forward函数中,输入为图像x
def forward(self,x):
x = x.view(-1,28*28) #使用view函数,将x展平
x = self.layer1(x) #将x输入至layer1
x = torch relu(x) #使用rulu激活
return self.layer2(x) #输入至layer2计算结果
#这里我们没有直接定义softmax层
#这是因为后面会使用CrossEntropyloss损失函数
#在这个损失函数中,会实现softmax的计算
2.训练数据的准备和处理
手写数字识别的训练数据可以直接使用MNISTS数据集,可以从torchvision.datasets中获取,将数据分别保存到train和test两个目录中,其中train有60000个数据,用作训练,test有10000个数据,用作测试。这两个目录中都包含了10个子目录,子目录的名字就是对应图像中的数字
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
#初学者在学习这一部分时,只要知道大致的数据处理流程即可
if __name__ == '__main__':
#实现图像的预处理pipeline
transform = transforms.Compose([
transforms.Grayscale(num_output_channels=1), #转换为单通道灰度图
transforms.ToTensor() #转换为张量
])
# 使用ImageFolder函数,读取数据文件夹,构建数据集dataset
# 这个函数会将保存数据的文件夹的名字,作为数据的标签,组织数据
# 例如,对于名字为“3”的文件夹
# 会将“3”作为文件夹中图像数据的标签,和图像配对,用于后续的训练,使用起来非常的方便
train_dataset = datasets.ImageFolder(root='./mnist_images/train',transform=transform)
test_dataset = datasets.ImageFolder(root='./mnist_images/test',transform=transform)
#使用train_loder,实现小批量的数据读取
#这里设置小批量的大小,batch_size=64.也就是每个批次,包括64个数据
train_loader = DataLoader(train_dataset,batch_size=64,shuffle=True)
#打印train_loader的长度
print("train_loader length:",len(train_loader))
#60000个训练数据,如果每个小批量,读入64个样本,那么60000个数据会被分成938组
#计算938*64=60032,这说明最后一组,会不够64个数据
#循环遍历train_loader
#每一次循环,都会取出64个图像数据,作为一个小批量batch
for batch_idx == 3: #打印前3个batch观察
break
print("batch_idx:",batch_idx)
print("data.shape:",data,shape) #数据的尺寸
print("label:",label.shape) #图像中的数字
print(label)
程序会输出 data.shape:torch.Size([64,1,28,28]) 每组数据包括64个图像,每个图像有1个灰色通道,图像的尺寸是28*28
3.模型的训练和测试
inprot torch
from torch import nn
from torch import optim
from model import Network
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
if __name__ == '__main__':
#图像的预处理
transform = transforms.Compose([
transforms.Grayscale(num_output_channels=1), #转换为单通道灰度图
transforms.ToTensor() #转换为张量
])
#读入并构造数据集
train_dataset = datasets.ImageFolder(root='./mnist_images/train',transform=transform)
print("train_datase length:",len(train_dataset))
#使用train_loder,实现小批量的数据读取
train_loader = DataLoader(train_dataset,batch_size=64,shuffle=True)
print("train_loader length:",len(train_loader))
#使用Pytorch训练模型时,需要创建三个对象:
model = Network() #1.模型本身,它就是我们设计的神经网络
optimizer = optim.Adam(model.parameters()) #2.优化器,优化模型中的参数
criterion = nn.CrossEntropyLoss() #3.损失函数,分类问题,使用交叉熵损失误差
#进入模型的迭代循环
for epoch in range(10): #外层循环,代表了整个训练数据集的遍历次数
#整个训练集要循环多少轮,是10次,20次或者100次都是可能的,
#内层循环使用train_loader,进行小批量的数据读取
for batch_idx,(data,label) in enumerate(train_loader):
#内层每循环一次,就会进行一次梯度下降算法
#包括了5个步骤:
output = model(data) #1.计算神经网络先前传播结果
loss = criterion(output,label) #2.计算output和标签label之间的损失loss
loss.backward() #3.使用backward计算梯度
optimizer.step() #4.使用optimizer.step更新参数
optimizer.zero_grad() #5.将梯度清零
#这5个步骤,是使用pytroch框架训练模型的定式,初学的时候,先记住就可以了
#每迭代100个小批量,就打印一次模型的损失,观察训练的过程
if batch_idx %100 == 0:
print(f"Epoch{epoch+1}/10"
f"| Batch{batch_idx}/{len(train_loader)}"
f"|Loss:{loss.item():.4f}")
torch.save(model.state_dict(),'mnist.pth')#保存模型
对模型进行测试
from model import Network
from torchvision import transforms
from torchvision import datasets
import torch
if __name__=='__main__':
transform = transforms.Compose([
transforms.Grayscale(num_output_channels=1),
transforms.ToTensor()
])
#读取测试数据集
test_dataset = datesets.ImageFolder(root='./mninst_images/test',transform=transform)
print("test_dataset lengeh:",len(test_dataset))
model= Network() #定义神经网络模型
model.load_state_dict(torch.load('mnist.pth')) #加载刚刚训练好的模型文件
right = 0 #保存正确识别的数量
for i,(x,y) in enumerate (test_dataset):
output = model(x) #将其中的数据x输入到模型
predict = output.argmax(1).item() #选择概率最大标签的作为预测结果
#对比预测值predict和真实标签y
if predict == y:
right+=1
else:
#将识别错误的样例打印了出来
img_path = test_dataset.samples[i][0]
print(f"wrong case: predict = {predict} y= {y} img_path={img_path}")
#计算出测试效果
sample_num = len(test_dataset)
acc=right*1.0/sample_num
print("test accuracy =%d/%d=%.3lf"%(right,sample_num,acc))