pytorch实现简单卷积神经网络(CNN)网络完成手写数字识别

版权声明:转载注明出处 https://blog.csdn.net/york1996/article/details/81809822

首先你需要安装torch,torchvision,然后使用torchvision来下载mnist数据集,如果下载数据集有什么问题,请查看PyTorch用最简单的多层感知机(深度神经网络)实现手写数字识别   和   使用torchvision下载外网数据集mnist没有进度的解决方案

本文假设你对CNN基本原理有一定的了解。

我首先建立了一个tools工具类,这样主模块的代码可以少一些了。我把它命名成tools.py:


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(均值)=.5,std(标准差standard deviation)=.5的分布
        ]
    )
    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)

然后是主模块,首先引入必要的模块:

import torch.nn as nn
import torch
import time
import tools

搭建CNN网络:

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的结构如下:

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

定义常量:

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

定义损失函数和优化器、加载数据、更改网络到gpu上运行(如果GPU可用的话):

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

整个模块的代码:

import torch.nn as nn
import torch
import time
import tools

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
print("start")
EPOCH=50#总的训练次数
BATCH_SIZE=20#批次的大小
LR=0.03#学习率#交叉熵损失函数不需要太大的学习率
DOWNLOAD_MNIST=False#运行代码的时候是否下载数据集


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

猜你喜欢

转载自blog.csdn.net/york1996/article/details/81809822