简单的卷积神经网络+mnist

目录

  • 前言
  • 一、数据可视化
    • 1.引入库
    • 2.读入数据
    • 3.数据集可视化
  • 二、模型训练
    • 1.引入库
    • 2.设置各种参数
    • 3.训练集加载
    • 4.模型定义
    • 5.训练过程
    • 6.测试集加载
    • 7.测试过程
    • 8.模型训练与测试

前言

首先对mnist数据集进行可视化展示,然后在mnist数据集上构建简单的神经网络。仅使用两层卷积训练准确度就达到99%。

一、数据可视化

1.引入库

#数据集的可视化使用jupyter notebook
#导入库
import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np
import matplotlib.pyplot as plt

2.读入数据

#数据集的加载,该数据集从本地进行加载或者直接网站下载都可
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))

在这里插入图片描述

3.数据集可视化

# 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]])

简单的卷积神经网络+mnist_第1张图片

#选择一张图片输出它归一化后的灰度图
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')

简单的卷积神经网络+mnist_第2张图片

二、模型训练

1.引入库

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

2.设置各种参数

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")

3.训练集加载

#训练集加载:
#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)

4.模型定义

#定义卷积神经网络
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)

5.训练过程

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()))

6.测试集加载

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)

7.测试过程

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)))

8.模型训练与测试

#定义损失函数和优化器
#先把模型放到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")

在这里插入图片描述

你可能感兴趣的:(简单的卷积神经网络+mnist)