首先对mnist数据集进行可视化展示,然后在mnist数据集上构建简单的神经网络。仅使用两层卷积训练准确度就达到99%。
#数据集的可视化使用jupyter notebook
#导入库
import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np
import matplotlib.pyplot as plt
#数据集的加载,该数据集从本地进行加载或者直接网站下载都可
train_dataset = datasets.MNIST(root='./mnist',
train=True,
transform=transforms.ToTensor(),
download=True)
test_dataset = datasets.MNIST(root='./mnist',
train=False,
transform=transforms.ToTensor(),
download=True)
print('Train data, number of images: ', len(train_dataset))
print('Test data, number of images: ', len(test_dataset))
# dataset 参数用于指定我们载入的数据集名称
# batch_size参数设置了每个包中的图片数据个数
# 在装载的过程会将数据随机打乱顺序并进打包
#建立一个数据迭代器
# 装载训练集
batch_size=20
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True)
# 装载测试集
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=True)
#注意lable是0-9
classes = ['0','1', '2', '3', '4', '5', '6', '7', '8', '9']
#实现单张图片可视化
dataiter = iter(train_loader)
images, labels = dataiter.__next__()
images = images.numpy()
#将该batch中的图片连同其lable一起绘制出来
fig = plt.figure(figsize=(25, 4))
for idx in np.arange(batch_size):
ax = fig.add_subplot(2, batch_size / 2, idx + 1, xticks=[], yticks=[])
ax.imshow(np.squeeze(images[idx]), cmap='gray')
ax.set_title(classes[labels[idx]])
#选择一张图片输出它归一化后的灰度图
idx = 2
img = np.squeeze(images[idx])
# display the pixel values in that image
fig = plt.figure(figsize = (12,12))
ax = fig.add_subplot(111)
ax.imshow(img, cmap='gray')
width, height = img.shape
thresh = img.max()/2.5
for x in range(width):
for y in range(height):
val = round(img[x][y],2) if img[x][y] !=0 else 0
ax.annotate(str(val), xy=(y,x),
horizontalalignment='center',
verticalalignment='center',
color='white' if img[x][y]<thresh else 'black')
import argparse # 使得我们能够手动输入命令行参数,
import torch # 以下这几行导入相关的pytorch包
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms#用于数据集和测试集的加载与转换
from torch.autograd import Variable
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
# batch_size参数,如果想改,如改成128可这么写:python main.py -batch_size=128
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=10, metavar='N',
help='number of epochs to train (default: 10)')
parser.add_argument('--lr', type=float, default=0.01, metavar='LR',
help='learning rate (default: 0.01)')
parser.add_argument('--momentum', type=float, default=0.5, metavar='M',
help='SGD momentum (default: 0.5)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')# GPU参数,默认为False
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')# 跑多少次batch进行一次日志记录
parser.add_argument('--save-model', action='store_true', default=False,
help='For Saving the current Model')
#使用argparse模块时的必备行,将参数进行关联,
args = parser.parse_args()
use_cuda = not args.no_cuda and torch.cuda.is_available()# 确认是否使用gpu的参数
torch.manual_seed(args.seed)# 设置一个随机数种子
#torch.device代表将torch.Tensor分配到的设备的对象。torch.device包含一个设备类型(‘cpu’或‘cuda’)和可选的设备序号
device = torch.device("cuda" if use_cuda else "cpu")
#训练集加载:
#kwargs是一个字典,**kwargs将字典中的值作为关键词参数传入函数中。
#num_workers:用多少个子进程加载数据。0表示数据将在主进程中加载(默认: 0)本例中为1。
# 这个值是什么意思呢,就是数据读入的速度到底有多快,你选的用来加载数据的子进程越多,那么显然数据读的就越快,这样的话消耗CPU的资源也就越多。
#实验时可进行尝试,既不要让花在加载数据上的时间太多,也不要占用太多电脑资源
kwargs = {'num_workers': 1, 'pin_memory': True} if use_cuda else {}
#torch.utils.data.DataLoader():数据加载器,结合了数据集和取样器,并且可以提供多个线程处理数据集。
#在训练模型时使用到此函数,用来把训练数据分成多个小组,此函数每次抛出一组数据。直至把所有的数据都抛出。
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('./mnist', train=True, download=True,
# **由于torchvision的datasets的输出是[0,1]的PILImage,所以我们先先归一化为[-1,1]的Tensor(张量)**
# 首先定义了一个变换transform:一个函数/转换,它接收PIL图像并返回转换后的版本,利用的是上面提到的transforms模块中的Compose( )
# 把多个变换组合在一起,可以看到这里面组合了ToTensor和Normalize这两个变换
transform=transforms.Compose([
#transforms.ToTensor() 将numpy的ndarray或PIL.Image读的图片转换成形状为(C,H,W)的Tensor格式,且/255归一化到[0,1.0]之间
transforms.ToTensor(),
#归一化Normalize()的参数数量由通道数决定,若为RGB三通道则Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
#参数中的两个元组分别为mean:各通道的均值+std:各通道的标准差
transforms.Normalize((0.1307,), (0.3081,))
])),
#shuffle=True将训练模型的数据集进行打乱,防止过拟合
#如:100条数据中前50条为A类剩余50条为B类,模型在很短的学习过程中就学会了50位分界点,且前半部分为A后半部分为B。则并没有学会真正的类别特征。
batch_size=args.batch_size, shuffle=True, **kwargs)
#定义卷积神经网络
class Net(nn.Module):# 定义网络时一般是继承的torch.nn.Module创建新的子类
def __init__(self):# 第二、三行都是python类继承的基本操作
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 20, 5, 1)# 添加第一个卷积层,调用了nn里面的Conv2d()
#Conv2d(in_channels, out_channels, kernel_size, stride=1,padding=0, dilation=1, groups=1,bias=True,)
self.conv2 = nn.Conv2d(20, 50, 5, 1)
self.fc1 = nn.Linear(4 * 4 * 50, 500)# 接着两个全连接层,将4 * 4 * 50种特征值的样本输出为500
self.fc2 = nn.Linear(500, 10)
def forward(self, x):#x为feature
x = F.relu(self.conv1(x))#激活函数
#max_pool2d(input, kernel_size, stride = None, padding = 0, dilation = 1,ceil_mode = False, return_indices = False)
x = F.max_pool2d(x, 2, 2)# 最大池化层,卷积核2*2,步长为2
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 2, 2)
#view函数相当于numpy的reshape,参数为行列,当行数=-1时,表示一个不确定的数,即reshape成几行不确定,但确定有4 * 4 * 50列
x = x.view(-1, 4 * 4 * 50)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return F.log_softmax(x, dim=1)
def train(args, model, device, train_loader, optimizer, epoch):# 定义每个epoch的训练细节
model.train()# 设置为trainning模式
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
data, target = Variable(data), Variable(target) # 计算前要把变量变成Variable形式,因为这样子才有梯度
optimizer.zero_grad() # 优化器梯度初始化为零
output = model(data)# 把数据输入网络并得到输出,即进行前向传播
loss = F.nll_loss(output, target)#计算损失函数,NLLLoss 函数输入 input 之前,需要对 input 进行 log_softmax 处理
loss.backward()# 反向传播梯度
optimizer.step()# 结束一次前传+反传之后,更新优化器参数
if batch_idx % args.log_interval == 0:# 准备打印相关信息,args.log_interval是最开头设置的好了的参数
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('./mnist', train=False, transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=args.test_batch_size, shuffle=True, **kwargs)
def test(args, model, device, test_loader):
model.eval() # 设置为test模式
test_loss = 0
correct = 0 # 初始化预测正确的数据个数为0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
data, target = Variable(data), Variable(target)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()# 对预测正确的数据个数进行累加
test_loss /= len(test_loader.dataset) # 因为把所有loss值进行过累加,所以最后要除以总得数据长度才得平均loss
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
#定义损失函数和优化器
#先把模型放到GPU上跑
model = Net().to(device)
#optim模块中的SGD梯度优化方式---随机梯度下降
optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum)
if __name__ == '__main__':
for epoch in range(1, args.epochs + 1):
train(args, model, device, train_loader, optimizer, epoch)
test(args, model, device, test_loader)
if (args.save_model):
torch.save(model.state_dict(), "mnist_cnn.pt")