C / C ++后端中设计的自定义数据结构已分为不同的层。 为简单起见,我们将省略 CUDA 数据结构,而将重点放在简单的 CPU 数据结构上。 PyTorch 中的面向用户的主要数据结构是`THTensor`对象,它保存有关尺寸,偏移,步幅等信息。 但是,`THTensor`存储的另一个主要信息是指向`THStorage`对象的指针,该对象是为存储而保存的张量对象的内部层。
C / C++ 后端中设计的自定义数据结构已分为不同的层。 为简单起见,我们将省略 CUDA 数据结构,而将重点放在简单的 CPU 数据结构上。 PyTorch 中的面向用户的主要数据结构是`THTensor`对象,它保存有关尺寸,偏移,步幅等信息。 但是,`THTensor`存储的另一个主要信息是指向`THStorage`对象的指针,该对象是为存储而保存的张量对象的内部层。
前两行设置了模型的输入。 我们创建一个`torch::jit::IValue`的向量(类型擦除的值类型`script::Module`方法接受并返回),并添加单个输入。 要创建输入张量,我们使用`torch::ones()`,等效于 C ++ API 中的`torch.ones`。 然后,我们运行`script::Module`的`forward`方法,并将其传递给我们创建的输入向量。 作为回报,我们得到了一个新的`IValue`,我们可以通过调用`toTensor()`将其转换为张量。
前两行设置了模型的输入。 我们创建一个`torch::jit::IValue`的向量(类型擦除的值类型`script::Module`方法接受并返回),并添加单个输入。 要创建输入张量,我们使用`torch::ones()`,等效于 C++ API 中的`torch.ones`。 然后,我们运行`script::Module`的`forward`方法,并将其传递给我们创建的输入向量。 作为回报,我们得到了一个新的`IValue`,我们可以通过调用`toTensor()`将其转换为张量。
小费
要总体上了解有关`torch::ones`和 PyTorch C ++ API 之类的功能的更多信息,请参阅[这个页面](https://pytorch.org/cppdocs)上的文档。 PyTorch C ++ API 提供了与 Python API 几乎相同的功能,使您可以像在 Python 中一样进一步操纵和处理张量。
要总体上了解有关`torch::ones`和 PyTorch C++ API 之类的功能的更多信息,请参阅[这个页面](https://pytorch.org/cppdocs)上的文档。 PyTorch C++ API 提供了与 Python API 几乎相同的功能,使您可以像在 Python 中一样进一步操纵和处理张量。
本教程有望使您对 PyTorch 模型从 Python 到 C ++的路径有一个大致的了解。 利用本教程中介绍的概念,您应该能够从原始的“急切的” PyTorch 模型,到 Python 中的已编译`ScriptModule`,再到磁盘上的序列化文件,以及–结束循环–到可执行文件`script::Module`在 C ++中。
本教程有望使您对 PyTorch 模型从 Python 到 C++ 的路径有一个大致的了解。 利用本教程中介绍的概念,您应该能够从原始的“急切的” PyTorch 模型,到 Python 中的已编译`ScriptModule`,再到磁盘上的序列化文件,以及–结束循环–到可执行文件`script::Module`在 C++ 中。
当然,有许多我们没有介绍的概念。 例如,您可能会发现自己想要扩展使用 C ++或 CUDA 实现的自定义运算符来扩展`ScriptModule`,并希望在纯 C ++生产环境中加载的`ScriptModule`内执行该自定义运算符。 好消息是:这是可能的,并且得到了很好的支持! 现在,您可以浏览[这个](https://github.com/pytorch/pytorch/tree/master/test/custom_operator)文件夹作为示例,我们将很快提供一个教程。 目前,以下链接通常可能会有所帮助:
当然,有许多我们没有介绍的概念。 例如,您可能会发现自己想要扩展使用 C++ 或 CUDA 实现的自定义运算符来扩展`ScriptModule`,并希望在纯 C++ 生产环境中加载的`ScriptModule`内执行该自定义运算符。 好消息是:这是可能的,并且得到了很好的支持! 现在,您可以浏览[这个](https://github.com/pytorch/pytorch/tree/master/test/custom_operator)文件夹作为示例,我们将很快提供一个教程。 目前,以下链接通常可能会有所帮助:
本教程是[自定义运算符](torch_script_custom_ops.html)教程的后续教程,并介绍了我们为将 C ++类同时绑定到 TorchScript 和 Python 而构建的 API。 该 API 与 [pybind11](https://github.com/pybind/pybind11) 非常相似,如果您熟悉该系统,则大多数概念都将转移过来。
本教程是[自定义运算符](torch_script_custom_ops.html)教程的后续教程,并介绍了我们为将 C++ 类同时绑定到 TorchScript 和 Python 而构建的 API。 该 API 与 [pybind11](https://github.com/pybind/pybind11) 非常相似,如果您熟悉该系统,则大多数概念都将转移过来。
## 用 C ++实现和绑定类
## 用 C++ 实现和绑定类
在本教程中,我们将定义一个简单的 C ++类,该类在成员变量中保持持久状态。
在本教程中,我们将定义一个简单的 C++ 类,该类在成员变量中保持持久状态。
```py
//ThisheaderisallyouneedtodotheC++portionsofthis
...
...
@@ -100,9 +100,9 @@ TORCH_LIBRARY(my_classes, m) {
```
## 使用 CMake 将示例构建为 C ++项目
## 使用 CMake 将示例构建为 C++ 项目
现在,我们将使用 [CMake](https://cmake.org) 构建系统来构建上述 C ++代码。 首先,将到目前为止介绍的所有 C ++代码放入`class.cpp`文件中。 然后,编写一个简单的`CMakeLists.txt`文件并将其放在同一目录中。 `CMakeLists.txt`应该是这样的:
现在,我们将使用 [CMake](https://cmake.org) 构建系统来构建上述 C++ 代码。 首先,将到目前为止介绍的所有 C++ 代码放入`class.cpp`文件中。 然后,编写一个简单的`CMakeLists.txt`文件并将其放在同一目录中。 `CMakeLists.txt`应该是这样的:
@@ -235,7 +235,7 @@ for expected in ["wow", "mom", "hi"]:
## 使用自定义类保存,加载和运行 TorchScript 代码
我们还可以在使用 libtorch 的 C ++进程中使用自定义注册的 C ++类。 举例来说,让我们定义一个简单的`nn.Module`,它实例化并调用 MyStackClass 类上的方法:
我们还可以在使用 libtorch 的 C++ 进程中使用自定义注册的 C++ 类。 举例来说,让我们定义一个简单的`nn.Module`,它实例化并调用 MyStackClass 类上的方法:
```py
importtorch
...
...
@@ -259,7 +259,7 @@ scripted_foo.save('foo.pt')
我们文件系统中的`foo.pt`现在包含我们刚刚定义的序列化 TorchScript 程序。
现在,我们将定义一个新的 CMake 项目,以展示如何加载此模型及其所需的.so 文件。 有关如何执行此操作的完整说明,请查看[在 C ++教程](https://pytorch.org/tutorials/advanced/cpp_export.html)中加载 TorchScript 模型。
现在,我们将定义一个新的 CMake 项目,以展示如何加载此模型及其所需的.so 文件。 有关如何执行此操作的完整说明,请查看[在 C++ 教程](https://pytorch.org/tutorials/advanced/cpp_export.html)中加载 TorchScript 模型。
与之前类似,让我们创建一个包含以下内容的文件结构:
...
...
@@ -276,7 +276,7 @@ cpp_inference_example/
```
请注意,我们已经复制了序列化的`foo.pt`文件以及上面`custom_class_project`的源代码树。 我们将把`custom_class_project`作为依赖项添加到此 C ++项目中,以便可以将自定义类构建到二进制文件中。
请注意,我们已经复制了序列化的`foo.pt`文件以及上面`custom_class_project`的源代码树。 我们将把`custom_class_project`作为依赖项添加到此 C++ 项目中,以便可以将自定义类构建到二进制文件中。
让我们用以下内容填充`infer.cpp`:
...
...
@@ -369,7 +369,7 @@ $ make -j
```
现在我们可以运行令人兴奋的 C ++二进制文件:
现在我们可以运行令人兴奋的 C++ 二进制文件:
```py
$./infer
...
...
@@ -381,7 +381,7 @@ $ ./infer
## 将自定义类移入或移出 IValues
也可能需要将自定义类从自定义 C ++类实例移入或移出`IValue``s, such as when you take or return ``IValue``s from TorchScript methods or you want to instantiate a custom class attribute in C++. For creating an ``IValue`:
也可能需要将自定义类从自定义 C++ 类实例移入或移出`IValue``s, such as when you take or return ``IValue``s from TorchScript methods or you want to instantiate a custom class attribute in C++. For creating an ``IValue`:
@@ -520,10 +520,10 @@ class TryCustomOp(torch.nn.Module):
注意
注册使用 C ++类作为参数的运算符时,要求已注册自定义类。 您可以通过确保自定义类注册和您的自由函数定义在同一`TORCH_LIBRARY`块中,并确保自定义类注册位于第一位来强制实施此操作。 将来,我们可能会放宽此要求,以便可以按任何顺序进行注册。
注册使用 C++ 类作为参数的运算符时,要求已注册自定义类。 您可以通过确保自定义类注册和您的自由函数定义在同一`TORCH_LIBRARY`块中,并确保自定义类注册位于第一位来强制实施此操作。 将来,我们可能会放宽此要求,以便可以按任何顺序进行注册。
## 结论
本教程向您介绍了如何向 TorchScript(以及扩展为 Python)公开 C ++类,如何注册其方法,如何从 Python 和 TorchScript 使用该类以及如何使用该类保存和加载代码以及运行该代码。 在独立的 C ++过程中。 现在,您可以使用与第三方 C ++库连接的 C ++类扩展 TorchScript 模型,或实现需要 Python,TorchScript 和 C ++之间的界线平滑融合的任何其他用例。
本教程向您介绍了如何向 TorchScript(以及扩展为 Python)公开 C++ 类,如何注册其方法,如何从 Python 和 TorchScript 使用该类以及如何使用该类保存和加载代码以及运行该代码。 在独立的 C++ 过程中。 现在,您可以使用与第三方 C++ 库连接的 C++ 类扩展 TorchScript 模型,或实现需要 Python,TorchScript 和 C++ 之间的界线平滑融合的任何其他用例。
@@ -79,7 +79,7 @@ TORCH_LIBRARY_IMPL(myops, CUDA, m) {
至此,我们有了一个同时具有 CPU 和 CUDA 实现的操作员。 我们如何为它添加 autograd 支持? 您可能会猜到,我们将注册一个 autograd 内核(类似于[自定义 autograd 函数](cpp_autograd)教程中描述的内容)! 但是,有一个变数:与 CPU 和 CUDA 内核不同,autograd 内核需要*重新分发*:它需要回调分派器才能到达最终的 CPU 和 CUDA 实现。
因此,在编写 autograd 内核之前,让我们编写一个*调度函数*,该函数调用调度程序以为您的操作员找到合适的内核。 该函数构成了供您的操作员使用的公共 C ++ API,实际上,PyTorch C ++ API 中的所有张量函数都在后台完全以相同的方式调用了调度程序。 调度功能如下所示:
因此,在编写 autograd 内核之前,让我们编写一个*调度函数*,该函数调用调度程序以为您的操作员找到合适的内核。 该函数构成了供您的操作员使用的公共 C++ API,实际上,PyTorch C++ API 中的所有张量函数都在后台完全以相同的方式调用了调度程序。 调度功能如下所示:
* 在第一行中,我们从调度程序中查找与要调度到的运算符相对应的类型化运算符句柄。 `findSchemaOrThrow`具有两个参数:运算符的(名称空间限定)名称和运算符的重载名称(通常只是空字符串)。 `typed`将动态类型的句柄转换为静态类型的句柄(进行运行时测试以确保您提供了正确的 C ++类型),以便我们可以对其进行常规的 C ++调用。 我们将其传递给`decltype(myadd)`,因为调度功能的类型与注册到调度程序的基础内核的类型相同。
* 在第一行中,我们从调度程序中查找与要调度到的运算符相对应的类型化运算符句柄。 `findSchemaOrThrow`具有两个参数:运算符的(名称空间限定)名称和运算符的重载名称(通常只是空字符串)。 `typed`将动态类型的句柄转换为静态类型的句柄(进行运行时测试以确保您提供了正确的 C++ 类型),以便我们可以对其进行常规的 C++ 调用。 我们将其传递给`decltype(myadd)`,因为调度功能的类型与注册到调度程序的基础内核的类型相同。