提交 20555ac9 编写于 作者: W wizardforcel

2021-01-23 22:46:26

上级 a4e95898
......@@ -12,7 +12,7 @@ PyTorch 最初是由 Facebook 实习生作为研究框架开始的,现已发
先前的深度学习工作流程几乎等同于业内几乎每个人所实现的工作流程,即使对于高度复杂的实现,也略有不同。 本章简要说明了第一和最后一个阶段,并进入了中间三个阶段的核心,即设计和实验,模型实现以及训练和验证。
工作流的最后阶段通常是人们很费劲的,尤其是在应用规模很大的情况下。 之前我曾提到,尽管 PyTorch 是作为面向研究的框架构建的,但是社区设法将 Caffe2 集成到 PyTorch 的后端,这为 Facebook 使用的数千种模型提供了支持。 因此,在第 8 章, “生产中的 PyTorch”中详细讨论了将模型交付生产的过程,并举例说明了如何使用 ONNX,PyTorch JIT 等来展示如何交付用于服务的 PyTorch 模型 数百万个请求,以及将模型迁移到单板计算机和移动设备。
工作流的最后阶段通常是人们很费劲的,尤其是在应用规模很大的情况下。 之前我曾提到,尽管 PyTorch 是作为面向研究的框架构建的,但是社区设法将 Caffe2 集成到 PyTorch 的后端,这为 Facebook 使用的数千种模型提供了支持。 因此,在第 8 章, “生产中的 PyTorch”中详细讨论了将模型交付生产的过程,并举例说明了如何使用 ONNX,PyTorch JIT 等来展示如何交付用于服务数百万个请求的 PyTorch 模型,以及将模型迁移到单板计算机和移动设备。
## 构思和计划
......@@ -30,7 +30,7 @@ PyTorch 最初是由 Facebook 实习生作为研究框架开始的,现已发
不同类型的深度学习问题需要不同类型的数据集,并且每种类型的可能需要不同类型的预处理,具体取决于我们使用的神经网络架构。 这是深度学习管道构建中的核心问题之一。
尽管社区已经免费提供了用于不同任务的数据集,但是编写预处理脚本几乎总是很痛苦。 PyTorch 通过提供抽象类来编写自定义数据集和数据加载器来解决此问题。 这里给出的示例是一个简单的`dataset`类,用于加载我们在第 2 章,“一个简单神经网络”中使用的`fizzbuzz`数据集,但是将其扩展为可以处理任何类型的数据集 非常简单。 PyTorch 的官方文档使用类似的方法对图像数据集进行预处理,然后再将其传递给复杂的**卷积神经网络****CNN**)架构。
尽管社区已经免费提供了用于不同任务的数据集,但是编写预处理脚本几乎总是很痛苦。 PyTorch 通过提供抽象类来编写自定义数据集和数据加载器来解决此问题。 这里给出的示例是一个简单的`dataset`类,用于加载我们在第 2 章,“一个简单神经网络”中使用的`fizzbuzz`数据集,但是将其扩展来可以处理任何类型的数据集非常简单。 PyTorch 的官方文档使用类似的方法对图像数据集进行预处理,然后再将其传递给复杂的**卷积神经网络****CNN**)架构。
PyTorch 中的`dataset`类是高级抽象,可处理数据加载程序几乎需要的所有内容。 用户定义的自定义`dataset`类需要覆盖父类的`__len__`函数和`__getitem__`函数,其中数据加载程序正在使用`__len__`来确定数据集的长度,而`__getitem__` 数据加载器正在使用该物品来获取物品。 `__getitem__`函数希望用户将索引作为参数传递,并获取驻留在该索引上的项目:
......@@ -292,7 +292,7 @@ print(next(iter(test_iter)))
如果`Iterator`类需要更巧妙地处理事情怎么办? 如果数据集用于语言建模,并且我们需要一个数据集来进行**时间上的反向传播****BPTT**),那该怎么办? `torchtext`也为这些模块抽象了模块,这些模块继承自我们刚刚使用的`Iterator`类。 `BucketIterator`模块将序列进行更智能的分组,以便将具有相同长度的序列归为一组,并且此减少了将噪声引入数据集的不必要填充的长度。 `BucketIterator`还可以在每个时期对批量进行混洗,并在数据集中保持足够的随机性,从而使网络无法从数据集中的顺序中学习,这实际上并没有在教授任何现实世界的信息。
`BPTTIterator`是从`Iterator`类继承的另一个模块,可帮助语言建模数据集,并且需要为`t`的每个输入从`t + 1`获取标签。`t`是时间。 `BPTTIterator`接受输入数据的连续流和输出数据的连续流(在翻译网络的情况下,输入流和输出流可以不同,在语言建模网络的情况下,输入流和输出流可以相同)进行转换 它遵循前面描述的时间序列规则的迭代器
`BPTTIterator`是从`Iterator`类继承的另一个模块,可帮助语言建模数据集,并且需要为`t`的每个输入从`t + 1`获取标签。`t`是时间。 `BPTTIterator`接受输入数据的连续流和输出数据的连续流(在翻译网络的情况下,输入流和输出流可以不同,在语言建模网络的情况下,输入流和输出流可以相同)并将其转换为迭代器,它遵循前面描述的时间序列规则
`torchtext`还保存了开箱即用的数据集。 下面是一个示例,说明访问数据集的可用版本有多么容易:
......@@ -437,7 +437,7 @@ python -m torch.utils.bottleneck /path/to/source/script.py [args]
## 训练和验证
尽管工作流实际上以将深度模型的部署到生产中而结束,但我们已经到达深度学习工作的最后一步,我们将在第 8 章和“PyTorch 投入生产”。 在完成所有预处理和模型构建之后,现在我们必须训练网络,测试准确率并验证可靠性。 在开源世界(甚至在本书中)中,我们看到的大多数现有代码实现都使用直接方法,在该方法中,我们明确编写了训练,测试和验证所需的每一行,以提高可读性,因为 可以避免样板的特定工具会增加学习曲线,尤其是对于新手。 很显然,对于那些每天都在使用神经网络的程序员来说,可以避免样板的工具将是一个救生员。 因此,PyTorch 社区构建的不是一个而是两个工具:Torchnet 和 Ignite。 本次会议仅与点燃有关,因为它被发现比 Torchnet 更为有用和抽象,但两者都是积极开发的工具,有可能在不久的将来合并。
尽管工作流实际上以将深度模型的部署到生产中而结束,但我们已经到达深度学习工作的最后一步,我们将在第 8 章和“PyTorch 投入生产”。 在完成所有预处理和模型构建之后,现在我们必须训练网络,测试准确率并验证可靠性。 在开源世界(甚至在本书中)中,我们看到的大多数现有代码实现都使用直接方法,在该方法中,我们明确编写了训练,测试和验证所需的每一行,以提高可读性,因为可以避免样板的特定工具会增加学习曲线,尤其是对于新手。 很显然,对于那些每天都在使用神经网络的程序员来说,可以避免样板的工具将是一个救生员。 因此,PyTorch 社区构建的不是一个而是两个工具:Torchnet 和 Ignite。 本次会议仅与点燃有关,因为它被发现比 Torchnet 更为有用和抽象,但两者都是积极开发的工具,有可能在不久的将来合并。
### Ignite
......
......@@ -257,7 +257,7 @@ trainloader, testloader = get_data()
在下一行中定义的损失函数也是`torch.nn.Module`的子类,它也具有`forward()`函数,该函数由`__call__()`和向后函数调用。 这使我们可以灵活地创建自定义损失函数。
在以后的章节中,我们将提供示例。 现在,我们将使用一个称为`CrossEntropyLoss()`的内置损失函数。 就像前面几章中的一样,我们将使用 PyTorch 优化程序包来获取预定义的优化程序。 对于此示例,我们将**随机梯度下降****SGD**)用于示例,但与上一章不同,我们将使用带有动量的 SGD,这有助于我们向右加速梯度 方向
在以后的章节中,我们将提供示例。 现在,我们将使用一个称为`CrossEntropyLoss()`的内置损失函数。 就像前面几章中的一样,我们将使用 PyTorch 优化程序包来获取预定义的优化程序。 对于此示例,我们将**随机梯度下降****SGD**)用于示例,但与上一章不同,我们将使用带有动量的 SGD,这有助于我们向正确方向加速梯度
##### 注意
......@@ -452,7 +452,7 @@ PyTorch 有几个用于池化操作的选项,我们从其中选择使用`MaxPo
如上图所示,LinkNet 中的每个编码器块均由四个卷积块组成。 前两个卷积块被分组为一个块。 然后将其与残差输出(由 ResNet 推动的架构决策)相加。 然后,带有该加法的残差输出将进入第二块,这也与第一块类似。 然后将块 2 的输入添加到块 2 的输出中,而无需通过单独的残差块。
第一个块用因子 2 对输入进行下采样,第二个块对输入的尺寸没有任何作用。 这就是为什么我们需要一个残差网以及第一个模块,而对于第二个模块,我们可以直接添加输入和输出。 实现该架构的代码如下。 `init`函数实际上是在初始化`conv`块和`residue`块。 PyTorch 帮助我们处理张量的加法,因此我们只需要编写我们想做的数学运算,就像您在普通的 Python 变量上执行此操作一样,而 PyTorch 的`autograd`在上完成 从那里
第一个块用因子 2 对输入进行下采样,第二个块对输入的尺寸没有任何作用。 这就是为什么我们需要一个残差网以及第一个模块,而对于第二个模块,我们可以直接添加输入和输出。 实现该架构的代码如下。 `init`函数实际上是在初始化`conv`块和`residue`块。 PyTorch 帮助我们处理张量的加法,因此我们只需要编写我们想做的数学运算,就像您在普通的 Python 变量上执行此操作一样,而 PyTorch 的`autograd`从那里完成
```py
class EncoderBlock(nn.Module):
......
......@@ -298,7 +298,7 @@ target_net.load_state_dict(policy_net.state_dict())
target_net.eval()
```
接下来,我们定义我们的网络。 网络采用当前状态,对其进行一些卷积运算,最后收敛到线性层,并给出当前状态值的输出和表示该状态有多大好处的值 在那种状态
接下来,我们定义我们的网络。 网络采用当前状态,对其进行一些卷积运算,最后收敛到线性层,并给出当前状态值的输出,和表示在该状态下有多大好处的值
我们定义了两个网络`policy_net``target_net`。 我们将`policy_net`的权重复制到`target_net`,以便它们代表相同的网络。 我们将`target_net`设为评估模式,以便在反向传播时不更新网络的权重。 我们将在每个步骤中推断`policy_net`,但会不时更新`target_net`
......
......@@ -154,7 +154,7 @@ Flask 为我们提供了`request`工具,它是一个全局变量,但对于
在 HTTP `POST`请求中,我们传递了输入数字为`14`的 JSON 对象,我们的服务器返回了下一个数字`FizBuz`。 所有这些魔术都发生在我们的`app.py`调用的`controller.run()`方法中。 现在,让我们看看该函数在做什么。
接下来是使用`run()`方法的`controller`文件。 在这里,我们将输入数字转换为 10 位二进制数(请记住,这是我们作为输入传递给第 2 章,“简单神经网络”的 fizzbuzz 网络)的函数 火炬张量。 然后将二进制张量传递给我们模型的正向函数,以得到具有预测的`1 x 4`张量。
接下来是使用`run()`方法的`controller`文件。 在这里,我们将输入数字转换为 10 位二进制数(请记住,在第 2 章,“简单神经网络”中,这是我们作为输入传递给 fizzbuzz 网络的东西),将其变为 Torch 张量。 然后将二进制张量传递给我们模型的正向函数,以得到具有预测的`1 x 4`张量。
通过从加载了保存的`.pth`文件的模型文件中调用`FizBuz`类来创建我们的模型。 我们使用 Torch 的`load_state_dict`方法将参数加载到初始化的模型中。 之后,我们将模型转换为`eval()`模式,这将模型设置为评估模式(它在评估模式下关闭了`batchnorm`丢弃层)。 模型的输出是运行`max`并确定哪个索引具有最大值,然后将其转换为可读输出的概率分布。
......@@ -295,7 +295,7 @@ netron -b fizbuz.onnx
现在我们离开了 PyTorch 世界。 我们现在有不同的模型服务器,但我们选择了 MXNet 模型服务器。 MXNet 模型服务器由社区维护,由亚马逊团队领导,也称为 MMS。 从这里开始,我将交替使用 MMS 和 MXNet 模型服务器。
MXNet 比其他服务模块更好。 在撰写本文时,TensorFlow 与 Python 3.7 不兼容,并且 MXNet 的服务模块已与内置的 ONNX 模型集成,这使开发人员可以轻松地以很少的命令行为模型提供服务,而无需了解其复杂性 分布式或高度可扩展的部署
MXNet 比其他服务模块更好。 在撰写本文时,TensorFlow 与 Python 3.7 不兼容,并且 MXNet 的服务模块已与内置的 ONNX 模型集成,这使开发人员可以轻松地以很少的命令行为模型提供服务,而无需了解分布式或高度可扩展的部署的复杂性
其他模型服务器,例如 TensorRT 和 Clipper,不像 MXNet 服务器那样易于设置和管理。 而且,MXNet 附带了另一个名为 MXNet 存档器的工具,该工具将所有必需的文件打包成一个捆绑包,这些文件可以独立部署,而不必担心其他依赖项。 除了 MXNet 模型服务器具备的所有这些很酷的功能之外,最大的好处是能够自定义预处理和后处理步骤。 我们将在接下来的部分中介绍如何完成所有这些操作。
......@@ -401,7 +401,7 @@ def initialize(self, context):
self.has_initialized = True
```
MMS 在调用`initialize()`时传递上下文参数,该参数具有在解压缩存档文件时获取的信息。 当首先使用存档文件路径作为参数调用 MMS 时,在调用服务文件之前,MMS 解压缩存档文件并安装模型,并收集信息,其中存储模型,MMS 可以使用多少个内核,是否 它具有 GPU 等。 所有这些信息都作为上下文参数传递给`initialize()`
MMS 在调用`initialize()`时传递上下文参数,该参数具有在解压缩存档文件时获取的信息。 当首先使用存档文件路径作为参数调用 MMS 时,在调用服务文件之前,MMS 解压缩存档文件并安装模型,并收集信息,其中存储模型,MMS 可以使用多少个内核,它是否具有 GPU 等。 所有这些信息都作为上下文参数传递给`initialize()`
`initialize()`的第一部分是收集此信息以及来自签名 JSON 文件的信息。 函数的第二部分从第一部分中收集的信息中获取与输入有关的数据。 然后,该功能的第三部分是创建 MXNet 模型并将训练后的参数加载到模型中。 最后,我们将`self.has_initialized`变量设置为`True`,然后将其用于检查服务文件其他部分的初始化状态:
......@@ -566,7 +566,7 @@ class WebsiteUser(HttpLocust):
现在的问题是:这不是我们对 ONNX 所做的吗? 也就是说,从 PyTorch 模型创建另一个 IR? 是的,过程相似,但区别在于 ONNX 使用跟踪创建了优化的 IR; 也就是说,它通过模型传递虚拟输入,并在执行模型时记录 PyTorch 操作,然后将这些操作转换为中间 IR。
这种方法有一个问题:如果模型是数据相关的,例如 RNN 中的循环,或者`if`/`else`条件是基于输入的,那么跟踪就不能真正做到这一点。 跟踪将仅发现在特定执行周期中发生的情况,而忽略其他情况。 例如,如果我们的虚拟输入是 10 个单词的句子,而我们的模型是基于循环的 RNN,则跟踪的图形将对 RNN 单元的 10 次执行进行硬编码,如果句子的长度大于 10,则它将中断 单词或单词较少的简短句子。 考虑到这一点引入了 TorchScript。
这种方法有一个问题:如果模型是数据相关的,例如 RNN 中的循环,或者`if`/`else`条件是基于输入的,那么跟踪就不能真正做到这一点。 跟踪将仅发现在特定执行周期中发生的情况,而忽略其他情况。 例如,如果我们的虚拟输入是 10 个单词的句子,而我们的模型是基于循环的 RNN,则跟踪的图形将对 RNN 单元的 10 次执行进行硬编码,如果句子的长度大于 10,或者较短的句子带有更少的单词,则它将中断。 考虑到这一点引入了 TorchScript。
TorchScript 支持此类 Python 控制流的一个子集,唯一要做的就是将现有程序转换为所有控制流都是 TorchScript 支持的控制流的阶段。 LibTorch 可以读取 TorchScript 创建的中间阶段。 在此会话中,我们将创建 TorchScript 输出并编写一个 C++ 模块以使用 LibTorch 加载它。
......@@ -643,7 +643,7 @@ RedisAI 带有三个选项来配置三个后端:PyTorch,TensorFlow 和 ONNX
RedisAI 是作为 Redis 服务器(`loadmodule`开关)的模块构建的。 通过 RedisAI 提供模型的好处不仅在于拥有多个运行时以及它们之间的互操作性。 实际上,这对于生产部署来说是最不重要的。 RedisAI 附带的最重要的功能是故障转移和分布式部署选项已经嵌入到 Redis 服务器中。
借助 Redis Sentinel 和 Redis Cluster,我们可以在多集群,高可用性设置中部署 RedisAI,而无需对 DevOps 或基础架构建设有足够的了解。 另外,由于 Redis 拥有所有流行语言的客户端,因此,通过 RedisAI 部署 TorchScript 模型后,您基本上可以使用 Redis 的任何语言客户端与服务器通信以运行模型,将输入传递给模型,从模型获取输出,以及 更多。
借助 Redis Sentinel 和 Redis Cluster,我们可以在多集群,高可用性设置中部署 RedisAI,而无需对 DevOps 或基础架构建设有足够的了解。 另外,由于 Redis 拥有所有流行语言的客户端,因此,通过 RedisAI 部署 TorchScript 模型后,您基本上可以使用 Redis 的任何语言客户端与服务器通信以运行模型,将输入传递给模型,从模型获取输出,以及更多。
使用 RedisAI 的下一个亮点是 Redis 整个大型生态系统的可用性,例如 RedisGears(可将任何 Python 函数作为管道的一部分运行),RedisTimeSeries,Redis Streams 等。
......@@ -723,7 +723,7 @@ while True:
在本章中,我们从最简单但性能最低的方法开始,使用了三种不同的方法将 PyTorch 投入生产:使用 Flask。 然后,我们转移到 MXNet 模型服务器,这是一个预先构建的,优化的服务器实现,可以使用管理 API 进行管理。 MXNet 模型服务器对不需要太多复杂性但需要可以根据需要扩展的高效服务器实现的人很有用。
最后,我们尝试使用 TorchScript 创建模型的最有效版本,并将其导入 C++ 中。 对于那些准备承担构建和维护 C++,Go 或 Rust 等底层语言服务器的复杂性的人,可以采用这种方法并构建自定义服务器,直到我们有更好的运行时可以读取脚本模块为止 就像 MXNet 在 ONNX 模型上一样。
最后,我们尝试使用 TorchScript 创建模型的最有效版本,并将其导入 C++ 中。 对于那些准备承担构建和维护 C++,Go 或 Rust 等底层语言服务器的复杂性的人,可以采用这种方法并构建自定义服务器,直到我们有可以读取脚本模块的更好的运行时为止,就像 MXNet 在 ONNX 模型上一样。
2018 年是模型服务器的一年; 有许多来自不同组织的模型服务器,它们具有不同的观点。 但是未来是光明的,我们可以看到越来越多的模型服务器每天都在问世,这可能会使所有前面提到的方法过时。
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册