Pytorch 实战:BP神经网络实现MINIST实现手写数字识别(单层感知机)
实验环境
- torch = 1.6.0
- torchvision = 0.7.0
- matplotlib = 3.3.3 # 绘图用
- progressbar = 2.5 # 绘制进度条用
- easydict # 超参数字典功能增强
使用数据集
导入相关的包
# 导包
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
import torchvision
from torchvision import datasets,transforms
import matplotlib.pyplot as plt
import random
from progressbar import *
设置超参数
from easydict import EasyDict #增强python的dict的功能用
# 定义超参数
super_param = {
"batch_size":128,
"device": torch.device('cuda:0' if torch.cuda.is_available() else 'cpu'),
"epochs":10,
"lr":0.3,
'hidden_num':15, #隐藏层神经元数量
}
super_param = EasyDict(super_param)
print(super_param)
{'batch_size': 16, 'device': device(type='cuda', index=0), 'epochs': 10, 'lr': 0.3, 'hidden_num': 15}
数据处理(下载、处理、加载数据到DataLoader)
# 下载、加载数据
# 构transform(pipeline),对图像做处理
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,),(0.3081,)) #正则化
])
# 下载数据
trainsets = datasets.MNIST('data',train=True,download=True,transform=transform)
testsets = datasets.MNIST('data',train=False,download=True,transform=transform)
# dataloader 加载数据
train_loader = DataLoader(trainsets,batch_size=super_param.batch_size,shuffle=True)
test_loader = DataLoader(trainsets,batch_size=super_param.batch_size,shuffle=True)
查看数据样例
查看数据样例【单张】
# 查看数据样例-单张
image,label = trainsets[random.randint(0,len(trainsets))]
print('label=',label)
plt.imshow(image.permute(1,2,0),cmap='gray')
plt.show()
查看数据样例【一批】
# 查看数据样例-一批
images,labels = next(iter(test_loader))
data_sample_img = torchvision.utils.make_grid(images).numpy().transpose(1,2,0)
print('labels=',labels)
plt.figure(dpi=200)
plt.xticks([])
plt.yticks([])
plt.imshow(data_sample_img)
plt.show()
构建BP网络模型 -简单版- 使用Sequential
## 构建网络模型-BP
model = nn.Sequential(
nn.Flatten(),
nn.Linear(28*28,super_param.hidden_num),
nn.ReLU(),
nn.Linear(super_param.hidden_num,10),
nn.ReLU(),
nn.Softmax(),
)
print(model)
Sequential(
(0): Flatten()
(1): Linear(in_features=784, out_features=15, bias=True)
(2): ReLU()
(3): Linear(in_features=15, out_features=10, bias=True)
(4): ReLU()
(5): Softmax(dim=None)
)
构建BP网络模型 -使用自定义类
## 构建BP网络模型 -使用自定义类
class Digit_Rec(nn.Module):
def __init__(self,hidden_num):
super(Digit_Rec,self).__init__()
self.fc1 = nn.Linear(28*28,hidden_num)
self.relu1 = nn.ReLU()
self.fc2 = nn.Linear(hidden_num,10)
self.relu2 = nn.ReLU()
self.softmax = nn.Softmax(dim=1)
def forward(self,x):
batch_size = x.size(0) # x的格式:batch_size x 1 x 28 x 28 拿到了batch_size
x = x.view(batch_size,28*28) # flatten
out = self.fc1(x)
out = self.relu1(out)
out = self.fc2(out)
out = self.relu2(out)
out = self.softmax(out)
return out
model = Digit_Rec(super_param.hidden_num)
print(model)
Digit_Rec(
(fc1): Linear(in_features=784, out_features=50, bias=True)
(relu1): ReLU()
(fc2): Linear(in_features=50, out_features=10, bias=True)
(relu2): ReLU()
(softmax): Softmax(dim=1)
)
定义损失函数和优化器
# 定义损失函数和优化
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(),lr=super_param.lr)
定义模型训练单个epoch函数
# 定义模型训练单个epoch函数
def train_model_epoch(model,train_loader,super_param,criterion,optimzer,epoch):
model.train()#训练声明
for batch_index,(images,labels) in enumerate(train_loader):
# 数据上device
images,labels = images.to(super_param.device),labels.to(super_param.device)
# 梯度清零
optimzer.zero_grad()
# 前向传播
output = model(images)
# 计算损失
loss = criterion(output,labels)
# 反向传播,计算梯度
loss.backward()
# 参数更新(优化)
optimzer.step()
# 打印训练参考信息,每1000个batch打印一次
if batch_index % 1000 == 0:
print("Epoch:{} Batch Index(batch_size={}):{}/{} Loss:{}".
format(epoch,super_param.batch_size,batch_index,len(train_loader),loss.item()))
定义模型验证方法
# 定义模型验证方法
def test_model(model,test_loader,super_param,criterion):
model.eval()#测试声明
# 数据统计
correct_num,test_loss = 0.0,0.0 #正确数,测试损失
#定义进度条
widgets = ['模型测试中: ',Percentage(), ' ', Bar('#'),' ', Timer(),' ', ETA()]
pbar = ProgressBar(widgets=widgets, maxval=100).start()
# 取消计算梯度,避免更新模型参数
with torch.no_grad():
for batch_index,(images,labels) in enumerate(test_loader):
# 数据上devics
images,labels = images.to(super_param.device),labels.to(super_param.device)
# 模型预测
output = model(images)
# 计算测试损失
test_loss += criterion(output,labels).item()
# 确定预测结果是哪个数字
pred = output.argmax(dim=1) #argmax返回 值,索引 dim=1表示要索引
# 统计预测正确数量
correct_num += pred.eq(labels.view_as(pred)).sum().item()
#更新进度条进度
pbar.update(batch_index/len(test_loader)*100)
#释放进度条
pbar.finish()
#打印测试信息
test_loss = test_loss/len(test_loader.dataset)
test_accuracy = correct_num / len(test_loader.dataset)
print("Test --- Avg Loss:{},Accuracy:{}\n".format(test_loss,test_accuracy))
return test_loss,test_accuracy
模型训练和测试
# 模型训练和测试
#模型上device
mode = model.to(super_param.device)
#记录每个epoch的测试数据、用于绘图
epoch_list = []
loss_list = []
accuracy_list = []
for epoch in range(super_param.epochs):
train_model_epoch(model,train_loader,super_param,criterion,optimizer,epoch)
test_loss,test_accuracy = test_model(model,test_loader,super_param,criterion)
# 数据统计
epoch_list.append(epoch)
loss_list.append(test_loss)
accuracy_list.append(test_accuracy)
查看数据统计结果
# 查看数据统计结果
fig = plt.figure(figsize=(12,12),dpi=70)
#子图1
ax1 = plt.subplot(2,1,1)
title = "hidden_neuron_num={},bach_size={},lr={}".format(super_param.hidden_num,super_param.batch_size,super_param.lr)
plt.title(title,fontsize=15)
plt.xlabel('Epochs',fontsize=15)
plt.ylabel('Loss',fontsize=15)
plt.xticks(fontsize=13)
plt.yticks(fontsize=13)
plt.plot(epoch_list,loss_list)
#子图2
ax2 = plt.subplot(2,1,2)
plt.xlabel('Epochs',fontsize=15)
plt.ylabel('Accuracy',fontsize=15)
plt.xticks(fontsize=13)
plt.yticks(fontsize=13)
plt.plot(epoch_list,accuracy_list,'r')
plt.show()
隐藏层神经元数量=15
隐藏层神经元数量=30
隐藏层神经元数量=50
评论 (0)