PyTorch详细入门-实现MNIST手写识别网络

实现MNIST手写识别网络

若想复现一个神经网络,MNIST手写识别可以算是简单的入门了,mnist是由七万张28*28像素的图片构成,其中60000用来训练我们的模型,剩下的10000张用来测试。今天就详细的介绍基于pytorch框架的网络实现:

CNN的几个概念

首先,构建的网络是卷积神经网络;分别是:

卷积层(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)

你可能感兴趣的:(深度学习,pytorch,神经网络,深度学习)