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

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

        在 PyTorch 里面可以很简单地定义三层全连接神经网络。

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

        对于这个三层网络,需要传递进去的参数包括:输入的维度、第一层网络的神经元个数、第二层网络神经元的个数,以及第三层网络(输出层)神经元的个数。

添加激活函数

        接着改进一下网络,添加激活函数增加网络的非线性,方法也非常简单。

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), 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.Linear() 和 nn.ReLU() 组合到一起作为 self.layer。注意最后一层输出层不能添加激活函数,因为输出的结果表示的是实际的得分。

添加批标准化

        最后添加一个加快收敛速度的方法——批标准化。

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.seguential() 将 nn.BatchNorm1d() 组合到网络层中,注意批标准化一般放在全连接层的后面、非线性层(激活函数)的前面。

训练网络

        网络的定义特别简单,现在用 MNIST 数据集训练网络并测试一下每种网络的结果。

        MNIST 数据集是一个手写字体数据集,包含 0 到 9 这10个数字,其中有55000张训练集,10000张测试集,5000张验证集,图片大小是28x28的灰度图,如图3.28所示。

        首先需要导入一些要用的包:

import torch
from torch import nn, optim
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

        然后可以定义一些超参数,如batch_size、learning_rate 还有 num_epoches等。

# 超参数 Hyperparameters
batch_size = 64
learning_rate = 1e-2
num_epoches = 20

        接着需要进行数据预处理,就像之前介绍的,需要将数据标准化,这里运用到的两数是torehvision.transforms,它提供了很多图片预处理的方法。这里使用两个方法:第一个是transforms.ToTensor(),第二个是 transforms.Normalize()。 

        transform.Torensor() 很好理解,就是将图片转换成 PyTorch 中处理的对象 Tensor 在转化的过程中 PyTorch自动将图片标准化了,也就是说 Tensor 的范围是0~1。接着我们使用 transforms.Normalize(),需要传入两个参数:第一个参数是均值,第二个参数是方差,做的处理就是减均值,再除以方差。

data_tf = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize([0.5], [0.5])]
)

        这里 transforms.Compose() 将各种预处理操作组合到一起,transforms.Normalize([0.5],[0.5]) 表示减去 0.5 再除以 0.5,这样将图片转化到了 -1 ~ 1 之间,注意因为图片是灰度图,所以只有一个通道,如果是彩色的图片,有三通道,那么用 transforms.Normalize([a,b,c],[d,e,f]) 来表示每个通道对应的均值和方差。 

        然后读取数据集。

# 下载训练集 MNISST 手写数字训练集
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)

        通过 PyTorch 的内置函数 torchvision.datasets.MNIST 导入数据集,传入数据预处理,前面介绍了如何定义自己的数据集,之后会用具体的例子说明。接着使用 torch.utils.data.DataLoader 建立一个数据迭代器,传入数据集和 batch_size,通过 shuffle=True 来表示每次迭代数据的时候是否将数据打乱。

        接着导入网络,定义损害函数和优化方法。

model = simpleNet(28 * 28, 300, 100, 10)
if torch.cuda.is_available():
    model.cuda()

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate)

         net.simpleNet 是定义的简单三层网络,里面的参数是28x28,300,100,10,其中输入的维度是 28x28,因为输入图片大小是28x28,然后定义两个隐藏层分别是 300 和 100,最后输出的结果必须是10,因为这是一个分类问题,一共有0~9这10个数字,所以是10分类。损失函数定义为分类问题中最常见的损失函数交叉熵,使用随机梯度下降来优化损失函数。

        接着开始训练网络。

for epoch in range(num_epoches):
    running_loss = 0.0
    correct = 0
    total = 0
    for i, data in enumerate(train_loader, 0):
        inputs, labels = data
        if torch.cuda.is_available():
            inputs, labels = inputs.cuda(), labels.cuda()
        optimizer.zero_grad()

        outputs = model(inputs.view(-1, 28 * 28))
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

    epoch_loss = running_loss / len(train_loader)
    epoch_acc = 100 * correct / total
    print('Epoch [{}/{}], Loss: {:.4f}, Accuracy: {:.2f}%'
        .format(epoch + 1, num_epoches, epoch_loss, epoch_acc))

        最后训练完网络之后需要测试网络,通过下面的代码来测试。

# eval
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).cuda()
        label = Variable(label).cuda()
    else:
        img = Variable(img)
        label = Variable(label)
    out = model(img)
    loss = criterion(out, label)
    eval_loss += loss.item() * label.size(0)
    _, pred = torch.max(out, 1)
    num_correct = (pred == label).sum()
    eval_acc += num_correct.item()

print('Test Loss: {:.6f}, ACC: {:.6f}'
    .format(eval_loss / (len(test_dataset)), eval_acc / (len(test_dataset))))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值