[pytorch]简单CNN网络在数据集MNIST上的实现

学习机器学习的小伙伴接触到第一个神经网络模型往往就是最简单的CNN网络,这个网络可能只有一些卷积层、池化层、激活函数和全连接层组成,但就是这样一个简单的网络其实其实在一些分类问题上已经能取得不错的效果。同样,接触到的第一个数据集也可能就是著名的手写数字数据集MNIST了,所以笔者就通过这篇文章分享一下在初探机器学习路上的方法。

数据集的下载与使用

这部分需要使用torchvision和dataloader来下载和加载数据集

import torch
import torchvision
from torch.utils.data import DataLoader
 
def get_trans():
    # 设置一个转换的集合,先把数据转换到tensor,再归一化为均值.5,标准差.5的正态分布
    trans = torchvision.transforms.Compose(
        [
            torchvision.transforms.ToTensor(),  # ToTensor方法把[0,255]变成[0,1]
            torchvision.transforms.Normalize( [0.5], [0.5] )
            # 变成mean(均值)=0,std(标准差standard deviation)=1的分布
        ]
    )
    return trans
 
DOWNLOAD_MNIST=False
train_data = torchvision.datasets.MNIST( root="./mnist",  # 设置数据集的根目录
    train=True,  # 是否是训练集
    transform=get_trans(),  # 对数据进行转换
    download=DOWNLOAD_MNIST
                                         )
test_data = torchvision.datasets.MNIST( root="./mnist", train=False,  # 测试集,所以false
    transform=get_trans(), download=DOWNLOAD_MNIST
                                        )
def get_trainLoader(BATCH_SIZE):
    # 第二个参数是数据分块之后每一个块的大小,第三个参数是是否大乱数据
    train_loader = DataLoader(train_data, batch_size=BATCH_SIZE, shuffle=True)
    return train_loader
 
def get_testLoader(BATCH_SIZE):
    test_loader = DataLoader(test_data, batch_size=BATCH_SIZE, shuffle=False)
    return test_loader
 
def get_cuda_available():
    available=torch.cuda.is_available()
    return available
 
def get_test_data_len():
    return len(test_data)

简单CNN网络的设计

超参设置

EPOCH=50#总的训练次数
BATCH_SIZE=20#批次的大小
LR=0.03#学习率#交叉熵损失函数不需要太大的学习率
DOWNLOAD_MNIST=False#运行代码的时候是否下载数据集

网络

所有网络都需要继承torch.nn.Module这个类,同时一个CNN的组成往往都是(conv->ReLU->pool)*M ->(FC->ReLU)*N->softmax。

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1=nn.Sequential(
            nn.Conv2d(          #(1,28,28)
                in_channels=1,
                out_channels=16,
                kernel_size=5,
                stride=1,
                padding=2   #padding=(kernelsize-stride)/2
            ),#(16,28,28)
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)#(16,14,14)
 
        )
        self.conv2=nn.Sequential(#(16,14,14)
            nn.Conv2d(16,32,5,1,2),#(32,14,14)
            nn.ReLU(),#(32,14,14)
            nn.MaxPool2d(2)#(32,7,7)
        )
        self.out=nn.Linear(32*7*7,10)
    #定义前向传播过程,过程名字不可更改,因为这是重写父类的方法
    def forward(self,x):
        x = self.conv1( x )
        x = self.conv2( x ) #(batch,32,7,7)
        x=x.view(x.size(0),-1) #(batch,32*7*7)
        output=self.out(x)
        return output

这个网络的结构如下:

CNN(
(conv1): Sequential(
(0): Conv2d(1, 16, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(conv2): Sequential(
(0): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(out): Linear(in_features=1568, out_features=10, bias=True)
)

训练与测试

模型加载

本文章笔者使用的优化器是Adam,损失函数是CrossEntropyLoss交叉熵函数。

cnn=CNN()
cuda_available=tools.get_cuda_available()
if cuda_available==True:
    cnn.cuda()
    
optimizer=torch.optim.Adam(cnn.parameters(),lr=LR)
loss_function=nn.CrossEntropyLoss()
 
train_loader=tools.get_trainLoader(BATCH_SIZE)
test_loader=tools.get_testLoader(BATCH_SIZE*10)

训练过程

#训练过程
for ep in range(EPOCH):
    # 记录把所有数据集训练+测试一遍需要多长时间
    startTick = time.clock()
    for data in tqdm(train_loader, desc='epoch: {}\n'.format(epoch), leave=False, total=len(train_loader)):  # 对于训练集的每一个batch
        img, label = data
        if cuda_available:
            img = img.cuda()
            label = label.cuda()
 
        out = cnn( img )  # 送进网络进行输出
        loss = loss_function( out, label )  # 获得损失
 
        optimizer.zero_grad()  # 梯度归零
        loss.backward()  # 反向传播获得梯度,但是参数还没有更新
        optimizer.step()  # 更新梯度
 
    num_correct = 0  # 正确分类的个数,在测试集中测试准确率
    for data in tqdm(test_loader, desc='epoch: {}\n'.format(epoch), leave=False, total=len(test_loader)):
        img, label = data
        if cuda_available:
            img = img.cuda()
            label = label.cuda()
 
        out = cnn( img )  # 获得输出
 
        _, prediction = torch.max( out, 1 )
        # torch.max()返回两个结果,
        # 第一个是最大值,第二个是对应的索引值;
        # 第二个参数 0 代表按列取最大值并返回对应的行索引值,1 代表按行取最大值并返回对应的列索引值。
        num_correct += (prediction == label).sum()  # 找出预测和真实值相同的数量,也就是以预测正确的数量
 
    accuracy = num_correct.cpu().numpy() / tools.get_test_data_len()  # 计算正确率,num_correct是gpu上的变量,先转换成cpu变量
    timeSpan = time.clock() - startTick
    #print( "第%d迭代期,准确率为%f,耗时%dS" % (ep + 1, accuracy, timeSpan) )
    print('epoch:{}/{}, accuracy: {}, time: {}'.format(ep, EPOCH, accuracy, timeSpan))

在训练过程中笔者使用了tqdm库来可视化训练的进度,CNN(
  (conv1): Sequential(
    (0): Conv2d(1, 16, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (1): ReLU()
    (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (conv2): Sequential(
    (0): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (1): ReLU()
    (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (out): Linear(in_features=1568, out_features=10, bias=True)
)
————————————————
版权声明:本文为CSDN博主「York1996」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/york1996/article/details/81809822具体使用的方法详见tqdm介绍

最后

在实际操作的过程中,为了方便调试,减少主代码块的大小,往往将数据集加载,模型和训练测试的代码分开放在utils,model和train不同的脚本里面。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页