卷积神经网络(手写数字识别)

卷积神经网络(手写数字识别)

何为卷积

首先在理解CNN前,需要先理解什么是卷积
卷积是一种数学的运算,以一个图片为例,它具有3个颜色通道,假如他的尺寸是4x4,那么在数学上我们可以将他表示为一个size = 3x4x4的张量,而我们规定卷积运算是输入张量与卷积核之间的运算,例如我们设置一个3x3x3的卷积核,并且约定步长为1

那么卷积运算可以这么理解:

在这里插入图片描述

对卷积核的每个通道,它将会分别和输入张量的对应通道做卷积,然后将所得的矩阵求和,对于单个通道与单个卷积核对应通道,具体的运算方法如图

在这里插入图片描述

值得注意的是,卷积核的通道数必须和输入张量的通道数保持一致

但是卷积操作其实是可以改变输入张量的通道数的,我们可以人为的设计多个卷积核,一个卷积核经过卷积将会得到输出张量的一个通道,n个卷积核将会得到n个通道

事实上卷积过程中我们还可以人为设置卷积的步长stride,当我们不去设置它时,他默认是1,即对一个区域卷积后,卷积核会向后移动一个单位长度,如果已经到了最右端,他将会向下移动.

如果我们设置了stride=2,那么卷积核将会两个单位长度的移动

在设计卷积层的过程中,我们常常会进行padding操作,要理解padding,首先我们要仔细观察一下卷积的过程,我们会发现处在边缘地带的区块,被卷积核提取信息的次数明显会少于中间的区块,事实上,边缘区域也很有可能包含了关键的信息,如果我们直接卷积,无形之中就让边缘区域变得不那么重要了

那么怎么解决这个问题呢,我们需要进行padding

我们可以在输入张量每一层的边缘补充0,将例如原来3x3的矩阵扩展为4x4,甚至5x5那么那些包含了信息的区域,就能够同等的被提取信息.

卷积神经网络的架构(简单的手写数字识别)

下面我就一个简单的卷积神经网络(AlexNet)谈谈我对架构的理解:

接下来先是一些基本的操作的概念.

如何分类

首先我们就MNIST数据集而言,输入的是一张张灰度图,然后输出的是属于哪个类别,这是一个明显的分类问题,对于分类问题我们通常有几个类别就输出一个几维向量,这个向量的每一个位置的元素表示该输入属于这一类别的概率,例如分类是不是猫,假如网络输出了(0.3,0.7)这样一个向量,如果我们约定零号位置是该输入是猫的概率,那么一号位置我们约定是不是猫的概率,这两个概率之和必须为一,更多类别时也是同理,我们一般取概率最大的位置对应的类别为分类结果.

什么是池化

池化(pooling)操作一般常用的分为两种,一种是max_pooling,一种是avg_pooling,即最大值池化,和平均池化,不过不管是哪种池化,我们首先需要定义单次池化的范围大小,一般我们都是对正方形区域进行池化,例如我们设置一个2x2的区域,如果使用最大值池化,那么这个区域里四个数值,我们只保留数值最大的,类似的平均值池化保留四个位置的平均值.进行池化后,矩阵变小,我们适当的舍弃了一些作用不大信息,这有助于我们减少训练成本,同时又尽可能保证了训练的结果,事实上,大多数时候max_pooling能取得比avg_pooling更好的结果.

具体的操作流程

因此首先我们要将输入的灰度图转换为可以处理的张量,然后利用卷积操作提取信息,之后我们对卷积后的结果进行一次池化,进一步精炼信息,然后我们进行非线性化,这便是一轮处理.同样的操作我们可以设置多轮,有助于网络更好的学习.最后我们需要设置一个全连接层,也就是一般的bp神经网络,输出一个十维的向量,放到分类器中,从而得到分类结果和相应的loss值,最后调用后向传播算法,经过多轮训练,使网络收敛.

具体的pytorch实现(对MNIST的手写数字识别)

第一步:加载MNIST数据集

import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

batch_size = 64 # 设置批量大小
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
# 根据MNIST的均值和标准差对数据集做一些处理
train_dataset = datasets.MNIST('../data', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST('../data', train=False, transform=transform, download=True)
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=True)

这里由于MNIST数据集在pytorch中已经内置了,所以我们直接使用对应的模块加载即可

第二步:定义网络的模块

class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5)
        self.pooling = torch.nn.MaxPool2d(kernel_size=2)
        self.fc1 = torch.nn.Linear(320, 50)  # 全连接层
        self.fc2 = torch.nn.Linear(50, 10)
        self.ReLU = torch.nn.ReLU()

    def forward(self, x):
        batch_size = x.size(0)
        x = self.ReLU(self.pooling(self.conv1(x)))  # 先卷积,再池化,再非线性化
        x = self.ReLU(self.pooling(self.conv2(x)))
        x = x.view(batch_size, -1)  # 展成一维向量,便于后续全连接层处理
        x = self.ReLU(self.fc1(x))
        x = self.fc2(x)  # 使用crossEntropyLoss时最后一层不能激活
        return x

值得注意的是,由于我们做的是多分类任务,最后一层不能激活,因为后续的分类器crossEntropyLoss已经自带了激活功能.

第三步:实现网络,分类器,优化器

net = Net()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")###
net.to(device)###
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(net.parameters(), lr=0.01, momentum=0.5)

没有GPU的,可以把带###号的GPU的操作去掉

第四步:训练模块

def train(epoch):
    running_loss = 0.0
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)  ### 计算的张量迁移到显卡
        y_pred = net(data)
        loss = criterion(y_pred, target)
        optimizer.zero_grad()
        loss.backward() # 反向传播
        optimizer.step()
        running_loss += loss.item() # 采用了MINI_BATCH 损失要累加起来
        if batch_idx % 300 == 299:
            print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
            running_loss = 0.0
            # 每三百次计算一次平均损失

这里其实就是一般的训练流程了,输入,前向传播,计算损失,反向传播,修改参数

第五步:测试模块

def Test():
    correct = 0
    total = 0
    with torch.no_grad():  # 测试集无需计算梯度,只是做测试,不对网络进行优化
        for data, target in test_loader:
            data, target = data.to(device), target.to(device) ###
            output = net(data)
            _, predicted = torch.max(output.data, 1) # 找到预测值最大的类别
            total += target.size(0)
            correct += (predicted == target).sum().item()  # 统计预测正确的次数
    print('Accuracy of the network on the test images: %d %% [%d/%d]' % (100 * correct / total, correct, total))

这里是在测试集上进行测试,统计正确率

第六步:开始训练,测试

if __name__ == '__main__':
    for epoch in range(0, 10):
        train(epoch)
        Test()

在本地训练结果如下:

[1,   300] loss: 1.036
[1,   600] loss: 0.221
[1,   900] loss: 0.154
Accuracy of the network on the test images: 96 % [9635/10000]
[2,   300] loss: 0.114
[2,   600] loss: 0.102
[2,   900] loss: 0.091
Accuracy of the network on the test images: 97 % [9728/10000]
[3,   300] loss: 0.076
[3,   600] loss: 0.072
[3,   900] loss: 0.073
Accuracy of the network on the test images: 98 % [9825/10000]
[4,   300] loss: 0.062
[4,   600] loss: 0.063
[4,   900] loss: 0.057
Accuracy of the network on the test images: 98 % [9842/10000]
[5,   300] loss: 0.052
[5,   600] loss: 0.051
[5,   900] loss: 0.049
Accuracy of the network on the test images: 98 % [9862/10000]
[6,   300] loss: 0.046
[6,   600] loss: 0.045
[6,   900] loss: 0.044
Accuracy of the network on the test images: 98 % [9874/10000]
[7,   300] loss: 0.045
[7,   600] loss: 0.039
[7,   900] loss: 0.039
Accuracy of the network on the test images: 98 % [9874/10000]
[8,   300] loss: 0.037
[8,   600] loss: 0.036
[8,   900] loss: 0.038
Accuracy of the network on the test images: 98 % [9875/10000]
[9,   300] loss: 0.036
[9,   600] loss: 0.033
[9,   900] loss: 0.033
Accuracy of the network on the test images: 98 % [9863/10000]
[10,   300] loss: 0.031
[10,   600] loss: 0.030
[10,   900] loss: 0.032
Accuracy of the network on the test images: 98 % [9893/10000]

在MNIST数据集上取得了98%的正确率.

总结

自此我们成功利用简单的卷积神经网络对实现了手写数字识别,事实上这只是最简单的卷积神经网络,后续还有 GoogleNet , ResNet等网络.以后我将会逐个复现.

本人只是一个深度学习的初学者,感谢您愿意阅读我的博文,如果文章有错误,或者有可以改进的地方,欢迎批评指正.再次感谢!
8 % [9893/10000]


在MNIST数据集上取得了98%的正确率.

## 总结

自此我们成功利用简单的卷积神经网络对实现了手写数字识别,事实上这只是最简单的卷积神经网络,后续还有 GoogleNet , ResNet等网络.以后我将会逐个复现.  

本人只是一个深度学习的初学者,感谢您愿意阅读我的博文,如果文章有错误,或者有可以改进的地方,欢迎批评指正.再次感谢!
  • 30
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值