若想复现一个神经网络,MNIST手写识别可以算是简单的入门了,mnist是由七万张28*28像素的图片构成,其中60000用来训练我们的模型,剩下的10000张用来测试。今天就详细的介绍基于pytorch框架的网络实现:
首先,构建的网络是卷积神经网络;分别是:
卷积层(Convolutional layer),卷积神经网路中每层卷积层由若干卷积单元组成,每个卷积单元的参数都是通过反向传播算法优化得到的。卷积运算的目的是提取输入的不同特征,第一层卷积层可能只能提取一些低级的特征如边缘、线条和角等层级,更多层的网络能从低级特征中迭代提取更复杂的特征。
激活函数(Rectified Linear Units layer, ReLU layer),这一层神经的活性化函数(Activation function)使用线性整流(Rectified Linear Units, ReLU)f(x)=max(0,x)f(x)=max(0,x)。
池化层(Pooling layer),通常在卷积层之后会得到维度很大的特征,将特征切成几个区域,取其最大值或平均值,得到新的、维度较小的特征。对图片进行压缩(降采样)
全连接层( Fully-Connected layer), 把所有局部特征结合变成全局特征,用来计算最后每一类的得分。
损失函数
在深度学习中,损失反映模型最后预测结果与实际真值之间的差距,可以用来分析训练过程的好坏、模型是否收敛等,例如均方损失、交叉熵损失等。
第一步
加载用到的库,
其中torch包含多维张量的数据结构,并定义了对这些张量的数学运算。
torch.nn.functional包含了卷积函数,池化函数,非线性激活函数,线性函数,损失函数等函数。
torch.optim是一个实现各种优化算法的包,必须构造一个优化器对象,该对象将保存当前状态并根据计算出的梯度更新参数
PyTorch 数据加载实用程序的核心是torch.utils.data.DataLoader 类,它代表一个 Python 可迭代的数据集
# 加载库
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
第二步
设置超参数,超参数类似于c++中的宏定义,便于控制,BATCH_SIZE是每一批次处理的数量,比如一次输入64张图片进行处理, DEVICE顾名思义就是设备,我们将数据部署到GPU上进行处理,如果用cpu也可以,改为cpu即可。EPOCHS是数据集迭代的轮次。
BATCH_SIZE = 64
DEVICE = torch.device("gpu")
EPOCHS = 10
第三步
定义数据变换pipline,把我们的数据集的图片信息转换为张量表示,并正则化处理。实现对图片的变换处理。
pipline = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
第四步
上文中提到的数据集可以从网上下载,可以直接在代码里通过指令下载,下载到当前目录下自定义设置的“data”这个目录下,通过datasets.MNIST完成下载。下载完了还要加载数据,用DataLoader函数实现加载。
from torch.utils.data import DataLoader
train_set = datasets.MNIST("data", train=True, download=True, transform=pipline)
test_set = datasets.MNIST("data", train=False, download=True, transform=pipline)
train_loader = DataLoader(train_set, batch_size=BATCH_SIZE, shuffle=True)
test_loader = DataLoader(test_set, batch_size=BATCH_SIZE, shuffle=True)
第五步
创建我们的模型,在这里用到了两层卷积层,两层全连接层。
class MMN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 10, 5)
self.conv2 = nn.Conv2d(10, 20, 3)
self.fc1 = nn.Linear(20*10*10, 500)
self.fc2 = nn.Linear(500, 10)
def forward(self, x):
inputsize = x.size(0)
x = self.conv1(x)
x = F.relu(x)
x = F.max_pool2d(x, 2, 2)
x = self.conv2(x)
x = F.relu(x)
x = x.view(-1, 20*10*10)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
第六步
建立模型,类似于c++中的对象,命名为model,将模型部署到设备上,也就是gpu上。建立优化器,对我们的训练参数进行优化,其中Adam是一种优化器,还有其他优化器可以从官网查找。
model = MMN().to(DEVICE)
optimzer = optim.Adam(model.parameters())
第七步
定义训练模型
过程中将数据(64128*28)放到模型中,得到输出的每种类别的归一化概率。继续计算输出结果与标签的差距——损失。将损失反向传播,对参数进行更新。
在这里,我们输出了每一轮次的损失
def train_model(model, device, train_loader, optimizer, epoch):
model.train()
for batch_index, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.cross_entropy(output, target)
loss.backward()
optimizer.step()
if batch_index % 3000 == 0:
print("train epoch:{}\t loss:{:.6f}".format(epoch, loss.item()))
第八步
定义测试模型
我们输出测试模型每一轮次损失与准确度
def test_model(model, device, test_loader):
model.eval()
correct = 0.0
test_loss = 0.0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.cross_entropy(output, target).item()
pred = output.max(1, keepdim=True)[1]
correct += pred.eq(target.view_as(pred).sum()).item()
test_loss /= len(test_loader.dataset)
print("test_average loss:{:.4f}, Accuary:{:.3f}\n".format(test_loss, 100.0*correct/len(test_loader.dataset)))
第九步
调用运行
for epoch in range(1, EPOCHS + 1):
train_model(model, DEVICE, train_loader, optimzer, epoch)
test_model(model, DEVICE, test_loader)
配有详细注释
# 加载库
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
# torchvision 主要包含三部分:
# models:提供深度学习中各种经典网络的网络结构以及预训练好的模型,包括 AlexNet 、VGG 系列、ResNet 系列、Inception 系列等;
# datasets: 实现对这些数据集的训练集和测试集的下载,只需要使用 torchvision.datasets 再加上需要下载的数据集的名称就可以了,提供常用的数据集加载,设计上都是继承 torch.utils.data.Dataset,主要包括 MNIST、CIFAR10/100、ImageNet、COCO等;
# transforms:提供常用的数据预处理操作,主要包括对 Tensor 以及 PIL Image 对象的操作
# # 超参数
# 一次处理64张图进入缓冲
BATCH_SIZE = 64
# 设置运行环境 torch.device("gpu")
DEVICE = torch.device("gpu")
# 设置了迭代6万张照片十次 so 一共60万张
EPOCHS = 5
# # 构建pipline对图像处理
# torchvision.transforms.Compose 类看作一种容器,它能够同时对多种数据变换进行组合
pipline = transforms.Compose([
transforms.ToTensor(),# 类型的转换变换
transforms.Normalize((0.1307,), (0.3081,))
# 进行数据的标准化,在经过标准化变换之后,数据全部符合均值为0、标准差为1的标准正态分布。
])
# 3, 下载数据集,
from torch.utils.data import DataLoader
train_set = datasets.MNIST("data", train=True, download=True, transform=pipline)
test_set = datasets.MNIST("data", train=False, download=True, transform=pipline)
# 加载下好的数据集
train_loader = DataLoader(train_set, batch_size=BATCH_SIZE, shuffle=True)
test_loader = DataLoader(test_set, batch_size=BATCH_SIZE, shuffle=True)
# 5搭建网络
# 继承 nn.Module(它本身是一个类并且能够跟踪状态)建立子类。我们想要建立一个包含权重、偏置和前向传播的方法的类。nn.Module 拥有许多我们将会使用的属性和方法(例如:.parameters() 和.zero_grad())
class MMN (nn.Module):
def __init__(self):
# 类似于C++类中的构造函数,self是形式参数
super().__init__()
# 初始化网络:第一层卷积输入一个通道,用十个核为5的filter输出;二层核为3的二十个来提取特征;全连接层第一层得到w*h*c=10*10*20,展平为2000,再输出500;第二层输出十个结果
self.conv1 = nn.Conv2d(1, 10, 5)
self.conv2 = nn.Conv2d(10, 20, 3)
# 分类器是一个简单的nn.Linear()结构
self.fc1 = nn.Linear(20*10*10, 500)
self.fc2 = nn.Linear(500, 10)
# 前向传播网络
def forward(self, x):
# x 是个张量( 16*1*28*28 4维),输入的大小batch_size16 ,
#
input_size = x.size(0)
x = self.conv1(x)
x = F.relu(x)
x = F.max_pool2d(x, 2, 2)
x = self.conv2(x)
x = F.relu(x)
# -1 自适应
x = x.view(input_size, -1)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
return output
# 6 更新参数模块
# 根据定义的模型新建模型,类似类的对象
model = MMN().to(DEVICE)
# 用的是Adam优化器
optimzer = optim.Adam(model.parameters())
# 7 定义训练模型
def train_model(model, device, train_loader, optimizer, epoch):
model.train()
# 启用batch normalization和drop out
for batch_index, (data, target) in enumerate(train_loader):
# enumerate多用于在for循环中得到计数
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
# 输出的是训练后的结果
output = model(data)
# 用交叉熵损失函数求损失
loss = F.cross_entropy(output, target)
# 反向传播以参数优化
loss.backward()
optimizer.step()
# batch_index 是由 train_loader循环得来,60000%3000 有20次,输出这二十次的损失
if batch_index % 3000 == 0:
print("train epoch:{}\t loss:{:.6f}".format(epoch, loss.item()))
# 8 测试方式
def test_model(model, device, test_loader):
model.eval()
# 使用model.eval(),这时神经网络会沿用batch normalization的值,并不使用drop out
# 准确率和测试损失
correct = 0.0
test_loss = 0.0
#
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.cross_entropy(output, target.long()).item()
# 预测的可能结果的概率值取出最大可能值,也就是得到的结果,[0][1]值和索引
pred = output.max(1, keepdim=True)[1]
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print("test_average loss:{:.4f}, Accuary:{:.3f}\n".format(test_loss, 100.0 * correct / len(test_loader.dataset)))
# 9 调用运行
for epoch in range(1, EPOCHS + 1):
train_model(model, DEVICE, train_loader, optimzer, epoch)
test_model(model, DEVICE, test_loader)