Machine-Learning-Mastery-PyTorch-教程-八-

41 阅读46分钟

Machine Learning Mastery PyTorch 教程(八)

原文:Machine Learning Mastery

协议:CC BY-NC-SA 4.0

使用 PyTorch 的 Autograd 解决回归问题

原文:machinelearningmastery.com/using-autograd-in-pytorch-to-solve-a-regression-problem/

我们通常使用 PyTorch 来构建神经网络。然而,PyTorch 不仅仅能做到这些。由于 PyTorch 还是一个具有自动微分能力的张量库,你可以轻松使用它来解决梯度下降的数值优化问题。在这篇文章中,你将学习 PyTorch 的自动微分引擎 autograd 是如何工作的。

完成此教程后,你将学到:

  • PyTorch 中的 autograd 是什么

  • 如何利用 autograd 和优化器解决优化问题

快速启动你的项目,参考我的书籍 《PyTorch 深度学习》。它提供了 自学教程可运行的代码

让我们开始吧!

使用 PyTorch 中的 autograd 来解决回归问题。

图片由 Billy Kwok 提供。版权所有。

概述

本教程分为三个部分:

  • PyTorch 中的 Autograd

  • 使用 Autograd 进行多项式回归

  • 使用 Autograd 解决数学难题

PyTorch 中的 Autograd

在 PyTorch 中,你可以将张量创建为变量或常量,并用它们构建表达式。这个表达式本质上是变量张量的函数。因此,你可以推导出其导数函数,即微分或梯度。这是深度学习模型训练循环的基础。PyTorch 核心中包含了这一特性。

用一个示例来解释 autograd 更容易。在 PyTorch 中,你可以如下创建一个常量矩阵:

import torch

x = torch.tensor([1, 2, 3])
print(x)
print(x.shape)
print(x.dtype)

上述打印:

tensor([1, 2, 3])
torch.Size([3])
torch.int64

这将创建一个整数向量(以 PyTorch 张量的形式)。这个向量在大多数情况下可以像 NumPy 向量一样工作。例如,你可以进行 x+x2*x,结果正是你所期望的。PyTorch 配有许多与 NumPy 匹配的数组操作函数,如 torch.transposetorch.concatenate

但这个张量不被视为函数的变量,因为不支持对其进行微分。你可以通过一个额外的选项创建像变量一样工作的张量:

import torch

x = torch.tensor([1., 2., 3.], requires_grad=True)
print(x)
print(x.shape)
print(x.dtype)

这将打印:

tensor([1., 2., 3.], requires_grad=True)
torch.Size([3])
torch.float32

请注意,上述创建了一个浮点值的张量。这是必要的,因为微分需要浮点数,而不是整数。

操作(如 x+x2*x)仍然可以应用,但在这种情况下,张量将记住它如何获得其值。你可以在以下示例中演示这一特性:

import torch

x = torch.tensor(3.6, requires_grad=True)
y = x * x
y.backward()
print(x.grad)

这将打印:

tensor(7.2000)

它的作用如下:这定义了一个变量 x(值为 3.6),然后计算 y=x*xy=x2y=x²。然后你请求 yy 的微分。由于 yy 的值来源于 xx,你可以在运行 y.backward() 之后立即在 x.grad 中以张量形式找到 dydx\dfrac{dy}{dx}。你知道 y=x2y=x² 意味着 y=2xy’=2x。因此输出会给你 3.6×2=7.23.6\times 2=7.2 的值。

想要开始使用 PyTorch 进行深度学习?

现在立即报名我的免费电子邮件速成课程(包括示例代码)。

点击报名,还可以获得免费 PDF 电子书版本的课程。

使用 Autograd 进行多项式回归

PyTorch 中这个特性有什么帮助?假设你有一个形式为 y=f(x)y=f(x) 的多项式,并且你得到了一些 (x,y)(x,y) 样本。你如何恢复多项式 f(x)f(x)?一种方法是对多项式假设一个随机系数,并将样本 (x,y)(x,y) 输入进去。如果多项式被找到,你应该看到 yy 的值与 f(x)f(x) 匹配。它们越接近,你的估计就越接近正确的多项式。

这确实是一个数值优化问题,你想最小化 yyf(x)f(x) 之间的差异。你可以使用梯度下降来解决它。

让我们考虑一个例子。你可以按照如下方式在 NumPy 中构建一个多项式 f(x)=x2+2x+3f(x)=x² + 2x + 3

import numpy as np

polynomial = np.poly1d([1, 2, 3])
print(polynomial)

这将输出:

   2
1 x + 2 x + 3

你可以将多项式用作函数,例如:

print(polynomial(1.5))

这将输出 8.25,因为 (1.5)2+2×(1.5)+3=8.25(1.5)²+2\times(1.5)+3 = 8.25

现在你可以使用 NumPy 从这个函数生成大量样本:

N = 20   # number of samples

# Generate random samples roughly between -10 to +10
X = np.random.randn(N,1) * 5
Y = polynomial(X)

上述内容中,XY 都是形状为 (20,1) 的 NumPy 数组,它们与多项式 f(x)f(x)y=f(x)y=f(x) 相关。

现在,假设你不知道这个多项式是什么,只知道它是二次的。你想恢复系数。由于二次多项式的形式为 Ax2+Bx+CAx²+Bx+C,你有三个未知数需要找出。你可以使用你实现的梯度下降算法或现有的梯度下降优化器来找到它们。以下展示了它是如何工作的:

import torch

# Assume samples X and Y are prepared elsewhere

XX = np.hstack([X*X, X, np.ones_like(X)])

w = torch.randn(3, 1, requires_grad=True)  # the 3 coefficients
x = torch.tensor(XX, dtype=torch.float32)  # input sample
y = torch.tensor(Y, dtype=torch.float32)   # output sample
optimizer = torch.optim.NAdam([w], lr=0.01)
print(w)

for _ in range(1000):
    optimizer.zero_grad()
    y_pred = x @ w
    mse = torch.mean(torch.square(y - y_pred))
    mse.backward()
    optimizer.step()

print(w)

循环之前的 print 语句给出了三个随机数字,例如:

tensor([[1.3827],
        [0.8629],
        [0.2357]], requires_grad=True)

但在循环之后的结果会给你非常接近多项式中的系数:

tensor([[1.0004],
        [1.9924],
        [2.9159]], requires_grad=True)

上述代码的作用如下:首先,它创建了一个包含 3 个值的变量向量 w,即系数 A,B,CA,B,C。然后,你创建了一个形状为 (N,3)(N,3) 的数组,其中 NN 是数组 X 中样本的数量。这个数组有 3 列:分别是 x2xx 和 1。这样的数组是通过 np.hstack() 函数从向量 X 构建的。类似地,你可以从 NumPy 数组 Y 构建 TensorFlow 常量 y

随后,你使用 for 循环在 1,000 次迭代中运行梯度下降。在每次迭代中,你以矩阵形式计算x×wx \times w以找到Ax2+Bx+CAx²+Bx+C并将其分配给变量y_pred。然后,比较yy_pred并计算均方误差。接下来,使用backward()函数导出梯度,即均方误差相对于系数w的变化率。根据这个梯度,你通过优化器使用梯度下降更新w

本质上,上述代码将找到最小化均方误差的系数w

综合以上,以下是完整的代码:

import numpy as np
import torch

polynomial = np.poly1d([1, 2, 3])
N = 20   # number of samples

# Generate random samples roughly between -10 to +10
X = np.random.randn(N,1) * 5
Y = polynomial(X)

# Prepare input as an array of shape (N,3)
XX = np.hstack([X*X, X, np.ones_like(X)])

# Prepare tensors
w = torch.randn(3, 1, requires_grad=True)  # the 3 coefficients
x = torch.tensor(XX, dtype=torch.float32)  # input sample
y = torch.tensor(Y, dtype=torch.float32)   # output sample
optimizer = torch.optim.NAdam([w], lr=0.01)
print(w)

# Run optimizer
for _ in range(1000):
    optimizer.zero_grad()
    y_pred = x @ w
    mse = torch.mean(torch.square(y - y_pred))
    mse.backward()
    optimizer.step()

print(w)

使用自动微分解决数学难题

在上述中,使用了 20 个样本,这足以拟合一个二次方程。你也可以使用梯度下降来解决一些数学难题。例如,以下问题:

[ A ]  +  [ B ]  =  9
  +         -
[ C ]  -  [ D ]  =  1
  =         =
  8         2

换句话说,要找到A,B,C,DA,B,C,D的值,使得:

A + B &= 9 \\ C – D &= 1 \\ A + C &= 8 \\ B – D &= 2 \end{aligned}$$ 这也可以使用自动微分来解决,如下所示: ```py import random import torch A = torch.tensor(random.random(), requires_grad=True) B = torch.tensor(random.random(), requires_grad=True) C = torch.tensor(random.random(), requires_grad=True) D = torch.tensor(random.random(), requires_grad=True) # Gradient descent loop EPOCHS = 2000 optimizer = torch.optim.NAdam([A, B, C, D], lr=0.01) for _ in range(EPOCHS): y1 = A + B - 9 y2 = C - D - 1 y3 = A + C - 8 y4 = B - D - 2 sqerr = y1*y1 + y2*y2 + y3*y3 + y4*y4 optimizer.zero_grad() sqerr.backward() optimizer.step() print(A) print(B) print(C) print(D) ``` 这个问题可能有多个解决方案。一个解决方案如下: ```py tensor(4.7191, requires_grad=True) tensor(4.2808, requires_grad=True) tensor(3.2808, requires_grad=True) tensor(2.2808, requires_grad=True) ``` 这意味着$A=4.72$,$B=4.28$,$C=3.28$,$D=2.28$。你可以验证这个解是否符合问题要求。 上述代码将四个未知数定义为具有随机初始值的变量。然后你计算四个方程的结果并与期望答案进行比较。接着,你将平方误差求和,并要求 PyTorch 的优化器最小化它。最小的平方误差是零,当我们的解完全符合问题时实现。 注意 PyTorch 生成梯度的方式:你要求`sqerr`的梯度,它注意到,除了其他内容外,只有`A`、`B`、`C`和`D`是其依赖项,且`requires_grad=True`。因此找到四个梯度。然后,你通过优化器在每次迭代中将每个梯度应用到相应的变量上。 ## 进一步阅读 如果你想深入了解这个主题,本节提供了更多资源。 **文章:** + [自动微分机制](https://pytorch.org/docs/stable/notes/autograd.html) + [自动微分包 – torch.autograd](https://pytorch.org/docs/stable/autograd.html) ## 总结 在这篇文章中,我们展示了 PyTorch 的自动微分是如何工作的。这是进行深度学习训练的基础。具体来说,你学到了: + PyTorch 中的自动微分是什么 + 如何使用梯度记录来进行自动微分 + 如何使用自动微分来解决优化问题 # 在 PyTorch 中使用数据集类 > 原文:[`machinelearningmastery.com/using-dataset-classes-in-pytorch/`](https://machinelearningmastery.com/using-dataset-classes-in-pytorch/) 在机器学习和深度学习问题中,大量的工作都在于准备数据。数据通常是混乱的,需要在用于训练模型之前进行预处理。如果数据准备不正确,模型将无法很好地泛化。 数据预处理的一些常见步骤包括: + 数据归一化:这包括将数据归一化到数据集中的一个值范围内。 + 数据增强:这包括通过添加噪声或特征的位移来生成新的样本,使它们更加多样化。 数据准备是任何机器学习流程中的关键步骤。PyTorch 带来了许多模块,例如 torchvision,它提供了数据集和数据集类,使数据准备变得容易。 在本教程中,我们将演示如何在 PyTorch 中使用数据集和变换,以便你可以创建自己的自定义数据集类,并以你希望的方式操作数据集。特别是,你将学习: + 如何创建一个简单的数据集类并对其应用变换。 + 如何构建可调用的变换并将其应用于数据集对象。 + 如何在数据集对象上组合各种变换。 请注意,在这里你将使用简单的数据集来对概念有一个总体了解,而在本教程的下一部分,你将有机会使用图像的数据集对象。 **通过我的书籍[《深度学习与 PyTorch》](https://machinelearningmastery.com/deep-learning-with-pytorch/)** 来**启动你的项目**。它提供了**自学教程**和**可运行的代码**。 让我们开始吧![](../Images/f1c2c2ed936adbba5fbf14c3624f34b9.png) 在 PyTorch 中使用数据集类 图片由[NASA](https://unsplash.com/photos/1lfI7wkGWZ4)提供。保留所有权利。 ## 概述 本教程分为三部分,它们是: + 创建一个简单的数据集类 + 创建可调用的变换 + 为数据集组合多个变换 ## 创建一个简单的数据集类 在开始之前,我们需要导入一些包,然后创建数据集类。 ```py import torch from torch.utils.data import Dataset torch.manual_seed(42) ``` 我们将从`torch.utils.data`中导入抽象类`Dataset`。因此,我们在数据集类中重写以下方法: + `__len__` 使得 `len(dataset)` 可以告诉我们数据集的大小。 + `__getitem__` 用于通过支持索引操作来访问数据集中的数据样本。例如,`dataset[i]` 可用于检索第 i 个数据样本。 同样,`torch.manual_seed()` 强制随机函数每次重新编译时生成相同的数字。 现在,让我们定义数据集类。 ```py class SimpleDataset(Dataset): # defining values in the constructor def __init__(self, data_length = 20, transform = None): self.x = 3 * torch.eye(data_length, 2) self.y = torch.eye(data_length, 4) self.transform = transform self.len = data_length # Getting the data samples def __getitem__(self, idx): sample = self.x[idx], self.y[idx] if self.transform: sample = self.transform(sample) return sample # Getting data size/length def __len__(self): return self.len ``` 在对象构造函数中,我们创建了特征和目标的值,即`x`和`y`,并将其值分配给张量`self.x`和`self.y`。每个张量包含 20 个数据样本,而属性`data_length`存储数据样本的数量。我们将在教程的后面讨论转换。 `SimpleDataset`对象的行为类似于任何 Python 可迭代对象,如列表或元组。现在,让我们创建`SimpleDataset`对象,查看其总长度和索引 1 处的值。 ```py dataset = SimpleDataset() print("length of the SimpleDataset object: ", len(dataset)) print("accessing value at index 1 of the simple_dataset object: ", dataset[1]) ``` 这将打印 ```py length of the SimpleDataset object: 20 accessing value at index 1 of the simple_dataset object: (tensor([0., 3.]), tensor([0., 1., 0., 0.])) ``` 由于我们的数据集是可迭代的,让我们使用循环打印出前四个元素: ```py for i in range(4): x, y = dataset[i] print(x, y) ``` 这将打印 ```py tensor([3., 0.]) tensor([1., 0., 0., 0.]) tensor([0., 3.]) tensor([0., 1., 0., 0.]) tensor([0., 0.]) tensor([0., 0., 1., 0.]) tensor([0., 0.]) tensor([0., 0., 0., 1.]) ``` ## 创建可调用的转换 在某些情况下,你需要创建可调用的转换,以便规范化或标准化数据。这些转换可以应用于张量。让我们创建一个可调用的转换,并将其应用于我们在本教程中早些时候创建的“简单数据集”对象。 ```py # Creating a callable tranform class mult_divide class MultDivide: # Constructor def __init__(self, mult_x = 2, divide_y = 3): self.mult_x = mult_x self.divide_y = divide_y # caller def __call__(self, sample): x = sample[0] y = sample[1] x = x * self.mult_x y = y / self.divide_y sample = x, y return sample ``` 我们创建了一个简单的自定义转换`MultDivide`,它将`x`乘以`2`并将`y`除以`3`。这没有实际用途,而是为了演示一个可调用的类如何作为我们数据集类的转换。记住,我们在`simple_dataset`中声明了一个参数`transform = None`。现在,我们可以用刚创建的自定义转换对象替换那个`None`。 那么,让我们展示一下如何操作,并在数据集上调用这个转换对象,以查看它如何转换数据集的前四个元素。 ```py # calling the transform object mul_div = MultDivide() custom_dataset = SimpleDataset(transform = mul_div) for i in range(4): x, y = dataset[i] print('Idx: ', i, 'Original_x: ', x, 'Original_y: ', y) x_, y_ = custom_dataset[i] print('Idx: ', i, 'Transformed_x:', x_, 'Transformed_y:', y_) ``` 这将打印 ```py Idx: 0 Original_x: tensor([3., 0.]) Original_y: tensor([1., 0., 0., 0.]) Idx: 0 Transformed_x: tensor([6., 0.]) Transformed_y: tensor([0.3333, 0.0000, 0.0000, 0.0000]) Idx: 1 Original_x: tensor([0., 3.]) Original_y: tensor([0., 1., 0., 0.]) Idx: 1 Transformed_x: tensor([0., 6.]) Transformed_y: tensor([0.0000, 0.3333, 0.0000, 0.0000]) Idx: 2 Original_x: tensor([0., 0.]) Original_y: tensor([0., 0., 1., 0.]) Idx: 2 Transformed_x: tensor([0., 0.]) Transformed_y: tensor([0.0000, 0.0000, 0.3333, 0.0000]) Idx: 3 Original_x: tensor([0., 0.]) Original_y: tensor([0., 0., 0., 1.]) Idx: 3 Transformed_x: tensor([0., 0.]) Transformed_y: tensor([0.0000, 0.0000, 0.0000, 0.3333]) ``` 如你所见,转换已成功应用于数据集的前四个元素。 ### 想要开始使用 PyTorch 进行深度学习吗? 现在就获取我的免费电子邮件速成课程(附带示例代码)。 点击注册,还可以获得课程的免费 PDF 电子书版本。 ## 为数据集组合多个转换 我们经常希望对数据集执行多个串行转换。这可以通过从 torchvision 中的 transforms 模块导入`Compose`类来实现。例如,假设我们构建了另一个转换`SubtractOne`,并将其应用于我们的数据集,除了我们之前创建的`MultDivide`转换。 一旦应用,新创建的转换将从数据集的每个元素中减去 1。 ```py from torchvision import transforms # Creating subtract_one tranform class SubtractOne: # Constructor def __init__(self, number = 1): self.number = number # caller def __call__(self, sample): x = sample[0] y = sample[1] x = x - self.number y = y - self.number sample = x, y return sample ``` 如前所述,现在我们将用`Compose`方法组合这两个转换。 ```py # Composing multiple transforms mult_transforms = transforms.Compose([MultDivide(), SubtractOne()]) ``` 请注意,首先将对数据集应用`MultDivide`转换,然后在转换后的数据集元素上应用`SubtractOne`转换。 我们将把包含两个转换(即`MultDivide()`和`SubtractOne()`)组合的`Compose`对象传递给我们的`SimpleDataset`对象。 ```py # Creating a new simple_dataset object with multiple transforms new_dataset = SimpleDataset(transform = mult_transforms) ``` 现在,多个转换的组合已应用于数据集,让我们打印出转换后的数据集的前四个元素。 ```py for i in range(4): x, y = dataset[i] print('Idx: ', i, 'Original_x: ', x, 'Original_y: ', y) x_, y_ = new_dataset[i] print('Idx: ', i, 'Transformed x_:', x_, 'Transformed y_:', y_) ``` 将所有内容放在一起,完整的代码如下: ```py import torch from torch.utils.data import Dataset from torchvision import transforms torch.manual_seed(2) class SimpleDataset(Dataset): # defining values in the constructor def __init__(self, data_length = 20, transform = None): self.x = 3 * torch.eye(data_length, 2) self.y = torch.eye(data_length, 4) self.transform = transform self.len = data_length # Getting the data samples def __getitem__(self, idx): sample = self.x[idx], self.y[idx] if self.transform: sample = self.transform(sample) return sample # Getting data size/length def __len__(self): return self.len # Creating a callable tranform class mult_divide class MultDivide: # Constructor def __init__(self, mult_x = 2, divide_y = 3): self.mult_x = mult_x self.divide_y = divide_y # caller def __call__(self, sample): x = sample[0] y = sample[1] x = x * self.mult_x y = y / self.divide_y sample = x, y return sample # Creating subtract_one tranform class SubtractOne: # Constructor def __init__(self, number = 1): self.number = number # caller def __call__(self, sample): x = sample[0] y = sample[1] x = x - self.number y = y - self.number sample = x, y return sample # Composing multiple transforms mult_transforms = transforms.Compose([MultDivide(), SubtractOne()]) # Creating a new simple_dataset object with multiple transforms dataset = SimpleDataset() new_dataset = SimpleDataset(transform = mult_transforms) print("length of the simple_dataset object: ", len(dataset)) print("accessing value at index 1 of the simple_dataset object: ", dataset[1]) for i in range(4): x, y = dataset[i] print('Idx: ', i, 'Original_x: ', x, 'Original_y: ', y) x_, y_ = new_dataset[i] print('Idx: ', i, 'Transformed x_:', x_, 'Transformed y_:', y_) ``` ## 总结 在本教程中,你学会了如何在 PyTorch 中创建自定义数据集和转换。特别是,你学到了: + 如何创建一个简单的数据集类并对其应用转换。 + 如何构建可调用的转换并将其应用于数据集对象。 + 如何在数据集对象上组合各种转换。 # 在 PyTorch 模型中使用 Dropout 正则化 > 原文:[`machinelearningmastery.com/using-dropout-regularization-in-pytorch-models/`](https://machinelearningmastery.com/using-dropout-regularization-in-pytorch-models/) [Dropout](https://machinelearningmastery.com/dropout-for-regularizing-deep-neural-networks/) 是一种简单而强大的神经网络和深度学习模型的正则化技术。 在这篇文章中,你将发现 Dropout 正则化技术及其如何应用于 PyTorch 模型。 阅读完这篇文章后,你将了解: + Dropout 正则化技术的工作原理 + 如何在输入层上使用 Dropout + 如何在隐藏层上使用 Dropout + 如何调整你的问题的 dropout 水平 **通过我的书籍** [Deep Learning with PyTorch](https://machinelearningmastery.com/deep-learning-with-pytorch/) **启动你的项目**。它提供了 **自学教程** 和 **有效代码**。 让我们开始吧![](../Images/53f7b7fdd299d10c88720ea518639304.png) 在 PyTorch 模型中使用 Dropout 正则化 照片由 [Priscilla Fraire](https://unsplash.com/photos/65dCe4Zuek4) 拍摄。部分权利保留。 ## 概述 本文分为六个部分;它们是 + 神经网络的 Dropout 正则化 + PyTorch 中的 Dropout 正则化 + 在输入层上使用 Dropout + 在隐藏层上使用 Dropout + 评估模式中的 Dropout + 使用 Dropout 的提示 ## 神经网络的 Dropout 正则化 Dropout 是一种用于神经网络模型的正则化技术,提出于 2012 年至 2014 年间。它是神经网络中的一层。在神经网络模型的训练过程中,它会从前一层接收输出,随机选择一些神经元并将其归零,然后传递到下一层,从而有效地忽略它们。这意味着它们对下游神经元激活的贡献在前向传递时被暂时移除,并且在反向传递时不会对这些神经元应用任何权重更新。 当模型用于推断时,dropout 层只是将所有神经元的权重缩放,以补偿训练过程中丢弃的影响。 Dropout 是一种破坏性的技术,但惊人的是,它可以提高模型的准确性。当神经网络学习时,神经元权重会在网络中形成其上下文。神经元的权重被调节以适应特定特征,从而提供一些专门化。相邻的神经元会依赖这种专门化,如果过度依赖,可能会导致模型过于专门化,从而对训练数据过于脆弱。这种在训练过程中对神经元上下文的依赖被称为复杂的协同适应。 你可以想象,如果在训练过程中神经元被随机丢弃,其他神经元将不得不介入并处理缺失神经元所需的表示。这被认为会导致网络学习到多个独立的内部表示。 其效果是网络对神经元的特定权重不那么敏感。这反过来使网络能够更好地进行泛化,并且不容易对训练数据进行过拟合。 ### 想要开始使用 PyTorch 进行深度学习吗? 立即参加我的免费电子邮件速成课程(包含示例代码)。 点击注册并获取免费的 PDF 电子书版本课程。 ## PyTorch 中的 Dropout 正则化 你不需要从 PyTorch 张量中随机选择元素来手动实现 dropout。可以将 PyTorch 的 `nn.Dropout()` 层引入到你的模型中。它通过以给定的概率 $p$(例如 20%)随机选择要丢弃的节点来实现。在训练循环中,PyTorch 的 dropout 层进一步将结果张量按 $\dfrac{1}{1-p}$ 的因子缩放,以保持平均张量值。由于这种缩放,dropout 层在推理时会成为一个恒等函数(即无效,仅将输入张量复制为输出张量)。你应确保在评估模型时将模型转换为推理模式。 让我们看看如何在 PyTorch 模型中使用 `nn.Dropout()`。 示例将使用 [Sonar 数据集](http://archive.ics.uci.edu/ml/datasets/Connectionist+Bench+(Sonar,+Mines+vs.+Rocks))。这是一个二分类问题,旨在正确识别从声纳回波返回的岩石和假矿。它是一个适合神经网络的测试数据集,因为所有输入值都是数值型且具有相同的尺度。 数据集可以 [从 UCI 机器学习库下载](http://archive.ics.uci.edu/ml/machine-learning-databases/undocumented/connectionist-bench/sonar/sonar.all-data)。你可以将声纳数据集放在当前工作目录中,文件名为 *sonar.csv*。 你将使用 scikit-learn 进行 10 折交叉验证来评估开发的模型,以便更好地揭示结果中的差异。 数据集中有 60 个输入值和一个输出值。输入值在用于网络之前会被标准化。基线神经网络模型有两个隐藏层,第一个层有 60 个单元,第二个层有 30 个。使用随机梯度下降来训练模型,学习率和动量相对较低。 完整的基线模型如下所示: ```py import numpy as np import pandas as pd import torch import torch.nn as nn import torch.optim as optim from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold # Read data data = pd.read_csv("sonar.csv", header=None) X = data.iloc[:, 0:60] y = data.iloc[:, 60] # Label encode the target from string to integer encoder = LabelEncoder() encoder.fit(y) y = encoder.transform(y) # Convert to 2D PyTorch tensors X = torch.tensor(X.values, dtype=torch.float32) y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1) # Define PyTorch model class SonarModel(nn.Module): def __init__(self): super().__init__() self.layer1 = nn.Linear(60, 60) self.act1 = nn.ReLU() self.layer2 = nn.Linear(60, 30) self.act2 = nn.ReLU() self.output = nn.Linear(30, 1) self.sigmoid = nn.Sigmoid() def forward(self, x): x = self.act1(self.layer1(x)) x = self.act2(self.layer2(x)) x = self.sigmoid(self.output(x)) return x # Helper function to train the model and return the validation result def model_train(model, X_train, y_train, X_val, y_val, n_epochs=300, batch_size=16): loss_fn = nn.BCELoss() optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.8) batch_start = torch.arange(0, len(X_train), batch_size) model.train() for epoch in range(n_epochs): for start in batch_start: X_batch = X_train[start:start+batch_size] y_batch = y_train[start:start+batch_size] y_pred = model(X_batch) loss = loss_fn(y_pred, y_batch) optimizer.zero_grad() loss.backward() optimizer.step() # evaluate accuracy after training model.eval() y_pred = model(X_val) acc = (y_pred.round() == y_val).float().mean() acc = float(acc) return acc # run 10-fold cross validation kfold = StratifiedKFold(n_splits=10, shuffle=True) accuracies = [] for train, test in kfold.split(X, y): # create model, train, and get accuracy model = SonarModel() acc = model_train(model, X[train], y[train], X[test], y[test]) print("Accuracy: %.2f" % acc) accuracies.append(acc) # evaluate the model mean = np.mean(accuracies) std = np.std(accuracies) print("Baseline: %.2f%% (+/- %.2f%%)" % (mean*100, std*100)) ``` 运行示例会产生 82% 的估计分类准确率。 ```py Accuracy: 0.81 Accuracy: 0.81 Accuracy: 0.76 Accuracy: 0.86 Accuracy: 0.81 Accuracy: 0.90 Accuracy: 0.86 Accuracy: 0.95 Accuracy: 0.65 Accuracy: 0.80 Baseline: 82.12% (+/- 7.78%) ``` ## 在输入层中使用 Dropout Dropout 可以应用于称为可见层的输入神经元。 在下面的示例中,在输入层和第一个隐藏层之间添加了一个新的 Dropout 层。dropout 率设置为 20%,意味着每次更新周期中将随机排除五分之一的输入。 从上面的基线示例继续,下面的代码使用输入 dropout 对相同的网络进行测试: ```py import numpy as np import pandas as pd import torch import torch.nn as nn import torch.optim as optim from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold # Read data data = pd.read_csv("sonar.csv", header=None) X = data.iloc[:, 0:60] y = data.iloc[:, 60] # Label encode the target from string to integer encoder = LabelEncoder() encoder.fit(y) y = encoder.transform(y) # Convert to 2D PyTorch tensors X = torch.tensor(X.values, dtype=torch.float32) y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1) # Define PyTorch model, with dropout at input class SonarModel(nn.Module): def __init__(self): super().__init__() self.dropout = nn.Dropout(0.2) self.layer1 = nn.Linear(60, 60) self.act1 = nn.ReLU() self.layer2 = nn.Linear(60, 30) self.act2 = nn.ReLU() self.output = nn.Linear(30, 1) self.sigmoid = nn.Sigmoid() def forward(self, x): x = self.dropout(x) x = self.act1(self.layer1(x)) x = self.act2(self.layer2(x)) x = self.sigmoid(self.output(x)) return x # Helper function to train the model and return the validation result def model_train(model, X_train, y_train, X_val, y_val, n_epochs=300, batch_size=16): loss_fn = nn.BCELoss() optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.8) batch_start = torch.arange(0, len(X_train), batch_size) model.train() for epoch in range(n_epochs): for start in batch_start: X_batch = X_train[start:start+batch_size] y_batch = y_train[start:start+batch_size] y_pred = model(X_batch) loss = loss_fn(y_pred, y_batch) optimizer.zero_grad() loss.backward() optimizer.step() # evaluate accuracy after training model.eval() y_pred = model(X_val) acc = (y_pred.round() == y_val).float().mean() acc = float(acc) return acc # run 10-fold cross validation kfold = StratifiedKFold(n_splits=10, shuffle=True) accuracies = [] for train, test in kfold.split(X, y): # create model, train, and get accuracy model = SonarModel() acc = model_train(model, X[train], y[train], X[test], y[test]) print("Accuracy: %.2f" % acc) accuracies.append(acc) # evaluate the model mean = np.mean(accuracies) std = np.std(accuracies) print("Baseline: %.2f%% (+/- %.2f%%)" % (mean*100, std*100)) ``` 运行示例会导致分类准确率略微下降,至少在单次测试运行中。 ```py Accuracy: 0.62 Accuracy: 0.90 Accuracy: 0.76 Accuracy: 0.62 Accuracy: 0.67 Accuracy: 0.86 Accuracy: 0.90 Accuracy: 0.86 Accuracy: 0.90 Accuracy: 0.85 Baseline: 79.40% (+/- 11.20%) ``` ## 在隐藏层中使用 Dropout Dropout 可以应用于网络模型的隐藏神经元。这种做法更为常见。 在下面的示例中,Dropout 被应用在两个隐藏层之间以及最后一个隐藏层和输出层之间。再次使用了 20%的 Dropout 率: ```py import numpy as np import pandas as pd import torch import torch.nn as nn import torch.optim as optim from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold # Read data data = pd.read_csv("sonar.csv", header=None) X = data.iloc[:, 0:60] y = data.iloc[:, 60] # Label encode the target from string to integer encoder = LabelEncoder() encoder.fit(y) y = encoder.transform(y) # Convert to 2D PyTorch tensors X = torch.tensor(X.values, dtype=torch.float32) y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1) # Define PyTorch model, with dropout at hidden layers class SonarModel(nn.Module): def __init__(self): super().__init__() self.layer1 = nn.Linear(60, 60) self.act1 = nn.ReLU() self.dropout1 = nn.Dropout(0.2) self.layer2 = nn.Linear(60, 30) self.act2 = nn.ReLU() self.dropout2 = nn.Dropout(0.2) self.output = nn.Linear(30, 1) self.sigmoid = nn.Sigmoid() def forward(self, x): x = self.act1(self.layer1(x)) x = self.dropout1(x) x = self.act2(self.layer2(x)) x = self.dropout2(x) x = self.sigmoid(self.output(x)) return x # Helper function to train the model and return the validation result def model_train(model, X_train, y_train, X_val, y_val, n_epochs=300, batch_size=16): loss_fn = nn.BCELoss() optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.8) batch_start = torch.arange(0, len(X_train), batch_size) model.train() for epoch in range(n_epochs): for start in batch_start: X_batch = X_train[start:start+batch_size] y_batch = y_train[start:start+batch_size] y_pred = model(X_batch) loss = loss_fn(y_pred, y_batch) optimizer.zero_grad() loss.backward() optimizer.step() # evaluate accuracy after training model.eval() y_pred = model(X_val) acc = (y_pred.round() == y_val).float().mean() acc = float(acc) return acc # run 10-fold cross validation kfold = StratifiedKFold(n_splits=10, shuffle=True) accuracies = [] for train, test in kfold.split(X, y): # create model, train, and get accuracy model = SonarModel() acc = model_train(model, X[train], y[train], X[test], y[test]) print("Accuracy: %.2f" % acc) accuracies.append(acc) # evaluate the model mean = np.mean(accuracies) std = np.std(accuracies) print("Baseline: %.2f%% (+/- %.2f%%)" % (mean*100, std*100)) ``` 您可以看到,在这种情况下,添加 Dropout 层稍微提高了准确性。 ```py Accuracy: 0.86 Accuracy: 1.00 Accuracy: 0.86 Accuracy: 0.90 Accuracy: 0.90 Accuracy: 0.86 Accuracy: 0.81 Accuracy: 0.81 Accuracy: 0.70 Accuracy: 0.85 Baseline: 85.50% (+/- 7.36%) ``` ## 评估模式下的 Dropout Dropout 将随机将部分输入重置为零。如果您想知道训练结束后会发生什么,答案是什么也不会发生!当模型处于评估模式时,PyTorch 的 Dropout 层应该像一个恒等函数一样运行。这很重要,因为 Dropout 层的目标是确保网络对输入学习足够的线索以进行预测,而不是依赖于数据中的罕见现象。但是在推理时,您应尽可能向模型提供尽可能多的信息。 ## 使用 Dropout 的技巧 Dropout 的原始论文提供了一系列标准机器学习问题的实验结果。因此,他们提供了一些在实践中使用 Dropout 时需要考虑的有用启发。 + 通常,使用 20%-50%的神经元的小 Dropout 值,其中 20%是一个很好的起点。概率过低几乎没有效果,而值过高会导致网络学习不足。 + 使用更大的网络。当在更大的网络上使用 Dropout 时,通常能够获得更好的性能,因为这给模型更多机会学习独立的表示。 + 在可见单元(输入)和隐藏单元上使用 Dropout。在网络的每一层应用 Dropout 已经显示出良好的结果。 + 使用大的学习率和衰减,以及大的动量。将学习率增加 10 到 100 倍,并使用 0.9 或 0.99 的高动量值。 + 约束网络权重的大小。大学习率可能导致非常大的网络权重。施加网络权重大小的约束,例如最大范数正则化,大小为 4 或 5,已被证明能够改善结果。 ## 进一步阅读 以下是可以进一步了解神经网络和深度学习模型中 Dropout 的资源。 论文 + [Dropout: 一种简单的防止神经网络过拟合的方法](http://jmlr.org/papers/v15/srivastava14a.html) + [通过防止特征检测器的共适应来改进神经网络](http://arxiv.org/abs/1207.0580) 在线资料 + [深度学习中 Dropout 方法如何工作?](https://www.quora.com/How-does-the-dropout-method-work-in-deep-learning) 在 Quora 上 + [nn.Dropout](https://pytorch.org/docs/stable/generated/torch.nn.Dropout.html) 来自 PyTorch 文档 ## 总结 在本文中,您将了解到用于深度学习模型的 Dropout 正则化技术。您将学到: + Dropout 是什么以及它如何工作 + 如何在自己的深度学习模型中使用 Dropout。 + 在您自己的模型上获得 Dropout 最佳结果的技巧。 # 在 PyTorch 训练中使用学习率调度 > 原文:[`machinelearningmastery.com/using-learning-rate-schedule-in-pytorch-training/`](https://machinelearningmastery.com/using-learning-rate-schedule-in-pytorch-training/) 训练神经网络或大型深度学习模型是一项困难的优化任务。 训练神经网络的经典算法称为 [随机梯度下降](https://machinelearningmastery.com/gradient-descent-for-machine-learning/)。已经很好地证明,通过在训练过程中使用会变化的 [学习率](https://machinelearningmastery.com/learning-rate-for-deep-learning-neural-networks/),你可以在某些问题上实现性能提升和更快的训练。 在这篇文章中,你将了解什么是学习率调度以及如何在 PyTorch 中为你的神经网络模型使用不同的学习率调度。 阅读本文后,你将了解到: + 学习率调度在模型训练中的作用 + 如何在 PyTorch 训练循环中使用学习率调度 + 如何设置自己的学习率调度 ### 想开始使用 PyTorch 深度学习吗? 现在就参加我的免费电子邮件速成课程(包含示例代码)。 点击注册,并获取课程的免费 PDF 电子书版本。 让我们开始吧![](../Images/7a13d9fa39a1b8fc273f193425bc5a11.png) 在 PyTorch 训练中使用学习率调度 图片由 [Cheung Yin](https://unsplash.com/photos/A_lVW8yIQM0) 提供。保留部分权利。 ## 概述 本文分为三个部分;它们是 + 训练模型的学习率调度 + 在 PyTorch 训练中应用学习率调度 + 自定义学习率调度 ## 训练模型的学习率调度 梯度下降是一种数值优化算法。它的作用是使用公式更新参数:

w := w – \alpha \dfrac{dy}{dw}

在这个公式中,$w$ 是参数,例如神经网络中的权重,而 $y$ 是目标,例如损失函数。它的作用是将 $w$ 移动到可以最小化 $y$ 的方向。这个方向由微分提供,即 $\dfrac{dy}{dw}$,但你应该移动 $w$ 的多少则由**学习率** $\alpha$ 控制。 一个简单的开始是使用在梯度下降算法中的恒定学习率。但使用**学习率调度**你可以做得更好。调度是使学习率适应梯度下降优化过程,从而提高性能并减少训练时间。 在神经网络训练过程中,数据以批次的形式输入网络,一个时期内有多个批次。每个批次触发一个训练步骤,其中梯度下降算法更新一次参数。然而,通常学习率调度只在每个 [训练时期](https://machinelearningmastery.com/difference-between-a-batch-and-an-epoch/) 更新一次。 你可以像每一步那样频繁地更新学习率,但通常它会在每个 epoch 更新一次,因为你需要了解网络的表现,以便决定学习率应该如何更新。通常,模型会在每个 epoch 使用验证数据集进行评估。 调整学习率的方式有多种。在训练开始时,你可能会倾向于使用较大的学习率,以便粗略地改进网络,从而加快进度。在非常复杂的神经网络模型中,你也可能会倾向于在开始时逐渐增加学习率,因为你需要网络在不同的预测维度上进行探索。然而,在训练结束时,你总是希望将学习率调整得更小。因为那时你即将获得模型的最佳性能,如果学习率过大会容易超调。 因此,在训练过程中,最简单且可能最常用的学习率适应方式是逐渐减少学习率的技术。这些技术的好处在于,在训练程序开始时使用较大的学习率值时,可以做出较大的更改,并在训练程序后期将学习率降低,从而使更新权重时的学习率较小,训练更新也较小。 这会在早期快速学习到好的权重,并在之后进行微调。 接下来,让我们看看如何在 PyTorch 中设置学习率调度。 **通过我的书籍** [《深度学习与 PyTorch》](https://machinelearningmastery.com/deep-learning-with-pytorch/) **来启动你的项目**。它提供了 **自学教程** 和 **可运行的代码**。 ## 在 PyTorch 训练中应用学习率调度 在 PyTorch 中,一个模型通过优化器进行更新,学习率是优化器的一个参数。学习率调度是一种算法,用于更新优化器中的学习率。 以下是创建学习率调度的示例: ```py import torch import torch.optim as optim import torch.optim.lr_scheduler as lr_scheduler scheduler = lr_scheduler.LinearLR(optimizer, start_factor=1.0, end_factor=0.3, total_iters=10) ``` PyTorch 在 `torch.optim.lr_scheduler` 子模块中提供了许多学习率调度器。所有的调度器都需要优化器作为第一个参数。根据调度器的不同,你可能需要提供更多的参数来进行设置。 我们从一个示例模型开始。下面的模型旨在解决 [电离层二分类问题](http://archive.ics.uci.edu/ml/datasets/Ionosphere)。这是一个小型数据集,你可以 [从 UCI 机器学习库下载](http://archive.ics.uci.edu/ml/machine-learning-databases/ionosphere/ionosphere.data)。将数据文件放置在你的工作目录中,文件名为 `ionosphere.csv`。 电离层数据集适合用于神经网络的练习,因为所有输入值都是相同量级的小数值。 一个小型神经网络模型构建了一个具有 34 个神经元的单隐藏层,使用 ReLU 激活函数。输出层有一个神经元,并使用 sigmoid 激活函数来输出类似概率的值。 使用的是普通随机梯度下降算法,固定学习率为 0.1。模型训练了 50 个周期。优化器的状态参数可以在`optimizer.param_groups`中找到;其中学习率是`optimizer.param_groups[0]["lr"]`的浮点值。在每个周期结束时,打印出优化器的学习率。 完整示例如下。 ```py import numpy as np import pandas as pd import torch import torch.nn as nn import torch.optim as optim from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split # load dataset, split into input (X) and output (y) variables dataframe = pd.read_csv("ionosphere.csv", header=None) dataset = dataframe.values X = dataset[:,0:34].astype(float) y = dataset[:,34] # encode class values as integers encoder = LabelEncoder() encoder.fit(y) y = encoder.transform(y) # convert into PyTorch tensors X = torch.tensor(X, dtype=torch.float32) y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1) # train-test split for evaluation of the model X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, shuffle=True) # create model model = nn.Sequential( nn.Linear(34, 34), nn.ReLU(), nn.Linear(34, 1), nn.Sigmoid() ) # Train the model n_epochs = 50 batch_size = 24 batch_start = torch.arange(0, len(X_train), batch_size) lr = 0.1 loss_fn = nn.BCELoss() optimizer = optim.SGD(model.parameters(), lr=lr) model.train() for epoch in range(n_epochs): for start in batch_start: X_batch = X_train[start:start+batch_size] y_batch = y_train[start:start+batch_size] y_pred = model(X_batch) loss = loss_fn(y_pred, y_batch) optimizer.zero_grad() loss.backward() optimizer.step() print("Epoch %d: SGD lr=%.4f" % (epoch, optimizer.param_groups[0]["lr"])) # evaluate accuracy after training model.eval() y_pred = model(X_test) acc = (y_pred.round() == y_test).float().mean() acc = float(acc) print("Model accuracy: %.2f%%" % (acc*100)) ``` 运行此模型产生: ```py Epoch 0: SGD lr=0.1000 Epoch 1: SGD lr=0.1000 Epoch 2: SGD lr=0.1000 Epoch 3: SGD lr=0.1000 Epoch 4: SGD lr=0.1000 ... Epoch 45: SGD lr=0.1000 Epoch 46: SGD lr=0.1000 Epoch 47: SGD lr=0.1000 Epoch 48: SGD lr=0.1000 Epoch 49: SGD lr=0.1000 Model accuracy: 86.79% ``` 你可以确认学习率在整个训练过程中没有变化。让我们让训练过程以较大的学习率开始,以较小的学习率结束。为了引入学习率调度器,你需要在训练循环中运行其`step()`函数。上述代码修改为以下内容: ```py import numpy as np import pandas as pd import torch import torch.nn as nn import torch.optim as optim import torch.optim.lr_scheduler as lr_scheduler from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split # load dataset, split into input (X) and output (y) variables dataframe = pd.read_csv("ionosphere.csv", header=None) dataset = dataframe.values X = dataset[:,0:34].astype(float) y = dataset[:,34] # encode class values as integers encoder = LabelEncoder() encoder.fit(y) y = encoder.transform(y) # convert into PyTorch tensors X = torch.tensor(X, dtype=torch.float32) y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1) # train-test split for evaluation of the model X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, shuffle=True) # create model model = nn.Sequential( nn.Linear(34, 34), nn.ReLU(), nn.Linear(34, 1), nn.Sigmoid() ) # Train the model n_epochs = 50 batch_size = 24 batch_start = torch.arange(0, len(X_train), batch_size) lr = 0.1 loss_fn = nn.BCELoss() optimizer = optim.SGD(model.parameters(), lr=lr) scheduler = lr_scheduler.LinearLR(optimizer, start_factor=1.0, end_factor=0.5, total_iters=30) model.train() for epoch in range(n_epochs): for start in batch_start: X_batch = X_train[start:start+batch_size] y_batch = y_train[start:start+batch_size] y_pred = model(X_batch) loss = loss_fn(y_pred, y_batch) optimizer.zero_grad() loss.backward() optimizer.step() before_lr = optimizer.param_groups[0]["lr"] scheduler.step() after_lr = optimizer.param_groups[0]["lr"] print("Epoch %d: SGD lr %.4f -> %.4f" % (epoch, before_lr, after_lr)) # evaluate accuracy after training model.eval() y_pred = model(X_test) acc = (y_pred.round() == y_test).float().mean() acc = float(acc) print("Model accuracy: %.2f%%" % (acc*100)) ``` 它打印出: ```py Epoch 0: SGD lr 0.1000 -> 0.0983 Epoch 1: SGD lr 0.0983 -> 0.0967 Epoch 2: SGD lr 0.0967 -> 0.0950 Epoch 3: SGD lr 0.0950 -> 0.0933 Epoch 4: SGD lr 0.0933 -> 0.0917 ... Epoch 28: SGD lr 0.0533 -> 0.0517 Epoch 29: SGD lr 0.0517 -> 0.0500 Epoch 30: SGD lr 0.0500 -> 0.0500 Epoch 31: SGD lr 0.0500 -> 0.0500 ... Epoch 48: SGD lr 0.0500 -> 0.0500 Epoch 49: SGD lr 0.0500 -> 0.0500 Model accuracy: 88.68% ``` 上述代码使用了`LinearLR()`。它是一个线性率调度器,并且需要三个附加参数,`start_factor`、`end_factor`和`total_iters`。你将`start_factor`设置为 1.0,`end_factor`设置为 0.5,`total_iters`设置为 30,因此它将在 10 个相等步骤中将乘法因子从 1.0 减少到 0.5。经过 10 步后,因子将保持在 0.5。这一因子随后会与优化器中的原始学习率相乘。因此,你将看到学习率从$0.1\times 1.0 = 0.1$减少到$0.1\times 0.5 = 0.05$。 除了`LinearLR()`,你还可以使用`ExponentialLR()`,其语法为: ```py scheduler = lr_scheduler.ExponentialLR(optimizer, gamma=0.99) ``` 如果你将`LinearLR()`替换为此,你将看到学习率更新如下: ```py Epoch 0: SGD lr 0.1000 -> 0.0990 Epoch 1: SGD lr 0.0990 -> 0.0980 Epoch 2: SGD lr 0.0980 -> 0.0970 Epoch 3: SGD lr 0.0970 -> 0.0961 Epoch 4: SGD lr 0.0961 -> 0.0951 ... Epoch 45: SGD lr 0.0636 -> 0.0630 Epoch 46: SGD lr 0.0630 -> 0.0624 Epoch 47: SGD lr 0.0624 -> 0.0617 Epoch 48: SGD lr 0.0617 -> 0.0611 Epoch 49: SGD lr 0.0611 -> 0.0605 ``` 在每次调度器更新时,学习率通过与常量因子`gamma`相乘来更新。 ## 自定义学习率调度 没有普遍适用的规则表明特定的学习率调度是最有效的。有时,你可能希望拥有 PyTorch 未提供的特殊学习率调度。可以使用自定义函数定义一个自定义学习率调度。例如,你希望有一个学习率为:

lr_n = \dfrac{lr_0}{1 + \alpha n}

在第$n$个周期,其中$lr_0$是第 0 个周期的初始学习率,$\alpha$是常量。你可以实现一个函数,给定周期$n$计算学习率$lr_n$: ```py def lr_lambda(epoch): # LR to be 0.1 * (1/1+0.01*epoch) base_lr = 0.1 factor = 0.01 return base_lr/(1+factor*epoch) ``` 然后,你可以设置`LambdaLR()`以根据以下函数更新学习率: ```py scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda) ``` 修改之前的示例以使用`LambdaLR()`,你将得到以下内容: ```py import numpy as np import pandas as pd import torch import torch.nn as nn import torch.optim as optim import torch.optim.lr_scheduler as lr_scheduler from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split # load dataset, split into input (X) and output (y) variables dataframe = pd.read_csv("ionosphere.csv", header=None) dataset = dataframe.values X = dataset[:,0:34].astype(float) y = dataset[:,34] # encode class values as integers encoder = LabelEncoder() encoder.fit(y) y = encoder.transform(y) # convert into PyTorch tensors X = torch.tensor(X, dtype=torch.float32) y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1) # train-test split for evaluation of the model X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, shuffle=True) # create model model = nn.Sequential( nn.Linear(34, 34), nn.ReLU(), nn.Linear(34, 1), nn.Sigmoid() ) def lr_lambda(epoch): # LR to be 0.1 * (1/1+0.01*epoch) base_lr = 0.1 factor = 0.01 return base_lr/(1+factor*epoch) # Train the model n_epochs = 50 batch_size = 24 batch_start = torch.arange(0, len(X_train), batch_size) lr = 0.1 loss_fn = nn.BCELoss() optimizer = optim.SGD(model.parameters(), lr=lr) scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda) model.train() for epoch in range(n_epochs): for start in batch_start: X_batch = X_train[start:start+batch_size] y_batch = y_train[start:start+batch_size] y_pred = model(X_batch) loss = loss_fn(y_pred, y_batch) optimizer.zero_grad() loss.backward() optimizer.step() before_lr = optimizer.param_groups[0]["lr"] scheduler.step() after_lr = optimizer.param_groups[0]["lr"] print("Epoch %d: SGD lr %.4f -> %.4f" % (epoch, before_lr, after_lr)) # evaluate accuracy after training model.eval() y_pred = model(X_test) acc = (y_pred.round() == y_test).float().mean() acc = float(acc) print("Model accuracy: %.2f%%" % (acc*100)) ``` 其结果为: ```py Epoch 0: SGD lr 0.0100 -> 0.0099 Epoch 1: SGD lr 0.0099 -> 0.0098 Epoch 2: SGD lr 0.0098 -> 0.0097 Epoch 3: SGD lr 0.0097 -> 0.0096 Epoch 4: SGD lr 0.0096 -> 0.0095 ... Epoch 45: SGD lr 0.0069 -> 0.0068 Epoch 46: SGD lr 0.0068 -> 0.0068 Epoch 47: SGD lr 0.0068 -> 0.0068 Epoch 48: SGD lr 0.0068 -> 0.0067 Epoch 49: SGD lr 0.0067 -> 0.0067 ``` 注意,虽然提供给`LambdaLR()`的函数假设有一个参数`epoch`,但它并不与训练循环中的周期绑定,而只是计数你调用了多少次`scheduler.step()`。 ## 使用学习率调度的技巧 本节列出了一些在使用神经网络的学习率调度时需要考虑的技巧和窍门。 + **增加初始学习率**。因为学习率很可能会减小,所以从较大的值开始减小。较大的学习率将导致权重产生更大的变化,至少在开始阶段是这样,这样可以使您后续的微调更加有效。 + **使用较大的动量**。许多优化器可以考虑动量。使用较大的动量值将有助于优化算法在学习率减小到较小值时继续朝正确方向进行更新。 + **尝试不同的调度**。不清楚要使用哪种学习率调度,因此尝试几种不同的配置选项,看看哪种在解决您的问题时效果最好。还可以尝试指数变化的调度,甚至可以根据模型在训练或测试数据集上的准确性响应的调度。 ## 进一步阅读 以下是有关在 PyTorch 中使用学习率的更多详细文档: + [如何调整学习率](https://pytorch.org/docs/stable/optim.html#how-to-adjust-learning-rate),来自 PyTorch 文档 ## 摘要 在本文中,您发现了用于训练神经网络模型的学习率调度。 阅读本文后,您学到了: + 学习率如何影响您的模型训练 + 如何在 PyTorch 中设置学习率调度 + 如何创建自定义学习率调度 # 使用 PyTorch 中的优化器 > 原文:[`machinelearningmastery.com/using-optimizers-from-pytorch/`](https://machinelearningmastery.com/using-optimizers-from-pytorch/) 优化是一个过程,我们试图为深度学习模型找到最佳的参数集。优化器生成新的参数值,并使用某些标准评估它们,以确定最佳选项。作为神经网络架构的重要组成部分,优化器有助于确定最佳的权重、偏置或其他超参数,以达到期望的输出。 在 PyTorch 中有许多种优化器,每种优化器都有其自身的优缺点。这些包括 Adagrad、Adam、RMSProp 等等。 在之前的教程中,我们实现了优化器的所有必要步骤,以在训练过程中更新权重和偏置。在这里,您将学习一些 PyTorch 包,这些包使优化器的实现更加简单。特别是,您将学习: + 如何使用 PyTorch 中的一些包实现优化器。 + 如何从 PyTorch 的 'nn' 包中导入线性类和损失函数。 + 如何使用 PyTorch 的 'optim' 包实现随机梯度下降和 Adam(最常用的优化器)。 + 如何自定义模型的权重和偏置。 注意,我们将在我们 PyTorch 系列的后续教程中使用相同的实现步骤。 **用我的书 [使用 PyTorch 进行深度学习](https://machinelearningmastery.com/deep-learning-with-pytorch/) 开始你的项目**。它提供了**自学教程**和**可工作的代码**。 让我们开始吧!![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/caef907a4dc84e7aae97d66205171d0c~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771200648&x-signature=HkuTh%2FLIFIOnPYiUIrFH3chp1Gk%3D) 使用 PyTorch 中的优化器。 图片由 [Jean-Daniel Calame](https://unsplash.com/photos/vK8a67HU7To) 提供。部分权利保留。 ## 概述 此教程分为五个部分;它们是 + 数据准备 + 构建模型和损失函数 + 使用随机梯度下降法训练模型 + 使用 Adam 优化器训练模型 + 绘制图表 ## 数据准备 让我们从导入我们将在本教程中使用的库开始。 ```py import matplotlib.pyplot as plt import numpy as np import torch from torch.utils.data import Dataset, DataLoader ``` 我们将使用一个自定义的数据类。数据是一条线,值从$-5$到$5$,斜率和偏置分别为$-5$和$1$。此外,我们将添加与 `x` 相同值的噪声,并训练我们的模型来估计这条线。 ```py # Creating our dataset class class Build_Data(Dataset): # Constructor def __init__(self): self.x = torch.arange(-5, 5, 0.1).view(-1, 1) self.func = -5 * self.x + 1 self.y = self.func + 0.4 * torch.randn(self.x.size()) self.len = self.x.shape[0] # Getting the data def __getitem__(self, index): return self.x[index], self.y[index] # Getting length of the data def __len__(self): return self.len ``` 现在让我们使用它来创建我们的数据集对象并绘制数据。 ```py # Create dataset object data_set = Build_Data() # Plot and visualizing the data points plt.plot(data_set.x.numpy(), data_set.y.numpy(), 'b+', label = 'y') plt.plot(data_set.x.numpy(), data_set.func.numpy(), 'r', label = 'func') plt.xlabel('x') plt.ylabel('y') plt.legend() plt.grid('True', color='y') plt.show() ``` ![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/dc701251e0454877a7d3271416e009aa~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771200648&x-signature=uJdrNERI19S1fUD%2BervQ4Pgwadw%3D) 自定数据集对象的数据 将所有东西放在一起,以下是创建图表的完整代码: ```py import matplotlib.pyplot as plt import numpy as np import torch from torch.utils.data import Dataset, DataLoader # Creating our dataset class class Build_Data(Dataset): # Constructor def __init__(self): self.x = torch.arange(-5, 5, 0.1).view(-1, 1) self.func = -5 * self.x + 1 self.y = self.func + 0.4 * torch.randn(self.x.size()) self.len = self.x.shape[0] # Getting the data def __getitem__(self, index): return self.x[index], self.y[index] # Getting length of the data def __len__(self): return self.len # Create dataset object data_set = Build_Data() # Plot and visualizing the data points plt.plot(data_set.x.numpy(), data_set.y.numpy(), 'b+', label = 'y') plt.plot(data_set.x.numpy(), data_set.func.numpy(), 'r', label = 'func') plt.xlabel('x') plt.ylabel('y') plt.legend() plt.grid('True', color='y') plt.show() ``` ## 构建模型和损失函数 在之前的教程中,我们为线性回归模型和损失函数创建了一些函数。PyTorch 允许我们仅用几行代码就能做到这一点。这是我们如何从 PyTorch 的 `nn` 包中导入我们内置的线性回归模型及其损失标准。 ```py model = torch.nn.Linear(1, 1) criterion = torch.nn.MSELoss() ``` 模型参数在创建时是随机的。我们可以通过以下方式验证这一点: ```py ... print(list(model.parameters())) ``` 打印 ```py [Parameter containing: tensor([[-5.2178]], requires_grad=True), Parameter containing: tensor([-5.5367], requires_grad=True)] ``` 虽然 PyTorch 会随机初始化模型参数,我们也可以自定义它们以使用自己的参数。我们可以按如下方式设置权重和偏差。注意,在实际应用中我们很少需要这样做。 ```py ... model.state_dict()['weight'][0] = -10 model.state_dict()['bias'][0] = -20 ``` 在开始训练之前,让我们创建一个 `DataLoader` 对象,将数据集加载到管道中。 ```py ... # Creating Dataloader object trainloader = DataLoader(dataset = data_set, batch_size=1) ``` ### 想开始使用 PyTorch 进行深度学习吗? 现在就参加我的免费电子邮件速成课程(包含示例代码)。 点击注册并获取免费 PDF 电子书版本的课程。 ## 使用随机梯度下降训练模型 要使用我们选择的优化器,我们可以从 PyTorch 中导入 `optim` 包。它包含了几个最先进的参数优化算法,只需一行代码即可实现。作为示例,随机梯度下降 (SGD) 如下所示。 ```py ... # define optimizer optimizer = torch.optim.SGD(model.parameters(), lr=0.01) ``` 作为输入,我们提供了 `model.parameters()` 给构造函数以表示要优化的内容。我们还定义了步长或学习率 (`lr`)。 为了帮助可视化优化器的进展,我们创建了一个空列表来存储损失,并让模型训练 20 个周期。 ```py ... loss_SGD = [] n_iter = 20 for i in range(n_iter): for x, y in trainloader: # making a pridiction in forward pass y_hat = model(x) # calculating the loss between original and predicted data points loss = criterion(y_hat, y) # store loss into list loss_SGD.append(loss.item()) # zeroing gradients after each iteration optimizer.zero_grad() # backward pass for computing the gradients of the loss w.r.t to learnable parameters loss.backward() # updateing the parameters after each iteration optimizer.step() ``` 在上述过程中,我们将数据样本输入模型进行预测,并计算损失。在反向传播过程中计算梯度,并优化参数。虽然在之前的会话中我们使用了一些额外的代码来更新参数和清零梯度,但 PyTorch 的 `zero_grad()` 和 `step()` 方法使这个过程更加简洁。 你可以增加上述 `DataLoader` 对象中的 `batch_size` 参数来进行小批量梯度下降。 综合来看,完整的代码如下: ```py import matplotlib.pyplot as plt import numpy as np import torch from torch.utils.data import Dataset, DataLoader # Creating our dataset class class Build_Data(Dataset): # Constructor def __init__(self): self.x = torch.arange(-5, 5, 0.1).view(-1, 1) self.func = -5 * self.x + 1 self.y = self.func + 0.4 * torch.randn(self.x.size()) self.len = self.x.shape[0] # Getting the data def __getitem__(self, index): return self.x[index], self.y[index] # Getting length of the data def __len__(self): return self.len # Create dataset object data_set = Build_Data() model = torch.nn.Linear(1, 1) criterion = torch.nn.MSELoss() # Creating Dataloader object trainloader = DataLoader(dataset = data_set, batch_size=1) # define optimizer optimizer = torch.optim.SGD(model.parameters(), lr=0.01) loss_SGD = [] n_iter = 20 for i in range(n_iter): for x, y in trainloader: # making a pridiction in forward pass y_hat = model(x) # calculating the loss between original and predicted data points loss = criterion(y_hat, y) # store loss into list loss_SGD.append(loss.item()) # zeroing gradients after each iteration optimizer.zero_grad() # backward pass for computing the gradients of the loss w.r.t to learnable parameters loss.backward() # updateing the parameters after each iteration optimizer.step() ``` ## 使用 Adam 优化器训练模型 Adam 是用于训练深度学习模型的最常用优化器之一。当你有大量训练数据时,它速度快且效率高。Adam 是一种带有**动量**的优化器,在模型复杂时(如大多数深度学习情况)比 SGD 更具优势。 在 PyTorch 中,将上述 SGD 优化器替换为 Adam 优化器非常简单。虽然所有其他步骤都是相同的,但我们只需将 `SGD()` 方法替换为 `Adam()` 即可实现该算法。 ```py ... # define optimizer optimizer = torch.optim.Adam(model.parameters(), lr=0.01) ``` 类似地,我们将定义迭代次数和一个空列表来存储模型损失。然后我们可以运行训练。 ```py ... loss_Adam = [] n_iter = 20 for i in range(n_iter): for x, y in trainloader: # making a pridiction in forward pass y_hat = model(x) # calculating the loss between original and predicted data points loss = criterion(y_hat, y) # store loss into list loss_Adam.append(loss.item()) # zeroing gradients after each iteration optimizer.zero_grad() # backward pass for computing the gradients of the loss w.r.t to learnable parameters loss.backward() # updateing the parameters after each iteration optimizer.step() ``` 综合所有内容,以下是完整的代码。 ```py import matplotlib.pyplot as plt import numpy as np import torch from torch.utils.data import Dataset, DataLoader # Creating our dataset class class Build_Data(Dataset): # Constructor def __init__(self): self.x = torch.arange(-5, 5, 0.1).view(-1, 1) self.func = -5 * self.x + 1 self.y = self.func + 0.4 * torch.randn(self.x.size()) self.len = self.x.shape[0] # Getting the data def __getitem__(self, index): return self.x[index], self.y[index] # Getting length of the data def __len__(self): return self.len # Create dataset object data_set = Build_Data() model = torch.nn.Linear(1, 1) criterion = torch.nn.MSELoss() # Creating Dataloader object trainloader = DataLoader(dataset = data_set, batch_size=1) # define optimizer optimizer = torch.optim.Adam(model.parameters(), lr=0.01) loss_Adam = [] n_iter = 20 for i in range(n_iter): for x, y in trainloader: # making a pridiction in forward pass y_hat = model(x) # calculating the loss between original and predicted data points loss = criterion(y_hat, y) # store loss into list loss_Adam.append(loss.item()) # zeroing gradients after each iteration optimizer.zero_grad() # backward pass for computing the gradients of the loss w.r.t to learnable parameters loss.backward() # updateing the parameters after each iteration optimizer.step() ``` ## 绘制图表 我们成功实现了 SGD 和 Adam 优化器用于模型训练。让我们可视化在训练过程中这两种算法中模型损失的减少情况,这些损失存储在 `loss_SGD` 和 `loss_Adam` 列表中: ```py ... plt.plot(loss_SGD,label = "Stochastic Gradient Descent") plt.plot(loss_Adam,label = "Adam Optimizer") plt.xlabel('epoch') plt.ylabel('Cost/ total loss') plt.legend() plt.show() ``` ![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/831391dd4e9e4a649c83917acb759088~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771200648&x-signature=yq5dcLEIRs8IP2pPnaY8T%2B3ZCuA%3D) 你可以看到在上述示例中,SGD 收敛速度快于 Adam。这是因为我们训练的是线性回归模型,而 Adam 提供的算法过于复杂。 综合所有内容,以下是完整的代码。 ```py import matplotlib.pyplot as plt import numpy as np import torch from torch.utils.data import Dataset, DataLoader # Creating our dataset class class Build_Data(Dataset): # Constructor def __init__(self): self.x = torch.arange(-5, 5, 0.1).view(-1, 1) self.func = -5 * self.x + 1 self.y = self.func + 0.4 * torch.randn(self.x.size()) self.len = self.x.shape[0] # Getting the data def __getitem__(self, index): return self.x[index], self.y[index] # Getting length of the data def __len__(self): return self.len # Create dataset object data_set = Build_Data() model = torch.nn.Linear(1, 1) criterion = torch.nn.MSELoss() # Creating Dataloader object trainloader = DataLoader(dataset = data_set, batch_size=1) # define optimizer optimizer = torch.optim.Adam(model.parameters(), lr=0.01) loss_SGD = [] n_iter = 20 for i in range(n_iter): for x, y in trainloader: # making a prediction in forward pass y_hat = model(x) # calculating the loss between original and predicted data points loss = criterion(y_hat, y) # store loss into list loss_SGD.append(loss.item()) # zeroing gradients after each iteration optimizer.zero_grad() # backward pass for computing the gradients of the loss w.r.t to learnable parameters loss.backward() # updating the parameters after each iteration optimizer.step() model = torch.nn.Linear(1, 1) loss_Adam = [] for i in range(n_iter): for x, y in trainloader: # making a prediction in forward pass y_hat = model(x) # calculating the loss between original and predicted data points loss = criterion(y_hat, y) # store loss into list loss_Adam.append(loss.item()) # zeroing gradients after each iteration optimizer.zero_grad() # backward pass for computing the gradients of the loss w.r.t to learnable parameters loss.backward() # updating the parameters after each iteration optimizer.step() plt.plot(loss_SGD,label = "Stochastic Gradient Descent") plt.plot(loss_Adam,label = "Adam Optimizer") plt.xlabel('epoch') plt.ylabel('Cost/ total loss') plt.legend() plt.show() ``` ## 总结 在本教程中,您使用了 PyTorch 中的一些内置包实现了优化算法。特别是,您学会了: + 如何使用 PyTorch 中的一些包实现优化器。 + 如何从 PyTorch 的`nn`包中导入线性类和损失函数。 + 如何使用 PyTorch 的`optim`包实现随机梯度下降和 Adam(最常用的优化器)。 + 如何自定义模型的权重和偏置。 # 可视化 PyTorch 模型 > 原文:[`machinelearningmastery.com/visualizing-a-pytorch-model/`](https://machinelearningmastery.com/visualizing-a-pytorch-model/) PyTorch 是一个深度学习库。你可以用 PyTorch 构建非常复杂的深度学习模型。然而,有时你可能需要模型架构的图形化表示。在这篇文章中,你将学习: + 如何将你的 PyTorch 模型保存为交换格式 + 如何使用 Netron 创建图形表示。 **启动你的项目**,请参考我的书籍 [《深度学习与 PyTorch》](https://machinelearningmastery.com/deep-learning-with-pytorch/)。它提供了 **自学教程** 和 **可运行代码**。 让我们开始吧。![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/23a7fde3b73c46af816a3bfa50d29f33~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771200648&x-signature=EIqSxubPd%2BeB%2Fo3AXt2E8BcJqGQ%3D) 可视化 PyTorch 模型 照片由 [Ken Cheung](https://unsplash.com/photos/10py7Mvmf1g) 拍摄。版权所有。 ## 概述 本文分为两部分;它们是 + 为什么 PyTorch 模型的图形化表示很困难 + 如何使用 Netron 创建模型图 ## 为什么 PyTorch 模型的图形化表示很困难 PyTorch 是一个非常灵活的深度学习库。严格来说,它从不强制规定你应如何构建模型,只要模型能像一个函数那样将输入张量转换为输出张量即可。这是一个问题:使用一个模型,你永远无法知道它是如何工作的,除非你跟踪输入张量并收集轨迹直到得到输出张量。因此,将 PyTorch 模型转换为图片并非易事。 解决这个问题有多个库。但一般来说,只有两种方法可以解决:你可以在前向传递中跟踪一个张量,看看应用了什么操作(即,层),或者在反向传递中跟踪一个张量,查看梯度是如何传播到输入的。你只能以这种方式找到关于模型内部结构的线索。 ### 想开始使用 PyTorch 进行深度学习吗? 立即获取我的免费电子邮件速成课程(包含示例代码)。 点击注册并获得课程的免费 PDF 电子书版本。 ## 如何使用 Netron 创建模型图 当你保存一个 PyTorch 模型时,你是在保存它的状态。你可以使用 `model.state_dict()` 获取模型状态。虽然权重张量有名称,这有助于你将它们恢复到模型中,但你无法获得权重之间如何连接的线索。你唯一能够连接张量并找出它们关系的方法是获取张量梯度:当你运行一个模型并获得输出时,包括对其他张量的依赖在内的计算会被每个中间张量记住,以便进行自动微分。 实际上,如果你想了解 PyTorch 模型背后的算法,这也是一种方法。只有少数工具可以从 PyTorch 模型创建图形。下面,你将了解工具 Netron。它是一个“深度学习模型查看器”。这是一个可以在 macOS、Linux 和 Windows 上安装和运行的软件。你可以访问以下页面并下载适用于你的平台的软件: + [`github.com/lutzroeder/netron/releases`](https://github.com/lutzroeder/netron/releases) 还有一个 [在线版本](https://netron.app/),你可以通过上传模型文件来查看你的模型。 Netron 不能从保存的状态中可视化 PyTorch 模型,因为没有足够的线索来说明模型的结构。然而,PyTorch 允许你将模型转换为 Netron 可以理解的交换格式 ONNX。 我们从一个例子开始。在下面,你创建了一个简单的模型来对鸢尾花数据集进行分类。这是一个有三个类别的分类问题。因此,模型应该输出一个包含三个值的向量。你为这个问题创建的完整代码如下,其中数据集来自 scikit-learn: ```py import torch import torch.nn as nn import torch.optim as optim from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split data = load_iris() X = data['data'] y = data['target'] X = torch.tensor(X, dtype=torch.float32) y = torch.tensor(y, dtype=torch.long) # split X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, shuffle=True) class IrisModel(nn.Module): def __init__(self): super().__init__() self.hidden = nn.Linear(4, 8) self.act = nn.ReLU() self.output = nn.Linear(8, 3) def forward(self, x): x = self.act(self.hidden(x)) x = self.output(x) return x # loss metric and optimizer model = IrisModel() loss_fn = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) # prepare model and training parameters n_epochs = 100 batch_size = 10 batch_start = torch.arange(0, len(X_train), batch_size) # training loop for epoch in range(n_epochs): for start in batch_start: # take a batch X_batch = X_train[start:start+batch_size] y_batch = y_train[start:start+batch_size] # forward pass y_pred = model(X_batch) loss = loss_fn(y_pred, y_batch) # backward pass optimizer.zero_grad() loss.backward() # update weights optimizer.step() # validating model y_pred = model(X_test) acc = (torch.argmax(y_pred, 1) == y_test).float().mean() acc = float(acc)*100 print("Model accuracy: %.2f%%" % acc) ``` 运行上述代码将生成以下结果,例如: ```py Model accuracy: 97.78% ``` 所以你知道 `model` 是一个可以接受张量并返回张量的 PyTorch 模型。你可以使用 `torch.onnx.export()` 函数将此模型**转换**为 ONNX 格式: ```py torch.onnx.export(model, X_test, 'iris.onnx', input_names=["features"], output_names=["logits"]) ``` 运行此操作将会在本地目录创建一个文件 `iris.onnx`。你需要提供一个与模型兼容的**示例张量**作为输入(上例中的`X_test`)。这是因为在转换过程中,需要跟随这个示例张量来理解应应用哪些操作,从而可以一步步将算法转换为 ONNX 格式。PyTorch 模型中的每个权重都是一个张量,并且每个张量都有一个名称。但输入和输出张量通常没有命名,因此你需要在运行 `export()` 时为它们提供一个名称。这些名称应作为字符串列表提供,因为通常情况下,一个模型可以接受多个张量并返回多个张量。 通常,你应该在训练循环之后运行 `export()`。这是因为创建的 ONNX 模型包含一个完整的模型,你可以在没有 PyTorch 库的情况下运行它。你希望将优化后的权重保存到其中。然而,为了在 Netron 中可视化模型,模型的质量并不是问题。你可以在创建 PyTorch 模型后立即运行 `export()`。 启动 Netron 后,你可以打开保存的 ONNX 文件。在这个例子中,你应该会看到以下屏幕: ![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/d0d6730eccd640528c39e1782809b791~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1771200648&x-signature=0NrU9iqYRCMmzhZOZ0YPK%2BXQkaA%3D) 它展示了输入张量如何通过不同的操作连接到深度学习模型的输出张量。你提供给 `export()` 函数的输入和输出张量的名称会在可视化中使用。点击一个框会给你更多关于该张量或操作的详细信息。然而,你在 Netron 中看到的操作名称可能与 PyTorch 中的名称不同。例如,在上面的屏幕中,`nn.Linear()` 层变成了“Gemm”,代表“通用矩阵乘法”操作。你甚至可以通过 Netron 对层的权重进行检查,方法是点击几下。 如果你希望保存这个可视化的副本,你可以在 Netron 中将其导出为 PNG 格式。 ## 进一步阅读 Netron 是一个开源项目,你可以在 GitHub 上找到它的源代码: + [`github.com/lutzroeder/netron`](https://github.com/lutzroeder/netron) Netron 的在线版本如下: + [`netron.app/`](https://netron.app/) 另一个可视化库是 torchviz,但与上面你看到的例子不同,它跟踪模型的反向传递: + [`github.com/szagoruyko/pytorchviz`](https://github.com/szagoruyko/pytorchviz) ## 总结 在这篇文章中,你学会了如何可视化一个模型。特别是,你学到了: + 为什么可视化 PyTorch 模型很困难 + 如何将 PyTorch 模型转换为 ONNX 格式 + 如何使用 Netron 可视化 ONNX 模型