使用 PyTorch 构建模型
原文:
pytorch.org/tutorials/beginner/introyt/modelsyt_tutorial.html
译者:飞龙
注意
点击这里下载完整示例代码
介绍 || 张量 || 自动微分 || 构建模型 || TensorBoard 支持 || 训练模型 || 模型理解
跟随下面的视频或在youtube上观看。
www.youtube.com/embed/OSqIP-mOWOI
torch.nn.Module
和torch.nn.Parameter
在这个视频中,我们将讨论 PyTorch 为构建深度学习网络提供的一些工具。
除了Parameter
,我们在这个视频中讨论的类都是torch.nn.Module
的子类。这是 PyTorch 的基类,旨在封装特定于 PyTorch 模型及其组件的行为。
torch.nn.Module
的一个重要行为是注册参数。如果特定的Module
子类具有学习权重,这些权重被表示为torch.nn.Parameter
的实例。Parameter
类是torch.Tensor
的子类,具有特殊行为,当它们被分配为Module
的属性时,它们被添加到该模块的参数列表中。这些参数可以通过Module
类上的parameters()
方法访问。
作为一个简单的例子,这里是一个非常简单的模型,有两个线性层和一个激活函数。我们将创建一个实例,并要求它报告其参数:
import torch class TinyModel(torch.nn.Module): def __init__(self): super(TinyModel, self).__init__() self.linear1 = torch.nn.Linear(100, 200) self.activation = torch.nn.ReLU() self.linear2 = torch.nn.Linear(200, 10) self.softmax = torch.nn.Softmax() def forward(self, x): x = self.linear1(x) x = self.activation(x) x = self.linear2(x) x = self.softmax(x) return x tinymodel = TinyModel() print('The model:') print(tinymodel) print('\n\nJust one layer:') print(tinymodel.linear2) print('\n\nModel params:') for param in tinymodel.parameters(): print(param) print('\n\nLayer params:') for param in tinymodel.linear2.parameters(): print(param)
The model: TinyModel( (linear1): Linear(in_features=100, out_features=200, bias=True) (activation): ReLU() (linear2): Linear(in_features=200, out_features=10, bias=True) (softmax): Softmax(dim=None) ) Just one layer: Linear(in_features=200, out_features=10, bias=True) Model params: Parameter containing: tensor([[ 0.0765, 0.0830, -0.0234, ..., -0.0337, -0.0355, -0.0968], [-0.0573, 0.0250, -0.0132, ..., -0.0060, 0.0240, 0.0280], [-0.0908, -0.0369, 0.0842, ..., -0.0078, -0.0333, -0.0324], ..., [-0.0273, -0.0162, -0.0878, ..., 0.0451, 0.0297, -0.0722], [ 0.0833, -0.0874, -0.0020, ..., -0.0215, 0.0356, 0.0405], [-0.0637, 0.0190, -0.0571, ..., -0.0874, 0.0176, 0.0712]], requires_grad=True) Parameter containing: tensor([ 0.0304, -0.0758, -0.0549, -0.0893, -0.0809, -0.0804, -0.0079, -0.0413, -0.0968, 0.0888, 0.0239, -0.0659, -0.0560, -0.0060, 0.0660, -0.0319, -0.0370, 0.0633, -0.0143, -0.0360, 0.0670, -0.0804, 0.0265, -0.0870, 0.0039, -0.0174, -0.0680, -0.0531, 0.0643, 0.0794, 0.0209, 0.0419, 0.0562, -0.0173, -0.0055, 0.0813, 0.0613, -0.0379, 0.0228, 0.0304, -0.0354, 0.0609, -0.0398, 0.0410, 0.0564, -0.0101, -0.0790, -0.0824, -0.0126, 0.0557, 0.0900, 0.0597, 0.0062, -0.0108, 0.0112, -0.0358, -0.0203, 0.0566, -0.0816, -0.0633, -0.0266, -0.0624, -0.0746, 0.0492, 0.0450, 0.0530, -0.0706, 0.0308, 0.0533, 0.0202, -0.0469, -0.0448, 0.0548, 0.0331, 0.0257, -0.0764, -0.0892, 0.0783, 0.0062, 0.0844, -0.0959, -0.0468, -0.0926, 0.0925, 0.0147, 0.0391, 0.0765, 0.0059, 0.0216, -0.0724, 0.0108, 0.0701, -0.0147, -0.0693, -0.0517, 0.0029, 0.0661, 0.0086, -0.0574, 0.0084, -0.0324, 0.0056, 0.0626, -0.0833, -0.0271, -0.0526, 0.0842, -0.0840, -0.0234, -0.0898, -0.0710, -0.0399, 0.0183, -0.0883, -0.0102, -0.0545, 0.0706, -0.0646, -0.0841, -0.0095, -0.0823, -0.0385, 0.0327, -0.0810, -0.0404, 0.0570, 0.0740, 0.0829, 0.0845, 0.0817, -0.0239, -0.0444, -0.0221, 0.0216, 0.0103, -0.0631, 0.0831, -0.0273, 0.0756, 0.0022, 0.0407, 0.0072, 0.0374, -0.0608, 0.0424, -0.0585, 0.0505, -0.0455, 0.0268, -0.0950, -0.0642, 0.0843, 0.0760, -0.0889, -0.0617, -0.0916, 0.0102, -0.0269, -0.0011, 0.0318, 0.0278, -0.0160, 0.0159, -0.0817, 0.0768, -0.0876, -0.0524, -0.0332, -0.0583, 0.0053, 0.0503, -0.0342, -0.0319, -0.0562, 0.0376, -0.0696, 0.0735, 0.0222, -0.0775, -0.0072, 0.0294, 0.0994, -0.0355, -0.0809, -0.0539, 0.0245, 0.0670, 0.0032, 0.0891, -0.0694, -0.0994, 0.0126, 0.0629, 0.0936, 0.0058, -0.0073, 0.0498, 0.0616, -0.0912, -0.0490], requires_grad=True) Parameter containing: tensor([[ 0.0504, -0.0203, -0.0573, ..., 0.0253, 0.0642, -0.0088], [-0.0078, -0.0608, -0.0626, ..., -0.0350, -0.0028, -0.0634], [-0.0317, -0.0202, -0.0593, ..., -0.0280, 0.0571, -0.0114], ..., [ 0.0582, -0.0471, -0.0236, ..., 0.0273, 0.0673, 0.0555], [ 0.0258, -0.0706, 0.0315, ..., -0.0663, -0.0133, 0.0078], [-0.0062, 0.0544, -0.0280, ..., -0.0303, -0.0326, -0.0462]], requires_grad=True) Parameter containing: tensor([ 0.0385, -0.0116, 0.0703, 0.0407, -0.0346, -0.0178, 0.0308, -0.0502, 0.0616, 0.0114], requires_grad=True) Layer params: Parameter containing: tensor([[ 0.0504, -0.0203, -0.0573, ..., 0.0253, 0.0642, -0.0088], [-0.0078, -0.0608, -0.0626, ..., -0.0350, -0.0028, -0.0634], [-0.0317, -0.0202, -0.0593, ..., -0.0280, 0.0571, -0.0114], ..., [ 0.0582, -0.0471, -0.0236, ..., 0.0273, 0.0673, 0.0555], [ 0.0258, -0.0706, 0.0315, ..., -0.0663, -0.0133, 0.0078], [-0.0062, 0.0544, -0.0280, ..., -0.0303, -0.0326, -0.0462]], requires_grad=True) Parameter containing: tensor([ 0.0385, -0.0116, 0.0703, 0.0407, -0.0346, -0.0178, 0.0308, -0.0502, 0.0616, 0.0114], requires_grad=True)
这显示了 PyTorch 模型的基本结构:有一个__init__()
方法定义了模型的层和其他组件,还有一个forward()
方法用于执行计算。注意我们可以打印模型或其子模块来了解其结构。
常见的层类型
线性层
最基本的神经网络层类型是线性或全连接层。这是一个每个输入都影响层的每个输出的程度由层的权重指定的层。如果一个模型有m个输入和n个输出,权重将是一个m x n矩阵。例如:
lin = torch.nn.Linear(3, 2) x = torch.rand(1, 3) print('Input:') print(x) print('\n\nWeight and Bias parameters:') for param in lin.parameters(): print(param) y = lin(x) print('\n\nOutput:') print(y)
Input: tensor([[0.8790, 0.9774, 0.2547]]) Weight and Bias parameters: Parameter containing: tensor([[ 0.1656, 0.4969, -0.4972], [-0.2035, -0.2579, -0.3780]], requires_grad=True) Parameter containing: tensor([0.3768, 0.3781], requires_grad=True) Output: tensor([[ 0.8814, -0.1492]], grad_fn=<AddmmBackward0>)
如果对x
进行矩阵乘法,乘以线性层的权重,并加上偏置,你会发现得到输出向量y
。
还有一个重要的特点需要注意:当我们用lin.weight
检查层的权重时,它报告自己是一个Parameter
(它是Tensor
的子类),并告诉我们它正在使用 autograd 跟踪梯度。这是Parameter
的默认行为,与Tensor
不同。
线性层在深度学习模型中被广泛使用。你最常见到它们的地方之一是在分类器模型中,通常在末尾会有一个或多个线性层,最后一层将有n个输出,其中n是分类器处理的类的数量。
卷积层
卷积层被设计用于处理具有高度空间相关性的数据。它们在计算机视觉中非常常见,用于检测特征的紧密组合,然后将其组合成更高级的特征。它们也出现在其他上下文中 - 例如,在 NLP 应用中,一个词的即时上下文(即,序列中附近的其他词)可以影响句子的含义。
我们在早期的视频中看到了 LeNet5 中卷积层的作用:
import torch.functional as F class LeNet(torch.nn.Module): def __init__(self): super(LeNet, self).__init__() # 1 input image channel (black & white), 6 output channels, 5x5 square convolution # kernel self.conv1 = torch.nn.Conv2d(1, 6, 5) self.conv2 = torch.nn.Conv2d(6, 16, 3) # an affine operation: y = Wx + b self.fc1 = torch.nn.Linear(16 * 6 * 6, 120) # 6*6 from image dimension self.fc2 = torch.nn.Linear(120, 84) self.fc3 = torch.nn.Linear(84, 10) def forward(self, x): # Max pooling over a (2, 2) window x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) # If the size is a square you can only specify a single number x = F.max_pool2d(F.relu(self.conv2(x)), 2) x = x.view(-1, self.num_flat_features(x)) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x def num_flat_features(self, x): size = x.size()[1:] # all dimensions except the batch dimension num_features = 1 for s in size: num_features *= s return num_features
让我们分解一下这个模型的卷积层中发生的事情。从conv1
开始:
- LeNet5 旨在接收 1x32x32 的黑白图像。**卷积层构造函数的第一个参数是输入通道的数量。**这里是 1。如果我们构建这个模型来查看 3 色通道,那么它将是 3。
- 卷积层就像一个窗口,扫描图像,寻找它认识的模式。这些模式称为特征,卷积层的一个参数是我们希望它学习的特征数量。**构造函数的第二个参数是输出特征的数量。**在这里,我们要求我们的层学习 6 个特征。
- 在上面,我将卷积层比作一个窗口 - 但窗口有多大呢?**第三个参数是窗口或内核大小。**在这里,“5”表示我们选择了一个 5x5 的内核。(如果您想要高度与宽度不同的内核,可以为此参数指定一个元组 - 例如,
(3, 5)
以获得一个 3x5 的卷积内核。)
卷积层的输出是一个激活图 - 表示输入张量中特征存在的空间表示。conv1
将给我们一个 6x28x28 的输出张量;6 是特征的数量,28 是我们地图的高度和宽度。(28 来自于在 32 像素行上扫描 5 像素窗口时,只有 28 个有效位置的事实。)
然后我们通过 ReLU 激活函数(稍后会详细介绍激活函数)将卷积的输出传递,然后通过一个最大池化层。最大池化层将激活图中相邻的特征组合在一起。它通过减少张量,将输出中的每个 2x2 组合的单元格合并为一个单元格,并将该单元格分配为其中输入的 4 个单元格的最大值。这给我们一个激活图的低分辨率版本,尺寸为 6x14x14。
我们的下一个卷积层conv2
期望 6 个输入通道(对应于第一层寻找的 6 个特征),有 16 个输出通道和一个 3x3 的内核。它输出一个 16x12x12 的激活图,然后再通过最大池化层减少到 16x6x6。在将此输出传递给线性层之前,它被重新塑造为一个 16 * 6 * 6 = 576 元素的向量,以供下一层使用。
有用于处理 1D、2D 和 3D 张量的卷积层。卷积层构造函数还有许多可选参数,包括步长(例如,仅扫描每第二个或第三个位置)在输入中,填充(这样您可以扫描到输入的边缘)等。有关更多信息,请参阅文档。
循环层
循环神经网络(或RNNs)用于顺序数据 - 从科学仪器的时间序列测量到自然语言句子到 DNA 核苷酸。RNN 通过保持作为其迄今为止在序列中看到的记忆的隐藏状态来实现这一点。
RNN 层的内部结构 - 或其变体,LSTM(长短期记忆)和 GRU(门控循环单元) - 是适度复杂的,超出了本视频的范围,但我们将通过一个基于 LSTM 的词性标注器来展示其工作原理(一种告诉你一个词是名词、动词等的分类器):
class LSTMTagger(torch.nn.Module): def __init__(self, embedding_dim, hidden_dim, vocab_size, tagset_size): super(LSTMTagger, self).__init__() self.hidden_dim = hidden_dim self.word_embeddings = torch.nn.Embedding(vocab_size, embedding_dim) # The LSTM takes word embeddings as inputs, and outputs hidden states # with dimensionality hidden_dim. self.lstm = torch.nn.LSTM(embedding_dim, hidden_dim) # The linear layer that maps from hidden state space to tag space self.hidden2tag = torch.nn.Linear(hidden_dim, tagset_size) def forward(self, sentence): embeds = self.word_embeddings(sentence) lstm_out, _ = self.lstm(embeds.view(len(sentence), 1, -1)) tag_space = self.hidden2tag(lstm_out.view(len(sentence), -1)) tag_scores = F.log_softmax(tag_space, dim=1) return tag_scores
构造函数有四个参数:
vocab_size
是输入词汇表中单词的数量。每个单词是一个在vocab_size
维空间中的单热向量(或单位向量)。tagset_size
是输出集合中标签的数量。embedding_dim
是词汇表的嵌入空间的大小。嵌入将词汇表映射到一个低维空间,其中具有相似含义的单词在空间中靠在一起。hidden_dim
是 LSTM 的记忆大小。
输入将是一个句子,其中单词表示为单热向量的索引。嵌入层将把这些映射到一个embedding_dim
维空间。LSTM 接受这些嵌入的序列并对其进行迭代,生成一个长度为hidden_dim
的输出向量。最终的线性层充当分类器;将log_softmax()
应用于最终层的输出将输出转换为给定单词映射到给定标签的估计概率的归一化集。
如果您想看到这个网络的运行情况,请查看 pytorch.org 上的序列模型和 LSTM 网络教程。
变压器
变压器是多功能网络,已经在 NLP 领域的最新技术中占据主导地位,如 BERT 模型。变压器架构的讨论超出了本视频的范围,但 PyTorch 有一个Transformer
类,允许您定义变压器模型的整体参数 - 注意头的数量,编码器和解码器层数的数量,dropout 和激活函数等(您甚至可以根据正确的参数从这个单一类构建 BERT 模型!)。torch.nn.Transformer
类还有类来封装各个组件(TransformerEncoder
,TransformerDecoder
)和子组件(TransformerEncoderLayer
,TransformerDecoderLayer
)。有关详细信息,请查看 pytorch.org 上有关变压器类的文档,以及有关 pytorch.org 上相关的教程。
其他层和函数
数据操作层
还有其他层类型在模型中执行重要功能,但本身不参与学习过程。
最大池化(以及它的孪生,最小池化)通过组合单元格并将输入单元格的最大值分配给输出单元格来减少张量(我们看到了这一点)。例如:
my_tensor = torch.rand(1, 6, 6) print(my_tensor) maxpool_layer = torch.nn.MaxPool2d(3) print(maxpool_layer(my_tensor))
tensor([[[0.5036, 0.6285, 0.3460, 0.7817, 0.9876, 0.0074], [0.3969, 0.7950, 0.1449, 0.4110, 0.8216, 0.6235], [0.2347, 0.3741, 0.4997, 0.9737, 0.1741, 0.4616], [0.3962, 0.9970, 0.8778, 0.4292, 0.2772, 0.9926], [0.4406, 0.3624, 0.8960, 0.6484, 0.5544, 0.9501], [0.2489, 0.8971, 0.7499, 0.1803, 0.9571, 0.6733]]]) tensor([[[0.7950, 0.9876], [0.9970, 0.9926]]])
如果您仔细查看上面的数值,您会发现 maxpooled 输出中的每个值都是 6x6 输入的每个象限的最大值。
归一化层在将一个层的输出重新居中和归一化之前将其馈送到另一个层。对中间张量进行居中和缩放具有许多有益的效果,例如让您在不爆炸/消失梯度的情况下使用更高的学习速率。
my_tensor = torch.rand(1, 4, 4) * 20 + 5 print(my_tensor) print(my_tensor.mean()) norm_layer = torch.nn.BatchNorm1d(4) normed_tensor = norm_layer(my_tensor) print(normed_tensor) print(normed_tensor.mean())
tensor([[[ 7.7375, 23.5649, 6.8452, 16.3517], [19.5792, 20.3254, 6.1930, 23.7576], [23.7554, 20.8565, 18.4241, 8.5742], [22.5100, 15.6154, 13.5698, 11.8411]]]) tensor(16.2188) tensor([[[-0.8614, 1.4543, -0.9919, 0.3990], [ 0.3160, 0.4274, -1.6834, 0.9400], [ 1.0256, 0.5176, 0.0914, -1.6346], [ 1.6352, -0.0663, -0.5711, -0.9978]]], grad_fn=<NativeBatchNormBackward0>) tensor(3.3528e-08, grad_fn=<MeanBackward0>)
运行上面的单元格,我们向输入张量添加了一个大的缩放因子和偏移量;您应该看到输入张量的mean()
大约在 15 的附近。通过归一化层后,您会看到值变小,并围绕零分组 - 实际上,均值应该非常小(> 1e-8)。
这是有益的,因为许多激活函数(下面讨论)在 0 附近具有最强的梯度,但有时会因为输入将它们远离零而出现消失或爆炸梯度。保持数据围绕梯度最陡峭的区域将倾向于意味着更快、更好的学习和更高的可行学习速度。
Dropout 层是鼓励模型中稀疏表示的工具 - 也就是说,推动它使用更少的数据进行推理。
Dropout 层通过在训练期间随机设置输入张量的部分来工作 - 推断时始终关闭 dropout 层。这迫使模型学习针对这个掩码或减少的数据集。例如:
my_tensor = torch.rand(1, 4, 4) dropout = torch.nn.Dropout(p=0.4) print(dropout(my_tensor)) print(dropout(my_tensor))
tensor([[[0.8869, 0.6595, 0.2098, 0.0000], [0.5379, 0.0000, 0.0000, 0.0000], [0.1950, 0.2424, 1.3319, 0.5738], [0.5676, 0.8335, 0.0000, 0.2928]]]) tensor([[[0.8869, 0.6595, 0.2098, 0.2878], [0.5379, 0.0000, 0.4029, 0.0000], [0.0000, 0.2424, 1.3319, 0.5738], [0.0000, 0.8335, 0.9647, 0.0000]]])
在上面,您可以看到对样本张量的 dropout 效果。您可以使用可选的p
参数设置单个权重丢失的概率;如果不设置,默认为 0.5。
激活函数
激活函数使深度学习成为可能。神经网络实际上是一个程序 - 具有许多参数 - 模拟数学函数。如果我们只是重复地将张量乘以层权重,我们只能模拟线性函数;此外,拥有许多层也没有意义,因为整个网络可以简化为单个矩阵乘法。在层之间插入非线性激活函数是让深度学习模型能够模拟任何函数,而不仅仅是线性函数的关键。
torch.nn.Module
包含了封装所有主要激活函数的对象,包括 ReLU 及其许多变体,Tanh,Hardtanh,sigmoid 等。它还包括其他函数,如 Softmax,在模型的输出阶段最有用。
损失函数
损失函数告诉我们模型的预测与正确答案之间有多远。PyTorch 包含各种损失函数,包括常见的 MSE(均方误差 = L2 范数),交叉熵损失和负对数似然损失(对分类器有用),以及其他函数。
脚本的总运行时间:(0 分钟 0.029 秒)
下载 Python 源代码:modelsyt_tutorial.py
下载 Jupyter 笔记本:modelsyt_tutorial.ipynb
PyTorch TensorBoard 支持
原文:
pytorch.org/tutorials/beginner/introyt/tensorboardyt_tutorial.html
译者:飞龙
注意
点击这里下载完整示例代码
介绍 || 张量 || 自动微分 || 构建模型 || TensorBoard 支持 || 训练模型 || 模型理解
请跟随下面的视频或youtube。
www.youtube.com/embed/6CEld3hZgqc
开始之前
要运行此教程,您需要安装 PyTorch、TorchVision、Matplotlib 和 TensorBoard。
使用conda
:
conda install pytorch torchvision -c pytorch conda install matplotlib tensorboard
使用pip
:
pip install torch torchvision matplotlib tensorboard
安装完依赖项后,在安装它们的 Python 环境中重新启动此笔记本。
介绍
在本笔记本中,我们将针对时尚-MNIST 数据集训练 LeNet-5 的变体。时尚-MNIST 是一组图像瓷砖,描绘了各种服装,有十个类标签表示所描绘的服装类型。
# PyTorch model and training necessities import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim # Image datasets and image manipulation import torchvision import torchvision.transforms as transforms # Image display import matplotlib.pyplot as plt import numpy as np # PyTorch TensorBoard support from torch.utils.tensorboard import SummaryWriter # In case you are using an environment that has TensorFlow installed, # such as Google Colab, uncomment the following code to avoid # a bug with saving embeddings to your TensorBoard directory # import tensorflow as tf # import tensorboard as tb # tf.io.gfile = tb.compat.tensorflow_stub.io.gfile
在 TensorBoard 中显示图像
让我们从我们的数据集中向 TensorBoard 添加样本图像:
# Gather datasets and prepare them for consumption transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) # Store separate training and validations splits in ./data training_set = torchvision.datasets.FashionMNIST('./data', download=True, train=True, transform=transform) validation_set = torchvision.datasets.FashionMNIST('./data', download=True, train=False, transform=transform) training_loader = torch.utils.data.DataLoader(training_set, batch_size=4, shuffle=True, num_workers=2) validation_loader = torch.utils.data.DataLoader(validation_set, batch_size=4, shuffle=False, num_workers=2) # Class labels classes = ('T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle Boot') # Helper function for inline image display def matplotlib_imshow(img, one_channel=False): if one_channel: img = img.mean(dim=0) img = img / 2 + 0.5 # unnormalize npimg = img.numpy() if one_channel: plt.imshow(npimg, cmap="Greys") else: plt.imshow(np.transpose(npimg, (1, 2, 0))) # Extract a batch of 4 images dataiter = iter(training_loader) images, labels = next(dataiter) # Create a grid from the images and show them img_grid = torchvision.utils.make_grid(images) matplotlib_imshow(img_grid, one_channel=True)
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to ./data/FashionMNIST/raw/train-images-idx3-ubyte.gz 0%| | 0/26421880 [00:00<?, ?it/s] 0%| | 65536/26421880 [00:00<01:09, 378414.86it/s] 1%| | 229376/26421880 [00:00<00:37, 693250.36it/s] 4%|3 | 950272/26421880 [00:00<00:11, 2219214.26it/s] 15%|#4 | 3833856/26421880 [00:00<00:02, 7688687.97it/s] 35%|###5 | 9273344/26421880 [00:00<00:01, 15802443.73it/s] 58%|#####7 | 15204352/26421880 [00:01<00:00, 21640902.59it/s] 80%|#######9 | 21102592/26421880 [00:01<00:00, 25246743.30it/s] 100%|##########| 26421880/26421880 [00:01<00:00, 19515987.25it/s] Extracting ./data/FashionMNIST/raw/train-images-idx3-ubyte.gz to ./data/FashionMNIST/raw Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz to ./data/FashionMNIST/raw/train-labels-idx1-ubyte.gz 0%| | 0/29515 [00:00<?, ?it/s] 100%|##########| 29515/29515 [00:00<00:00, 329627.44it/s] Extracting ./data/FashionMNIST/raw/train-labels-idx1-ubyte.gz to ./data/FashionMNIST/raw Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz to ./data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz 0%| | 0/4422102 [00:00<?, ?it/s] 1%|1 | 65536/4422102 [00:00<00:11, 363060.61it/s] 5%|5 | 229376/4422102 [00:00<00:06, 683092.95it/s] 19%|#8 | 819200/4422102 [00:00<00:01, 1861301.92it/s] 64%|######4 | 2850816/4422102 [00:00<00:00, 5548383.23it/s] 100%|##########| 4422102/4422102 [00:00<00:00, 6080037.27it/s] Extracting ./data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz to ./data/FashionMNIST/raw Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz to ./data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz 0%| | 0/5148 [00:00<?, ?it/s] 100%|##########| 5148/5148 [00:00<00:00, 39618856.87it/s] Extracting ./data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz to ./data/FashionMNIST/raw
以上,我们使用 TorchVision 和 Matplotlib 创建了一个输入数据小批量的可视网格。下面,我们使用SummaryWriter
上的add_image()
调用来记录图像以供 TensorBoard 使用,并调用flush()
确保它立即写入磁盘。
# Default log_dir argument is "runs" - but it's good to be specific # torch.utils.tensorboard.SummaryWriter is imported above writer = SummaryWriter('runs/fashion_mnist_experiment_1') # Write image data to TensorBoard log dir writer.add_image('Four Fashion-MNIST Images', img_grid) writer.flush() # To view, start TensorBoard on the command line with: # tensorboard --logdir=runs # ...and open a browser tab to http://localhost:6006/
如果您在命令行启动 TensorBoard 并在新的浏览器选项卡中打开它(通常在 localhost:6006),您应该在 IMAGES 选项卡下看到图像网格。
绘制标量以可视化训练
TensorBoard 对于跟踪训练的进展和有效性非常有用。在下面,我们将运行一个训练循环,跟踪一些指标,并保存数据供 TensorBoard 使用。
让我们定义一个模型来对我们的图像瓷砖进行分类,以及用于训练的优化器和损失函数:
class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 6, 5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(6, 16, 5) self.fc1 = nn.Linear(16 * 4 * 4, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): x = self.pool(F.relu(self.conv1(x))) x = self.pool(F.relu(self.conv2(x))) x = x.view(-1, 16 * 4 * 4) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x net = Net() criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
现在让我们训练一个 epoch,并在每 1000 批次时评估训练与验证集的损失:
print(len(validation_loader)) for epoch in range(1): # loop over the dataset multiple times running_loss = 0.0 for i, data in enumerate(training_loader, 0): # basic training loop inputs, labels = data optimizer.zero_grad() outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() if i % 1000 == 999: # Every 1000 mini-batches... print('Batch {}'.format(i + 1)) # Check against the validation set running_vloss = 0.0 # In evaluation mode some model specific operations can be omitted eg. dropout layer net.train(False) # Switching to evaluation mode, eg. turning off regularisation for j, vdata in enumerate(validation_loader, 0): vinputs, vlabels = vdata voutputs = net(vinputs) vloss = criterion(voutputs, vlabels) running_vloss += vloss.item() net.train(True) # Switching back to training mode, eg. turning on regularisation avg_loss = running_loss / 1000 avg_vloss = running_vloss / len(validation_loader) # Log the running loss averaged per batch writer.add_scalars('Training vs. Validation Loss', { 'Training' : avg_loss, 'Validation' : avg_vloss }, epoch * len(training_loader) + i) running_loss = 0.0 print('Finished Training') writer.flush()
2500 Batch 1000 Batch 2000 Batch 3000 Batch 4000 Batch 5000 Batch 6000 Batch 7000 Batch 8000 Batch 9000 Batch 10000 Batch 11000 Batch 12000 Batch 13000 Batch 14000 Batch 15000 Finished Training
切换到您打开的 TensorBoard,并查看 SCALARS 选项卡。
可视化您的模型
TensorBoard 还可以用于检查模型内部的数据流。为此,请使用模型和样本输入调用add_graph()
方法。当您打开
# Again, grab a single mini-batch of images dataiter = iter(training_loader) images, labels = next(dataiter) # add_graph() will trace the sample input through your model, # and render it as a graph. writer.add_graph(net, images) writer.flush()
当您切换到 TensorBoard 时,您应该看到一个 GRAPHS 选项卡。双击“NET”节点以查看模型内部的层和数据流。
使用嵌入可视化您的数据集
我们使用的 28x28 图像瓷砖可以建模为 784 维向量(28 * 28 = 784)。将其投影到较低维度表示可能很有启发性。add_embedding()
方法将一组数据投影到具有最高方差的三个维度,并将它们显示为交互式 3D 图表。add_embedding()
方法通过自动投影到具有最高方差的三个维度来实现这一点。
接下来,我们将取一部分数据,并生成这样一个嵌入:
# Select a random subset of data and corresponding labels def select_n_random(data, labels, n=100): assert len(data) == len(labels) perm = torch.randperm(len(data)) return data[perm][:n], labels[perm][:n] # Extract a random subset of data images, labels = select_n_random(training_set.data, training_set.targets) # get the class labels for each image class_labels = [classes[label] for label in labels] # log embeddings features = images.view(-1, 28 * 28) writer.add_embedding(features, metadata=class_labels, label_img=images.unsqueeze(1)) writer.flush() writer.close()
现在,如果您切换到 TensorBoard 并选择 PROJECTOR 选项卡,您应该看到投影的 3D 表示。您可以旋转和缩放模型。在大尺度和小尺度上检查它,并查看是否可以在投影数据和标签的聚类中发现模式。
为了更好地可见,建议:
- 从左侧的“按颜色分类”下拉菜单中选择“标签”。
- 在顶部切换到夜间模式图标,将浅色图像放在黑色背景上。
其他资源
有关更多信息,请查看:
- PyTorch 关于torch.utils.tensorboard.SummaryWriter的文档
- PyTorch.org 教程中的 Tensorboard 教程内容
- 有关 TensorBoard 的更多信息,请参阅TensorBoard 文档
脚本的总运行时间:(2 分钟 34.092 秒)
下载 Python 源代码:tensorboardyt_tutorial.py
下载 Jupyter 笔记本:tensorboardyt_tutorial.ipynb
使用 PyTorch 进行训练
原文:
pytorch.org/tutorials/beginner/introyt/trainingyt.html
译者:飞龙
注意
点击这里下载完整示例代码
介绍 || 张量 || 自动微分 || 构建模型 || TensorBoard 支持 || 训练模型 || 模型理解
跟随下面的视频或者在youtube上进行操作。
www.youtube.com/embed/jF43_wj_DCQ
介绍
在过去的视频中,我们已经讨论并演示了:
- 使用 torch.nn 模块的神经网络层和函数构建模型
- 自动梯度计算的机制是基于梯度的模型训练的核心
- 使用 TensorBoard 来可视化训练进度和其他活动
在这个视频中,我们将为您的工具库添加一些新工具:
- 我们将熟悉数据集和数据加载器的抽象,以及它们如何简化在训练循环中向模型提供数据的过程
- 我们将讨论具体的损失函数以及何时使用它们
- 我们将研究 PyTorch 优化器,它们实现了根据损失函数的结果调整模型权重的算法。
最后,我们将把所有这些内容整合在一起,看到完整的 PyTorch 训练循环的运行。
PyTorch 2.2 中文官方教程(三)(2)https://developer.aliyun.com/article/1482488