我的环境:
语言环境:Python 3.6.13
编译器:Pycharm 2020.2
深度学习环境:Pytorch 1.10.0
显卡及显存: Tesla T4 16G(服务器)
要求:
拔高(可选)
目录
一、 前言
二、 准备工作
2.1 设置显卡GPU,没有用CPU
2.2 导入数据
2.2.1 torchvision.datasets.MNIST
2.2.2 torch.utils.data.DataLoader
2.3 数据可视化
2.3.1 squeeze 函数
三、 CNN网络
3.1 nn.Conv2d
3.3 nn.ReLU
3.4 nn.Linear
3.5 nn.Sequential
3.6 模型设计(改进CNN模型)
四、 训练模型
4.1 设置超参数
4.2 编写训练函数
4.3 编写测试函数
4.4 正式训练
4.5 结果可视化
五、 总结
众所周知,MNIST是机器学习入门的hello word!它是一个大型的手写数字数据库,通常用于训练各种图像处理系统。MNIST 数据库包含 60,000 张训练图像和 10,000 张测试图像。传统机器学习方法已经用这个训练集和测试集进行了测试。结果如下表:
类型 | 准确率(%) |
线性分类器 | 92.4 |
非线性分类器 | 96.7 |
随机森林 | 97.2 |
可以看出,传统机器学习方法的准确率还有待提升,因此本文采用深度学习的方法来实现mnist手写数字识别。统计结果来源于MNIST 数据库-维基百科。
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import torchvision
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device(type='cuda')
torchvision.datasets.MNIST
本文使用的Pytorch自带的数据库torchvision.datasets
,这里使用的是torchvision.datasets
中的MNIST
数据集。
函数原型:
torchvision.datasets.MNIST(root, train=True, transform=None, target_transform=None, download=False)
参数详解:
root ( string ) -- 数据集所在的根目录 。训练集文件目录/data/precessed/training.pt,测试集文件目录 /data/precessed/test.pt
train ( bool , optional ) – 如果为真,则创建训练集,否则创建测试集。
download ( bool , optional ) – 如果为 True,则从 Internet 下载数据集并将其放在根目录中。如果数据集已经下载,则不会再次下载。
transform ( callable , optional ) -- 一个函数/转换,它接收 PIL 图像并返回转换后的版本。例如,transforms.RandomCrop
target_transform ( callable , optional ) – 接收目标并对其进行转换的函数/转换。
train_ds = torchvision.datasets.MNIST('data',
train=True,
transform=torchvision.transforms.ToTensor(), # 将数据类型转化为Tensor
download=True)
test_ds = torchvision.datasets.MNIST('data',
train=False,
transform=torchvision.transforms.ToTensor(), # 将数据类型转化为Tensor
download=True)
torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=None,...)
参数说明(常用有5个):
batch_size = 32
train_dl = torch.utils.data.DataLoader(train_ds,
batch_size=batch_size,
shuffle=True)
test_dl = torch.utils.data.DataLoader(test_ds,
batch_size=batch_size)
# 取一个批次查看数据格式
# 数据的shape为:[batch_size, channel, height, weight]
# 其中batch_size为自己设定,channel,height和weight分别是图片的通道数,高度和宽度。
imgs, labels = next(iter(train_dl))
imgs.shape
测试结果
参考PyTorch官方文档
函数原型:
torch.squeeze( input , dim = None , * , out = None )
返回一个张量,其中所有input
大小为1的维度都已删除。
举例:
输入:AX1XBXCX1XD
输出:AXBXCXD
#%%
import numpy as np
# 指定图片大小,图像大小为20宽、5高的绘图(单位为英寸inch)
plt.figure(figsize=(20, 5))
for i, imgs in enumerate(imgs[:20]):
# 维度缩减
npimg = np.squeeze(imgs.numpy())
print(imgs.shape)
print( npimg.shape)
# 将整个figure分成2行10列,绘制第i+1个子图。
plt.subplot(2, 10, i+1)
plt.imshow(npimg, cmap=plt.cm.binary)
plt.axis('off')
plt.show()
#%%
构成:特征提取网络和分类网络
其中特征提取网络用于提取图片的特征,分类网络用于将图片进行分类。
函数:
参考PyTorch官方文档,再结合这篇卷积神经网络(CNN)的整体框架及细节(详细简单)来理解卷积。
为卷积层,用于提取图片的特征,传入参数 (输入通道,输出通道,卷积核大小)
输入:(N,Cin,w,w)
输出:(N,Cout,o,o)
为池化层,进行下采样。
激活函数,使模型可以拟合非线性数据
参数:
输入:任意数量的维度。
输出:与输入的形状相同。
对输入数据应用线性变换。
参数:
in_features – 每个输入样本的大小
out_features – 每个输出样本的大小
详细解析可以参考这篇博客.pytorch中的顺序容器——torch.nn.Sequential
总的来说,构建模块有以下两种方式:
本文模型使用Sequential构建,第一层使用两个5*5的卷积核,第二层使用两个3*3的卷积核,层与层之间用dropout减少网络规模。更多提升方法参加这篇博客MNIST手写数字识别准确度提升最全、最实用的方法-基于tensorflow。
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential,ReLU,Dropout
num_classes = 10
class Model(nn.Module):
def __init__(self):
super(Model,self).__init__()
self.conv1=Sequential(
Conv2d(1, 32, kernel_size=5,padding='same'),# 32*28*28
ReLU(),
Conv2d(32, 32, kernel_size=5,padding='same'), # 32*28*28
ReLU(),
MaxPool2d(2), #高宽减半 #32*14*14
Dropout(0.25)
)
self.conv2=Sequential(
Conv2d(32, 64, kernel_size=3,padding='same'), # 64*14*14
ReLU(),
Conv2d(64, 64, kernel_size=3,padding='same'), # 64*14*14
ReLU(),
MaxPool2d(2), #高宽减半 # 64*7*7
Dropout(0.25)
)
self.fc=Sequential(
Linear(32*7*7,64),
ReLU(),
Linear(64,num_classes)
)
def forward(self, x):
batch_size = x.size(0)
x = self.conv1(x) # 一层卷积层,一层激活层,一层卷积层,一层激活层,一层Dropout
x = self.conv2(x) # 再来一次
x = x.view(batch_size, -1) # flatten 变成全连接网络需要的输入 (batch, 64,7,7) ==> (batch,64*7*7), -1 此处自动算出的是3136
x = self.fc(x)
return x
from torchinfo import summary
# 将模型转移到GPU中(我们模型运行均在GPU中进行)
model = Model().to(device)
summary(model)
loss_fn = nn.CrossEntropyLoss() # 创建损失函数
learn_rate = 1e-3 # 学习率
opt = torch.optim.Adam(model.parameters(),lr=learn_rate)
loss.backward()
tensor.backward()
的话,梯度值将会是None,优先执行loss.backward()
函数来计算梯度#%%
# 训练循环
def train(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset) # 训练集的大小,一共60000张图片
num_batches = len(dataloader) # 批次数目,1875(60000/32)
train_loss, train_acc = 0, 0 # 初始化训练损失和正确率
for X, y in dataloader: # 获取图片及其标签
X, y = X.to(device), y.to(device)
# 计算预测误差
pred = model(X) # 网络输出
loss = loss_fn(pred, y) # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
# 反向传播
optimizer.zero_grad() # grad属性归零
loss.backward() # 反向传播
optimizer.step() # 每一步自动更新
# 记录acc与loss
train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
train_loss += loss.item()
train_acc /= size
train_loss /= num_batches
return train_acc, train_loss
def test (dataloader, model, loss_fn):
size = len(dataloader.dataset) # 测试集的大小,一共10000张图片
num_batches = len(dataloader) # 批次数目,313(10000/32=312.5,向上取整)
test_loss, correct = 0, 0
# 当不进行训练时,停止梯度更新,节省计算内存消耗
with torch.no_grad():
for imgs, target in dataloader:
imgs, target = imgs.to(device), target.to(device)
# 计算loss
target_pred = model(imgs)
loss = loss_fn(target_pred, target)
test_loss += loss.item()
test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()
test_acc /= size
test_loss /= num_batches
return test_acc, test_loss
epochs = 20
train_loss = []
train_acc = []
test_loss = []
test_acc = []
for epoch in range(epochs):
model.train()
epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)
model.eval()
epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
train_acc.append(epoch_train_acc)
train_loss.append(epoch_train_loss)
test_acc.append(epoch_test_acc)
test_loss.append(epoch_test_loss)
template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}')
print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss))
print('Done')
可以看到模型准确率达到99.5%。
1.了解pytorch开发基础流程,学习一些函数的使用方法。模型使用Sequential构建,第一层使用两个5*5的卷积核,第二层使用两个3*3的卷积核,层与层之间使用dropout减少网络规模,同时修改优化器为Adam,学习率要修改为0.001.
2.改进后的CNN模型准确率达到了99.5%。
3.其他提升准确率的方法可以参考这篇博客。MNIST手写数字识别准确度提升最全、最实用的方法-tensorflow。