MNIST是一个非常有名的手写体数字识别数据集,在很多资料中,这个数据集都会被用作深度学习的入门样例,接下来将围绕 MNIST数据集 训练多层的神经网络模型。
读取数据
数据集描述
MNIST数据集由250 个不同人手写的数字构成,其训练数据集包含 60,000 个样本, 测试数据集包含 10,000 样本。在 MNIST 数据集中的每张图片由 28 x 28 个像素点构成, 每个像素点用一个灰度值表示。labels 包含了相应的目标变量, 也就是手写数字的类标签(整数 0-9)
读取数据
## 读取pytorch自带数据集Minist
import torch
import torchvision.datasets as dset
root = 'D:\\pytorch\\mnistData\\'
train_set = dset.MNIST('root', train=True, download=False) # train为true表示读取训练集,false表示读取测试集,
test_set = dset.MNIST('root', train=False, download=False) # download为True表示下载,为false表示从本地读取
## 描述数据集
print("train_data:", train_set.train_data.size())
print("train_labels:", train_set.train_labels.size())
print("test_data:", test_set.test_data.size())
样本数据量大小:
train_set是一个torchvision.datasets.mnist.MNIST类型, 其包含如下信息:
对train_set 取样发现train_set包含两类信息:
一个是手写数字的像素信息,由于在读取数据时未transform参数,所以特征信息并未转化为tensor类型,而是图片类型;另一个信息则是label,类型为int。
接下来打印前20组数据图像:
for i in range(24):
image,label = train_set[i]
path = 'D:\\pytorch\\mnistData\\MNIST\\figure\\{}-{}.png'.format(i,label)
image.save(path)
由于image.show()仅显示一个临时图片,故将图片保存后做一个展示。
test 内的数据和训练集数据结构相同,不再赘述。
数据预处理
由于读取数据时未定义Transform参数,故读取数据并非是tensor,参考博客link的处理方法,先定义一个转换,而后在读取数据时将此转换赋值到参数transform中读取。
- 定义数据转换方式
# transforms.ToTensor()将图片转换成PyTorch中处理的对象Tensor,并且进行标准化(数据在0~1之间)
# transforms.Normalize()做归一化。它进行了减均值,再除以标准差。两个参数分别是均值和标准差
# transforms.Compose()函数则是将各种预处理的操作组合到了一起
data_tf = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize([0.5], [0.5])])
其中,transforms.ToTensor函数将图像类型数据转化为Tensor类型,并且做了一个归一化处理,即将Tensor数值均除以255;而Normalize函数对tensor做标准化处理,即将色彩值映射到[-1,1]闭区间里面,假设原色彩值是0,那么映射后就是-1。因为mnist图片都是灰度图,所以只有一个色彩通道。正常的彩色图片是RGB三通道的,transforms.Normalize([r,g,b],[d,e,f])这样对应每个RGB的均值和方差。
利用dataloader读取train_set及test_set,该函数将读取的dataset根据batch size大小、是否shuffle等封装成一个Batch Size大小的Tensor,用于后面的训练。
from torch.utils.data import dataloader
train_dataset = dset.MNIST('root', train=True, transform=data_tf, download=False)
test_dataset = dset.MNIST('root', train=False, transform=data_tf) # 按data_tf的方式转化数据
train_loader = dataloader.DataLoader(dataset=train_set, batch_size=64, shuffle=True) # 数据加载器。组合了一个数据集和采样器,并提供关于数据的迭代器
test_loader = dataloader.DataLoader(dataset=test_set, batch_size=64)
# 将原数据集做一个shuffle并做批处理,每个mini-batch的数目为64个
dataloader将原数据集做一个shuffle并做批处理,每个mini-batch的数目为64个,共有938个mini-batch。
构建计算图
选择网络层
这里选择一个三层的全连接网络且每层为一个线性函数。
选择激活函数
激活函数是人工神经网络的一个极其重要的特征。它决定一个神经元是否应该被激活,激活代表神经元接收的信息与给定的信息有关。激活函数对输入信息进行非线性变换。 然后将变换后的输出信息作为输入信息传给下一层神经元。
- 常见分类器的适用场景
1、用于分类器时,Sigmoid函数及其组合通常效果更好。
2、由于梯度消失问题,有时要避免使用sigmoid和tanh函数。
3、ReLU函数是一个通用的激活函数,目前在大多数情况下使用。
4、ReLU函数只能在隐藏层中使用。
5、如果神经网络中出现死神经元,那么PReLU函数就是最好的选择。
Tip:可以从ReLU函数开始,如果ReLU函数没有提供最优结果,再尝试其他激活函数。
import torch
import torch.nn.functional as F
import torch.nn as nn
## 定义一个三层的全连接网络
class Model(torch.nn.Module):
def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
super(Model,self).__init__()
self.l1 = torch.nn.Linear(in_dim, n_hidden_1) #Linear中的w,b一定要初始化,若自己不定义初始化,则用默认的初始化方式初始化
self.l2 = torch.nn.Linear(n_hidden_1, n_hidden_2)
self.l3 = torch.nn.Linear(n_hidden_2, out_dim)
# 激活函数既可以使用nn,又可以调用nn.functional
def forward(self,x):
out = F.relu(self.l1(x)) # # 激活函数,直接调用torch.nn.functional中集成好的Relu
out = self.l2(out)
out = self.l3(out)
return out
model = Model(28 * 28, 300, 100, 10)
选择损失函数与优化器
常见损失函数
torch.nn内置很多损失函数,下面做一个简要介绍
损失函数 | 表达式 | 应用场景 |
---|---|---|
nn.CrossEntropyLoss | loss ( x , c l a s s ) = − log ( exp ( x [ c l a s s ] ) ∑ j exp ( x [ j ] ) ) = |