CNN 手写数字识别

  • 时间:
  • 来源:互联网
  • 文章标签:

构建CNN

  • CNN的输入和层与传统神经网络不同,需重新设计,训练模块基本一致
import torch 
import torch.nn as nn
from torch.optim import Adam
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
import numpy as np 
%matplotlib inline

读取数据

  • 分别构建测试集和训练集(验证集)
  • dataloader迭代取数据
# 定义超参数
input_size = 28 #  图像的尺寸28*28*1
num_classes = 10  # minist 标签种类10个
num_epoches = 3  # 训练总的循环次数
batch_size = 64  # 一个批次大小,64张图片

# 训练集 
train_dataset = datasets.MNIST(
    './data_cnn', 
    train = True,
    transform = transforms.ToTensor(),
    download = True,
)

# 测试集
test_dataset = datasets.MNIST(
    './data_cnn',
    train = False,
    transform = transforms.ToTensor(),
    download = True
)
# 构建dataloader 
train_dataloader = DataLoader(dataset = train_dataset, batch_size = batch_size, shuffle = True)
test_dataloader = DataLoader(dataset = test_dataset, batch_size = batch_size, shuffle = True)

CNN 模块构建

  • 一般卷积层,relu层,池化作为一个整体
  • 注意卷积最后的结果还是一个特征图,需要把图转换为向量才能继续进行分类或回归任务
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(   # 输入大小 (1,28,28)
            nn.Conv2d(
                in_channels = 1,   # 灰度图,通道为1;RGB 通道为3
                out_channels = 16,  # 得到多少个特征图,即卷积核fliter 个数
                kernel_size = 5,  # 卷积核大小 5*5 
                stride = 1,  # 滑动步长
                padding = 2,  # 0 填充大小, 如果希望卷积后大小和原来相同,需要设置padding = (kernels_size - 1 )/2 if stride = 1
            ),                 # 输出特征图大小为(16,28,28)  ((28-5+2*2)/1 + 1 = 28)
            nn.ReLU(),  # relu
            nn.MaxPool2d(kernel_size = 2),  # 池化(2*2区域), 输出形状为16,14,14
        )
        self.conv2 = nn.Sequential(    # 下一层 输入形状 16*14*14
            nn.Conv2d(16, 32, 5, 1, 2),  # 输出(32, 14, 14)
    #         nn.Conv2d (
    #             in_channels = 16,
    #             out_channels = 32, 
    #             kernel_size = 5,
    #             stride = 1,
    #             padding = 2,
    #         ),     # 输出 (32, 14, 14 )   ((14-5+2*2)/1 + 1 = 14)
            nn.ReLU(),
            nn.MaxPool2d(2),   # 输出(32,7,7   )   
        )
        self.out = nn.Linear(32*7*7, 10)  # 全连接层,图转化为向量得到32*32*7的向量作为输入,输出特征为10(Mnist 10个类别)
    
    
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        # print('x.size()',x.size())  # torch.Size([64, 32, 7, 7])
        x = x.view(x.size(0), -1)  # flatten操作,结果为(batch_size, 32*7*7)
        output = self.out(x)
        return output

计算准确率

def accuracy(predictions, labels):
    pred = torch.max(predictions.data, 1)[1]  
    rights = pred.eq(labels.data.view_as(pred)).sum()
    return rights, len(labels)

训练

# 实例化
net = CNN()
# 损失函数 - 多分类问题,交叉熵损失
criterion = nn.CrossEntropyLoss()
# 优化器
optimizer = Adam(net.parameters(), lr = 0.001)


# 开始训练
for epoch in range(num_epoches):
    # 保存当前epoch 结果,针对容器中每一个批进行遍历
    train_rights = []
    for batch_idx, (data, target) in enumerate(train_dataloader):
        net.train()
        # 前向传播
        output = net(data)
        # 梯度置0
        optimizer.zero_grad()
        # 计算损失
        loss = criterion(output, target)
        # 反向传播
        loss.backward()
        # 参数更新
        optimizer.step()
        # 每epoch准确率
        right = accuracy(output, target)
        train_rights.append(right)
        
        
        if batch_idx % 100 == 0:
            net.eval()
            val_rights = []
            
            
            for (data, target) in test_dataloader:
                output = net(data)
                right = accuracy(output, target)
                val_rights.append(right)
            # 准确率计算
            train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))
            val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))
            print('当前epoch: {} [{}/{} ({:.0f}%)]\t损失: {:.6f}\t训练集准确率: {:.2f}%\t测试集正确率: {:.2f}%'.format(
                epoch, batch_idx * batch_size, len(train_dataloader.dataset),
                100. * batch_idx / len(train_dataloader), 
                loss.data, 
                100. * train_r[0].numpy() / train_r[1], 
                100. * val_r[0].numpy() / val_r[1]))
当前epoch: 0 [0/60000 (0%)]	损失: 2.301583	训练集准确率: 12.50%	测试集正确率: 16.58%
当前epoch: 0 [6400/60000 (11%)]	损失: 0.424204	训练集准确率: 73.50%	测试集正确率: 90.34%
当前epoch: 0 [12800/60000 (21%)]	损失: 0.215105	训练集准确率: 82.87%	测试集正确率: 94.93%
当前epoch: 0 [19200/60000 (32%)]	损失: 0.078426	训练集准确率: 86.98%	测试集正确率: 96.12%
当前epoch: 0 [25600/60000 (43%)]	损失: 0.090398	训练集准确率: 89.28%	测试集正确率: 96.33%
当前epoch: 0 [32000/60000 (53%)]	损失: 0.055314	训练集准确率: 90.73%	测试集正确率: 97.49%
当前epoch: 0 [38400/60000 (64%)]	损失: 0.077539	训练集准确率: 91.78%	测试集正确率: 97.70%
当前epoch: 0 [44800/60000 (75%)]	损失: 0.062360	训练集准确率: 92.60%	测试集正确率: 97.83%
当前epoch: 0 [51200/60000 (85%)]	损失: 0.062269	训练集准确率: 93.22%	测试集正确率: 97.67%
当前epoch: 0 [57600/60000 (96%)]	损失: 0.016506	训练集准确率: 93.70%	测试集正确率: 97.89%
当前epoch: 1 [0/60000 (0%)]	损失: 0.040640	训练集准确率: 100.00%	测试集正确率: 98.06%
当前epoch: 1 [6400/60000 (11%)]	损失: 0.180822	训练集准确率: 98.14%	测试集正确率: 98.11%
当前epoch: 1 [12800/60000 (21%)]	损失: 0.042927	训练集准确率: 98.17%	测试集正确率: 98.25%
当前epoch: 1 [19200/60000 (32%)]	损失: 0.008258	训练集准确率: 98.18%	测试集正确率: 98.26%
当前epoch: 1 [25600/60000 (43%)]	损失: 0.062447	训练集准确率: 98.16%	测试集正确率: 98.45%
当前epoch: 1 [32000/60000 (53%)]	损失: 0.059591	训练集准确率: 98.23%	测试集正确率: 98.36%
当前epoch: 1 [38400/60000 (64%)]	损失: 0.102044	训练集准确率: 98.28%	测试集正确率: 98.37%
当前epoch: 1 [44800/60000 (75%)]	损失: 0.032021	训练集准确率: 98.26%	测试集正确率: 98.68%
当前epoch: 1 [51200/60000 (85%)]	损失: 0.079563	训练集准确率: 98.25%	测试集正确率: 98.58%
当前epoch: 1 [57600/60000 (96%)]	损失: 0.117565	训练集准确率: 98.24%	测试集正确率: 98.67%
当前epoch: 2 [0/60000 (0%)]	损失: 0.012694	训练集准确率: 100.00%	测试集正确率: 98.68%
当前epoch: 2 [6400/60000 (11%)]	损失: 0.068502	训练集准确率: 98.44%	测试集正确率: 98.54%
当前epoch: 2 [12800/60000 (21%)]	损失: 0.020304	训练集准确率: 98.56%	测试集正确率: 98.79%
当前epoch: 2 [19200/60000 (32%)]	损失: 0.006254	训练集准确率: 98.63%	测试集正确率: 98.51%
当前epoch: 2 [25600/60000 (43%)]	损失: 0.044162	训练集准确率: 98.62%	测试集正确率: 98.53%
当前epoch: 2 [32000/60000 (53%)]	损失: 0.007522	训练集准确率: 98.65%	测试集正确率: 98.88%
当前epoch: 2 [38400/60000 (64%)]	损失: 0.039185	训练集准确率: 98.66%	测试集正确率: 98.80%
当前epoch: 2 [44800/60000 (75%)]	损失: 0.053606	训练集准确率: 98.69%	测试集正确率: 98.71%
当前epoch: 2 [51200/60000 (85%)]	损失: 0.013480	训练集准确率: 98.68%	测试集正确率: 98.83%
当前epoch: 2 [57600/60000 (96%)]	损失: 0.052318	训练集准确率: 98.69%	测试集正确率: 98.96%

GPU训练

  • 对相关变量进行to device
  • train_r[0].data.cpu().numpy() cuda的tensor 需要进行cpu转换才可读取
# gpu
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

# 实例化
net = CNN().to(device)
# 损失函数 - 多分类问题,交叉熵损失
criterion = nn.CrossEntropyLoss().to(device)
# 优化器
optimizer = Adam(net.parameters(), lr = 0.001)


# 开始训练
for epoch in range(num_epoches):
    # 保存当前epoch 结果,针对容器中每一个批进行遍历
    train_rights = []
    for batch_idx, (data, target) in enumerate(train_dataloader):
        data = data.to(device)
        target = target.to(device)
        net.train()
        # 前向传播
        output = net(data).to(device)
        # 梯度置0
        optimizer.zero_grad()
        # 计算损失
        loss = criterion(output, target)
        # 反向传播
        loss.backward()
        # 参数更新
        optimizer.step()
        # 每epoch准确率
        right = accuracy(output, target)
        train_rights.append(right)
        
        
        if batch_idx % 100 == 0:
            net.eval()
            val_rights = []
            
            
            for (data, target) in test_dataloader:
                data = data.to(device)
                target = target.to(device)
                output = net(data).to(device)
                right = accuracy(output, target)
                val_rights.append(right)
            # 准确率计算
            train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))
            val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))
            print('当前epoch: {} [{}/{} ({:.0f}%)]\t损失: {:.6f}\t训练集准确率: {:.2f}%\t测试集正确率: {:.2f}%'.format(
                epoch, batch_idx * batch_size, len(train_dataloader.dataset),
                100. * batch_idx / len(train_dataloader), 
                loss.data, 
                100. * train_r[0].data.cpu().numpy() / train_r[1], 
                100. * val_r[0].data.cpu().numpy() / val_r[1]))
当前epoch: 0 [0/60000 (0%)]	损失: 2.315115	训练集准确率: 17.19%	测试集正确率: 17.75%
当前epoch: 0 [6400/60000 (11%)]	损失: 0.277280	训练集准确率: 76.27%	测试集正确率: 90.14%
当前epoch: 0 [12800/60000 (21%)]	损失: 0.281072	训练集准确率: 84.65%	测试集正确率: 94.77%
当前epoch: 0 [19200/60000 (32%)]	损失: 0.149770	训练集准确率: 88.12%	测试集正确率: 95.89%
当前epoch: 0 [25600/60000 (43%)]	损失: 0.103880	训练集准确率: 90.07%	测试集正确率: 96.83%
当前epoch: 0 [32000/60000 (53%)]	损失: 0.055352	训练集准确率: 91.38%	测试集正确率: 97.62%
当前epoch: 0 [38400/60000 (64%)]	损失: 0.127742	训练集准确率: 92.34%	测试集正确率: 97.67%
当前epoch: 0 [44800/60000 (75%)]	损失: 0.414217	训练集准确率: 93.06%	测试集正确率: 97.75%
当前epoch: 0 [51200/60000 (85%)]	损失: 0.025299	训练集准确率: 93.62%	测试集正确率: 97.24%
当前epoch: 0 [57600/60000 (96%)]	损失: 0.042849	训练集准确率: 94.06%	测试集正确率: 98.24%
当前epoch: 1 [0/60000 (0%)]	损失: 0.016275	训练集准确率: 100.00%	测试集正确率: 97.99%
当前epoch: 1 [6400/60000 (11%)]	损失: 0.100614	训练集准确率: 98.04%	测试集正确率: 98.06%
当前epoch: 1 [12800/60000 (21%)]	损失: 0.120712	训练集准确率: 97.96%	测试集正确率: 98.28%
当前epoch: 1 [19200/60000 (32%)]	损失: 0.094157	训练集准确率: 97.91%	测试集正确率: 98.29%
当前epoch: 1 [25600/60000 (43%)]	损失: 0.123230	训练集准确率: 98.00%	测试集正确率: 98.48%
当前epoch: 1 [32000/60000 (53%)]	损失: 0.026183	训练集准确率: 98.02%	测试集正确率: 98.44%
当前epoch: 1 [38400/60000 (64%)]	损失: 0.127735	训练集准确率: 98.05%	测试集正确率: 98.52%
当前epoch: 1 [44800/60000 (75%)]	损失: 0.035834	训练集准确率: 98.06%	测试集正确率: 98.52%
当前epoch: 1 [51200/60000 (85%)]	损失: 0.008565	训练集准确率: 98.11%	测试集正确率: 98.67%
当前epoch: 1 [57600/60000 (96%)]	损失: 0.106901	训练集准确率: 98.15%	测试集正确率: 98.58%
当前epoch: 2 [0/60000 (0%)]	损失: 0.028257	训练集准确率: 98.44%	测试集正确率: 98.75%
当前epoch: 2 [6400/60000 (11%)]	损失: 0.075383	训练集准确率: 98.64%	测试集正确率: 98.59%
当前epoch: 2 [12800/60000 (21%)]	损失: 0.043502	训练集准确率: 98.66%	测试集正确率: 98.73%
当前epoch: 2 [19200/60000 (32%)]	损失: 0.014652	训练集准确率: 98.66%	测试集正确率: 98.83%
当前epoch: 2 [25600/60000 (43%)]	损失: 0.006605	训练集准确率: 98.58%	测试集正确率: 98.29%
当前epoch: 2 [32000/60000 (53%)]	损失: 0.170396	训练集准确率: 98.56%	测试集正确率: 98.72%
当前epoch: 2 [38400/60000 (64%)]	损失: 0.062538	训练集准确率: 98.53%	测试集正确率: 98.70%
当前epoch: 2 [44800/60000 (75%)]	损失: 0.068417	训练集准确率: 98.56%	测试集正确率: 98.77%
当前epoch: 2 [51200/60000 (85%)]	损失: 0.013739	训练集准确率: 98.59%	测试集正确率: 98.91%
当前epoch: 2 [57600/60000 (96%)]	损失: 0.043758	训练集准确率: 98.60%	测试集正确率: 98.99%

tqdm

  • iterable=None,
  • desc=None, 传入str类型,作为进度条标题(类似于说明)
  • total=None, 预期的迭代次数
  • set_decription 进度条中显示的变量等信息
from tqdm import tqdm
# gpu
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

# 实例化
net = CNN().to(device)
# 损失函数 - 多分类问题,交叉熵损失
criterion = nn.CrossEntropyLoss().to(device)
# 优化器
optimizer = Adam(net.parameters(), lr = 0.001)


# 开始训练
for epoch in range(num_epoches):
    # 保存当前epoch 结果,针对容器中每一个批进行遍历
    train_rights = []
    bar = tqdm(enumerate(train_dataloader),desc = 'train',total = len(train_dataloader))
    for batch_idx, (data, target) in bar:
        data = data.to(device)
        target = target.to(device)
        net.train()
        # 前向传播
        output = net(data).to(device)
        # 梯度置0
        optimizer.zero_grad()
        # 计算损失
        loss = criterion(output, target)
        # 反向传播
        loss.backward()
        # 参数更新
        optimizer.step()
        # 每epoch准确率
        right = accuracy(output, target)
        train_rights.append(right)
        
        
        if batch_idx % 100 == 0:
            net.eval()
            val_rights = []
            
            
            for (data, target) in test_dataloader:
                data = data.to(device)
                target = target.to(device)
                output = net(data).to(device)
                right = accuracy(output, target)
                val_rights.append(right)
            # 准确率计算
            train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))
            val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))
            bar.set_description('当前epoch: {} [{}/{} ({:.0f}%)]\t损失: {:.6f}\t训练集准确率: {:.2f}%\t测试集正确率: {:.2f}%'.format(
                epoch, batch_idx * batch_size, len(train_dataloader.dataset),
                100. * batch_idx / len(train_dataloader), 
                loss.data, 
                100. * train_r[0].data.cpu().numpy() / train_r[1], 
                100. * val_r[0].data.cpu().numpy() / val_r[1]))
当前epoch: 0 [57600/60000 (96%)]	损失: 0.336853	训练集准确率: 94.21%	测试集正确率: 97.99%: 100%|███████████████████████████████| 938/938 [00:28<00:00, 33.25it/s][00:00<?, ?it/s]
当前epoch: 1 [57600/60000 (96%)]	损失: 0.177681	训练集准确率: 98.21%	测试集正确率: 98.78%: 100%|███████████████████████████████| 938/938 [00:29<00:00, 32.11it/s][00:00<?, ?it/s]
当前epoch: 2 [57600/60000 (96%)]	损失: 0.018598	训练集准确率: 98.69%	测试集正确率: 98.95%: 100%|███████████████████████████████| 938/938 [00:28<00:00, 32.35it/s][00:00<?, ?it/s]

本文链接http://www.taodudu.cc/news/show-1782092.html