提交 e7817337 编写于 作者: W wizardforcel

2021-01-24 19:19:02

上级 fc2c6711
......@@ -314,11 +314,11 @@ if not self.explore_limit:
# 工作原理
在本秘籍中,我们完成了 DQN 类,并添加了所有必需的功能来训练 DQN。 在构造器中,我们初始化了探索的初始状态,观察空间和动作空间,然后定义了一个存储单元来保存 DQN 的经验。 我们创建了称为`policy_net``target_net`的软骨模型的两个实例。 我们需要两个网络,因为在训练的每个步骤中,Q 网络的值都会移动,并且如果我们使用不断变化的目标值来调整我们的网络,则该网络可能会由于陷入此变化的目标与估计的 Q 值之间的反馈回路而变得不稳定。 网络值。 如果发生这种情况,值估计将失去控制。 因此,我们使用了两个网络并将`target_net`保持在`eval`模式。 然后,我们使用`MSELoss()`作为损失函数以及`Adam`优化器来更新权重。
在本秘籍中,我们完成了 DQN 类,并添加了所有必需的函数来训练 DQN。 在构造器中,我们初始化了探索的初始状态,观察空间和动作空间,然后定义了一个存储单元来保存 DQN 的经验。 我们创建了称为`policy_net``target_net`的软骨模型的两个实例。 我们需要两个网络,因为在训练的每个步骤中,Q 网络的值都会移动,并且如果我们使用不断变化的目标值来调整我们的网络,则该网络可能会由于陷入此变化的目标与估计的 Q 值之间的反馈回路而变得不稳定。 网络值。 如果发生这种情况,值估计将失去控制。 因此,我们使用了两个网络并将`target_net`保持在`eval`模式。 然后,我们使用`MSELoss()`作为损失函数以及`Adam`优化器来更新权重。
`load_memory()`方法中,我们从环境中存储了状态,操作,奖励,下一个状态和终端,以用于训练网络。 我们使用的下一个方法是`predict_action`。 在此方法中,我们使用`np.random.rand()`选择了`random_number`,这为我们提供了`[0,1)`的值。 如果此`random_number`小于当前的`exploration_rate`,则我们选择一个随机动作,该动作由`exploration_rate`控制。 这就是我们合并探索的方式。 但是,如果`random_number`大于`exploration_rate`,则`target_net`会预测`q_values`并选择具有最大 Q 值的动作。
最后,我们实现了`experience_replay`方法。 在这里,我们等待数据点的数量至少为`BATCH_SIZE`,然后从内存中随机采样一批。 这使它可以从一系列不同的观察中学习,而不是从一系列紧密相关的观察中学习。 遍历批量时,我们使用`target_net`根据 Q 值更新公式更新了 Q 值。 然后,我们根据新 Q 值与`policy_net`预测的 Q 值之间的误差训练了`policy_net`。 之后,通过将其乘以探索衰减量逐渐降低探索速率,直到获得最低探索速率。 我们这样做是因为,在训练的初始阶段,我们希望智能体进行更多探索; 但是,随着训练的进行,我们希望算法能够收敛。 至此,我们已经完成了 DQN 类的所有功能
最后,我们实现了`experience_replay`方法。 在这里,我们等待数据点的数量至少为`BATCH_SIZE`,然后从内存中随机采样一批。 这使它可以从一系列不同的观察中学习,而不是从一系列紧密相关的观察中学习。 遍历批量时,我们使用`target_net`根据 Q 值更新公式更新了 Q 值。 然后,我们根据新 Q 值与`policy_net`预测的 Q 值之间的误差训练了`policy_net`。 之后,通过将其乘以探索衰减量逐渐降低探索速率,直到获得最低探索速率。 我们这样做是因为,在训练的初始阶段,我们希望智能体进行更多探索; 但是,随着训练的进行,我们希望算法能够收敛。 至此,我们已经完成了 DQN 类的所有函数
# 更多
......@@ -602,7 +602,7 @@ agents.append(agent)
# 操作步骤
在本秘籍中,我们将编写用于评估智能体,多次评估以及按给定序列评估所有智能体的功能。 按着这些次序:
在本秘籍中,我们将编写用于评估智能体,多次评估以及按给定序列评估所有智能体的函数。 按着这些次序:
1. 我们将从导入开始:
......
......@@ -188,15 +188,15 @@ for epoch in range(epochs):
图 1.7:显示客户购买产品的数据集
即使不是全部,我们也可以预测大多数结果。 但是,如果我们可以从中进行预测的数据点数量太多而又无法用凡人的大脑来处理它们该怎么办? 计算机可以浏览数据,并可能根据以前的数据吐出答案。 这种数据驱动的方法可以为我们提供很多帮助,因为我们唯一要做的就是假设相关的功能,然后将其交给包含不同算法的黑盒,以从功能集中学习规则或模式。
即使不是全部,我们也可以预测大多数结果。 但是,如果我们可以从中进行预测的数据点数量太多而又无法用凡人的大脑来处理它们该怎么办? 计算机可以浏览数据,并可能根据以前的数据吐出答案。 这种数据驱动的方法可以为我们提供很多帮助,因为我们唯一要做的就是假设相关的特征,然后将其交给包含不同算法的黑盒,以从特征集中学习规则或模式。
有问题。 即使我们知道要查找的内容,清理数据并提取特征也不是一件有趣的事情。 然而,最主要的麻烦不是这个。 我们无法有效预测高维数据和其他媒体类型的数据的功能。 例如,在人脸识别中,我们最初使用基于规则的程序找到人脸的细节长度,并将其作为输入输入神经网络,因为我们认为这是人类用来识别人脸的特征集。
有问题。 即使我们知道要查找的内容,清理数据并提取特征也不是一件有趣的事情。 然而,最主要的麻烦不是这个。 我们无法有效预测高维数据和其他媒体类型的数据的特征。 例如,在人脸识别中,我们最初使用基于规则的程序找到人脸的细节长度,并将其作为输入输入神经网络,因为我们认为这是人类用来识别人脸的特征集。
![Exploring deep learning](img/B09475_01_08.jpg)
图 1.8:人为选择的面部特征
事实证明,对于人类来说如此明显的功能对计算机而言并不那么明显,反之亦然。 特征选择问题的实现使我们进入了深度学习的时代。 这是机器学习的子集,其中我们使用相同的数据驱动方法,但不是让计算机明确选择功能,而是让计算机决定功能应该是什么。
事实证明,对于人类来说如此明显的功能对计算机而言并不那么明显,反之亦然。 特征选择问题的实现使我们进入了深度学习的时代。 这是机器学习的子集,其中我们使用相同的数据驱动方法,但不是让计算机明确选择特征,而是让计算机决定特征应该是什么。
让我们再次考虑面部识别示例。 Google 于 2014 年发表的 FaceNet 论文在深度学习的帮助下解决了它。 FaceNet 使用两个深层网络实现了整个应用。 第一个网络是从面孔识别特征集,第二个网络是使用该特征集并识别面孔(从技术上讲,将面孔分类为不同的存储桶)。 本质上,第一个网络正在做我们以前做的事情,第二个网络是一个简单而传统的机器学习算法。
......@@ -206,7 +206,7 @@ for epoch in range(epochs):
图 1.9:一个简单的神经网络
一个简单的神经网络具有一个单独的隐藏层,一个输入层和一个输出层。 从理论上讲,单个隐藏层应该能够近似任何复杂的数学方程式,并且对于单个层我们应该没问题。 然而,事实证明,单隐藏层理论并不是那么实用。 在深度网络中,每一层负责查找某些功能。 初始层找到更详细的功能,而最终层抽象这些详细功能并找到高级功能
一个简单的神经网络具有一个单独的隐藏层,一个输入层和一个输出层。 从理论上讲,单个隐藏层应该能够近似任何复杂的数学方程式,并且对于单个层我们应该没问题。 然而,事实证明,单隐藏层理论并不是那么实用。 在深度网络中,每一层负责查找某些特征。 初始层找到更详细的特征,而最终层抽象这些详细特征并找到高级特征
![Exploring deep learning](img/B09475_01_10.jpg)
......@@ -262,7 +262,7 @@ RNN 是最常见的深度学习算法之一,它们席卷全球。 我们现在
**卷积神经网络****CNN**)使我们能够在计算机视觉中获得超人的表现。 在的早期,我们达到了的人类准确率,并且我们仍在逐年提高准确率。
卷积网络是最易理解的网络,因为我们有可视化工具可以显示每一层的功能**Facebook AI Research****FAIR**)负责人 Yann LeCun 于 1990 年代发明了 CNN。 那时我们无法使用它们,因为我们没有足够的数据集和计算能力。 CNN 基本上像滑动窗口一样扫描您的输入并进行中间表示,然后在最终到达全连接层之前对其进行逐层抽象。 CNN 也成功地用于非图像数据集中。
卷积网络是最易理解的网络,因为我们有可视化工具可以显示每一层的特征**Facebook AI Research****FAIR**)负责人 Yann LeCun 于 1990 年代发明了 CNN。 那时我们无法使用它们,因为我们没有足够的数据集和计算能力。 CNN 基本上像滑动窗口一样扫描您的输入并进行中间表示,然后在最终到达全连接层之前对其进行逐层抽象。 CNN 也成功地用于非图像数据集中。
Facebook 研究团队发现了一种具有卷积网络的先进自然语言处理系统,该系统优于 RNN,RNN 被认为是任何序列数据集的首选架构。 尽管一些神经科学家和一些 AI 研究人员不喜欢 CNN,但是由于他们认为大脑不能像 CNN 那样工作,因此基于 CNN 的网络正在击败所有现有实现。
......@@ -350,7 +350,7 @@ True
```
现在,由于您知道张量是什么以及如何创建张量,因此我们将从最基本的数学运算开始。 一旦您熟悉乘法加法和矩阵运算之类的操作,其他所有功能都不过是乐高积木。
现在,由于您知道张量是什么以及如何创建张量,因此我们将从最基本的数学运算开始。 一旦您熟悉乘法加法和矩阵运算之类的操作,其他所有都不过是乐高积木。
PyTorch 张量对象具有覆盖了 Python 的数值运算,并且您可以使用普通运算符。 张量标量运算可能是最简单的:
......@@ -419,7 +419,7 @@ tensor([[0.5594, 0.8875, 0.9234, 1.1294],
索引张量就像索引普通的 Python 列表一样。 可以通过递归索引每个维度来索引多个维度。 索引从第一个可用维中选择索引。 索引时可以使用逗号分隔每个维度。 切片时可以使用此方法。 起始和结束索引可以使用完整的冒号分隔。 可以使用属性`t`访问矩阵的转置。 每个 PyTorch 张量对象都具有`t`属性。
连接是工具箱中需要执行的另一项重要操作。 PyTorch 出于相同的目的制作了功能`cat`。 所有尺寸上的两个张量相同的张量(一个张量除外)可以根据需要使用`cat`进行连接。 例如,大小为`3 x 2 x 4`的张量可以与另一个大小为`3 x 2 x 4`的张量在第一维上级联,以获得大小为`3 x 2 x 4`的张量。`stack`操作看起来非常类似于连接,但这是完全不同的操作。 如果要向张量添加新尺寸,则可以使用`stack`。 与`cat`相似,您可以将轴传递到要添加新尺寸的位置。 但是,请确保两个张量的所有尺寸都与附着尺寸相同。
连接是工具箱中需要执行的另一项重要操作。 PyTorch 出于相同的目的制作了函数`cat`。 所有尺寸上的两个张量相同的张量(一个张量除外)可以根据需要使用`cat`进行连接。 例如,大小为`3 x 2 x 4`的张量可以与另一个大小为`3 x 2 x 4`的张量在第一维上级联,以获得大小为`3 x 2 x 4`的张量。`stack`操作看起来非常类似于连接,但这是完全不同的操作。 如果要向张量添加新尺寸,则可以使用`stack`。 与`cat`相似,您可以将轴传递到要添加新尺寸的位置。 但是,请确保两个张量的所有尺寸都与附着尺寸相同。
`split``chunk`是用于拆分张量的类似操作。 `split`接受每个输出张量要的大小。 例如,如果要在第 0 个维度上拆分大小为`3 x 2`的张量,尺寸为 1,则将得到三个大小均为`3 x 2`的张量。但是,如果在第 0 个维度上使用 2 作为大小,则会得到`3 x 2`的张量和另一个`3 x 2`的张量。
......
......@@ -60,7 +60,7 @@ def get_numpy_data(input_size=10, limit=1000):
return training_test_gen(np.array(x), np.array(y))
```
编码器功能将输入编码为二进制数,从而使神经网络易于学习。 将数值直接传递到神经网络会对网络施加更多约束。 不要担心最后一行中的`training_test_gen`函数; 我们将在第 3 章和“深度学习工作流程”中进行更多讨论。 现在,请记住,它将数据集拆分为训练和测试集,并将其作为 NumPy 数组返回。
编码器函数将输入编码为二进制数,从而使神经网络易于学习。 将数值直接传递到神经网络会对网络施加更多约束。 不要担心最后一行中的`training_test_gen`函数; 我们将在第 3 章和“深度学习工作流程”中进行更多讨论。 现在,请记住,它将数据集拆分为训练和测试集,并将其作为 NumPy 数组返回。
利用到目前为止我们拥有的关于数据集的信息,我们可以按以下方式构建网络:
......@@ -80,7 +80,7 @@ output_size = 4
hidden_size = 100
```
我们需要在程序顶部定义输入和输出大小,这将帮助我们在不同的地方使用输入和输出大小,例如网络设计功能。 隐藏大小是隐藏层中神经元的数量。 如果要手动设计神经网络,则权重矩阵的大小为`input_size` x `hidden_size`,这会将您输入的大小`input_size`转换为大小`hidden_size``epoch`是通过网络进行迭代的计数器值。 `epoch`的概念最终取决于程序员如何定义迭代过程。 通常,对于每个时期,您都要遍历整个数据集,然后对每个时期重复一次。
我们需要在程序顶部定义输入和输出大小,这将帮助我们在不同的地方使用输入和输出大小,例如网络设计函数。 隐藏大小是隐藏层中神经元的数量。 如果要手动设计神经网络,则权重矩阵的大小为`input_size` x `hidden_size`,这会将您输入的大小`input_size`转换为大小`hidden_size``epoch`是通过网络进行迭代的计数器值。 `epoch`的概念最终取决于程序员如何定义迭代过程。 通常,对于每个时期,您都要遍历整个数据集,然后对每个时期重复一次。
```py
for i in epoch:
......@@ -123,7 +123,7 @@ b2 = torch.zeros(1, output_size, requires_grad=True, device=device, dtype=dtype)
#### 注意
**注意**:自动区分,有时也称为算法区分,是通过计算机程序利用功能执行顺序的技术。 自动区分的两种主要方法是正向模式和反向模式。 在前向模式自动微分中,我们首先找到外部函数的导数,然后递归进入内部,直到我们探索所有子节点。 反向模式自动区分正好相反,并且被深度学习社区和框架使用。 它由 Seppo Linnainmaa 于 1970 年在其硕士论文中首次出版。反向模式微分的主要构建模块是存储中间变量的存储器,以及使这些变量计算导数的功能,同时从子节点移回到父节点。 节点。
**注意**:自动区分,有时也称为算法区分,是通过计算机程序利用函数执行顺序的技术。 自动区分的两种主要方法是正向模式和反向模式。 在前向模式自动微分中,我们首先找到外部函数的导数,然后递归进入内部,直到我们探索所有子节点。 反向模式自动区分正好相反,并且被深度学习社区和框架使用。 它由 Seppo Linnainmaa 于 1970 年在其硕士论文中首次出版。反向模式微分的主要构建模块是存储中间变量的存储器,以及使这些变量计算导数的功能,同时从子节点移回到父节点。
正如 PyTorch 主页所说,PyTorch 中所有神经网络的中心都是 Autograd 软件包。 PyTorch 借助 Autograd 软件包获得了动态功能。 程序执行时,Autograd 将每个操作写入磁带状数据结构并将其存储在内存中。
......@@ -166,7 +166,7 @@ tensor([1.])
#### 张量的 Autograd 属性
当成为图形的一部分时,张量需要存储 Autograd 自动区分所需的信息。 张量充当计算图中的一个节点,并通过功能模块实例连接到其他节点。 张量实例主要具有支持 Autograd 的三个属性:`.grad``.data``grad_fn()`(注意字母大小写:`Function`代表 PyTorch `Function`模块,而`function`代表 Python 函数)。
当成为图形的一部分时,张量需要存储 Autograd 自动区分所需的信息。 张量充当计算图中的一个节点,并通过函数式模块实例连接到其他节点。 张量实例主要具有支持 Autograd 的三个属性:`.grad``.data``grad_fn()`(注意字母大小写:`Function`代表 PyTorch `Function`模块,而`function`代表 Python 函数)。
`.grad`属性在任何时间点存储梯度,所有向后调用将当前梯度累积到`.grad`属性。 `.data`属性可访问其中包含数据的裸张量对象。
......@@ -176,7 +176,7 @@ tensor([1.])
如果您想知道,前面的代码片段中的`required_grad`参数会通知张量或 Autograd 引擎在进行反向传播时需要梯度。 创建张量时,可以指定是否需要该张量来承载梯度。 在我们的示例中,我们没有使用梯度更新输入张量(输入永远不会改变):我们只需要更改权重即可。 由于我们没有在迭代中更改输入,因此不需要输入张量即可计算梯度。 因此,在包装输入张量时,我们将`False`作为`required_grad`参数传递,对于权重,我们传递`True`。 检查我们之前创建的张量实例的`grad``data`属性。
`Tensor``Function`实例在图中时是相互连接的,并且一起构成了非循环计算图。 除了用户明确创建的张量以外,每个张量都连接到一个函数。 (如果用户未明确创建张量,则必须通过函数创建张量。例如,表达式`c = a + b`中的`c`由加法函数创建。 )您可以通过在张量上调用`grade_fn`来访问创建器功能。 打印`grad``.data``.grade_fn()`的值可得到以下结果:
`Tensor``Function`实例在图中时是相互连接的,并且一起构成了非循环计算图。 除了用户明确创建的张量以外,每个张量都连接到一个函数。 (如果用户未明确创建张量,则必须通过函数创建张量。例如,表达式`c = a + b`中的`c`由加法函数创建。 )您可以通过在张量上调用`grade_fn`来访问创建器函数。 打印`grad``.data``.grade_fn()`的值可得到以下结果:
```py
print(x.grad, x.grad_fn, x)
......@@ -245,7 +245,7 @@ print(a2.grad, a2.grad_fn, a2)
###### 注意
**Softmax**:让 Sigmoid 曲面吐出分类问题的预测是很不寻常的,但是我们将其保留下来,因为这样会使我们的模型易于理解,因为它重复了第一层。 通常,分类问题由 softmax 层和交叉熵损失处理,这会增加一类相对于另一类的概率。 由于所有类别的概率加在一起,因此增加一个类别的概率会降低其他类别的概率,这是一个不错的功能。 在以后的章节中将对此进行更多介绍。
**Softmax**:让 Sigmoid 曲面吐出分类问题的预测是很不寻常的,但是我们将其保留下来,因为这样会使我们的模型易于理解,因为它重复了第一层。 通常,分类问题由 softmax 层和交叉熵损失处理,这会增加一类相对于另一类的概率。 由于所有类别的概率加在一起,因此增加一个类别的概率会降低其他类别的概率,这是一个不错的函数。 在以后的章节中将对此进行更多介绍。
##### 查找误差
......@@ -304,7 +304,7 @@ SGD 的主要缺点是效率低下。 例如,考虑我们的 *FizzBu​​zz*
我们已经到达了模型构建旅程的最后一部分。 到目前为止,所有操作都很直观,简单,但是最后一部分有点令人困惑。 `zero_grad`做什么? 还记得关于重量`w1.grad`的第一份印刷声明吗? 它是空的,现在具有当前反向传递的梯度。 因此,我们需要在下一次反向传播之前清空梯度,因为梯度会累积而不是被重写。 参数更新后,我们在每个迭代的每个张量上调用`zero_grad()`,然后继续进行下一个迭代。
`.grad_fn`通过连接函数和张量将图形保持在一起。 在`Function`模块中定义了对张量的每种可能的操作。 所有张量的`.grad_fn`始终指向功能对象,除非用户创建了它。 PyTorch 允许您使用`grad_fn`向后浏览图形。 从图形中的任何节点,可以通过在`grad_fn`的返回值上调用`next_functions`来到达任何父节点。
`.grad_fn`通过连接函数和张量将图形保持在一起。 在`Function`模块中定义了对张量的每种可能的操作。 所有张量的`.grad_fn`始终指向函数对象,除非用户创建了它。 PyTorch 允许您使用`grad_fn`向后浏览图形。 从图形中的任何节点,可以通过在`grad_fn`的返回值上调用`next_functions`来到达任何父节点。
```py
# traversing the graph using .grad_fn
......@@ -326,13 +326,13 @@ print(output.grad_fn.next_functions[0][0].next_functions[0][0])
到目前为止,我们已经以 NumPy-PyTorch 混合形式开发了一个简单的两层神经网络。 我们已经在 NumPy 中逐行编码了每个操作,就像我们在 NumPy 中进行编码一样,并且我们采用了与 PyTorch 的自动区分,因此我们不必对反向传递进行编码。
在途中,我们学习了如何在 PyTorch 中包装矩阵(或张量),这有助于我们进行反向传播。 使用 PyTorch 进行相同操作的方式更加方便,这就是我们将在本节中讨论的内容。 PyTorch 可以访问内置的深度学习项目所需的几乎所有功能。 由于 PyTorch 支持 Python 中所有可用的数学函数,因此,如果在内核中不可用,则构建一个函数并不是一件艰巨的任务。 您不仅可以构建所需的任何功能,而且 PyTorch 隐式定义了所构建功能的派生功能
在途中,我们学习了如何在 PyTorch 中包装矩阵(或张量),这有助于我们进行反向传播。 使用 PyTorch 进行相同操作的方式更加方便,这就是我们将在本节中讨论的内容。 PyTorch 可以访问内置的深度学习项目所需的几乎所有功能。 由于 PyTorch 支持 Python 中所有可用的数学函数,因此,如果在内核中不可用,则构建一个函数并不是一件艰巨的任务。 您不仅可以构建所需的任何函数,而且 PyTorch 隐式定义了所构建函数的导函数
PyTorch 对需要了解底层操作的人很有帮助,但同时,PyTorch 通过`torch.nn`模块提供了高层 API。 因此,如果用户不想知道黑盒内部发生了什么,而只需要构建模型,则 PyTorch 允许他们这样做。 同样,如果用户不喜欢引擎盖下的提升操作,并且需要知道到底发生了什么,PyTorch 也可以提供这种灵活性。 将这种组合构建到单个框架上可以改变游戏规则,并使 PyTorch 成为整个深度学习社区最喜欢的框架之一。
### 高级 API
高级 API 使初学者可以从头开始构建网络,同时,它们使高级用户可以花时间在其他关键部件上,而不必将发明的模块留给 PyTorch。 PyTorch 中构建神经网络所需的所有模块都是具有前进和后退功能的 Python 类实例。 当您开始执行神经网络时,在后台执行的是正向功能,该功能又将操作添加到磁带上。 由于 PyTorch 知道所有操作的派生功能,因此 PyTorch 很容易在磁带上移回。 现在,我们将代码模块化为较小的单元,以制造相同的 *FizzBu​​zz* 网络。
高级 API 使初学者可以从头开始构建网络,同时,它们使高级用户可以花时间在其他关键部件上,而不必将发明的模块留给 PyTorch。 PyTorch 中构建神经网络所需的所有模块都是具有正向反向函数的 Python 类实例。 当您开始执行神经网络时,在后台执行的是正向函数,该函数又将操作添加到磁带上。 由于 PyTorch 知道所有操作的导函数,因此 PyTorch 很容易在磁带上移回。 现在,我们将代码模块化为较小的单元,以制造相同的 *FizzBu​​zz* 网络。
模块化代码具有相同的结构,因为我们获取数据并从 NumPy 数据输入创建张量。 其余的“复杂”代码可以替换为我们创建的模型类。
......@@ -372,11 +372,11 @@ class FizBuzNet(nn.Module):
图 2.9:一个简单的网络,用于硬币排名并将输出传递给主要网络
`nn.Module`使您更容易拥有如此漂亮的抽象。 初始化`class`对象时,将调用`__init__()`,这又将初始化层并返回对象。 `nn.Module`实现了两个主要功能,即`__call__``backward()`,并且用户需要覆盖`forward``__init__()`
`nn.Module`使您更容易拥有如此漂亮的抽象。 初始化`class`对象时,将调用`__init__()`,这又将初始化层并返回对象。 `nn.Module`实现了两个主要函数,即`__call__``backward()`,并且用户需要覆盖`forward``__init__()`
一旦返回了层初始化的对象,就可以通过调用`model`对象本身将输入数据传递给模型。 通常,Python 对象不可调用。 要调用对象方法,用户必须显式调用它们。 但是,`nn.Module`实现了魔术函数`__call__()`,该函数又调用了用户定义的`forward`函数。 用户具有在前向呼叫中定义所需内容的特权。
只要 PyTorch 知道如何反向传播`forward`中的内容,您就很安全。 但是,如果您在`forward`中具有自定义功能或层,则 PyTorch 允许您覆盖`backward`函数,并且该功能将在返回磁带时执行。
只要 PyTorch 知道如何反向传播`forward`中的内容,您就很安全。 但是,如果您在`forward`中具有自定义函数或层,则 PyTorch 允许您覆盖`backward`函数,并且该函数将在返回磁带时执行。
用户可以选择在`__init__()`定义中构建层,这将照顾我们在新手模型中手工完成的权重和偏差创建。 在下面的`FizBuzNet`中,`__init__()`中的线创建了线性层。 线性层也称为全连接层或密集层,它在权重和输入之间进行矩阵乘法,并在内部进行偏差加法:
......@@ -407,7 +407,7 @@ a2 = x_.matmul(w1)
a2 = a2.add(b1)
```
在以后的章节中,我们将使用`nn.module`的更重要的功能
在以后的章节中,我们将使用`nn.module`的更重要的函数
##### `apply()`
......@@ -415,7 +415,7 @@ a2 = a2.add(b1)
##### `cuda()`和`cpu()`
这些功能与我们之前讨论的目的相同。 但是,`model.cpu()`将所有参数转换为 CPU 张量,当您的模型中有多个参数并且分别转换每个参数很麻烦时,这非常方便。
这些函数与我们之前讨论的目的相同。 但是,`model.cpu()`将所有参数转换为 CPU 张量,当您的模型中有多个参数并且分别转换每个参数很麻烦时,这非常方便。
```py
net = FizBuzNet(input_size, hidden_size, output_size)
......@@ -440,7 +440,7 @@ y = torch.from_numpy(trY).type(ytype)
##### `train()`和`eval()`
就像名称所示,这些功能告诉 PyTorch 模型正在训练模式或评估模式下运行。 仅在要关闭或打开模块(例如`Dropout``BatchNorm`)时,此功能才有效。 在以后的章节中,我们将经常使用它们。
就像名称所示,这些函数告诉 PyTorch 模型正在训练模式或评估模式下运行。 仅在要关闭或打开模块(例如`Dropout``BatchNorm`)时,此函数才有效。 在以后的章节中,我们将经常使用它们。
##### `parameters()`
......@@ -482,7 +482,7 @@ net = nn.Sequential(
### `functional`模块
`nn.functional`模块附带我们需要将网络节点连接在一起的操作。 在我们的模型中,我们使用`functional`模块中的 Sigmoid 作为非线性激活。 `functional`模块具有更多功能,例如您正在执行的所有数学函数都指向`functional`模块。 在下面的示例中,乘法运算符从`functional`模块调用`mul`运算符:
`nn.functional`模块附带我们需要将网络节点连接在一起的操作。 在我们的模型中,我们使用`functional`模块中的 Sigmoid 作为非线性激活。 `functional`模块具有更多函数,例如您正在执行的所有数学函数都指向`functional`模块。 在下面的示例中,乘法运算符从`functional`模块调用`mul`运算符:
```py
>>> a = torch.randn(1,2)
......@@ -509,11 +509,11 @@ tensor([[1]], dtype=torch.uint8)
```
如前面的示例所示,`F.linear`允许我们传递权重和输入,并返回与在新手模型中使用的普通`matmul`相同的值。 `functional`中的其他层功能也以相同的方式工作。
如前面的示例所示,`F.linear`允许我们传递权重和输入,并返回与在新手模型中使用的普通`matmul`相同的值。 `functional`中的其他层函数也以相同的方式工作。
#### 注意
**Sigmoid 激活**:激活函数在神经网络的各层之间创建非线性。 这是必不可少的,因为在没有非线性的情况下,各层只是将输入值与权重相乘。 在那种情况下,神经网络的单层可以完成 100 层的确切功能; 这只是增加或减少权重值的问题。 Sigmoid激活可能是最传统的激活函数。 它将输入压缩到[0,1]的范围。
**Sigmoid 激活**:激活函数在神经网络的各层之间创建非线性。 这是必不可少的,因为在没有非线性的情况下,各层只是将输入值与权重相乘。 在那种情况下,神经网络的单层可以完成 100 层的确切函数; 这只是增加或减少权重值的问题。 Sigmoid激活可能是最传统的激活函数。 它将输入压缩到`[0,1]`的范围。
![The functional module](img/B09475_02_10.jpg)
......
......@@ -65,7 +65,7 @@ class FizBuzDataset(Dataset):
return self.end - self.start
```
自定义数据集的实现使用 Python 3.7 中的全新`dataclasses``dataclasses`通过使用动态代码生成,有助于消除 Python 魔术功能的样板代码,例如`__init__`。 这需要代码被类型提示,这就是类中前三行的用途。 您可以在 Python 的官方文档[1]中阅读有关`dataclasses`的更多信息。
自定义数据集的实现使用 Python 3.7 中的全新`dataclasses``dataclasses`通过使用动态代码生成,有助于消除 Python 魔术函数的样板代码,例如`__init__`。 这需要代码被类型提示,这就是类中前三行的用途。 您可以在 Python 的官方文档[1]中阅读有关`dataclasses`的更多信息。
`__len__`函数返回传递给该类的结束值和起始值之间的差。 在`fizzbuzz`数据集中,数据正在由程序生成。 数据生成的实现在`__getitem__`函数内部,其中,类实例根据`DataLoader`传递的索引生成数据。 PyTorch 使类抽象尽可能通用,以便用户可以定义数据加载器应为每个 ID 返回的内容。 在这种特殊情况下,类实例为每个索引返回输入和输出,其中输入`x`是索引本身的二进制编码器版本,而输出是具有四个状态的单热编码输出。 四个状态表示下一个数字是三的倍数(嘶嘶声)或五的倍数(嗡嗡声),三或五的倍数(嘶嘶声)或不是三或五的倍数。
......@@ -136,7 +136,7 @@ Dataset MNIST
```
`torchvision`使用枕头(`PIL`)作为加载图像的默认后端。 但是通过方便的功能`torchvision.set_image_backend(backend)`,可以将其更改为任何兼容的后端。 `torchvision`提供的所有数据都继承自`torch.utils.data.Dataset`类,因此,已经针对其中每个实现了`__len__``__getitem__`。 这两个魔术函数都使所有这些数据集都能与`DataLoader`兼容,就像我们实现简单数据集并将其加载到`DataLoader`的方式一样。
`torchvision`使用枕头(`PIL`)作为加载图像的默认后端。 但是通过方便的函数`torchvision.set_image_backend(backend)`,可以将其更改为任何兼容的后端。 `torchvision`提供的所有数据都继承自`torch.utils.data.Dataset`类,因此,已经针对其中每个实现了`__len__``__getitem__`。 这两个魔术函数都使所有这些数据集都能与`DataLoader`兼容,就像我们实现简单数据集并将其加载到`DataLoader`的方式一样。
```py
>>> mnist[1]
......@@ -248,11 +248,11 @@ train, val, test = data.TabularDataset.splits(
fields=[('Text', TEXT), ('Label', LABEL)])
```
上一小节的第一部分在 spaCy 中加载英语,并定义了分词器功能。 下一部分是使用`torchtext.data.Field`定义输入和输出字段的位置。 `Field`类用于定义将数据加载到`DataLoader`之前的预处理步骤。
上一小节的第一部分在 spaCy 中加载英语,并定义了分词器函数。 下一部分是使用`torchtext.data.Field`定义输入和输出字段的位置。 `Field`类用于定义将数据加载到`DataLoader`之前的预处理步骤。
在所有输入语句之间共享`Field`变量`TEXT`,并且在所有输出标签之间共享`Field`变量`LABEL`。 该示例中的`TEXT`设置为顺序的,这告诉`Field`实例数据是顺序相关的,并且分词是将其分成较小块的更好选择。 如果`sequential`设置为`False`,则不会对数据应用分词。
由于`sequential``TEXT``True`,因此我们开发的分词函数设置为`tokenizer`。 该选项默认为 Python 的`str.split`,但是我们需要更智能的分词功能,而 spaCy 的分词功能可以为我们提供帮助。
由于`sequential``TEXT``True`,因此我们开发的分词函数设置为`tokenizer`。 该选项默认为 Python 的`str.split`,但是我们需要更智能的分词函数,而 spaCy 的分词功能可以为我们提供帮助。
常规 NLP 管道所做的另一个重要修改是将所有数据转换为相同的情况。 将`lower`设置为`True`会发生这种情况,但是默认情况下是`False`。 除了示例中给出的三个参数外,`Field`类还接受许多其他参数,其中包括`fix_length`以固定序列的长度; `pad_token`,默认为`<pad>`,用于填充序列以匹配`fixed_length`或批量中最长序列的长度; 和`unk_token`(默认为`<unk>`),用于替换没有词汇向量的标记。
......@@ -477,7 +477,7 @@ trainer = Engine(training_loop)
这很聪明,但这并没有节省用户大量时间,也没有兑现承诺,例如删除样板。 它所做的只是删除两个`for`循环并添加`Engine`对象创建的另一行。 这并不是 Ignite 的真正目的。 Ignite 尝试同时使编码变得有趣且灵活,从而有助于避免重复样板。
Ignite 提供了一些常用功能,例如有监督的训练或有监督的评估,并且还使用户可以灵活地定义自己的训练函数,例如训练 GAN,**强化学习****RL**)算法,依此类推。
Ignite 提供了一些常用函数,例如有监督的训练或有监督的评估,并且还使用户可以灵活地定义自己的训练函数,例如训练 GAN,**强化学习****RL**)算法,依此类推。
```py
from ignite.engine import create_supervised_trainer, create_supervised_evaluator
......@@ -490,7 +490,7 @@ trainer.run(train_loader, max_epochs=epochs)
evaluator.run(val_loader)
```
函数`create_supervised_trainer``create_supervised_evaluator`返回一个`Engine`对象,该对象具有类似于`training_loop`功能来执行代码的公共模式,如先前给出的那样。 除了给定的参数,这两个函数还接受一个设备(CPU 或 GPU),该设备返回在我们指定的设备上运行的训练器或评估器`Engine`实例。 现在情况越来越好了吧? 我们传递了定义的模型,所需的优化器以及正在使用的损失函数,但是在有了训练器和`evaluator`对象之后我们该怎么办?
函数`create_supervised_trainer``create_supervised_evaluator`返回一个`Engine`对象,该对象具有类似于`training_loop`函数来执行代码的公共模式,如先前给出的那样。 除了给定的参数,这两个函数还接受一个设备(CPU 或 GPU),该设备返回在我们指定的设备上运行的训练器或评估器`Engine`实例。 现在情况越来越好了吧? 我们传递了定义的模型,所需的优化器以及正在使用的损失函数,但是在有了训练器和`evaluator`对象之后我们该怎么办?
`Engine`对象定义了`run`方法,该方法使循环根据传递给`run`函数的时期和加载器开始执行。 与往常一样,`run`方法使`trainer`循环从零到历元数。 对于每次迭代,我们的训练器都会通过加载程序进行梯度更新。
......@@ -500,7 +500,7 @@ evaluator.run(val_loader)
#### 事件
Ignite 打开了一种通过事件或触发器与循环进行交互的特殊方式。 当事件发生并执行用户在功能中定义的操作时,每个设置功能都会触发。 这样,用户就可以灵活地设置任何类型的事件,并且通过避免将那些复杂的事件写入循环中并使循环变得更大且不可读,从而使用户的生活变得更加轻松。 `Engine`中当前可用的事件是:
Ignite 打开了一种通过事件或触发器与循环进行交互的特殊方式。 当事件发生并执行用户在函数中定义的操作时,每个设置函数都会触发。 这样,用户就可以灵活地设置任何类型的事件,并且通过避免将那些复杂的事件写入循环中并使循环变得更大且不可读,从而使用户的生活变得更加轻松。 `Engine`中当前可用的事件是:
* `EPOCH_STARTED`
* `EPOCH_COMPLETED`
......@@ -529,13 +529,13 @@ def run_evaluator_on_validation_data(engine):
evaluator.run(val_loader)
```
到目前为止,我必须已经使您相信 Ignite 是工具箱中的必备工具。 在前面的示例中,已为三个事件设置了`@trainer.on`装饰器; 实际上,在两个事件上,我们在`EPOCH_COMPLETED`事件上设置了两个功能。 使用第一个功能,我们可以将训练状态打印到终端上。 但是有些事情我们还没有看到。 状态是`Engine`用来保存有关执行信息的`state`变量。 在示例中,我们看到状态保存了有关时期,迭代乃至输出的信息,这实际上是训练循环的损失。 `state`属性包含周期,迭代,当前数据,指标(如果有)(我们将很快了解指标); 调用`run`函数时设置的最大周期,以及`training_loop`函数的输出。
到目前为止,我必须已经使您相信 Ignite 是工具箱中的必备工具。 在前面的示例中,已为三个事件设置了`@trainer.on`装饰器; 实际上,在两个事件上,我们在`EPOCH_COMPLETED`事件上设置了两个函数。 使用第一个函数,我们可以将训练状态打印到终端上。 但是有些事情我们还没有看到。 状态是`Engine`用来保存有关执行信息的`state`变量。 在示例中,我们看到状态保存了有关时期,迭代乃至输出的信息,这实际上是训练循环的损失。 `state`属性包含周期,迭代,当前数据,指标(如果有)(我们将很快了解指标); 调用`run`函数时设置的最大周期,以及`training_loop`函数的输出。
##### 注意
**注意**:在`create_supervised_trainer`的情况下,`training_loop`函数返回损失,在`create_supervised_evaluator`的情况下,`training_loop`函数返回模型的输出。 但是,如果我们定义一个自定义`training_loop`函数,则此函数返回的内容将是`Engine.state.output`保留的内容。
第二和第三事件处理程序正在`EPOCH_COMPLETED`上运行`evaluator`,但具有不同的数据集。 在第一个功能中,`evaluator`使用训练数据集,在第二个功能中,它使用评估数据集。 太好了,因为现在我们可以在每个时期完成时运行`evaluator`,而不是像第一个示例那样在整个执行结束时运行。 但是,除了运行它之外,处理程序实际上并没有做任何事情。 通常,这里是我们检查平均准确率和平均损失的地方,并且我们会进行更复杂的分析,例如混淆度量的创建,我们将在后面看到。 但是,目前的主要收获是:可以为单个事件设置`n`处理程序数量,Ignite 会毫不犹豫地依次调用所有这些处理程序。 接下来是事件的内部`_fire_event`函数,该事件在`training_loop`函数的每个事件中触发。
第二和第三事件处理程序正在`EPOCH_COMPLETED`上运行`evaluator`,但具有不同的数据集。 在第一个函数中,`evaluator`使用训练数据集,在第二个函数中,它使用评估数据集。 太好了,因为现在我们可以在每个时期完成时运行`evaluator`,而不是像第一个示例那样在整个执行结束时运行。 但是,除了运行它之外,处理程序实际上并没有做任何事情。 通常,这里是我们检查平均准确率和平均损失的地方,并且我们会进行更复杂的分析,例如混淆度量的创建,我们将在后面看到。 但是,目前的主要收获是:可以为单个事件设置`n`处理程序数量,Ignite 会毫不犹豫地依次调用所有这些处理程序。 接下来是事件的内部`_fire_event`函数,该事件在`training_loop`函数的每个事件中触发。
```py
def _fire_event(self, event_name, *event_args):
......
......@@ -49,9 +49,9 @@ CNN 通过两种机制实现位置不变:跨步和合并。 步幅值决定了
## 使用 PyTorch 的计算机视觉
PyTorch 为计算机视觉提供了几个便捷功能,其中包括卷积层和池化层。 PyTorch 在`torch.nn`包下提供`Conv1d``Conv2d``Conv3d`。 听起来,`Conv1d`处理一维卷积,`Conv2d`处理带有图像之类输入的二维卷积,`Conv3d`处理诸如视频之类的输入上的三维卷积。 显然,这很令人困惑,因为指定的尺寸从未考虑输入的深度。 例如,`Conv2d`处理四维输入,其中第一维将是批量大小,第二维将是图像的深度(在 RGB 通道中),最后两个维将是图像的高度和宽度。 图片。
PyTorch 为计算机视觉提供了几个便捷函数,其中包括卷积层和池化层。 PyTorch 在`torch.nn`包下提供`Conv1d``Conv2d``Conv3d`。 听起来,`Conv1d`处理一维卷积,`Conv2d`处理带有图像之类输入的二维卷积,`Conv3d`处理诸如视频之类的输入上的三维卷积。 显然,这很令人困惑,因为指定的尺寸从未考虑输入的深度。 例如,`Conv2d`处理四维输入,其中第一维将是批量大小,第二维将是图像的深度(在 RGB 通道中),最后两个维将是图像的高度和宽度。 图片。
除了用于计算机视觉的高层功能之外,`torchvision`还具有一些方便的工具函数来建立网络。 在本章中,我们将探讨其中的一些。
除了用于计算机视觉的高层函数之外,`torchvision`还具有一些方便的工具函数来建立网络。 在本章中,我们将探讨其中的一些。
本章使用两个神经网络应用说明 PyTorch:
......@@ -62,7 +62,7 @@ PyTorch 为计算机视觉提供了几个便捷功能,其中包括卷积层和
我们正在开发 CNN 以执行简单的分类任务。 使用简单 CNN 的想法是为了了解 CNN 的工作原理。 弄清基础知识后,我们将转到高级网络设计,在其中使用高级 PyTorch 函数,该函数与该应用具有相同的功能,但效率更高。
我们将使用 CIFAR10 作为输入数据集,它由 10 类 60,000 张`32x32`彩色图像组成,每类 6,000 张图像。 `torchvision`具有更高级别的功能,可下载和处理数据集。 如我们在第 3 章,“深度学习工作流”中看到的示例一样,我们下载数据集,然后使用转换对其进行转换,并将其包装在`get_data()`函数下。
我们将使用 CIFAR10 作为输入数据集,它由 10 类 60,000 张`32x32`彩色图像组成,每类 6,000 张图像。 `torchvision`具有更高级别的函数,可下载和处理数据集。 如我们在第 3 章,“深度学习工作流”中看到的示例一样,我们下载数据集,然后使用转换对其进行转换,并将其包装在`get_data()`函数下。
```py
def get_data():
......@@ -253,7 +253,7 @@ optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
trainloader, testloader = get_data()
```
我们创建神经网络类的实例。 还记得前进功能的工作原理吗? 网络类将定义`__call__()`函数,并依次调用我们为正向传播定义的`forward()`函数。
我们创建神经网络类的实例。 还记得正向函数的工作原理吗? 网络类将定义`__call__()`函数,并依次调用我们为正向传播定义的`forward()`函数。
在下一行中定义的损失函数也是`torch.nn.Module`的子类,它也具有`forward()`函数,该函数由`__call__()`和向后函数调用。 这使我们可以灵活地创建自定义损失函数。
......@@ -388,7 +388,7 @@ LinkNet 中的所有卷积都紧随其后的是批量规范化和 ReLU 层,但
标准化每一层被证明是解决此特定问题的一种方法,即使我们提高了学习速度,也可以提高准确率。 批量归一化还可以帮助网络从更稳定的输入分布中学习,从而加快了网络的收敛速度。 PyTorch 对不同尺寸的输入实现了批量归一化,就像卷积层一样。 在这里我们使用`BatchNorm2d`,因为我们有四维数据,其中一维是批量大小,另一维是深度。
`BatchNorm2d`用两个可学习的参数实现:伽玛和贝塔。 除非我们将仿射参数设置为`False`,否则 PyTorch 会在反向传播时处理这些功能的学习。 现在,`BatchNorm2d`接受特征数量,ε 值,动量和仿射作为参数。
`BatchNorm2d`用两个可学习的参数实现:伽玛和贝塔。 除非我们将仿射参数设置为`False`,否则 PyTorch 会在反向传播时处理这些特征的学习。 现在,`BatchNorm2d`接受特征数量,ε 值,动量和仿射作为参数。
ε值将添加到平方根内的分母中以保持数值稳定性,而动量因子决定应从上一层获得多少动量以加快操作速度。
......
......@@ -497,7 +497,7 @@ SPINN 将输入的句子编码为固定长度的向量,就像基于 RNN 的编
#### 简化
简化网络将最左边的单词,最右边的单词和句子上下文作为输入,并在`forward`调用中生成单个归约的输出。 句子上下文由另一个称为`Tracker`的深度网络给出。 `Reduce`不在乎网络中正在发生的事情; 它总是接受三个输入,并由此减少输出。 树 LSTM 是标准 LSTM 的变体,用于与`bundle``unbundle`等其他辅助功能一起批量`Reduce`网络中发生的繁重操作。
简化网络将最左边的单词,最右边的单词和句子上下文作为输入,并在`forward`调用中生成单个归约的输出。 句子上下文由另一个称为`Tracker`的深度网络给出。 `Reduce`不在乎网络中正在发生的事情; 它总是接受三个输入,并由此减少输出。 树 LSTM 是标准 LSTM 的变体,用于与`bundle``unbundle`等其他辅助函数一起批量`Reduce`网络中发生的繁重操作。
```py
class Reduce(nn.Module):
......
......@@ -249,7 +249,7 @@ dilatedcausalconv = torch.nn.Conv1d(
图 6.11:带有内核扩展的膨胀卷积
PyTorch 通过使用户能够将膨胀作为关键字参数传递,从而使进行膨胀卷积变得容易,如先前代码块中的`DilatedCausalConv1d`节点中所给出的。 如前所述,每一层具有不同的扩张因子,并且可以为每一层的扩张卷积节点创建传递该因子。 由于跨步为 1,所以填充保持为 0,目的不是上采样或下采样。 `init_weights_for_test`是通过将权重矩阵填充 1 来进行测试的便捷功能
PyTorch 通过使用户能够将膨胀作为关键字参数传递,从而使进行膨胀卷积变得容易,如先前代码块中的`DilatedCausalConv1d`节点中所给出的。 如前所述,每一层具有不同的扩张因子,并且可以为每一层的扩张卷积节点创建传递该因子。 由于跨步为 1,所以填充保持为 0,目的不是上采样或下采样。 `init_weights_for_test`是通过将权重矩阵填充 1 来进行测试的便捷函数
PyTorch 提供的灵活性使用户可以在线调整参数,这对于调试网络更加有用。 `forward`传递仅调用 PyTorch `conv1d`对象,该对象是可调用的并保存在`self.conv`变量中:
......@@ -441,7 +441,7 @@ def train_discriminator(optimizer, real_data, fake_data):
在前面的代码块中定义的函数`train_generator`接受`optimizer`对象,伪数据和实数据,然后将它们传递给判别器。 函数`fake_data_target`(在下面的代码块中提供)创建一个零张量,该张量的大小与预测大小相同,其中预测是从判别器返回的值。 判别器的训练策略是使任何真实数据被归类为真实分布的概率最大化,并使任何数据点被归类为真实分布的概率最小化。 在实践中,使用了来自判别器或生成器的结果的日志,因为这会严重损害网络的分类错误。 然后在应用`optimizer.step`函数之前将错误反向传播,该函数将通过学习速率以梯度更新权重。
接下来给出用于获得真实数据目标和伪数据目标的功能,这与前面讨论的最小化或最大化概率的概念基本一致。 实际数据生成器返回一个张量为 1s 的张量,该张量是我们作为输入传递的形状。 在训练生成器时,我们正在尝试通过生成图像来最大程度地提高其概率,该图像看起来应该是从真实数据分布中获取的。 这意味着判别器应将 1 预测为图像来自真实分布的置信度分数。
接下来给出用于获得真实数据目标和伪数据目标的函数,这与前面讨论的最小化或最大化概率的概念基本一致。 实际数据生成器返回一个张量为 1s 的张量,该张量是我们作为输入传递的形状。 在训练生成器时,我们正在尝试通过生成图像来最大程度地提高其概率,该图像看起来应该是从真实数据分布中获取的。 这意味着判别器应将 1 预测为图像来自真实分布的置信度分数。
```py
def real_data_target(size):
......@@ -505,7 +505,7 @@ class GeneratorNet(torch.nn.Module):
return x
```
生成器训练器功能比判别器训练器功能简单得多,因为它不需要从两个来源获取输入,也不必针对不同的目的进行训练,而判别器则必须最大化将真实图像分类为真实图像的可能性。 图像,并最小化将噪声图像分类为真实图像的可能性。 此功能仅接受伪图像数据和优化器,其中伪图像是生成器生成的图像。 生成器训练器功能代码可以在 GitHub 存储库中找到。
生成器训练器函数比判别器训练器函数简单得多,因为它不需要从两个来源获取输入,也不必针对不同的目的进行训练,而判别器则必须最大化将真实图像分类为真实图像的可能性。 图像,并最小化将噪声图像分类为真实图像的可能性。 此函数仅接受伪图像数据和优化器,其中伪图像是生成器生成的图像。 生成器训练器函数代码可以在 GitHub 存储库中找到。
我们分别创建判别器和生成器网络的实例。 到目前为止,我们所有的网络实现都具有单个模型或单个神经网络,但第一次,我们有两个单独的网络在同一个数据集上工作,并具有不同的优化目标。 对于两个单独的网络,我们还需要创建两个单独的优化器。 从历史上看,`Adam`优化器最适合学习速度非常慢的 GAN。
......
......@@ -136,7 +136,7 @@ MDP 定义有五件事:
但是起初,我们可能不知道最佳作用值函数。 因此,由此产生的策略也将不是最佳策略。 我们将需要遍历动作值函数,并找到提供最佳回报的函数。 一旦找到它,我们将获得最优的`Q`。 最佳`Q`也称为`Q*`。 因此,我们将能够找到最优的`Pi`,也称为`Pi*`
`Q`函数是智能体必须学习的功能。 我们将使用神经网络来学习此功能,因为神经网络也是通用函数逼近器。 一旦有了行动值函数,座席就可以了解问题的最佳策略,我们就可以完成目标。
`Q`函数是智能体必须学习的函数。 我们将使用神经网络来学习此函数,因为神经网络也是通用函数逼近器。 一旦有了行动值函数,座席就可以了解问题的最佳策略,我们就可以完成目标。
### 贝尔曼方程
......@@ -219,7 +219,7 @@ class ReplayMemory(object):
memory = ReplayMemory(10000)
```
在这里,我们为交易定义了一个存储库。 有一个称为`push`功能可将事务推送到内存中。 还有另一个功能可以从内存中随机采样。
在这里,我们为交易定义了一个存储库。 有一个称为`push`函数可将事务推送到内存中。 还有另一个函数可以从内存中随机采样。
### Gym
......@@ -371,7 +371,7 @@ for i_episode in range(num_episodes):
env.close()
```
让我们看看我们的训练循环。 对于每个情节,我们都会重置环境。 我们从环境中获得了两个屏幕,将当前状态定义为两个屏幕之间的差异。 然后,对于情节中的每个时间步,我们使用`select_action`函数选择一个动作。 我们要求环境采取该行动,并将奖励和`done`标志归还(它告诉我们情节是否结束,也就是卡塔普尔跌倒了)。 我们观察到已经提出的新状态。 然后,我们将刚刚经历的事务推入存储体,并移至下一个状态。 下一步是优化模型。 我们将很快介绍该功能
让我们看看我们的训练循环。 对于每个情节,我们都会重置环境。 我们从环境中获得了两个屏幕,将当前状态定义为两个屏幕之间的差异。 然后,对于情节中的每个时间步,我们使用`select_action`函数选择一个动作。 我们要求环境采取该行动,并将奖励和`done`标志归还(它告诉我们情节是否结束,也就是卡塔普尔跌倒了)。 我们观察到已经提出的新状态。 然后,我们将刚刚经历的事务推入存储体,并移至下一个状态。 下一步是优化模型。 我们将很快介绍该函数
我们还将每五集使用`policy_net`权重的副本更新`target_net`
......
......@@ -47,7 +47,7 @@ def hello():
* 第一行是我们导入 Flask 包的位置。
* 我们创建一个 Flask 对象,这是我们的大型 Web 应用对象,Flask 服务器将使用该对象来运行我们的服务器。
* 有了应用对象后,我们需要存储有关对象应对其执行操作的 URL 的信息。 为此,应用对象带有`route`方法,该方法接受所需的 URL 并返回装饰器。 这是我们希望应用现在提供的 URL。
* 由应用对象返回的装饰器对一个函数进行装饰,当 URL 命中时,将触发该函数。 我们将其命名为`hello`。 函数的名称在这里并不重要。 在前面的示例中,它只是检查输入并做出相应的响应。 但是对于我们的模型服务器,我们使此功能稍微复杂一点,以便它可以接受输入并将该输入提供给我们构建的模型。 然后,我们模型的返回值将作为 HTTP 响应推回给用户。
* 由应用对象返回的装饰器对一个函数进行装饰,当 URL 命中时,将触发该函数。 我们将其命名为`hello`。 函数的名称在这里并不重要。 在前面的示例中,它只是检查输入并做出相应的响应。 但是对于我们的模型服务器,我们使此函数稍微复杂一点,以便它可以接受输入并将该输入提供给我们构建的模型。 然后,我们模型的返回值将作为 HTTP 响应推回给用户。
我们通过建立`flask_trial`目录开始实现,并将该文件另存为`app.py`在该目录中:
......@@ -72,7 +72,7 @@ flask run
```
我们已经建立了简单的 Flask 应用。 现在,将 fizzbuzz 模型引入我们的应用。 以下代码片段显示了与第 2 章和“简单神经网络”相同的模型,供您参考。 该模型将从路由器功能中调用。 我们已经在第 2 章和“一个简单的神经网络”中对模型进行了训练,因此,我们将在这里加载训练后的模型,而不是再次对其进行训练:
我们已经建立了简单的 Flask 应用。 现在,将 fizzbuzz 模型引入我们的应用。 以下代码片段显示了与第 2 章和“简单神经网络”相同的模型,供您参考。 该模型将从路由函数中调用。 我们已经在第 2 章和“一个简单的神经网络”中对模型进行了训练,因此,我们将在这里加载训练后的模型,而不是再次对其进行训练:
```py
import torch.nn as nn
......@@ -139,7 +139,7 @@ Flask 为我们提供了`request`工具,它是一个全局变量,但对于
我们的下一个导入是`controller`,我们在其中加载了模型文件。 我们正在调用`run`方法并将数字传递给模型。 然后,将`controller`的预测值作为字典传递回。 Flask 会将其转换为响应正文并将其发送回用户。
在继续之前,我们可以从以前的简单 Flask 应用的扩展版本中看到两个主要差异。 一种是 URL 路由:`/predictions/fizbuz_package`。 如前所述,Flask 允许您将任何 URL 端点映射到您选择的功能
在继续之前,我们可以从以前的简单 Flask 应用的扩展版本中看到两个主要差异。 一种是 URL 路由:`/predictions/fizbuz_package`。 如前所述,Flask 允许您将任何 URL 端点映射到您选择的函数
其次,我们在装饰器中使用了另一个关键字参数:`methods`。 这样,我们告诉 Flask,不仅需要通过 URL 规则来调用此函数,而且还需要在对该 URL 的`POST`方法调用上进行调用。 因此,我们像以前一样使用`flask run`运行该应用,并使用`curl`命令对其进行测试。
......@@ -403,7 +403,7 @@ def initialize(self, context):
MMS 在调用`initialize()`时传递上下文参数,该参数具有在解压缩存档文件时获取的信息。 当首先使用存档文件路径作为参数调用 MMS 时,在调用服务文件之前,MMS 解压缩存档文件并安装模型,并收集信息,其中存储模型,MMS 可以使用多少个内核,它是否具有 GPU 等。 所有这些信息都作为上下文参数传递给`initialize()`
`initialize()`的第一部分是收集此信息以及来自签名 JSON 文件的信息。 函数的第二部分从第一部分中收集的信息中获取与输入有关的数据。 然后,该功能的第三部分是创建 MXNet 模型并将训练后的参数加载到模型中。 最后,我们将`self.has_initialized`变量设置为`True`,然后将其用于检查服务文件其他部分的初始化状态:
`initialize()`的第一部分是收集此信息以及来自签名 JSON 文件的信息。 函数的第二部分从第一部分中收集的信息中获取与输入有关的数据。 然后,该函数的第三部分是创建 MXNet 模型并将训练后的参数加载到模型中。 最后,我们将`self.has_initialized`变量设置为`True`,然后将其用于检查服务文件其他部分的初始化状态:
```py
def handle(self, data, context):
......@@ -430,7 +430,7 @@ def handle(self, data, context):
return [str(e)] * self._batch_size
```
MMS 被编程为在每个请求上调用相同类的`handle()`方法,这是我们控制流程的地方。 `initialize()`函数只会在启动线程时被调用一次; 每个用户请求都将调用`handle()`函数。 由于`handle()`函数是针对每个用户请求被调用的,以及上下文信息,因此它也将在参数中获取当前数据。 但是,为了使程序模块化,我们没有在`handle()`中进行任何操作; 取而代之的是,我们正在调用其他仅指定做一件事的功能:该功能应该做什么。
MMS 被编程为在每个请求上调用相同类的`handle()`方法,这是我们控制流程的地方。 `initialize()`函数只会在启动线程时被调用一次; 每个用户请求都将调用`handle()`函数。 由于`handle()`函数是针对每个用户请求被调用的,以及上下文信息,因此它也将在参数中获取当前数据。 但是,为了使程序模块化,我们没有在`handle()`中进行任何操作; 取而代之的是,我们正在调用其他仅指定做一件事的函数:该函数应该做什么。
我们将整个流分为四个部分:预处理,推理,后处理和矩阵记录。 在`handle()`的第一行中,我们验证是否正在使用上下文和数据信息初始化线程。 完成后,我们将进入流程。 现在,我们将逐步完成流程。
......@@ -578,13 +578,13 @@ TorchScript 支持此类 Python 控制流的一个子集,唯一要做的就是
通过将 TorchScript 推到核心,PyTorch 可以投入生产了。 TorchScript 可以将用 Python 编写的模型转换为高度优化的 IR,然后可由 LibTorch 读取。 然后,可以将 LibTorch 加载的模型保存为 C++ 对象,并可以在 C++ 程序或其他高效编程语言(例如 Go)中运行。
PyTorch 允许您通过两种方法制作 TorchScript IR。 最简单的是通过跟踪,就像 ONNX 一样。 您可以通过虚拟输入将模型(甚至函数)传递给`torch.jit.trace`。 PyTorch 通过模型/功能运行虚拟输入,并在运行输入时跟踪操作。
PyTorch 允许您通过两种方法制作 TorchScript IR。 最简单的是通过跟踪,就像 ONNX 一样。 您可以通过虚拟输入将模型(甚至函数)传递给`torch.jit.trace`。 PyTorch 通过模型/函数运行虚拟输入,并在运行输入时跟踪操作。
然后,可以将跟踪的函数(PyTorch 操作)转换为优化的 IR,也称为静态单分配 IR。 像 ONNX 图一样,该图中的指令也具有张量库(ATen,PyTorch 的后端)可以理解的原始运算符。
这确实很容易,但是要付出代价。 基于跟踪的推理具有 ONNX 的基本问题:它无法处理依赖于数据的模型结构更改,即`if`/`else`条件检查或循环(序列数据)。 为了处理这种情况,PyTorch 引入了脚本模式。
可以通过使用`torch.jit.script`装饰器(用于常规功能)和`torch.jit.script_method`(用于 PyTorch 模型上的方法)来启用脚本模式。 通过此装饰器,函数/方法中的内容将直接转换为 TorchScript。 在对模型类使用`torch.jit.script_method`时要记住的另一件重要事情是关于父类。 通常,我们从`torch.nn.Module`继承,但是为了制作 TorchScript,我们从`torch.jit.ScriptModule`继承。 这有助于 PyTorch 避免使用无法转换为 TorchScript 的纯 Python 方法。 目前,TorchScript 不支持所有 Python 功能,但具有支持数据相关张量操作的所有必需功能
可以通过使用`torch.jit.script`装饰器(用于常规函数)和`torch.jit.script_method`(用于 PyTorch 模型上的方法)来启用脚本模式。 通过此装饰器,函数/方法中的内容将直接转换为 TorchScript。 在对模型类使用`torch.jit.script_method`时要记住的另一件重要事情是关于父类。 通常,我们从`torch.nn.Module`继承,但是为了制作 TorchScript,我们从`torch.jit.ScriptModule`继承。 这有助于 PyTorch 避免使用无法转换为 TorchScript 的纯 Python 方法。 目前,TorchScript 不支持所有 Python 函数,但具有支持数据相关张量操作的所有必需函数
我们将首先将模型导出到`ScriptModule` IR,以此开始 fizzbuzz 模型的 C++ 实现,就像我们对 ONNX 导出所做的一样:
......@@ -603,7 +603,7 @@ traced.save('fizbuz.pt')
#include <string>
```
最重要的头是`torch/script.h`,它带来了 LibTorch 所需的所有方法和功能。 我们决定将模型名称和示例输入作为命令行参数传递。 因此,主程序的第一部分是读取命令行参数并将其解析为程序的其余部分:
最重要的头是`torch/script.h`,它带来了 LibTorch 所需的所有方法和函数。 我们决定将模型名称和示例输入作为命令行参数传递。 因此,主程序的第一部分是读取命令行参数并将其解析为程序的其余部分:
```py
std::string arg = argv[2];
......
......@@ -6,7 +6,7 @@
PyTorch 提供了与神经网络,任意张量代数,数据整理和其他目的有关的大量操作。 但是,您仍然可能发现自己需要更多的自定义操作。 例如,您可能想使用论文中发现的新颖的激活函数,或者实现您在研究过程中开发的操作。
在 PyTorch 中集成这样的自定义操作的最简单方法是通过扩展[此处](https://pytorch.org/docs/master/notes/extending.html)概述的`Function``Module`来用 Python 编写它。 这为您提供了自动区分的全部功能(使您不必编写派生函数)以及 Python 的通常表达能力。 但是,有时您的操作可以用 C++ 更好地实现。 例如,您的代码可能*确实*需要速度,因为在模型中它经常被调用,或者即使很少调用也很昂贵。 另一个合理的原因是它依赖于其他 C 或 C++ 库或与之交互。 为了解决这种情况,PyTorch 提供了一种非常简单的方式来编写自定义 *C++ 扩展*
在 PyTorch 中集成这样的自定义操作的最简单方法是通过扩展[此处](https://pytorch.org/docs/master/notes/extending.html)概述的`Function``Module`来用 Python 编写它。 这为您提供了自动区分的全部功能(使您不必编写函数)以及 Python 的通常表达能力。 但是,有时您的操作可以用 C++ 更好地实现。 例如,您的代码可能*确实*需要速度,因为在模型中它经常被调用,或者即使很少调用也很昂贵。 另一个合理的原因是它依赖于其他 C 或 C++ 库或与之交互。 为了解决这种情况,PyTorch 提供了一种非常简单的方式来编写自定义 *C++ 扩展*
C++ 扩展是我们开发的一种机制,允许用户(您)创建源外定义的 PyTorch 运算符,即与 PyTorch 后端分开。 该方法不同于本机 PyTorch 操作的实现方式。 C++ 扩展旨在为您节省大量与将操作与 PyTorch 后端集成在一起相关的样板,同时为基于 PyTorch 的项目提供高度的灵活性。 但是,一旦将操作定义为 C++ 扩展,将其转换为本地 PyTorch 函数在很大程度上取决于代码组织,如果您决定在上游进行操作,则可以解决此问题。
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册