# torch.onnx

`torch.onnx`模块包含将模型导出为`ONNX IR`格式的功能。这些模型可以加载`ONNX`库，然后转换为在其他深度学习框架上运行的模型。 ![ONNX](2017121722000236815.jpg)

### 示例：从 PyTorch 到 Caffe2 的端到端的 AlexNet

这是一个简单的脚本，将`torchvision`中定义的预训练的`AlexNet`导出到`ONNX`中。它运行一轮推理，然后将结果跟踪模型保存到`alexnet.proto`：

```py
from torch.autograd import Variable
import torch.onnx
import torchvision

dummy_input = Variable(torch.randn(10, 3, 224, 224)).cuda()
model = torchvision.models.alexnet(pretrained=True).cuda()
torch.onnx.export(model, dummy_input, "alexnet.proto", verbose=True) 
```

保存文件`alexnet.proto`是一个二进制`protobuf`文件，其中包含您导出的模型（在本例中为`AlexNet`）的网络结构和参数。关键字参数`verbose=True`导致导出器打印出一个人类可读的网络表示：

```py
# All parameters are encoded explicitly as inputs.  By convention,
# learned parameters (ala nn.Module.state_dict) are first, and the
# actual inputs are last.
graph(%1 : Float(64, 3, 11, 11)
      %2 : Float(64)
      # The definition sites of all variables are annotated with type
      # information, specifying the type and size of tensors.
      # For example, %3 is a 192 x 64 x 5 x 5 tensor of floats.
      %3 : Float(192, 64, 5, 5)
      %4 : Float(192)
      # ---- omitted for brevity ----
      %15 : Float(1000, 4096)
      %16 : Float(1000)
      %17 : Float(10, 3, 224, 224)) { # the actual input!
  # Every statement consists of some output tensors (and their types),
  # the operator to be run (with its attributes, e.g., kernels, strides,
  # etc.), its input tensors (%17, %1)
  %19 : UNKNOWN_TYPE = Conv[kernels=[11, 11], strides=[4, 4], pads=[2, 2, 2, 2], dilations=[1, 1], group=1](%17, %1), uses = [[%20.i0]];
  # UNKNOWN_TYPE: sometimes type information is not known.  We hope to eliminate
  # all such cases in a later release.
  %20 : Float(10, 64, 55, 55) = Add[broadcast=1, axis=1](%19, %2), uses = [%21.i0];
  %21 : Float(10, 64, 55, 55) = Relu(%20), uses = [%22.i0];
  %22 : Float(10, 64, 27, 27) = MaxPool[kernels=[3, 3], pads=[0, 0, 0, 0], dilations=[1, 1], strides=[2, 2]](%21), uses = [%23.i0];
  # ...
  # Finally, a network returns some tensors
  return (%58);
} 
```

您也可以使用[onnx](https://github.com/onnx/onnx/)库来验证`protobuf`。你可以`onnx`用`conda`安装：

```py
conda install -c conda-forge onnx 
```

然后，你可以运行：

```py
import onnx

# Load the ONNX model
model = onnx.load("alexnet.proto")

# Check that the IR is well formed
onnx.checker.check_model(model)

# Print a human readable representation of the graph
onnx.helper.printable_graph(model.graph) 
```

要用`caffe2`运行导出的脚本，您将需要三件事情：

*   1、您需要安装`Caffe2`。如果您还没有，请 按照安装说明进行操作。

*   2、你需要`onnx-caffe2`，一个纯`Python`库，为`ONNX`提供一个`Caffe2`后端。`onnx-caffe2`你可以用`pip`来安装：

```py
pip install onnx-caffe2 
```

安装完成后，您可以使用`Caffe2`的后端：

```py
# ...continuing from above
import onnx_caffe2.backend as backend
import numpy as np

rep = backend.prepare(model, device="CUDA:0") # or "CPU"
# For the Caffe2 backend:
#     rep.predict_net is the Caffe2 protobuf for the network
#     rep.workspace is the Caffe2 workspace for the network
#       (see the class onnx_caffe2.backend.Workspace)
outputs = rep.run(np.random.randn(10, 3, 224, 224).astype(np.float32))
# To run networks with more than one input, pass a tuple
# rather than a single numpy ndarray.
print(outputs[0]) 
```

未来，其他框架也会有后端。

### 限制与不足

*   `ONNX`出口是一个基于跟踪的出口，这意味着它通过执行一次模型来运行，并导出在此运行期间实际运行的运营商。这意味着如果您的模型是动态的，例如，根据输入数据改变行为，则导出将不准确。类似地，跟踪可能仅对特定输入大小有效（这是我们为什么需要明确输入跟踪的一个原因）。我们建议检查模型跟踪并确保跟踪的运算符看起来合理。
*   `PyTorch`和`Caffe2`通常具有一些数字差异的操作符的实现。根据模型结构的不同，这些差异可能是微不足道的，但是它们也会造成行为上的重大分歧（特别是在未经训练的模型上）。在未来的版本中，我们计划让`Caffe2`直接调用操作员的`Torch`实现，以帮助您平滑在精度很重要的时候处理这些差异，并记录这些差异。

### 支持的运算函数

支持以下运算符： `add (nonzero alpha not supported)`,`sub (nonzero alpha not supported)`,`mul`,`div`,`cat`,`mm`,`addmm`,`neg`,`tanh`,`sigmoid`,`mean`,`t`,`expand (only when used before a broadcasting ONNX operator; e.g. add)`,`transpose`,`view`,`split`,`squeeze`,`prelu (single weight shared among input channels not supported)`,`threshold (non-zero threshold/non-zero value not supported)`,`leaky_relu`,`glu`,`softmax`,`avg_pool2d (ceil_mode not supported)`,`log_softmax`,`unfold (experimental support with ATen-Caffe2 integration)`,`elu`,`Conv`,`BatchNorm`,`MaxPool1d (ceil_mode not supported)`,`MaxPool2d (ceil_mode not supported)`,`MaxPool3d (ceil_mode not supported)`,`Embedding (no optional arguments supported)`,`RNN`,`ConstantPadNd`,`Dropout`,`FeatureDropout (training mode not supported)`,`Index (constant integer and tuple indices supported)`,`Negate`

以上设置的操作足以导出以下型号： `AlexNet`,`DCGAN`,`DenseNet`,`Inception (warning: this model is highly sensitive to changes in operator implementation)`,`ResNet`,`SuperResolution`,`VGG`,`word_language_model`

用于指定运算符定义的界面是高度实验性的，并且没有记录; 有冒险精神的用户应该注意，这些`API`可能会在未来的界面中发生变化。

### torch.onnx 功能

```py
torch.onnx.export(model, args, f, export_params=True, verbose=False, training=False) 
```

将模型导出为`ONNX`格式。这个导出器运行你的模型一次，以获得其导出的执行轨迹; 目前，它不支持动态模型（例如，`RNN`）。

另见：`onnx-export`

#### 参数：

*   模型（`torch.nn.Module`） - 要导出的模型。
*   args（参数元组） - 模型的输入，例如，这-model(*args)是模型的有效调用。任何非变量参数将被硬编码到导出的模型中; 任何变量参数都将成为输出模型的输入，按照它们在参数中出现的顺序。如果 args 是一个变量，这相当于用该变量的一个元组来调用它。（注意：将关键字参数传递给模型目前还不支持，如果需要，给我们留言。）

*   f - 类文件对象（必须实现返回文件描述符的 fileno）或包含文件名的字符串。一个二进制 Protobuf 将被写入这个文件。

*   export_params（布尔，默认为 True） - 如果指定，所有参数将被导出。如果要导出未经训练的模型，请将其设置为 False。在这种情况下，导出的模型将首先将其所有参数作为参数，按照指定的顺序 model.state_dict().values()

*   verbose（布尔，默认为 False） - 如果指定，我们将打印出一个调试描述的导出轨迹。

*   training（布尔，默认为 False） - 在训练模式下导出模型。目前，ONNX 只是为了推导出口模型，所以你通常不需要将其设置为 True。

### 译者署名

| 用户名 | 头像 | 职能 | 签名 |
| --- | --- | --- | --- |
| [Song](https://ptorch.com) | ![](img/2018033000352689884.jpeg) | 翻译 | 人生总要追求点什么 |