基于pytorch平台实现对MNIST数据集的分类分析(前馈神经网络、softmax)进阶版
文章目录
一、基于“前馈神经网络”模型(升级版)
import numpy as np
import torch
import matplotlib.pyplot as plt
from torchvision.datasets import mnist
from torchvision import transforms
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
train_batch_size = 64#超参数
test_batch_size = 128#超参数
learning_rate = 0.01#学习率
nums_epoches = 20#训练次数
lr = 0.1#优化器参数
momentum = 0.5#优化器参数
train_dataset = mnist.MNIST('./data', train=True, transform=transforms.ToTensor(), target_transform=None, download=True)
test_dataset = mnist.MNIST('./data', train=False, transform=transforms.ToTensor(), target_transform=None, download=False)
train_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False)
class model(nn.Module):
def __init__(self, in_dim, hidden_1, hidden_2, out_dim):
super(model, self).__init__()
self.layer1 = nn.Sequential(nn.Linear(in_dim, hidden_1, bias=True), nn.BatchNorm1d(hidden_1))
self.layer2 = nn.Sequential(nn.Linear(hidden_1, hidden_2, bias=True), nn.BatchNorm1d(hidden_2))
self.layer3 = nn.Sequential(nn.Linear(hidden_2, out_dim))
def forward(self, x):
# 注意 F 与 nn 下的激活函数使用起来不一样的
x = F.relu(self.layer1(x))
x = F.relu(self.layer2(x))
x = self.layer3(x)
return x
#实例化网络
model = model(28*28,300,100,10)
#定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
#momentum:动量因子
optimizer = optim.SGD(model.parameters(),lr=lr,momentum=momentum)
def train():
# 开始训练 先定义存储损失函数和准确率的数组
losses = []
acces = []
# 测试用
eval_losses = []
eval_acces = []
for epoch in range(nums_epoches):
# 每次训练先清零
train_loss = 0
train_acc = 0
# 将模型设置为训练模式
model.train()
# 动态学习率
if epoch % 5 == 0:
optimizer.param_groups[0]['lr'] *= 0.1
for img, label in train_loader:
# 例如 img=[64,1,28,28] 做完view()后变为[64,1*28*28]=[64,784]
# 把图片数据格式转换成与网络匹配的格式
img = img.view(img.size(0), -1)
# 前向传播,将图片数据传入模型中
# out输出10维,分别是各数字的概率,即每个类别的得分
out = model(img)
# 这里注意参数out是64*10,label是一维的64
loss = criterion(out, label)
# 反向传播
# optimizer.zero_grad()意思是把梯度置零,也就是把loss关于weight的导数变成0
optimizer.zero_grad()
loss.backward()
# 这个方法会更新所有的参数,一旦梯度被如backward()之类的函数计算好后,我们就可以调用这个函数
optimizer.step()
# 记录误差
train_loss += loss.item()
# 计算分类的准确率,找到概率最大的下标
_, pred = out.max(1)
num_correct = (pred == label).sum().item() # 记录标签正确的个数
acc = num_correct / img.shape[0]
train_acc += acc
losses.append(train_loss / len(train_loader))
acces.append(train_acc / len(train_loader))
eval_loss = 0
eval_acc = 0
model.eval()
for img, label in test_loader:
img = img.view(img.size(0), -1)
out = model(img)
loss = criterion(out, label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
eval_loss += loss.item()
_, pred = out.max(1)
num_correct = (pred == label).sum().item()
acc = num_correct / img.shape[0]
eval_acc += acc
eval_losses.append(eval_loss / len(test_loader))
eval_acces.append(eval_acc / len(test_loader))
print('epoch:{},Train Loss:{:.4f},Train Acc:{:.4f},Test Loss:{:.4f},Test Acc:{:.4f}'
.format(epoch, train_loss / len(train_loader), train_acc / len(train_loader),
eval_loss / len(test_loader), eval_acc / len(test_loader)))
plt.title('trainloss')
plt.plot(np.arange(len(losses)), losses)
plt.legend(['Train Loss'], loc='upper right')
#测试
from sklearn.metrics import confusion_matrix
import seaborn as sns
def test():
correct = 0
total = 0
y_predict=[]
y_true=[]
with torch.no_grad():
for data in test_loader:
input, target = data
input = input.view(input.size(0), -1)
output = model(input)#输出十个最大值
_, predict = torch.max(output.data, dim=1)#元组取最大值的下表
#
#print('predict:',predict)
total += target.size(0)
correct += (predict == target).sum().item()
y_predict.extend(predict.tolist())
y_true.extend(target.tolist())
print('正确率:', correct / total)
print('correct=', correct)
sns.set()
f, ax = plt.subplots()
C2 = confusion_matrix(y_true, y_predict, labels=[0, 1, 2,3,4,5,6,7,8,9])
print(C2)
plt.imshow(C2, cmap=plt.cm.Blues)
plt.xticks(range(10),labels=[0, 1, 2,3,4,5,6,7,8,9] , rotation=45)
plt.yticks(range(10),labels=[0, 1, 2,3,4,5,6,7,8,9])
plt.colorbar()
plt.xlabel('True Labels')
plt.ylabel('Predicted Labels')
plt.title('Confusion matrix (acc=' + str(correct / total)+ ')')
plt.show()
train()
test()
二、基于“softmax”模型(升级版)
import torch
from torch import nn
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import time
import sys
sys.path.append("") # 为了导入上层目录的d2lzh_pytorch
#import d2lzh_pytorch as d2l
from d2l import torch as d2
#from sklearn.metrics import confusion_matrix
import numpy as np
mnist_train = torchvision.datasets.MNIST(root='~/Datasets/MNIST', train=True, download=True, transform=transforms.ToTensor())
mnist_test = torchvision.datasets.MNIST(root='~/Datasets/MNIST', train=False, download=True, transform=transforms.ToTensor())
print(type(mnist_train))
print(len(mnist_train), len(mnist_test))
feature, label = mnist_train[0]
print(feature.shape, label) # Channel x Height x Width
batch_size = 256
if sys.platform.startswith('win'):
num_workers = 0 # 0表示不用额外的进程来加速读取数据
else:
num_workers = 4
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
#######softmax
num_inputs = 784
num_outputs = 10
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)
#定义Softmax函数
def softmax(X):
X_exp = torch.exp(X)
partition = X_exp.sum(1, keepdim=True)
return X_exp / partition # 这里应用了广播机制
#定义模型
def net(X):
return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
#定义损失函数
def cross_entropy(y_hat, y):
return - torch.log(y_hat.gather(1, y.view(-1, 1)))
#定义优化算法
def sgd(params, lr, batch_size): # lr 为学习率
for param in params:
param.data -= lr * param.grad / batch_size
#定义准确率函数
def accuracy(y_hat, y):
return (y_hat.argmax(dim=1) == y).float().mean().item()
#我们可以评估在任意模型 net 的准确率
def evaluate_accuracy(data_iter, net):
"""计算在指定数据集上模型的精度。"""
if isinstance(net, torch.nn.Module):
net.eval()
metric = d2.Accumulator(2)
for X, y in data_iter:
metric.add(accuracy(net(X), y), y.numel())
return metric[0] / metric[1]
# #训练模型
num_epochs,lr=20,0.1
def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params=None, lr=None):
for epoch in range(num_epochs):
train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
for X, y in train_iter:
y_hat = net(X)
l = loss(y_hat, y).sum()
#梯度清零
if params is not None and params[0].grad is not None:
for param in params:
param.grad.data.zero_()
l.backward()#求梯度
sgd(params, lr, batch_size)
train_l_sum += l.item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
n += y.shape[0]
test_acc = evaluate_accuracy(test_iter, net)
print('epoch %d, loss %.6f, train_acc %.6f, test_acc %.6f'
%(epoch+1, train_l_sum / n, train_acc_sum/n, test_acc))
#测试模型
from sklearn.metrics import confusion_matrix
import seaborn as sns
def test():
correct = 0
total = 0
y_predict=[]
y_true=[]
with torch.no_grad():
for data in test_iter:
input, target = data
output = net(input)#输出十个最大值
_, predict = torch.max(output.data, dim=1)#元组取最大值的下表
#
#print('predict:',predict)
total += target.size(0)
correct += (predict == target).sum().item()
y_predict.extend(predict.tolist())
y_true.extend(target.tolist())
print('正确率:', correct / total)
print('correct=', correct)
sns.set()
f, ax = plt.subplots()
C2 = confusion_matrix(y_true, y_predict, labels=[0, 1, 2,3,4,5,6,7,8,9])
print(C2)
plt.imshow(C2, cmap=plt.cm.Blues)
plt.xticks(range(10),labels=[0, 1, 2,3,4,5,6,7,8,9] , rotation=45)
plt.yticks(range(10),labels=[0, 1, 2,3,4,5,6,7,8,9])
plt.colorbar()
plt.xlabel('True Labels')
plt.ylabel('Predicted Labels')
plt.title('Confusion matrix (acc=' + str(correct / total)+ ')')
plt.show()
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W, b], lr)
test()
总结
至此,基于pytorch平台实现对MNIST数据集的分类分析,并以分类的准确度和混淆矩阵为衡量指标,分析二个模型的精度。