PyTorch神经网络工具箱(实现神经网络实例)

实现神经网络实例

使用PyTorch构建神经网络使用的主要工具(或类)及相互关系,如图3-2所示。

从图3-2可知,构建网络层可以基于Module类或函数(nn.functional)。nn中的大多数

层(Layer)在functional中都有与之对应的函数。nn.functional中函数与nn.Module中的

Layer的主要区别是后者继承Module类,会自动提取可学习的参数。而nn.functional更像是

纯函数。两者功能相同,且性能也没有很大区别,那么如何选择呢?像卷积层、全连接

层、Dropout层等因含有可学习参数,一般使用nn.Module,而激活函数、池化层不含可学

习参数,可以使用nn.functional中对应的函数。下面通过实例来说明如何使用nn构建一个

网络模型。

背景说明

这节将利用神经网络完成对手写数字进行识别的实例,来说明如何借助nn工具箱来实

现一个神经网络,并对神经网络有个直观了解。在这个基础上,后续我们将对nn的各模块

进行详细介绍。实例环境使用PyTorch1.0+,GPU或CPU,源数据集为MNIST。

主要步骤:

1)利用PyTorch内置函数mnist下载数据。

2)利用torchvision对数据进行预处理,调用torch.utils建立一个数据迭代器。

3)可视化源数据。

4)利用nn工具箱构建神经网络模型。

5)实例化模型,并定义损失函数及优化器。

6)训练模型。

7)可视化结果。

神经网络的结构如图3-3所示。

使用两个隐含层,每层激活函数为ReLU,最后使用torch.max(out,1)找出张量out最大

值对应索引作为预测值。

准备数据

1.导人必要的模块

python 复制代码
import numpy as np
import torch
# 导入 PyTorch 内置的 mnist 数据
from torchvision.datasets import mnist
#导入预处理模块
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
#导入nn及优化器
import torch.nn.functional as F
import torch.optim as optim
from torch import nn

2.定义一些超参数

python 复制代码
# 定义一些超参数
train_batch_size = 64
test_batch_size = 128
learning_rate = 0.01
num_epoches = 20
lr = 0.01
momentum = 0.5

3.下载数据并对数据进行预处理

复制代码
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize([0.5], [0.5])])
#下载数据,并对数据进行预处理
train_dataset = mnist.MNIST('./data', train=True, transform=transform, download=True)
test_dataset = mnist.MNIST('./data', train=False, transform=transform)
#dataloader是一个可迭代对象,可以使用迭代器一样使用。
train_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False)

完整代码

python 复制代码
import numpy as np
import torch
#导入Pytorch内置的mnist数据
from torchvision.datasets import mnist
#导入预处理模块
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
#导入nn及优化器
import torch.nn.functional as F
import torch.optim as optim
from torch import nn

#定义一些超参数
train_batch_size=64
test_batch_size=128
learning_rate=0.01
num_epochs=20
lr=0.01
momentum=0.5

#定义预处理函数,这些预处理依次放在Compose函数中
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize([0.5],[0.5])])
#下载数据,并对数据进行预处理
train_dataset=mnist.MNIST('./data',train=True,transform=transform,download=True)
test_dataset=mnist.MNIST('./data',train=False,transform=transform)
#dataloader是一个可迭代对象,可以使用迭代器一样使用
train_loader=DataLoader(train_dataset,batch_size=train_batch_size,shuffle=True)
test_loader=DataLoader(test_dataset,batch_size=test_batch_size,shuffle=False)

可视化源数据

python 复制代码
import matplotlib.pyplot as plt
%matplotlib inline
examples = enumerate(test_loader)
batch_idx, (example_data, example_targets) = next(examples)
fig = plt.figure()
for i in range(6):
plt.subplot(2,3,i+1)
plt.tight_layout()
plt.imshow(example_data[i][0], cmap='gray', interpolation='none')
plt.title("Ground Truth: {}".format(example_targets[i]))
plt.xticks([])
plt.yticks([])

MNIST源数据示例如图3-4所示。

构建模型

数据预处理之后,我们开始构建网络,创建模型。

1)构建网络。

python 复制代码
class Net(nn.Module):
"""
使用sequential构建网络,Sequential()函数的功能是将网络的层组合到一起
"""
def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
super(Net, self).__init__()
self.layer1 = nn.Sequential(nn.Linear(in_dim, n_hidden_1),nn.BatchNorm1d(n_hidden_1))
self.layer2 = nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2),nn.BatchNorm1d (n_hidden_2))
self.layer3 = nn.Sequential(nn.Linear(n_hidden_2, out_dim))
def forward(self, x):
x = F.relu(self.layer1(x))
x = F.relu(self.layer2(x))
x = self.layer3(x)
return x

2)实例化网络。

python 复制代码
#检测是否有可用的GPU,有则使用,否则使用CPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#实例化网络
model = Net(28 * 28, 300, 100, 10)
model.to(device)
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum)

训练模型

训练模型,这里使用for循环,进行迭代。其中包括对训练数据的训练模型,然后用

测试数据的验证模型。

1.训练模型

python 复制代码
# 开始训练
losses = []
acces = []
eval_losses = []
eval_acces = []
for epoch in range(num_epoches):
train_loss = 0
train_acc = 0
model.train()
#动态修改参数学习率
if epoch%5==0:
optimizer.param_groups[0]['lr']*=0.1
for img, label in train_loader:
img=img.to(device)
label = label.to(device)
img = img.view(img.size(0), -1)
# 前向传播
out = model(img)
loss = criterion(out, label)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 记录误差
train_loss += loss.item()
# 计算分类的准确率
_, pred = out.max(1)
num_correct = (pred == label).sum().item()
acc = num_correct / img.shape[0]
train_acc += acc
losses.append(train_loss / len(train_loader))
acces.append(train_acc / len(train_loader))
# 在测试集上检验效果
eval_loss = 0
eval_acc = 0
# 将模型改为预测模式
model.eval()
for img, label in test_loader:
img=img.to(device)
label = label.to(device)
img = img.view(img.size(0), -1)
out = model(img)
loss = criterion(out, label)
# 记录误差
eval_loss += loss.item()
# 记录准确率
_, pred = out.max(1)
num_correct = (pred == label).sum().item()
acc = num_correct / img.shape[0]
eval_acc += acc
eval_losses.append(eval_loss / len(test_loader))
eval_acces.append(eval_acc / len(test_loader))
print('epoch: {}, Train Loss: {:.4f}, Train Acc: {:.4f}, Test Loss: {:.4f}, Test Acc: {:.4f}'
.format(epoch, train_loss / len(train_loader), train_acc / len(train_loader),
eval_loss / len(test_loader), eval_acc / len(test_loader)))

完整代码

python 复制代码
import torch
import torch.nn  as nn
import torch.nn.functional  as F
import torch.optim  as optim 
from torchvision import datasets, transforms 
import matplotlib.pyplot  as plt 
import numpy as np
 
# 超参数设置 
batch_size = 64 
lr = 0.01
momentum = 0.9
num_epoches = 10
 
# 数据加载与预处理 
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])
 
train_dataset = datasets.MNIST(
    './data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(
    './data', train=False, transform=transform)
 
train_loader = torch.utils.data.DataLoader( 
    train_dataset, batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader( 
    test_dataset, batch_size=batch_size, shuffle=False)
 
# 数据可视化
examples = enumerate(test_loader)
batch_idx, (example_data, example_targets) = next(examples)
 
fig = plt.figure(figsize=(10,  6))
for i in range(6):
    plt.subplot(2,  3, i+1)
    plt.tight_layout() 
    plt.imshow(example_data[i][0](),  cmap='gray', interpolation='none')
    plt.title("Ground  Truth: {}".format(example_targets[i]))  # 修复双花括号问题 
    plt.xticks([]) 
    plt.yticks([]) 
plt.show() 
 
# 网络定义
class Net(nn.Module):
    def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
        super(Net, self).__init__()
        self.layer1  = nn.Sequential(
            nn.Linear(in_dim, n_hidden_1),
            nn.BatchNorm1d(n_hidden_1)
        )
        self.layer2  = nn.Sequential(
            nn.Linear(n_hidden_1, n_hidden_2),
            nn.BatchNorm1d(n_hidden_2)
        )
        self.layer3  = nn.Linear(n_hidden_2, out_dim)
        self.dropout  = nn.Dropout(0.2)  # 添加Dropout防止过拟合 
 
    def forward(self, x):
        x = F.relu(self.layer1(x)) 
        x = self.dropout(x) 
        x = F.relu(self.layer2(x)) 
        x = self.dropout(x) 
        x = self.layer3(x) 
        return x 
 
# 设备检测
device = torch.device("cuda:0"  if torch.cuda.is_available()  else "cpu")
print(f"Using device: {device}")
 
# 模型初始化 
model = Net(28*28, 300, 100, 10).to(device)
 
# 损失函数和优化器 
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(),  lr=lr, momentum=momentum)
scheduler = optim.lr_scheduler.StepLR(optimizer,  step_size=5, gamma=0.1)  # 学习率调度器
 
# 训练过程记录
losses, acces, eval_losses, eval_acces = [], [], [], []
 
# 训练循环
for epoch in range(num_epoches):
    model.train() 
    train_loss, train_acc = 0, 0 
    
    # 使用学习率调度器替代手动调整 
    scheduler.step() 
    
    for img, label in train_loader:
        img, label = img.to(device),  label.to(device) 
        img = img.view(img.size(0),  -1)
        
        # 前向传播 
        out = model(img)
        loss = criterion(out, label)
        
        # 反向传播
        optimizer.zero_grad() 
        loss.backward() 
        optimizer.step() 
        
        # 记录指标 
        train_loss += loss.item() 
        _, pred = out.max(1) 
        train_acc += (pred == label).sum().item() / img.shape[0] 
    
    # 验证阶段 
    model.eval() 
    eval_loss, eval_acc = 0, 0 
    
    with torch.no_grad():   # 减少内存消耗 
        for img, label in test_loader:
            img, label = img.to(device),  label.to(device) 
            img = img.view(img.size(0),  -1)
            
            out = model(img)
            loss = criterion(out, label)
            
            eval_loss += loss.item() 
            _, pred = out.max(1) 
            eval_acc += (pred == label).sum().item() / img.shape[0] 
    
    # 记录每个epoch的指标
    losses.append(train_loss/len(train_loader)) 
    acces.append(train_acc/len(train_loader)) 
    eval_losses.append(eval_loss/len(test_loader)) 
    eval_acces.append(eval_acc/len(test_loader)) 
    
    # 打印训练进度 
    print(f'Epoch [{epoch+1}/{num_epoches}], '
          f'Train Loss: {losses[-1]:.4f}, Train Acc: {acces[-1]:.4f}, '
          f'Test Loss: {eval_losses[-1]:.4f}, Test Acc: {eval_acces[-1]:.4f}')
 
# 训练过程可视化 
plt.figure(figsize=(12,  4))
plt.subplot(1,  2, 1)
plt.plot(losses,  label='Train Loss')
plt.plot(eval_losses,  label='Test Loss')
plt.legend() 
plt.title('Loss  Curves')
plt.xlabel('Epoch') 
plt.ylabel('Loss') 
 
plt.subplot(1,  2, 2)
plt.plot(acces,  label='Train Accuracy')
plt.plot(eval_acces,  label='Test Accuracy')
plt.legend() 
plt.title('Accuracy  Curves')
plt.xlabel('Epoch') 
plt.ylabel('Accuracy') 
plt.tight_layout() 
plt.show() 
 
# 模型保存
torch.save(model.state_dict(),  'mnist_model.pth') 
print("Model saved to mnist_model.pth") 

最后5次迭代的结果:

复制代码
epoch: 15, Train Loss: 0.0047, Train Acc: 0.9995, Test Loss: 0.0543, Test Acc: 0.9839
epoch: 16, Train Loss: 0.0048, Train Acc: 0.9997, Test Loss: 0.0532, Test Acc: 0.9839
epoch: 17, Train Loss: 0.0049, Train Acc: 0.9996, Test Loss: 0.0544, Test Acc: 0.9839
epoch: 18, Train Loss: 0.0049, Train Acc: 0.9995, Test Loss: 0.0535, Test Acc: 0.9839
epoch: 19, Train Loss: 0.0049, Train Acc: 0.9996, Test Loss: 0.0536, Test Acc: 0.9836

这个神经网络的结构比较简单,只用了两层,也没有使用Dropout层,迭代20次,测

试准确率达到98%左右,效果还可以。不过,还是有提升空间,如果采用cnn、Dropout等

层,应该还可以提升模型性能。

可视化训练及测试损失值

python 复制代码
plt.title('trainloss')
plt.plot(np.arange(len(losses)), losses)
plt.legend(['Train Loss'], loc='upper right')

运行结果如图3-5所示。