深度学习(五)-全连接神经网络实现 MNIST 手写数字分类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/keyue123/article/details/89225255

  前面我们讲了这么多理论知识,下面我们来简单的来一个实战学习,利用全连接神经网络实现 M N I S T MNIST 手写数字分类。 M N I S T MNIST 数据集是一个手写体数据集,包括 0~9 这 10 个数字,图片大小是 28 x 28 的灰度图。数据集由四部分组成,分别是:

其内容为:

数据处理

  在开始训练网络之前,良好的数据预处理和参数初始化可以使训练效率更高,效果更优 。如果有了解机器学习的童鞋,对这些肯定不陌生,机器学习在处理数据时,也会先将数据标准化后再进行特征的操作,和深度学习是大同小异的。常用有以下几种处理方法:

中心化

  中心化为将每个特征维度都减去相应的均值,这样可以使得数据变成 0 均值,特别对于一些图像数据,为 了方便我们将所有的数据都减去一个相同的值 。

标准化

  在使得数据都变成 0 均值之后,还需要使用标准化的做出让数据不同的特征维度都有着相同的规模。有两种常用的方法:一种是除以标准差,这样可以使得新数据的分布接近标准高斯分布;还有一种做法就是让每个特征维度的最大值和最小值按比例缩放到 -1 ~1 之间。

P C A PCA

  PCA 会将数据中心化,然后计算数据的协方差矩阵,通过这个协方差矩阵来进行奇异值分解 ( S V D SVD ),然后对数据进行
去相关性,将其投影到一个特征空间,我们取一些较大的、主要的特征向量来降低数据的维数,去掉一些没有方差的维度。这个操作对于一些线性模型和神经网络,都能取得良好的效果。

白噪声

  白噪声首先会跟 P C A PCA 一样将数据投影到一个特征空间,然后每个维度除以特征值来标准化这些数据,直观上就是一个多元高斯分布转化到了一个 0 均值,协方差矩阵为 1 的多元高斯分布。

M I N S T MINST 数据处理

  pytorch中使用的数据处理的函数为 t o r c h v i s i o n . t r a n s f o r m s torchvision.transforms ,它提供了很多图片预处理的方法 。

	from torchvision import transforms
	from torchvision.datasets import mnist  # 导入内置的 mnist 数据
	
	transform = transforms.Compose([
	    transforms.ToTensor(),   # 数据转为tensor
	    transforms.Normalize([0.5], [0.5])   # 正则化,均值: 0.5 方差: 0.5
	])
	
	train_set = mnist.MNIST('./data', train=True, transform=transform, download=False)
	test_set = mnist.MNIST('./data', train=False, transform=transform, download=False)

  我们的图片是黑白色,所以图片只有1通道, t r a n s f o r m s . N o r m a l i z e transforms.Normalize 只需要使用 t r a n s f o r m s . N o r m a l i z e ( [ a ] , [ a ] ) transforms.Normalize([a], [a]) ,如果是彩色图片,那么就有三通道,需要使用 t r a n s f o r m s . N o r m a l i z e ( [ a , b , c ] , [ a , b , c ] ) transforms.Normalize([a, b, c], [a, b, c]) 来表示每个通道对应的均值与方差。
   m n i s t . M N I S T mnist.MNIST 参数解释:
      t r a i n train T r u e True 训练, F a l s e False 无需训练;
      t r a n s f o r m transform :数据处理;
      d o w n l o a d download T r u e True 下载, F a l s e False 无需下载。我这里已经下载过了,所以使用的 F a l s e False

  数据加载之后,我们可以看看数据信息:

	a_data, a_label = train_set[0]
	a_data.shape

  可以看到第一张图片的大小为:torch.Size([1, 28, 28])

	import matplotlib.pyplot as plt
	
	print(train_set.train_data.size())     # 训练集
	print(train_set.train_labels.size())   # 训练集标签数量
	plt.imshow(train_set.train_data[0].numpy(), cmap='gray')
	plt.title('%i' % train_set.train_labels[0])
	plt.show()

  我们一次有这么多数据,训练会很繁琐, P y t o r c h Pytorch 提供了一个 t o r c h . u t i l s . d a t a . D a t a l o a d e r torch.utils.data.Dataloader 的封装接口将数据按照一定大小封装成 T e n s o r Tensor ,这样我们可以按批次训练,提高效率。

	train_data = DataLoader(train_set, batch_size=64, shuffle=True)
	test_data = DataLoader(test_set, batch_size=128, shuffle=False)

   b a t c h _ s i z e batch\_size 表示每次取数据数量, s h u f f l e shuffle 表示数据是否打乱, T r u e True 打乱。

网络模型

  我这里简单的定义一个四层的全连接神经网络,参数我直接带上了,也可以定义为变量,在引用模型的时候直接使用。

	import torch
	from torch import nn

	class fnn(nn.Module):
	    def __init__(self):
	        super(fnn, self).__init__()
	        
	        self.layer1 = nn.Sequential(  # 全连接层     [1, 28, 28]
	            nn.Linear(784, 400),       # 输入维度,输出维度
	            nn.BatchNorm1d(400),  # 批标准化,加快收敛,可不需要
	            nn.ReLU()  				 # 激活函数
	        )
	        
	        self.layer2 = nn.Sequential(
	            nn.Linear(400, 200),
	            nn.BatchNorm1d(200),
	            nn.ReLU() 
	        ) 
	
	        self.layer3 = nn.Sequential(   # 全连接层 
	            nn.Linear(200, 100),
	            nn.BatchNorm1d(100),
	            nn.ReLU()
	        )
	        
	        self.layer4 = nn.Sequential(   # 最后一层为实际输出,不需要激活函数,因为有 10 个数字,所以输出维度为 10,表示10 类
	            nn.Linear(100, 10),
	        )
	        
	    def forward(self, x):
	        x = self.layer1(x)
	        x = self.layer2(x)
	        x = self.layer3(x)
	        output = self.layer4(x)
	        
	        return output

  导入网络:

	net = fnn()    # 导入网络
	
	net.parameters   # 查看网络参数

  我们可以看到网络每层的参数信息

  接着定义损失函数和优化函数:

	criterion = nn.CrossEntropyLoss()   # 损失函数
	optimizer = torch.optim.Adam(net.parameters(), 1e-1)

网络训练

	from torch.autograd import Variable
	
	for epoch in range(20):
	    train_loss = 0
	    train_acc = 0
	    for im, label in train_data:
	        im = Variable(im.view(im.size(0), -1))
	        label = Variable(label)
	
	        out = net(im)   # 加载模型
	        
	        loss = criterion(out, label)  # 计算误差
	
	        optimizer.zero_grad()   # 梯度归零
	        loss.backward()  		# 反向传播
	        optimizer.step()   		# 参数更新
	
	  
	        train_loss += loss.data
	     
	        # 计算分类的准确率
	        _, pred = out.max(1)
	        num_correct = (pred == label).sum().item()   # 预测正确数目
	        acc = num_correct / im.shape[0]   # 正确率
	        train_acc += acc
	    
	    print('epoch: {}, Train Loss: {:.6f}, Train Acc: {:.6f}'.format(epoch, train_loss/len(train_data), train_acc/len(train_data)))

结果测试

  网络训练好之后,我们从训练结果可以看到,正确率可以高达 98.6%,使用测试集在看看效果。

	eval_loss = 0
	eval_acc = 0
	net.eval() 	# 将模型改为预测模式
	for im, label in test_data:
	    im = Variable(im.view(im.size(0), -1))
	    label = Variable(label)
	    
	    out = net(im)
	    
	    loss = criterion(out, label)
	    eval_loss += loss.data

	    _, pred = out.max(1)
	    num_correct = (pred == label).sum().item()
	    acc = num_correct / im.shape[0]
	    eval_acc += acc
	
	eval_losses.append(eval_loss / len(test_data))
	eval_acces.append(eval_acc / len(test_data))
	print('epoch: {}, Eval Loss: {:.6f}, Eval Acc: {:.6f}'.format(e, eval_loss/len(test_data), eval_acc/len(test_data)))
  同样发现,网络模型在测试集上的准确率也有 97.5%。我们使用一组数据看看效果:
	import torchvision
	from torchvision.transforms import ToPILImage
	show = ToPILImage()
	
	classes = ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")
	
	dataiter = iter(test_data)   # 显示4张图片
	images, labels = dataiter.next()
	print("实际类别: ", "  ".join("%11s"%classes[labels[j]] for j in range(8)))
	show(torchvision.utils.make_grid((images+1)/2)).resize((1000, 1000))   # resize: 图片缩放    make_grid:将多个图片拼到一个网格
  预测:
	images = Variable(images.view(images.size(0), -1))
	outputs = net(Variable(images))
	
	_, predicted = torch.max(outputs.data, 1)
	print("预测类别: ", "  ".join("%11s"%classes[predicted[j]] for j in range(8)))
  和上面的实际类别对比一下,发现居然和实际类别完全一样,100% 准确。好吧,我承认这个效果有点惊呆我了....。通过上面的详细步骤,实现了 MINST 数字分类,可以提高对全连接神经网络的熟悉和理解,感兴趣的童鞋可以使用其他数据集试试,还等什么呢。

猜你喜欢

转载自blog.csdn.net/keyue123/article/details/89225255