我们从PyTorch中经典的quickstart示例开始,从中学习神经网络构建和训练过程。

其中要学到并熟练掌握的是如下这些流程:

  • 数据预处理
  • 构建模型
  • 定制模型损失函数和优化器
  • 训练并观察超参数

下面我们就一步步分解这个过程,其中也会学习认识到一些pytorch为我们提供的框架内置对象和函数。初次接触可能还不是很适应,所以以先完成完整的模型训练流程为重。后续再根据任务需求,一步步的扩展pytorch的认知版图


数据预处理

模型训练用的样本,大部分都来自于外部文件系统。本次训练用的数据来自框架内置的数据集,所以代码没有泛化性。涉及到具体数据再做补充。

PyTorch 有两个用于处理数据的工具:torch.utils.data.DataLoadertorch.utils.data.DatasetDataset 存储的是数据样本和对应的标签,DataLoader 把Dataset包装成一个可迭代的对象。

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda, Compose
import matplotlib.pyplot as plt

本次的数据样本来自于Pytorch的TorchVision 数据集。

# 下载的数据集文件会保存到当前用户工作目录的data子目录中。
# 如果不想下载后就找不到了,建议修改root参数的值。
# 例如"D:\\\\datasets\\\\fashionMNIST\\\\"一类的绝对路径
training_data = datasets.FashionMNIST(root="data",train=True,download=True,transform=ToTensor(),
)# 测试集也需要下载,代码和上面一样。但参数train=False代表不是训练集(逻辑取反,就是测试集)
test_data = datasets.FashionMNIST(root="data",train=False,download=True,transform=ToTensor(),
)

下一步就是对已加载数据集的封装,把Dataset 作为参数传递给 DataLoader。这样,就在我们的数据集上包装了一个迭代器(iterator),这个迭代器还支持自动批处理采样打乱顺序和多进程数据加载等这些强大的功能。这里我们定义了模型训练期间,每个批次的数据样本量大小为64,即数据加载器在迭代中,每次返回一批 64 个数据特征和标签。

batch_size = 64# 创建数据加载器
train_dataloader = DataLoader(training_data, batch_size=batch_size, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=batch_size)# 测试数据加载器输出
for X, y in test_dataloader:print("Shape of X [N, C, H, W]: ", X.shape)print("Shape of y: ", y.shape, y.dtype)break

Shape of X [N, C, H, W]: torch.Size([64, 1, 28, 28]) Shape of y: torch.Size([64]) torch.int64


构建模型

为了在 PyTorch 中定义神经网络,我们创建了一个继承自 nn.Module 的类。我们在 init 函数中定义网络层,并在 forward 函数中指定数据将如何通过网络。为了加速神经网络中的操作,我们将其移至 GPU(如果可用)。

# 检验可以使用的设备
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"使用 {device} 设备")# 定义神经网络模型
class NeuralNetwork(nn.Module):def __init__(self):super(NeuralNetwork, self).__init__()self.flatten = nn.Flatten()self.linear_relu_stack = nn.Sequential(nn.Linear(28*28, 512),  # wx + b = [64,1,784] * [784,512] = 64,1,512nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10))def forward(self, x):x = self.flatten(x)logits = self.linear_relu_stack(x)return logitsmodel = NeuralNetwork().to(device)
print(model)

使用 cuda 设备

NeuralNetwork(

(flatten): Flatten(start_dim=1, end_dim=-1)

(linear_relu_stack): Sequential(

(0): Linear(in_features=784, out_features=512, bias=True)

(1): ReLU()

(2): Linear(in_features=512, out_features=512, bias=True)

(3): ReLU()

(4): Linear(in_features=512, out_features=10, bias=True) ) )

pytorch是通过继承 nn.Module 父类来实现自定义的网络模型。

init 中初始化神经网络层。

在 forward 方法中实现对输入数据的操作。


模型层分解

为了方便分解 FashionMNIST 模型中的各个层进行说明,我们取一个由 3 张大小为 28x28 的图像组成的小批量样本,看看当它们通过网络传递时会发生什么。

input_image = torch.rand(3,28,28)
print(input_image.size())

torch.Size([3, 28, 28])

nn.Flatten

我们初始化 nn.Flatten 层,将每个28x28 大小的二维图像转换为 784 个像素值的连续数组(保持小批量维度(dim=0))。

flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size())

torch.Size([3, 784])

nn.Linear

linear layer线性层是一个模块,它使用其存储的权重和偏置对输入应用线性变换。

layer1 = nn.Linear(in_features=28*28, out_features=20)
hidden1 = layer1(flat_image)
print(hidden1.size())

torch.Size([3, 20])

nn.ReLU

为了在模型的输入和输出之间创建复杂映射,我们使用非线性激活。激活函数在线性变换之后被调用,以便把结果值转为非线性,帮助神经网络学习到各种各样的关键特征值。 在这个模型中,线性层之间使用了 nn.ReLU,其实还有很多激活函数可以在模型中引入非线性。

print(f"ReLU 之前的数据: {hidden1}\\n\\n")
hidden1 = nn.ReLU()(hidden1)
print(f"ReLU 之后的数据: {hidden1}")

ReLU 之前的数据: tensor([[-0.2496, 0.4432, -0.1536, 0.4439, 0.3256, 0.6594, -0.2536, 0.2348, -0.1113, 0.0732, -0.0658, 0.6014, -0.6135, -0.4709, 0.3016, 0.1500, 0.0801, 0.3644, -0.6113, 0.4129], [-0.7556, 0.1309, -0.2760, 0.3292, 0.5749, 0.6503, -0.1372, 0.3096, 0.1499, -0.0446, -0.1845, 0.2553, -0.6012, -0.3562, -0.0291, 0.1380, 0.2641, 0.2835, -0.5634, 0.1305], [-0.3772, -0.0354, -0.3879, 0.1846, 0.5425, 0.5019, 0.3323, 0.3478, 0.1171, 0.1153, -0.3414, 0.1688, -0.4068, 0.0950, -0.0322, 0.1272, 0.1653, 0.1538, -0.8849, 0.1446]], grad_fn=<AddmmBackward0>)

ReLU 之后的数据: tensor([[0.0000, 0.4432, 0.0000, 0.4439, 0.3256, 0.6594, 0.0000, 0.2348, 0.0000, 0.0732, 0.0000, 0.6014, 0.0000, 0.0000, 0.3016, 0.1500, 0.0801, 0.3644, 0.0000, 0.4129], [0.0000, 0.1309, 0.0000, 0.3292, 0.5749, 0.6503, 0.0000, 0.3096, 0.1499, 0.0000, 0.0000, 0.2553, 0.0000, 0.0000, 0.0000, 0.1380, 0.2641, 0.2835, 0.0000, 0.1305], [0.0000, 0.0000, 0.0000, 0.1846, 0.5425, 0.5019, 0.3323, 0.3478, 0.1171, 0.1153, 0.0000, 0.1688, 0.0000, 0.0950, 0.0000, 0.1272, 0.1653, 0.1538, 0.0000, 0.1446]], grad_fn=<ReluBackward0>)

nn.Sequential

nn.Sequential 是一个有序的模块容器。数据按照容器中定义的顺序通过所有模块。我们可以使用顺序容器来组合一个像 seq_modules 这样的快速处理网络。

seq_modules = nn.Sequential(flatten,layer1,nn.ReLU(),nn.Linear(20, 10)
)
input_image = torch.rand(3,28,28)
logits = seq_modules(input_image)

nn.Softmax

神经网络的最后一个线性层返回的是 logits类型的值,它们的取值是[-∞, ∞]。 把这些值传递给 nn.Softmax 模块。 logits的值将会被缩放到 [0, 1] 的取值区间,代表模型对每个类别的预测概率。 dim 参数指示我们在向量的哪个维度中计算softmax的值(和为1)。

softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)

模型参数

神经网络内的许多层都是包含可训练参数的,即具有在训练期间可以优化的相关权重(weight)和偏置(bias)。子类 nn.Module 可以自动跟踪模型对象中定义的所有参数字段。使用模型的 parameters()named_parameters() 方法可以访问模型中所有的参数。 下面的代码可以迭代模型中的每一个参数,并打印出它们的大小和它们的值。

print("Model structure: ", model, "\\n\\n")for name, param in model.named_parameters():print(f"Layer: {name} | Size: {param.size()} | Values : {param[:2]} \\n")

Model structure:

NeuralNetwork(

(flatten): Flatten(start_dim=1, end_dim=-1)

(linear_relu_stack): Sequential(

(0): Linear(in_features=784, out_features=512, bias=True)

(1): ReLU()

(2): Linear(in_features=512, out_features=512, bias=True)

(3): ReLU()

(4): Linear(in_features=512, out_features=10, bias=True) ) )

Layer: linear_relu_stack.0.weight | Size: torch.Size([512, 784]) | Values : tensor([[-0.0186, -0.0036, -0.0014, ..., -0.0294, -0.0217, 0.0293], [-0.0041, 0.0305, -0.0148, ..., 0.0036, -0.0100, 0.0239]], device='cuda:0', grad_fn=<SliceBackward0>)

Layer: linear_relu_stack.0.bias | Size: torch.Size([512]) | Values : tensor([-0.0168, -0.0071], device='cuda:0', grad_fn=<SliceBackward0>)

Layer: linear_relu_stack.2.weight | Size: torch.Size([512, 512]) | Values : tensor([[ 0.0054, 0.0381, -0.0331, ..., 0.0163, -0.0087, -0.0287], [ 0.0252, 0.0025, -0.0126, ..., -0.0261, 0.0020, -0.0217]], device='cuda:0', grad_fn=<SliceBackward0>)

Layer: linear_relu_stack.2.bias | Size: torch.Size([512]) | Values : tensor([-0.0202, -0.0242], device='cuda:0', grad_fn=<SliceBackward0>)

Layer: linear_relu_stack.4.weight | Size: torch.Size([10, 512]) | Values : tensor([[ 0.0142, -0.0315, -0.0046, ..., 0.0150, 0.0205, -0.0144], [ 0.0070, -0.0086, -0.0114, ..., -0.0193, 0.0310, 0.0325]], device='cuda:0', grad_fn=<SliceBackward0>)

Layer: linear_relu_stack.4.bias | Size: torch.Size([10]) | Values : tensor([-0.0353, -0.0112], device='cuda:0', grad_fn=<SliceBackward0>)


定制模型损失函数和优化器

训练模型之前,我们需要为模型定制一个损失函数loss function和一个优化器 optimizer

loss_fn = nn.CrossEntropyLoss()  # 交叉熵损失函数
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)  # 使用随机梯度下降方法的优化器

训练并观察超参数

在单个训练循环中,模型对训练数据集进行预测(分批输入),并反向传播预测误差以调整模型参数。

def train(dataloader, model, loss_fn, optimizer):size = len(dataloader.dataset)  # 训练数据样本总量model.train() # 设置模型为训练模式for batch, (X, y) in enumerate(dataloader):X, y = X.to(device), y.to(device)  # 张量加载到设备# 计算预测的误差pred = model(X)  # 调用模型获得结果(forward时被自动调用)loss = loss_fn(pred, y) # 计算损失# 反向传播 Backpropagationmodel.zero_grad() # 重置模型中参数的梯度值为0loss.backward() # 计算梯度optimizer.step() # 更新模型中参数的梯度值if batch % 100 == 0:loss, current = loss.item(), batch * len(X)print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")

我们还要依赖测试数据集来检查模型的性能,以确保它的学习优化效果。

def test(dataloader, model, loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)model.eval()  # 模型设置为评估模式,代码等效于 model.train(False)test_loss, correct = 0, 0with torch.no_grad():for X, y in dataloader:X, y = X.to(device), y.to(device)pred = model(X)test_loss += loss_fn(pred, y).item()correct += (pred.argmax(1) == y).type(torch.float).sum().item()test_loss /= num_batchescorrect /= sizeprint(f"Test Error: \\n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \\n")

默认情况下,所有 requires_grad=True 属性值的张量都会被跟踪,以便于根据上一次的值来支持对梯度计算。但是,在某些情况下我们并不需要这样做,例如,当我们训练了模型但只想将其应用于某些输入数据的时。或者说白了就是我们只想通过网络进行前向计算时。我们可以把所有计算代码写在 torch.no_grad() 下面来停止跟踪计算。

训练过程在多轮迭代(epochs)中进行。在每个epoch中,模型通过学习更新内置的参数,以期做出更好的预测。我们在每个epochs打印模型的准确率和损失值;当然,最希望看到的,就是每个 epoch 过程中准确率的增加而损失函数值的减小。

epochs = 5
for t in range(epochs):print(f"Epoch {t+1}\\n-------------------------------")train(train_dataloader, model, loss_fn, optimizer)test(test_dataloader, model, loss_fn)
print("训练完成!")

pytorch神经网络实现相关推荐

  1. PyTorch 神经网络

    PyTorch 神经网络 神经网络 神经网络可以通过 torch.nn 包来构建. 现在对于自动梯度(autograd)有一些了解,神经网络是基于自动梯度 (autograd)来定义一些模型.一个 n ...

  2. PyTorch神经网络集成技术

    PyTorch神经网络集成技术 create_python_neuropod 将任意python代码打包为一个neurood包. create_python_neuropod( neuropod_pa ...

  3. 【Pytorch神经网络实战案例】21 基于Cora数据集实现Multi_Sample Dropout图卷积网络模型的论文分类

    Multi-sample Dropout是Dropout的一个变种方法,该方法比普通Dropout的泛化能力更好,同时又可以缩短模型的训练时间.XMuli-sampleDropout还可以降低训练集和 ...

  4. 总结 | 深度学习PyTorch神经网络箱使用

    点上方蓝字计算机视觉联盟获取更多干货 在右上方 ··· 设为星标 ★,与你不见不散   计算机视觉联盟笔记   作者:王博Kings.Sophia 本文内容概述PyTorch神经网络箱 AI博士笔记系 ...

  5. Tensor:Pytorch神经网络界的Numpy

    摘要:Tensor,它可以是0维.一维以及多维的数组,你可以将它看作为神经网络界的Numpy,它与Numpy相似,二者可以共享内存,且之间的转换非常方便. 本文分享自华为云社区<Tensor:P ...

  6. 神经网络模型中class的forward函数何时调用_总结深度学习PyTorch神经网络箱使用...

    ↑ 点击蓝字 关注极市平台来源丨计算机视觉联盟编辑丨极市平台 极市导读 本文介绍了Pytorch神经网络箱的使用,包括核心组件.神经网络实例.构建方法.优化器比较等内容,非常全面.>>加入 ...

  7. pytorch神经网络解决回归问题(非常易懂)

    pytorch神经网络解决回归问题(非常易懂) 参考文章: (1)pytorch神经网络解决回归问题(非常易懂) (2)https://www.cnblogs.com/Yanjy-OnlyOne/p/ ...

  8. 使用Google-Colab训练PyTorch神经网络

    Colaboratory 是免费的 Jupyter 笔记本环境,不需要进行任何设置就可以使用,并且完全在云端运行.关键是还有免费的GPU可以使用!用Colab训练PyTorch神经网络步骤如下: 1: ...

  9. 数据分析实战:python热门音乐分析 附代码+数据 +论文(PCA 主成分分析,sklearn 机器学习,pytorch 神经网络,k-means 聚类,Librosa 音频处理,midi 音序)

    项目概述: 本选取了抖音当下最热门的 400 首音乐,通过一系列方法提取每首歌的波形特征,再经过降维以及机器学习等手段,进行无监督学习对音乐数据进行聚类的同时训练并使用监督学习分类器进行音乐流派分类, ...

  10. 第3章 PyTorch神经网络工具箱(1/2)

    前面已经介绍了PyTorch的数据结构及自动求导机制,充分运行这些技术可以大大提高我们的开发效率.这章将介绍PyTorch的另一利器:神经网络工具箱.利用这个工具箱,设计一个神经网络就像搭积木一样,可 ...

最新文章

  1. PAT(甲级)2019年秋季考试 7-2 Merging Linked Lists
  2. 流程 - 什么是真正的Scrum?
  3. Python 动态添加类方法
  4. 浏览器中打开IOS应用并传参
  5. linux shell mysql版本,Linux中Shell操作MySQL
  6. 关于CSDN是什么网站
  7. java web后台开发规范01
  8. 数字电子钟设计(基于quartus软件)
  9. 安装moodle3.6
  10. vc++键盘钩子和鼠标钩子
  11. 雅可比矩阵(Jacobian)、海森矩阵(Hessian)
  12. junos pulse_IT怪胎:如何使用DHCP中继(JUNOS)
  13. 32 Pin和 8 Pin(引脚 )flash烧录操作指导
  14. Win10 远程桌面黑屏问题
  15. NUCLEAR-ID细胞周期分析试剂盒特点与应用
  16. 《Adobe Premiere Pro CS4经典教程》——复习
  17. Mixamo使用笔记
  18. win10彻底关闭windows defender的两种方法
  19. 中国私有云向头部玩家集中,如何走出差异化竞争路线?
  20. UOS20编译Qt程序:搭环境、解决bug

热门文章

  1. openpyxl详解
  2. Mybatis的Spring集成、Aop整合
  3. 学UI设计,用对这5款设计软件是关键
  4. linux下解压命令大全
  5. 常用的几种设计模式详解
  6. Gson 中对象类型的相互转换
  7. 迅搜 php,关于 Xunsearch PHP-SDK
  8. mapper-spring-boot-starter的使用
  9. mysqldump单个库导出_mysql mysqldump只导出表结构或只导出数据的实现方法
  10. DOSBox debug中查看标志位