基于PyTorch框架的多层全连接神经网络实现MNIST手写数字分类

多层全连接神经网络实现MNIST手写数字分类

  • 1 简单的三层全连接神经网络
  • 2 添加激活函数
  • 3 添加批标准化
  • 4 训练网络
  • 5 结论

先用PyTorch实现最简单的三层全连接神经网络,然后添加激活层查看实验结果,最后再加上批标准化验证是否能够更加有效。

1 简单的三层全连接神经网络

  • 对于这个三层网络,需要传递进去的参数包括:输入的维度,第一次网络的神经元个数,第二层网络神经元的个数,以及第三层网络(输出层)神经元个数。
import torch
import torch.nn as nn
from torch.autograd.variable import Variable
import numpy as np


# 定义三层全连接神经网络
class SimpleNet(nn.Module):
    def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
        super(SimpleNet, self).__init__()
        self.layer1 = nn.Linear(in_dim, n_hidden_1)
        self.layer2 = nn.Linear(n_hidden_1, n_hidden_2)
        self.layer3 = nn.Linear(n_hidden_2, out_dim)

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        return x

2 添加激活函数

  • 接着改进一下网络,添加激活函数增加网络的非线性
# 添加激活函数,增加网络的非线性
class Activation_Net(nn.Module):
    def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
        super(Activation_Net, self).__init__()
        self.layer1 = nn.Sequential(
            nn.Linear(in_dim, n_hidden_1), nn.ReLU(True)
        )
        self.layer2 = nn.Sequential(
            nn.Linear(n_hidden_1, n_hidden_2)
        )
        self.layer3 = nn.Sequential(nn.Linear(n_hidden_2, out_dim))

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        return x
  
  • 这里只需要在每层网络的输出部分添加激活函数就可以了,nn.Sequential()这个函数是将网络的层组合到一起,比如上面将nn.Linear()和nn.ReLU()组合到一起作为self.layer。最后一层输出层不能添加激活函数,因为输出的结果表示的是实际的得分。

3 添加批标准化

  • 最后添加一个加快收敛速度的方法–批标准化。
class Batch_Net(nn.Module):
    def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
        super(Batch_Net, self).__init__()
        self.layer1 = nn.Sequential(
            nn.Linear(in_dim, n_hidden_1),
            nn.BatchNorm1d(n_hidden_1),
            nn.ReLU(True)
        )
        self.layer2 = nn.Sequential(
            nn.Linear(n_hidden_1, n_hidden_2),
            nn.BatchNorm1d(n_hidden_2),
            nn.ReLU(True)
        )
        self.layer3 = nn.Sequential(
            nn.Linear(n_hidden_2, out_dim)
        )

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        return x
  • 同样使用nn.Sequential()将nn.BatchNorm1d()组合到网络层中,注意批标准化一般放在全连接层的后面,非线性层(激活函数)的前面。

4 训练网络

  • 网络的定义较简单,现在用MNIST数据集训练网络并测试一下每种网络的结果。
  • MNIST数据集是一个手写字体数据集,包括0~9这10个数字,其中有55000张训练集,10000张测试集,5000张验证集,图片是28*28的灰度图。
    在这里插入图片描述
  • 导入需要用的包
import torch
from torch import nn, optim
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import net
  • 定义超参数
# 超参数 Hyper parameters
batch_size = 64
learning_rate = 1e-2
num_epochs = 20
  • 数据预处理,需要将数据标准化,运用的函数是torchvision.transforms,它提供了很多图片预处理的方法。transforms.ToTensor()将图片转换成PyTorch中处理的对象Tensor,在转换的过程中,PyTorch自动将图片标准化,即Tensor的范围是0~1。transforms.Normalize()需要传入两个参数,第一个是均值,第二个是方差,做的处理就是减均值,再除以方差。
  • transforms.Compose()将各种预处理操作组合到一起,transforms.Normalize([0.5], [0.5])表示减去0.5再除以0.5,这样图片转换到-1到1之间。因为图片是灰度图片,所以只有一个通道,如果是彩色图像,有三个通道,用transforms.Normalize([mean_r,mean_g,mean_b], [var_r,var_g,var_b])来表示每个通道对应的均值和方差。
# 数据预处理
data_tf = transforms.Compose(
    [
        # 将图片转换成PyTorch中处理的对象Tensor,在转换的过程中自动将图片标准化,即Tensor的范围是0~1
        transforms.ToTensor(),
        # 第一个参数是均值,第二个参数是方差,做的处理就是减均值,再除以方差
        transforms.Normalize([0.5], [0.5])
    ]
)
  • 读取数据集。
  • 通过PyTorch的内置函数torchvision.datasets.MNIST导入数据集,传入数据预处理。使用torch.utils.data.DataLoader建立一个数据迭代器,传入数据集和batch_size,通过shuffle=True,来表示每次迭代数据的时候是否将数据打乱。
# 下载训练集MNIST手写数字训练集
train_dataset = datasets.MNIST(
    root='./data',
    train=True,
    transform=data_tf,
    download=True
)
test_dataset = datasets.MNIST(
    root='./data',
    train=False,
    transform=data_tf
)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
  • 导入网络,定义损失函数和优化方法。
  • 通过net.SimpleNet定义简单三层网络,里面的参数是2828,300,100,10,其中输入的维度是2828,因为输入图片大小是28*28,然后定义两个隐藏层分别是300和100。最后输出的结果必须是10,因为这是一个分类问题,一共有0~9这10个数字,所以是10分类。
  • 损失函数定义为分类问题中最常见的损失函数交叉熵,使用随机梯度下降来优化损失函数。
# 导入网络
model = net.SimpleNet(28 * 28, 300, 100, 10)
if torch.cuda.is_available():
    model = model.cuda()

# 定义损失函数和优化方法
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate)
  • 开始训练网络
  • img = Variable(img, volatile=True)里面的volatile=True表示前向传播时,不会保留缓存,因为对于测试集,不需要做反向传播,所以在前向传播时释放内存,节约内存空间。
# 开始训练网络
model.eval()
eval_loss = 0
eval_acc = 0
for data in test_loader:
    img, label = data
    img = img.view(img.size(0), -1)
    if torch.cuda.is_available():
        img = Variable(img, volatile=True).cuda()
        label = Variable(label, volatile=True).cuda()
    else:
        img = Variable(img, volatile=True)
        label = Variable(label, volatile=True)
    out = model(img)
    loss = criterion(out, label)
    eval_loss += loss.data[0] * label.size(0)
    _, pred = torch.max(out, 1)
    num_correct = (pred == label).sum()
    eval_acc += num_correct.data[0]
print(
    'Test Loss:{:.6f},ACC:{:.6f}'.format(
        eval_loss / (len(test_dataset)),
        eval_acc / (len(test_dataset))
    )
)

5 结论

  • 依次测试简单三层网络、添加激活层的网络、添加批标准化的网络,网络的预测准确率越来越高,同时还可以通过Dropout、正则化,增加网络的泛化能力。

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