从 functorch 迁移到 torch.func¶
torch.func,之前被称为 “functorch”,是 PyTorch 的类似 JAX 的可组合函数变换。
functorch 最初是 pytorch/functorch 仓库中的一个树外库。我们的目标一直是将 functorch 直接上游合并到 PyTorch 中,并将其作为核心 PyTorch 库提供。
作为上游合并的最后一步,我们决定从作为顶级包 (functorch
) 迁移到成为 PyTorch 的一部分,以反映函数变换如何直接集成到 PyTorch 核心中。从 PyTorch 2.0 开始,我们正在弃用 import functorch
,并要求用户迁移到最新的 API,我们将继续维护这些 API。import functorch
将被保留一段时间,以保持几个版本的向后兼容性。
函数变换¶
以下 API 是以下 functorch API 的直接替代品。它们完全向后兼容。
functorch API |
PyTorch API(PyTorch 2.0 版本起) |
---|---|
functorch.vmap |
|
functorch.grad |
|
functorch.vjp |
|
functorch.jvp |
|
functorch.jacrev |
|
functorch.jacfwd |
|
functorch.hessian |
|
functorch.functionalize |
此外,如果您正在使用 torch.autograd.functional API,请尝试使用 torch.func
的等效 API。在许多情况下,torch.func
函数变换更具可组合性且性能更高。
torch.autograd.functional API |
torch.func API(PyTorch 2.0 版本起) |
---|---|
NN 模块实用工具¶
我们更改了 API,以便在 NN 模块上应用函数变换,使其更好地符合 PyTorch 的设计理念。新 API 有所不同,因此请仔细阅读本节。
functorch.make_functional¶
torch.func.functional_call()
是 functorch.make_functional 和 functorch.make_functional_with_buffers 的替代品。但是,它不是直接替代品。
如果您时间紧迫,可以使用此 gist 中的辅助函数来模拟 functorch.make_functional 和 functorch.make_functional_with_buffers 的行为。我们建议直接使用 torch.func.functional_call()
,因为它是一个更明确且更灵活的 API。
具体来说,functorch.make_functional 返回一个函数式模块和参数。函数式模块接受参数和模型输入作为参数。torch.func.functional_call()
允许使用新参数和缓冲区以及输入来调用现有模块的前向传播。
这是一个如何使用 functorch 与 torch.func 计算模型参数梯度的示例
# ---------------
# using functorch
# ---------------
import torch
import functorch
inputs = torch.randn(64, 3)
targets = torch.randn(64, 3)
model = torch.nn.Linear(3, 3)
fmodel, params = functorch.make_functional(model)
def compute_loss(params, inputs, targets):
prediction = fmodel(params, inputs)
return torch.nn.functional.mse_loss(prediction, targets)
grads = functorch.grad(compute_loss)(params, inputs, targets)
# ------------------------------------
# using torch.func (as of PyTorch 2.0)
# ------------------------------------
import torch
inputs = torch.randn(64, 3)
targets = torch.randn(64, 3)
model = torch.nn.Linear(3, 3)
params = dict(model.named_parameters())
def compute_loss(params, inputs, targets):
prediction = torch.func.functional_call(model, params, (inputs,))
return torch.nn.functional.mse_loss(prediction, targets)
grads = torch.func.grad(compute_loss)(params, inputs, targets)
这是另一个如何计算模型参数 Jacobian 矩阵的示例
# ---------------
# using functorch
# ---------------
import torch
import functorch
inputs = torch.randn(64, 3)
model = torch.nn.Linear(3, 3)
fmodel, params = functorch.make_functional(model)
jacobians = functorch.jacrev(fmodel)(params, inputs)
# ------------------------------------
# using torch.func (as of PyTorch 2.0)
# ------------------------------------
import torch
from torch.func import jacrev, functional_call
inputs = torch.randn(64, 3)
model = torch.nn.Linear(3, 3)
params = dict(model.named_parameters())
# jacrev computes jacobians of argnums=0 by default.
# We set it to 1 to compute jacobians of params
jacobians = jacrev(functional_call, argnums=1)(model, params, (inputs,))
请注意,对于内存消耗而言,重要的是您应该只携带一份参数副本。model.named_parameters()
不会复制参数。如果在模型训练中就地更新模型参数,那么作为您的模型的 nn.Module
将拥有参数的单个副本,一切正常。
但是,如果您想在字典中携带参数并在异地更新它们,则会有两个参数副本:字典中的副本和模型中的副本。在这种情况下,您应该通过 model.to('meta')
将模型转换为 meta 设备,使其不持有内存。
functorch.combine_state_for_ensemble¶
请使用 torch.func.stack_module_state()
而不是 functorch.combine_state_for_ensemble torch.func.stack_module_state()
。torch.func.stack_module_state()
返回两个字典,一个包含堆叠的参数,另一个包含堆叠的缓冲区,然后可以将它们与 torch.vmap()
和 torch.func.functional_call()
一起用于集成。
例如,这是一个如何在一个非常简单的模型上进行集成的示例
import torch
num_models = 5
batch_size = 64
in_features, out_features = 3, 3
models = [torch.nn.Linear(in_features, out_features) for i in range(num_models)]
data = torch.randn(batch_size, 3)
# ---------------
# using functorch
# ---------------
import functorch
fmodel, params, buffers = functorch.combine_state_for_ensemble(models)
output = functorch.vmap(fmodel, (0, 0, None))(params, buffers, data)
assert output.shape == (num_models, batch_size, out_features)
# ------------------------------------
# using torch.func (as of PyTorch 2.0)
# ------------------------------------
import copy
# Construct a version of the model with no memory by putting the Tensors on
# the meta device.
base_model = copy.deepcopy(models[0])
base_model.to('meta')
params, buffers = torch.func.stack_module_state(models)
# It is possible to vmap directly over torch.func.functional_call,
# but wrapping it in a function makes it clearer what is going on.
def call_single_model(params, buffers, data):
return torch.func.functional_call(base_model, (params, buffers), (data,))
output = torch.vmap(call_single_model, (0, 0, None))(params, buffers, data)
assert output.shape == (num_models, batch_size, out_features)
functorch.compile¶
我们不再支持 functorch.compile(也称为 AOTAutograd)作为 PyTorch 中编译的前端;我们已将 AOTAutograd 集成到 PyTorch 的编译流程中。如果您是用户,请改用 torch.compile()
。