推理模式#
c10::InferenceMode
是一种新的 RAII guard,类似于 NoGradMode
,当您确定您的操作不会与 autograd 交互时(例如模型训练),可以使用它。与 NoGradMode
相比,在此模式下运行的代码通过禁用与 autograd 相关的工作(如视图跟踪和版本计数器递增)来获得更好的性能。然而,在 c10::InferenceMode
内创建的 tensors 在与 autograd 系统交互时也有更多限制。
可以为给定的代码块启用 InferenceMode
。在 InferenceMode
内部,所有新分配的(非视图)tensors 都被标记为推理 tensors。推理 tensors
没有版本计数器,因此如果您尝试读取它们的版本(例如,因为您保存了此 tensor 用于反向传播),将会引发错误。
在
InferenceMode
之外是不可变的。因此,如果您尝试: - 在 InferenceMode 之外修改它们的数据。 - 在 InferenceMode 之外将它们修改为requires_grad=True
,将会引发错误。要解决此问题,您可以在InferenceMode
之外进行克隆以获得普通 tensor,然后再进行修改。
一个非视图 tensor 是推理 tensor 当且仅当它是在 InferenceMode
内分配的。一个视图 tensor 是推理 tensor 当且仅当它是推理 tensor 的视图。
在 InferenceMode
块内部,我们提供以下性能保证:
与
NoGradMode
一样,即使输入具有requires_grad=True
,所有操作也不会记录grad_fn
。这适用于推理 tensors 和普通 tensors。对推理 tensors 的视图操作不会进行视图跟踪。视图推理 tensors 和非视图推理 tensors 是无法区分的。
对推理 tensors 的原地(inplace)操作保证不会进行版本递增。
有关 InferenceMode
的更多实现细节,请参阅 RFC-0011-InferenceMode。
从 AutoNonVariableTypeMode
的迁移指南#
在 PyTorch 用于推理工作负载的生产实践中,我们看到 C++ guard AutoNonVariableTypeMode
(现为 AutoDispatchBelowADInplaceOrView
)被广泛使用,它禁用了 autograd、视图跟踪和版本计数器递增。不幸的是,目前这种 guard 在推理工作负载中的普遍用法是不安全的:使用 AutoNonVariableTypeMode
可能会绕过 PyTorch 的安全检查并导致静默的错误结果,例如,当用于反向传播的 tensors 随后被修改时,PyTorch 会抛出错误,但发生在 AutoNonVariableTypeMode
内的修改会静默地绕过检查并向用户返回错误的梯度。
当前 AutoNonVariableTypeMode
用户在考虑迁移时,以下步骤可能有助于您确定最佳替代方案:
尝试仅在推理模式下运行工作负载(例如加载预训练的 JIT 模型并在 C++ 运行时中运行推理)的用户应添加
c10::InferenceMode guard
来保护所有 tensor 操作(包括模型加载)。请参阅下面的推理工作负载示例
c10::InferenceMode guard;
model.load_jit(saved_model);
auto inputs = preprocess_tensors(data);
auto out = model.forward(inputs);
auto outputs = postprocess_tensors(out);
注意,c10::InferenceMode
为 AutoNonVariableTypeMode
提供了一个即时替换方案,保留了 AutoNonVariableTypeMode
的性能特性。但它们也有一些用户需要额外注意的区别:
这两种 guard 都会影响 tensor 执行过程,跳过与推理无关的工作,但
InferenceMode
也会影响 tensor 的创建,而AutoNonVariableTypeMode
不会。换句话说,在InferenceMode
内部创建的 tensors 被标记为推理 tensors,以便在退出InferenceMode
后应用某些限制。启用/禁用
InferenceMode
状态可以嵌套,而AutoNonVariableTypeMode
只允许启用状态。
{
InferenceMode guard(true);
// InferenceMode is on
{
InferenceMode guard(false);
// InferenceMode is off
}
// InferenceMode is on
}
// InferenceMode is off
尝试实现自定义 kernel,希望在
Autograd
dispatch keys 下重新分派的用户应改用AutoDispatchBelowADInplaceOrView
。注意,AutoDispatchBelowADInplaceOrView
只是AutoNonVariableTypeMode
的新名称,因为它更好地解释了该 guard 的功能。我们正在弃用AutoNonVariableTypeMode
,它将在 1.10 版本中移除。请参阅pytorch/vision
中自定义 kernelROIAlignFunction
的示例。
class ROIAlignFunction : public torch::autograd::Function<ROIAlignFunction> {
public:
static torch::autograd::variable_list forward(
torch::autograd::AutogradContext* ctx,
const torch::autograd::Variable& input,
const torch::autograd::Variable& rois,
double spatial_scale,
int64_t pooled_height,
int64_t pooled_width,
int64_t sampling_ratio,
bool aligned) {
ctx->saved_data["spatial_scale"] = spatial_scale;
ctx->saved_data["pooled_height"] = pooled_height;
ctx->saved_data["pooled_width"] = pooled_width;
ctx->saved_data["sampling_ratio"] = sampling_ratio;
ctx->saved_data["aligned"] = aligned;
ctx->saved_data["input_shape"] = input.sizes();
ctx->save_for_backward({rois});
// Used to be at::AutoNonVariableTypeMode g;
at::AutoDispatchBelowADInplaceOrView guard;
auto result = roi_align(
input, rois, spatial_scale, pooled_height,
pooled_width, sampling_ratio, aligned);
return {result};
}
自定义原地 (inplace) 和视图 kernel 除了上述 guard 外还需要一些特殊处理,详情请参阅自定义 kernel 教程。