上一节我们学习了Pytorch优化网络的基本方法,本节我们将以MNIST数据集为例,通过搭建一个完整的神经网络,来加深对Pytorch的理解。
一、数据集
MNIST是一个非常经典的数据集,下载链接:http://yann.lecun.com/exdb/mnist/
下载下来的文件如下:
该手写数字数据库具有60,000个示例的训练集和10,000个示例的测试集。它是NIST提供的更大集合的子集。数字已经过尺寸标准化,并以固定尺寸的图像为中心。
手写数字识别是一个比较简单的任务,它是一个10分类问题,(0-9),之所以选这个数据集,是因为识别难度低,计算量小,数据容易获得。
二、模型搭建
1、网络节点的确定
对于不同的目的,网络的选择也是不一样的。一般来说,网络容量和数据集大小是对应的。一个小型数据集也只需要一个小型的网络。
这里有一个经验值:
1)model_size=sqrt(in_size*out_size)
2)model_size=log(in_size)
3) model_size=sqrt(in_size*out_size)
model_size:网络的节点量
in_size:输入的节点量
out_size输出的节点量
2、导入pytorch包
import torch import torchvision import trochvision import datasets import trochvision import transforms from torch.autograd import Variable
3、获取训练集和测试集
#root用于指定数据集下载后的存放路径
#transform用于指定导入数据集需要对数据进行变换操作
#train指定在数据集下载后需要载入哪部分数据,true为训练集,false为测试集
data_train=datasets.MNIST(root="./data/",transform=transform,train=True,download=True) data_test=datasets.MNIST(root='./data/',transform=transform,train=False)
4、数据预览和装载
#数据装载,可以理解为对图片的处理 #处理完成后,将图片送给模型训练,装载就是打包的过程 #dataset 用于指定载入的数据集名称 #batch_size设置了每个包的图片数据数据个数 #shuffle 装载过程将数据随机打乱并打包 data_loader_train=torch.utils.data.DataLoader(dataset=data_train,batch_size=64,shuffle=True) data_loader_test=torch.utils.data.DataLoader(dataset=data_test,batch_size=64,shuffle=True)
装载后,可以选取其中一个批次数据进行预览
images,labels=next(iter(data_loader_train)) img=torchvision.utils.make_grid(images) #batch_size,channel,height,weight img=img.numpy().transpose(1,2,0) #height,weight,channel std=[0.5,0.5,0.5] mean=[0.5,0.5,0.5] img=img*std+mean print(labels) cv2.show('win',img) key_pressed=cv2.waitKey(0)
5、模型搭建和参数优化
#卷积神经网络CNN结构一般包含几层: 输入层:数据输入 卷积层:卷积核进行特征提取和特征映射 torch.nn.Conv2d 激励层:卷积也是线性,所以要增加非线性映射 torch.nn.ReLU 池化层:进行下采样,对特征图稀疏处理,去除噪声 torch.nn.MaxPool2d 全连接层:torch.nn.Linear 输出层:输出结果 import torch.nn as nn class Model(nn.Module): def __init__(self): self.conv1=nn.Sequential( nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1), nn.ReLU(), nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1), nn.ReLU(), nn.Maxpool2d(stride=2,kernel_size=2)) self.dense=nn.Sequential( nn.Linear(14*14*128,1024), nn.ReLU(), nn.Dropout(p=0.5), nn.Linear(1024,10)) def forward(self,x): net=self.conv1(x) net=net.view(-1,14*14*128) net=self.dense(net) return net
由于MNIST分类任务较简单,所以,网络结构只使用了两个卷积层,一个最大池化层和两个全连接。
在前向传播forward函数中,首先,经过self.conv1卷积处理,然后使用view(-1,14*14*128)对参数实现扁平化,来满足紧接的全连接。
搭建完模型后,就可以开始进行模型训练和对参数进行优化了。
#模型实例化 #定义损失函数和优化函数 #损失函数使用交叉熵 #优化函数使用自适应优化算法 model=Model() cost=torch.nn.CrossEntropyLoss() optimizer=torch.optim.Adam(model.parameters())
可以打印搭建好的网络结构
print(model) 显示如下: Model( (conv1): Sequential( (0): Conv2d(1, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (1): ReLU() (2): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (3): ReLU() (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) ) (dense): Sequential( (0): Linear(in_features=25088, out_features=1024, bias=True) (1): ReLU() (2): Dropout(p=0.5) (3): Linear(in_features=1024, out_features=10, bias=True) ) )
n_epochs = 10
for epoch in range(n_epochs):
total_loss = 0.0
total_corrrect = 0
print("Epoch {}/{}".format(epoch, n_epochs))
print("-"*10)
for data in data_loader_train:
X_train , y_train = data
X_train,y_train = Variable(X_train),Variable(y_train)
outputs = model(X_train)
_,pred = torch.max(outputs.data,1)
optimizer.zero_grad()
loss = cost(outputs,y_train)
loss.backward()
optimizer.step()
total_loss += loss.data[0]
total_corrrect += torch.sum(pred == y_train.data)
testing_correct = 0
for data in data_loader_train:
X_test,y_test = data
X_test,y_test = Variable(X_test),Variable(y_test)
outputs = model(X_test)
_,pred = torch.max(X_test)
testing_correct += torch.sum(pred == y_test.data)
print("Loss is {:.4f}, Train Accuracy is:{:.4f}%,Test Accuracy is:{:.4f}"
.format(total_loss/len(data_train),100*total_corrrect/len(data_train),
100*testing_correct/len(data_test)))
总的训练周期为10次,使用print显示训练过程细节。且在每轮训练完成后,使用测试集验证模型泛化能力,并计算准确率。
6、完整训练代码
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')
7、模型验证
为了验证训练好的模型效果,我们选取一部分测试集中的图片,看看与真实值的偏差,并对结果可视化。
import matplotlib.pyplot as plt import cv2 import torch import torchvision from torchvision import datasets from torchvision import transforms from torch.autograd import Variable transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])]) data_test = datasets.MNIST(root="./data/", transform = transform, train = False) data_loader_test = torch.utils.data.DataLoader(dataset =data_test, batch_size = 4, shuffle = True) #模型搭建和参数优化 # 在顺利完成数据装载后,我们可以开始编写卷积神经网络模型的搭建和参数优化的代码 #卷积层使用torch.nn.Conv2d类来搭建 # 激活层使用torch.nn.ReLU 类方法来搭建 # 池化层使用torch.nn.MaxPool2d类方法来搭建 # 全连接层使用 torch.nn.Linear 类方法来搭建 class Model(torch.nn.Module): def __init__(self): super(Model,self).__init__() self.conv1 = torch.nn.Sequential( torch.nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1), torch.nn.ReLU(), torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1), torch.nn.ReLU(), torch.nn.MaxPool2d(stride=2,kernel_size=2)) self.dense = torch.nn.Sequential( torch.nn.Linear(14*14*128,1024), torch.nn.ReLU(), torch.nn.Dropout(p = 0.5), torch.nn.Linear(1024,10) ) # 我们通过继承torch.nn.Modeule来构造网络,因为手写数字 # 识别比较简单,我们只是用了两个卷积层,一个最大池化层,两个全连接层。 # 在向前传播过程中进行x.view(-1, 14 * 14 * 128) # 对参数实现扁平化。最后通过自己self.dense定义的全连接层进行最后的分类 def forward(self, x): x = self.conv1(x) x = x.view(-1,14*14*128) x = self.dense(x) return x # 在编写完搭建卷积神经网络模型的代码后,我们可以对模型进行训练和参数进行优化了 # 首先 定义在训练之前使用哪种损失函数和优化函数 # 下面定义了计算损失值的损失函数使用的是交叉熵 # 优化函数使用的额是Adam自适应优化算法 model = Model() # 将所有的模型参数移动到GPU上 if torch.cuda.is_available(): model.cuda() cost = torch.nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters()) # print(model) # 卷积神经网络模型进行模型训练和参数优化的代码 n_epochs = 5 X_test,y_test = next(iter(data_loader_test)) inputs = Variable(X_test) pred = model(inputs) _,pred = torch.max(pred,1) print("Predict Label is:",(i for i in pred)) print("Real Label is :",[i for i in y_test]) img = torchvision.utils.make_grid(X_test) img = img.numpy().transpose(1,2,0) std = [0.5,0.5,0.5] mean = [0.5,0.5,0.5] img = img*std +mean cv2.imshow('win',img) key_pressed=cv2.waitKey(0)
附原文完整代码:
#_*_coding:utf-8_*_ import matplotlib.pyplot as plt import numpy as np import cv2 import time import torch # torchvision包的主要功能是实现数据的处理,导入和预览等 import torchvision from torchvision import datasets from torchvision import transforms from torch.autograd import Variable start_time = time.time() # 对数据进行载入及有相应变换,将Compose看成一种容器,他能对多种数据变换进行组合 # 传入的参数是一个列表,列表中的元素就是对载入的数据进行的各种变换操作 transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])]) # 首先获取手写数字的训练集和测试集 # root 用于指定数据集在下载之后的存放路径 # transform 用于指定导入数据集需要对数据进行那种变化操作 # train是指定在数据集下载完成后需要载入那部分数据, # 如果设置为True 则说明载入的是该数据集的训练集部分 # 如果设置为FALSE 则说明载入的是该数据集的测试集部分 data_train = datasets.MNIST(root="./data/", transform = transform, train = True, download = True) data_test = datasets.MNIST(root="./data/", transform = transform, train = False) #数据预览和数据装载 # 下面对数据进行装载,我们可以将数据的载入理解为对图片的处理, # 在处理完成后,我们就需要将这些图片打包好送给我们的模型进行训练 了 而装载就是这个打包的过程 # dataset 参数用于指定我们载入的数据集名称 # batch_size参数设置了每个包中的图片数据个数 # 在装载的过程会将数据随机打乱顺序并进打包 data_loader_train = torch.utils.data.DataLoader(dataset =data_train, batch_size = 64, shuffle = True) data_loader_test = torch.utils.data.DataLoader(dataset =data_test, batch_size = 64, shuffle = True) # 在装载完成后,我们可以选取其中一个批次的数据进行预览 images,labels = next(iter(data_loader_train)) img = torchvision.utils.make_grid(images) img = img.numpy().transpose(1,2,0) std = [0.5,0.5,0.5] mean = [0.5,0.5,0.5] img = img*std +mean # print(labels) print([labels[i] for i in range(64)]) # 由于matplotlab中的展示图片无法显示,所以现在使用OpenCV中显示图片 # plt.imshow(img) # cv2.imshow('win',img) # key_pressed=cv2.waitKey(0) #模型搭建和参数优化 # 在顺利完成数据装载后,我们可以开始编写卷积神经网络模型的搭建和参数优化的代码 #卷积层使用torch.nn.Conv2d类来搭建 # 激活层使用torch.nn.ReLU 类方法来搭建 # 池化层使用torch.nn.MaxPool2d类方法来搭建 # 全连接层使用 torch.nn.Linear 类方法来搭建 class Model(torch.nn.Module): def __init__(self): super(Model,self).__init__() self.conv1 = torch.nn.Sequential( torch.nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1), torch.nn.ReLU(), torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1), torch.nn.ReLU(), torch.nn.MaxPool2d(stride=2,kernel_size=2)) self.dense = torch.nn.Sequential( torch.nn.Linear(14*14*128,1024), torch.nn.ReLU(), torch.nn.Dropout(p = 0.5), torch.nn.Linear(1024,10) ) # 我们通过继承torch.nn.Modeule来构造网络,因为手写数字 # 识别比较简单,我们只是用了两个卷积层,一个最大池化层,两个全连接层。 # 在向前传播过程中进行x.view(-1, 14 * 14 * 128) # 对参数实现扁平化。最后通过自己self.dense定义的全连接层进行最后的分类 def forward(self, x): x = self.conv1(x) x = x.view(-1,14*14*128) x = self.dense(x) return x # 在编写完搭建卷积神经网络模型的代码后,我们可以对模型进行训练和参数进行优化了 # 首先 定义在训练之前使用哪种损失函数和优化函数 # 下面定义了计算损失值的损失函数使用的是交叉熵 # 优化函数使用的额是Adam自适应优化算法 model = Model() # 将所有的模型参数移动到GPU上 if torch.cuda.is_available(): model.cuda() cost = torch.nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters()) # print(model) # 卷积神经网络模型进行模型训练和参数优化的代码 n_epochs = 5 for epoch in range(n_epochs): running_loss = 0.0 running_correct = 0 print("Epoch {}/{}".format(epoch, n_epochs)) print("-"*10) for data in data_loader_train: X_train , y_train = data # 有GPU加下面这行,没有不用加 # X_train, y_train = X_train.cuda(), y_train.cuda() X_train , y_train = Variable(X_train),Variable(y_train) # print(y_train) outputs = model(X_train) # print(outputs) _,pred = torch.max(outputs.data,1) optimizer.zero_grad() loss = cost(outputs,y_train) loss.backward() optimizer.step() # running_loss += loss.data[0] running_loss += loss.item() running_correct += torch.sum(pred == y_train.data) # print("ok") # print("**************%s"%running_corrrect) print("train ok ") testing_correct = 0 for data in data_loader_test: X_test,y_test = data # 有GPU加下面这行,没有不用加 # X_test, y_test = X_test.cuda(), y_test.cuda() X_test,y_test = Variable(X_test),Variable(y_test) outputs = model(X_test) _, pred = torch.max(outputs,1) testing_correct += torch.sum(pred == y_test.data) # print(testing_correct) print( "Loss is :{:.4f},Train Accuracy is:{:.4f}%,Test Accuracy is:{:.4f}".format( running_loss / len(data_train),100 * running_correct / len(data_train), 100 * testing_correct / len(data_test))) stop_time = time.time() print("time is %s" %(stop_time-start_time))
上一篇:
下一篇:
未完待续。。。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:如何入门Pytorch之四:搭建神经网络训练MNIST - Python技术站