赞
踩
本文将在PyTorch中构建一个简单的卷积神经网络,并使用MNIST数据集训练它识别手写数字。在MNIST数据集上训练分类器可以看作是图像识别的“hello world”。
首先需要确定网络的层数和每层的节点数。关于第一个问题,实际上并没有什么理论化的方法,大家都是根据经验来拍,如果没有经验的话就随便设一个。然后可以多试几个值,训练不同层数的神经网络,看看哪个效果最好就用哪个。嗯,现在可能明白为什么说深度学习是个手艺活了,有些手艺很让人无语,而有些手艺还是很有技术含量的。
不过,有些基本道理,都知道网络层数越多越好,也知道层数越多训练难度越大。对于全连接网络,隐藏层最好不要超过三层。那么,可以先试试仅有一个隐藏层的神经网络效果怎么样。毕竟模型小的话,训练起来也快些(刚开始玩模型的时候,都希望快点看到结果)。
输入层节点数是确定的。因为MNIST数据集每个训练数据是28*28的图片,共784个像素,因此,输入层节点数应该是784,每个像素对应一个输入节点。
输出层节点数也是确定的。因为是10分类, 可以用10个节点,每个节点对应一个分类。输出层10个节点中,输出最大值的那个节点对应的分类,就是模型的预测结果。
隐藏层节点数量是不好确定的,从1到100万都可以。下面有几个经验公式:
因此,可以先根据上面的公式设置一个隐藏层节点数。如果有时间,也可以设置不同的节点数,分别训练,看看哪个效果最好就用哪个。 设隐藏层节点数为300 。
对于3层78430010的全连接网络,总共有300*(784+1)+10*(300+1)=238510个参数!神经网络之所以强大,是它提供了一种非常简单的方法去实现大量的参数。目前百亿参数、千亿样本的超大规模神经网络也是有的。因为MNIST只有6万个训练样本,参数太多了很容易过拟合,效果反而不好。
MNIST数据集包含10000个测试样本。 先用60000个训练样本训练我们的网络,然后再用测试样本对网络进行测试,计算识别错误率:
每训练10轮,评估一次准确率。当准确率开始下降时(出现了过拟合)终止训练。
首先,导入必要的包。对这个手写数字识别问题的解决只用到了torchvision中的部分功能,所以这里通过 from torchvision import方法导入其中的两个子包 datasets和transforms。
import torch
import torch.nn as nn
import torchvision.datasets as normal_datasets
import torchvision.transforms as transforms
from torch.autograd import Variable
使用torchvision.datasets可以轻易实现对这些数据集的训练集和测试集的下载,只需要使用 torchvision.datasets再加上需要下载的数据集的名称就可以了,比如在这个问题中我们要用到手写数字数据集,它的名称是MNIST,那么实现下载的代码就是torchvision.datasets.MNIST。其他常用的数据集如COCO、ImageNet、CIFCAR等都可以通过这个方法快速下载和载入。实现数据集下载的代码如下:
# 首先获取手写数字的训练集和测试集
# root 用于指定数据集在下载之后的存放路径
# transform 用于指定导入数据集需要对数据进行那种变化操作
# train是指定在数据集下载完成后需要载入那部分数据,
# 如果设置为True 则说明载入的是该数据集的训练集部分
# 如果设置为FALSE 则说明载入的是该数据集的测试集部分
# 从torchvision.datasets中加载数据集
train_dataset = normal_datasets.MNIST(
root='./mnist/', # 数据集保存路径
train=True, # 是否作为训练集
transform=transforms.ToTensor(), # 数据处理
download=True) # 下载数据
其中,root用于指定数据集在下载之后的存放路径,这里存放在根目录下的data文件夹中;transform用于指定导入数据集时需要对数据进行哪种变换操作,在后面会介绍详细的变换操作类型,注意,要提前定义这些变换操作;train用于指定在数据集下载完成后需要载入哪部分数据,如果设置为True,则说明载入的是该数据集的训练集部分;如果设置为False,则说明载入的是该数据集的测试集部分。
在顺利完成数据装载后,我们就可以开始编写卷积神经网络模型的搭建和参数优化的代码了,因为我们想要搭建一个包含了卷积层,激活函数,池化层,全连接层的卷积神经网络来解决这个问题,所以模型在结构上会和之前简单的神经网络有所区别,当然,各个部分的功能实现依然是通过torch.nn中的类来完成的,比如卷积层使用torch.nn.Conv2d类方法来搭建,激活层使用torch.nn.ReLU类来搭建;池化层使用torch.nn.MaxPool2d类方法来搭建;全连接层使用torch.nn.Linear类方法来搭建。
卷积神经网络CNN的结构一般包含这几层:
输入层:用于数据的输入
卷积层:使用卷积核进行特征提取和特征映射
激励层:由于卷积也是一种线性运算,因此需要增加非线性映射
池化层:进行下采样,对特征图稀疏处理,减少特征信息的损失
输出层:用于输出结果
实现卷积神经网络模型搭建的代码如下:
# 两层卷积 class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() # 使用序列工具快速构建 self.conv1 = nn.Sequential( nn.Conv2d(1, 16, kernel_size=5, padding=2), nn.BatchNorm2d(16), nn.ReLU(), nn.MaxPool2d(2)) self.conv2 = nn.Sequential( nn.Conv2d(16, 32, kernel_size=5, padding=2), nn.BatchNorm2d(32), nn.ReLU(), nn.MaxPool2d(2)) self.fc = nn.Linear(7 * 7 * 32, 10) def forward(self, x): out = self.conv1(x) out = self.conv2(out) out = out.view(out.size(0), -1) # reshape,扁平化 out = self.fc(out) return out
对模型进行训练和对参数进行优化了。首先,定义在训练之前使用哪种损失函数和优化函数:
# 定义了计算损失值的损失函数使用的是交叉熵
# 优化函数使用的额是Adam自适应优化算法
cnn = CNN()
if torch.cuda.is_available():
cnn = cnn.cuda()
# 选择损失函数和优化方法
loss_func = nn.CrossEntropyLoss()#损失函数
optimizer = torch.optim.Adam(cnn.parameters(), lr=learning_rate)#优化
import torch import torch.nn as nn import torchvision.datasets as normal_datasets import torchvision.transforms as transforms from torch.autograd import Variable num_epochs = 5 batch_size = 100 learning_rate = 0.001 # 将数据处理成Variable, 如果有GPU,转成cuda形式 def get_variable(x): x = Variable(x) return x.cuda() if torch.cuda.is_available() else x # 从torchvision.datasets中加载数据集 train_dataset = normal_datasets.MNIST( root='./mnist/', # 数据集保存路径 train=True, # 是否作为训练集 transform=transforms.ToTensor(), # 数据处理 download=True) # 下载数据 # 数据加载器和batch test_dataset = normal_datasets.MNIST(root='./mnist/', train=False, transform=transforms.ToTensor()) train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True) test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False) # 两层卷积 class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() # 使用序列工具快速构建 self.conv1 = nn.Sequential( nn.Conv2d(1, 16, kernel_size=5, padding=2), nn.BatchNorm2d(16), nn.ReLU(), nn.MaxPool2d(2)) self.conv2 = nn.Sequential( nn.Conv2d(16, 32, kernel_size=5, padding=2), nn.BatchNorm2d(32), nn.ReLU(), nn.MaxPool2d(2)) self.fc = nn.Linear(7 * 7 * 32, 10) def forward(self, x): out = self.conv1(x) out = self.conv2(out) out = out.view(out.size(0), -1) # reshape out = self.fc(out) return out cnn = CNN() if torch.cuda.is_available(): cnn = cnn.cuda() # 选择损失函数和优化方法 loss_func = nn.CrossEntropyLoss()#损失函数 optimizer = torch.optim.Adam(cnn.parameters(), lr=learning_rate)#优化 for epoch in range(num_epochs): for i, (images, labels) in enumerate(train_loader): images = get_variable(images) labels = get_variable(labels) outputs = cnn(images) loss = loss_func(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() if (i + 1) % 100 == 0: print('Epoch [%d/%d], Iter [%d/%d] Loss: %.4f' % (epoch + 1, num_epochs, i + 1, len(train_dataset) // batch_size, loss.item())) # Save the Trained Model torch.save(cnn.state_dict(), 'cnn.pkl')
结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。