MNIST数据集介绍
MNIST包含70,000张手写数字图像: 60,000张用于培训,10,000张用于测试。图像是灰度的(即单通道),28x28像素的,并且居中的,以减少预处理和加快运行。我们可以在Pytorch官网中的Docs->torchvision->Datasets找到关于MNIST数据集的详细介绍。
ANN网络结构
本文所示代码搭建了一个较简单的ANN模型(注意不是CNN),其模型图如下所示:
注意事项
1.由于该网络不是卷积网络,因此图片输入进网络之前需要进行额外的处理,即需要将28*28*1的图片通过x.view(-1,28*28*1)语句转换成一维向量;
2.在最开始利用torchvision.transforms处理数据集时,Normalize()转换使用的值0.1307和0.3081是MNIST数据集的全局平均值和标准偏差,这里我们将它们作为给定值;
3.在计算损失函数时。如果直接调用nn.CrossEntropyLoss(),那么就无需在网络输出处添加softmax;如果使用的是F.nll_loss,则需要添加F.log_softmax(x,dim=1);
4.若在网络中加入了dropout层,需要在测试集上时加入model.eval()来关闭dropout.
代码实现
该代码将每训练100次后的Loss和Accuracy及每个epoch的测试集的Accuracy输出,并绘制了训练集的损失函数下降趋势图。
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
import torch.nn.functional as F
import matplotlib.pyplot as plt
import numpy as np
#MNIST包含70,000张手写数字图像: 60,000张用于培训,10,000张用于测试。图像是灰度的,28x28像素的,并且居中的,以减少预处理和加快运行。
transforms_fn=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(mean=(0.1307,),std=(0.3081,)) #
]) # Normalize()转换使用的值0.1307和0.3081是MNIST数据集的全局平均值和标准偏差,这里我们将它们作为给定值
#训练集
train_data=torchvision.datasets.MNIST('./mnist_data',train=True,transform=transforms_fn,download=True)
#测试集
test_data=torchvision.datasets.MNIST('./mnist_data',train=False,transform=transforms_fn,download=True)
train_data_size=len(train_data)
test_data_size=len(test_data)
print("训练数据集的长度为{}".format(train_data_size))
print("测试数据集的长度为{}".format(test_data_size))
#利用dataloader来加载数据集
train_data=DataLoader(train_data,batch_size=64)
test_data=DataLoader(test_data,batch_size=64)
examples = enumerate(test_data) #将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
batch_idx, (example_data, example_targets) = next(examples)
fig = plt.figure()
for i in range(6):
plt.subplot(2,3,i+1)
plt.tight_layout()
plt.imshow(example_data[i][0], cmap='gray', interpolation='none')
plt.title("Ground Truth: {}".format(example_targets[i]))
plt.xticks([])
plt.yticks([])
plt.show()
#构建ANN网络
class mnist_ann(nn.Module):
def __init__(self):
super(mnist_ann, self).__init__()
self.model=nn.Sequential(
nn.Linear(28*28*1, 512),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(512,256),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(256,64),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(64,10)
)
def forward(self,x):
x=x.view(-1,28*28*1) #要加上这句话,相当于把一张28*28的图片变成一维的向量
x=self.model(x)
# x=F.log_softmax(x,dim=1) #当dim=1时, 是对某一维度的列进行softmax运算;dim=2/-1时, 是对某一维度的行进行softmax运算
return x
#网络模型
model=mnist_ann()
#损失函数
loss_fn=nn.CrossEntropyLoss() #对于cross_entropy来说,他首先会对input进行log_softmax操作,然后再将log_softmax(input)的结果送入nll_loss;而nll_loss的input就是input。
#在多分类问题中,如果使用nn.CrossEntropyLoss(),则预测模型的输出层无需添加softmax层!!!
#如果是F.nll_loss,则需要添加softmax层!!!
# # SGD 就是随机梯度下降
# opt_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)
# # momentum 动量加速,在SGD函数里指定momentum的值即可
# opt_Momentum = torch.optim.SGD(net_Momentum.parameters(), lr=LR, momentum=0.8)
# # RMSprop 指定参数alpha
# opt_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr=LR, alpha=0.9)
# # Adam 参数betas=(0.9, 0.99)
# opt_Adam = torch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9, 0.99))
learning_rate=0.01
optimizer=torch.optim.SGD(params=model.parameters(),lr=learning_rate)
#记录训练的次数
total_train_step=0
#记录测试的次数
total_test_step=0
#记录测试的准确率
total_accuracy=0
#训练的轮数
epochs=10
#训练损失
train_loss=[]
for epoch in range(epochs):
print("-----第{}轮训练开始------".format(epoch + 1))
total,correct=0,0
# 训练步骤开始
for batch_idx,(data,target) in enumerate(train_data):
optimizer.zero_grad()
output = model(data)
loss=loss_fn(output,target)
loss.backward()
optimizer.step()
_, predicted = torch.max(output.data, 1) # 选择最大的(概率)值所在的列数就是他所对应的类别数,
total += target.size(0)
correct += (predicted == target).sum().item()
total_train_step+=1
if total_train_step%100==0:
train_loss.append(loss.item()) # 使用.item()精度更高
print("训练次数:{} , Loss:{},Accuracy:{:.2f}%".format(total_train_step, loss,100 * correct / total))
#测试步骤开始
total_test_loss = 0
total_accuracy = 0
model.eval()
with torch.no_grad():
for data,target in test_data:
output=model(data)
loss=loss_fn(output,target)
total_test_loss+=loss.item()
accuracy=(output.argmax(1)==target).sum()
total_accuracy+=accuracy
print("整体测试集上的准确率:{:2f}%".format(100*total_accuracy.float() / test_data_size))
# 保存网络
torch.save(model,"MNIST_cpu_model(epoch={}).ckpt".format(epochs))
plt.plot(np.squeeze(train_loss))
plt.ylabel('cost')
plt.xlabel('iterations (per 100)')
plt.title("Learning rate =" + str(learning_rate))
plt.show()