(小白笔记,有问题请指正)
手写数字识别可以说是学习深度学习的第一个代码了,相当于我们学习其他语言时的Hello World。在这里,我们要利用卷积来构建神经网络去完成手写数字识别案例。
卷积CNN
在我们使用卷积神经网络去完成手写数字识别案例时,我们要首先了解什么是卷积(CNN)。
传统神经网络层之间都采用全连接方式,但这种方式,如果输入的层数较多,输入又是高纬度的话,那么其参数将会很多很多以至于难以计算。例如训练一张1000×1000像素的灰色图片,如果隐层节点是100,那么参数就是1000×1000×100。因此机器学习在一段时间内一直处于停滞期,直到卷积的出现。
卷积神经网络由一个或多个卷积层和顶端的全连通层组成,同时也包括关联权重和池化层。卷积层就是卷积神经网络的核心层,而卷积又是卷积层的核心。简单的说,卷积就是两个函数的一种运算。
例如:输入一个2×2的矩阵[[1,2],[3,4]],卷积核设为2,那么输出为[[2,4],[6,8]]。那么我们再把输入和卷积核维度提高,输入变为5×5,卷积核变为3×3,那么这个卷积核将会在输入的矩阵上遍历,并提取特征,那么这些不同的特征可作为输出的不同通道,因此可扩展更高维度。
那如果我们处理的数据是多通道呢?其实我们输入的矩阵在图形角度是灰色的,没有考虑彩色图片的情况,但我们实际应用时输入的数据往往是多通道的,彩色图片就有三个通道(R,G,B),而多通道的计算和单通道基本一样的。无非是将每个单通道与卷积核进行卷积运算,并把3通道的和相加,得到输出图片的一个像素值。
池化
通过卷积层获得图像的特征后,理论上可以直接使用这些特征训练分类器,但是,这样计算量过大,并且容易出现过拟合。因此,为了进一步降低网络训练参数及模型的过拟合程度,就要对卷积层进行池化处理。
池化层在卷积神经网络中可以用来减小尺寸,提高运算速度及减小噪声影响,让各特征更具有健壮性。
手写数字识别
import numpy as np
import torch
import torchvision
import torch.utils.data as Data
from torch import nn
from torch.autograd import Variable
import matplotlib.pyplot as plt
#定义超参数
EPOCH = 1 #因为用的是cpu 所以这里只训练1次
BATCH_SIZE = 50#定义每50个数据为1块
LR = 0.001# 学习率
DOWNLOAD_MNIST = False#看是否有数据
2.获取数据(从MNIST数据集中下载数据)
train_data = torchvision.datasets.MNIST(
root='./minst',#根目录,如果没有数据则创建目录并下载,如果有则直接获取
train=True,#是训练数据
transform=torchvision.transforms.ToTensor(),#把下载的数据改成Tensor的格式 数据的值都会被压缩到(0,1)
download=DOWNLOAD_MNIST #如果以及下载好数据,设置为False 没有则为True
)
train_loader = Data.DataLoader(dataset=train_data,batch_size=BATCH_SIZE,shuffle=True,num_workers=0)
test_data = torchvision.datasets.MNIST(root='./minst',train=False)
#提取test_data中的test_data,用unsqueeze加上维度,用Variable包裹一下
# 这里的归一化用的是除以255,因为RGB大小是在0到255之间的,我们把他压缩到0到1
test_x = Variable(torch.unsqueeze(test_data.test_data,dim=1),volatile=True).type(torch.FloatTensor)[:2000]/255.
test_y = test_data.test_labels[:2000]
3.定义神经网络,选取优化器和损失函数。
其中经历了两次卷积,两次池化和一次全连接
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,#在一个位置提取16个特征,是16个卷积核,分别对图片进行卷积
kernel_size=5,#过滤器是5*5的,在扫描图片时一次扫描5×5的区域
stride=1,#是扫描时每隔几个跳一下 比如现在扫描的是12345 下次就是23456
padding=2,#在原始的图片周围围上一圈0,因为过滤器扫描到边上时可能会多出来一部分
),#卷积像是一个过滤器,他有长宽高,长和宽决定了在图片上一次能搜集多少信息
#高度决定了在这一地方提取的特征值
#在卷积后维度变成了(16,28,28)
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),#在进行一次卷积后会生成一张更厚(很多层)的图片,但此时长和宽也没有改变,
# 因此要用池化筛选,相当与1个2×2的过滤器对他进行裁剪,
#在池化后维度变成了(16,14,14)
)
self.conv2 = nn.Sequential(#此时的输入维度是(16,14,14)
nn.Conv2d(16,32,5,1,2),#此时进行卷积变为了(32,14,14)
nn.ReLU(),
nn.MaxPool2d(2)#再次池化变为了(32,7,7)
)
self.out = nn.Linear(32*7*7,10)#因为图片要展开。因为图片是0-9有十个分类 所以输出10个分类
def forward(self,x):
x = self.conv1(x)
x = self.conv2(x) #此时考虑了batch 为(batch,32,7,7)
x = x.view(x.size(0),-1) #保留batch维度(batch,32*7*7) -1的作用就是把这些维度变一起
output = self.out(x)
return output
cnn = CNN()
# print(cnn)
optimizer = torch.optim.Adam(cnn.parameters(),lr=LR)#使用Adam过滤器
loss_func = nn.CrossEntropyLoss()
4.开始训练测试。
for epoch in range(EPOCH):
train_loss = 0
for step,(x,y) in enumerate(train_loader):
b_x = Variable(x)
b_y = Variable(y)#放入Variable中 可省略
output = cnn(b_x)#向前传播
loss = loss_func(output,b_y)#得到损失值
optimizer.zero_grad()#设梯度为0
loss.backward()#反向传播
optimizer.step()#优化
# train_loss += loss.item()
if step%50 == 0:
test_output = cnn(test_x)
pred_y = torch.max(test_output,1)[1].data.squeeze()
accuracy = float(sum(pred_y == test_y)) / float(test_y.size(0))
# accuracy = float((pred_y == test_y.data.numpy()).astype(int).sum())/float(test_y.size(0))
print('Epoch:',epoch,'| train loss :{:.4f}'.format(loss.item()),'| test accuracy:%.2f' % accuracy)
#loss.item()
test_output = cnn(test_x[:10])
pred_y = torch.max(test_output,1)[1].data.numpy().squeeze()
print(pred_y,'prediction number')
print(test_y[:10].numpy(),'real number')
5.运行结果
import numpy as np
import torch
import torchvision
import torch.utils.data as Data
from torch import nn
from torch.autograd import Variable
import matplotlib.pyplot as plt
#定义超参数
EPOCH = 1 #因为用的是cpu 所以这里只训练1次
BATCH_SIZE = 50#定义每50个数据为1块
LR = 0.001# 学习率
DOWNLOAD_MNIST = False#看是否有数据
train_data = torchvision.datasets.MNIST(
root='./minst',#根目录,如果没有数据则创建目录并下载,如果有则直接获取
train=True,#是训练数据
transform=torchvision.transforms.ToTensor(),#把下载的数据改成Tensor的格式 数据的值都会被压缩到(0,1)
download=DOWNLOAD_MNIST #如果以及下载好数据,设置为False 没有则为True
)
# print(train_data.train_data.size())
# print(train_data.train_labels.size())
# plt.imshow(train_data.train_data[0].numpy(),cmap='gray')#显示train_data的第一张图片
# plt.title('%i'% train_data.train_labels[0])
# plt.show()
train_loader = Data.DataLoader(dataset=train_data,batch_size=BATCH_SIZE,shuffle=True,num_workers=0)
test_data = torchvision.datasets.MNIST(root='./minst',train=False)
#提取test_data中的test_data,用unsqueeze加上维度,用Variable包裹一下
# 这里的归一化用的是除以255,因为RGB大小是在0到255之间的,我们把他压缩到0到1
test_x = Variable(torch.unsqueeze(test_data.test_data,dim=1),volatile=True).type(torch.FloatTensor)[:2000]/255.
test_y = test_data.test_labels[:2000]
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,#在一个位置提取16个特征,是16个卷积核,分别对图片进行卷积
kernel_size=5,#过滤器是5*5的,在扫描图片时一次扫描5×5的区域
stride=1,#是扫描时每隔几个跳一下 比如现在扫描的是12345 下次就是23456
padding=2,#在原始的图片周围围上一圈0,因为过滤器扫描到边上时可能会多出来一部分
),#卷积像是一个过滤器,他有长宽高,长和宽决定了在图片上一次能搜集多少信息
#高度决定了在这一地方提取的特征值
#在卷积后维度变成了(16,28,28)
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),#在进行一次卷积后会生成一张更厚(很多层)的图片,但此时长和宽也没有改变,
# 因此要用池化筛选,相当与1个2×2的过滤器对他进行裁剪,
#在池化后维度变成了(16,14,14)
)
self.conv2 = nn.Sequential(#此时的输入维度是(16,14,14)
nn.Conv2d(16,32,5,1,2),#此时进行卷积变为了(32,14,14)
nn.ReLU(),
nn.MaxPool2d(2)#再次池化变为了(32,7,7)
)
self.out = nn.Linear(32*7*7,10)#因为图片要展开。因为图片是0-9有十个分类 所以输出10个分类
def forward(self,x):
x = self.conv1(x)
x = self.conv2(x) #此时考虑了batch 为(batch,32,7,7)
x = x.view(x.size(0),-1) #保留batch维度(batch,32*7*7) -1的作用就是把这些维度变一起
output = self.out(x)
return output
cnn = CNN()
# print(cnn)
optimizer = torch.optim.Adam(cnn.parameters(),lr=LR)#使用Adam过滤器
loss_func = nn.CrossEntropyLoss()
for epoch in range(EPOCH):
train_loss = 0
for step,(x,y) in enumerate(train_loader):
b_x = Variable(x)
b_y = Variable(y)#放入Variable中 可省略
output = cnn(b_x)#向前传播
loss = loss_func(output,b_y)#得到损失值
optimizer.zero_grad()#设梯度为0
loss.backward()#反向传播
optimizer.step()#优化
# train_loss += loss.item()
if step%50 == 0:
test_output = cnn(test_x)
pred_y = torch.max(test_output,1)[1].data.squeeze()
accuracy = float(sum(pred_y == test_y)) / float(test_y.size(0))
# accuracy = float((pred_y == test_y.data.numpy()).astype(int).sum())/float(test_y.size(0))
print('Epoch:',epoch,'| train loss :{:.4f}'.format(loss.item()),'| test accuracy:%.2f' % accuracy)
#loss.item()
test_output = cnn(test_x[:10])
pred_y = torch.max(test_output,1)[1].data.numpy().squeeze()
print(pred_y,'prediction number')
print(test_y[:10].numpy(),'real number')